Quintiq file version 2.0 
 | 
#parent: #root 
 | 
Method GetUpstreamPISPIPs ( 
 | 
  Number depth, 
 | 
  ProductInStockingPointInPeriods pispips_o 
 | 
) 
 | 
{ 
 | 
  Description: 
 | 
  [* 
 | 
    Selects all upstream PISPIPs related to this pispip.  
 | 
    Please note that this will create the required PeriodTaskOperations/Trips to be able to follow the dependent demand.  
 | 
    Therefore, it is recommended to delete any unused PeriodTaskOperations/Trips after calling this method 
 | 
  *] 
 | 
  TextBody: 
 | 
  [* 
 | 
    // Martijn Oct-18-2016 (created) 
 | 
    // Martijn: I think we could remove some code duplication between this method and the SetOptimizerInputSmartPlan methods 
 | 
     
 | 
    depth := depth - 1; 
 | 
     
 | 
    if( depth > 0 ) 
 | 
    { 
 | 
      pispips_o.Add( this ); 
 | 
      period := this.Period_MP(); 
 | 
      // Traverse over the operations that produce this product as an output 
 | 
      // If this product is a byproduct, we do not want to add its operations, unless they only produce byproduct. 
 | 
      // This is to avoid adding products to the smart plan that are only related to the smart plan input pispips because they are produced together with the same byproduct 
 | 
      traverse( this, ProductInStockingPoint_MP.OperationOutput.Operation, operation, 
 | 
                operation.GetIsAvailableForOptimization()                                     // The operation must be available 
 | 
                and PeriodTaskOperation::GetIsValidPeriodTask( operation, period )  // The resulting period task must be valid 
 | 
                and ( not this.ProductInStockingPoint_MP().Product_MP().IsByProduct()       // This product is not a byproduct 
 | 
                      or forall( operation, OperationOutput.ProductInStockingPoint_MP.Product_MP, product, product.IsByProduct() ) ) )  // or all outputs are byproducts 
 | 
      { 
 | 
        // Create/Select the related periodtaskoperation 
 | 
        periodtaskoperation := PeriodTaskOperation::FindPeriodTaskOperationTypeIndex( period.Start() , operation.ID() ); 
 | 
     
 | 
        if( isnull( periodtaskoperation ) ) 
 | 
        { 
 | 
          unit := operation.Unit(); 
 | 
          unitperiod := UnitPeriod::FindUnitPeriodTypeIndex( unit.ID(), period.Start(), period.End() ); 
 | 
          periodtaskoperation := PeriodTaskOperation::Create( operation, unitperiod, 0.0, false ); 
 | 
          // I think this transaction propagate is necessary, we need to get the dependent demand from the newly created periodtask 
 | 
          Transaction::Transaction().Propagate( relation( PeriodTaskOperation, DependentDemand ) ) 
 | 
        } 
 | 
         
 | 
        traverse( periodtaskoperation, DependentDemand.ProductInStockingPointInPeriodPlanningLeaf, newpispip ) 
 | 
        { 
 | 
          if( pispips_o.Find( newpispip ) < 0 )  //PISPIP not already added to the set 
 | 
          { 
 | 
            newpispip.GetUpstreamPISPIPs( depth, pispips_o ); 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
     
 | 
     
 | 
      // Traverse over the lanelegs that supply this PISPIP 
 | 
      traverse( this, ProductInStockingPoint_MP.LaneLegOutput.LaneLeg, laneleg, laneleg.GetIsAvailableForOptimization() ) 
 | 
      {     
 | 
        unit := laneleg.Lane().Unit(); 
 | 
        unitperiod := UnitPeriod::FindUnitPeriodTypeIndex( unit.ID(), period.Start(), period.End() ); 
 | 
     
 | 
        // Select the existing trips of this laneleg on this unit period 
 | 
        // Normally there should only be a single trip, but the user may have created multiple trips for a single unit period 
 | 
        trips := selectset( laneleg, Trip, trip, trip.ArrivalUnitPeriod() = unitperiod ); 
 | 
         
 | 
        // If no such trip exists and it would be a valid nonfrozen trip, then create a new trip 
 | 
        if( trips.Elements( relsize ) = 0 
 | 
            and Trip::GetIsValidNonFrozenTrip( laneleg, period ) ) 
 | 
        { 
 | 
          trips.Add( Trip::Create( laneleg, period ) ); 
 | 
          // Without the Transaction.Propagate() the trips will not have the required relations 
 | 
          Transaction::Transaction().Propagate( relation( Trip, PeriodTaskLaneLeg ) ); 
 | 
        } 
 | 
     
 | 
        // Add the current product to the trips (if it is not yet part of these trips) 
 | 
        traverse( trips, Elements, trip, 
 | 
                  not exists( trip, ProductInTrip.Product_MP, product, 
 | 
                              product = this.ProductInStockingPoint_MP().Product_MP() ) ) 
 | 
        { 
 | 
          trip.AddProduct( this.ProductInStockingPoint_MP().Product_MP(), 0.0, false ); 
 | 
           
 | 
          // Without the Transaction.Propagate() the trips will not have the required relations 
 | 
          Transaction::Transaction().Propagate( relation( ProductInTrip, DependentDemand ) ); 
 | 
        } 
 | 
        
 | 
        // Add the trips to the optimizer run 
 | 
        traverse( trips, Elements, trip, trip.GetIsValidNonFrozenTrip() ) 
 | 
        { 
 | 
          // Add the related productintrip to productintripsforoptimization 
 | 
          traverse( trip, ProductInTrip, productintrip, 
 | 
                    productintrip.ProductID() = this.ProductInStockingPoint_MP().ProductID() ) 
 | 
          { 
 | 
            newpispip := null(ProductInStockingPointInPeriod ) 
 | 
            newpispip := guard( productintrip.DependentDemand().ProductInStockingPointInPeriodPlanningLeaf(), null(ProductInStockingPointInPeriodPlanningLeaf) ); 
 | 
            if( isnull( newpispip ) ) 
 | 
            { 
 | 
              newpispip := this; 
 | 
            } 
 | 
            if( pispips_o.Find( newpispip ) < 0 )  //PISPIP not already added to the set 
 | 
            { 
 | 
              newpispip.GetUpstreamPISPIPs( depth, pispips_o ); 
 | 
            } 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
    } 
 | 
  *] 
 | 
} 
 |