| Quintiq file version 2.0 | 
| #parent: #root | 
| Method GenerateOptCampaignSubPeriods | 
| { | 
|   Description: 'Generate campaign sub period objects for opt campaign unit' | 
|   TextBody: | 
|   [* | 
|     this.OptCampaignUnitSubPeriod( relflush ); | 
|      | 
|     startpuzzle := this.OptCampaign().PuzzleStart(); | 
|     endpuzzle := this.OptCampaign().PuzzleEnd(); | 
|     tokenizer := OptCampaign::Tokenizer() | 
|      | 
|     unit := Unit::FindUnitTypeIndex( this.UnitID() ); | 
|      | 
|     //First: create ocusp for last period of frozen horizon | 
|     lastfrozenup := maxselect( unit,  | 
|                             UnitPeriod,  | 
|                             up,  | 
|                             not up.IsFrozen() | 
|                             and up.IsPlanning()  | 
|                             and up.Start() <= endpuzzle  | 
|                             and up.End() > startpuzzle | 
|                             and Unit::GetIsPeriodFrozen( unit, up.Period_MP() ), | 
|                             up.Start() ) | 
|      | 
|     firstcampaignvalue := ""; | 
|     firsttransitionvalue := "" | 
|      | 
|     if( not isnull( lastfrozenup ) ) | 
|     { | 
|       lastfrozenperiod := lastfrozenup.Period_MP() | 
|       campaignonfrozenlimit := select( unit, Campaign_MP, c, c.Start() < lastfrozenperiod.End(), c.End() >= lastfrozenperiod.End() ) | 
|       transitiononfrozenlimit := select( unit, Transition_MP, t, t.Start() < lastfrozenperiod.End(), t.End() >= lastfrozenperiod.End() ) | 
|        | 
|       if( not isnull( campaignonfrozenlimit ) ) | 
|       { | 
|         firstcampaignvalue := campaignonfrozenlimit.Start().Format( "YM2D2 H2:m:s" )  | 
|                               + tokenizer + campaignonfrozenlimit.End().Format( "YM2D2 H2:m:s" )  | 
|                               + tokenizer + OptCampaign::TypeCampaign()  | 
|                               + tokenizer + [String]campaignonfrozenlimit.Start()  | 
|                               + tokenizer + [String]lastfrozenperiod.End()  | 
|                               + tokenizer + [String]campaignonfrozenlimit.Key() | 
|                                            | 
|       } | 
|       else if( not isnull( transitiononfrozenlimit ) ) | 
|       { | 
|         firsttransitionvalue := transitiononfrozenlimit.Start().Format( "YM2D2 H2:m:s" )  | 
|                                 + tokenizer + transitiononfrozenlimit.End().Format( "YM2D2 H2:m:s" )  | 
|                                 + tokenizer + OptCampaign::TypeTransition()  | 
|                                 + tokenizer + [String]transitiononfrozenlimit.Start()  | 
|                                 + tokenizer + [String]minvalue(  transitiononfrozenlimit.End(), unit.EndOfFrozen() ) | 
|                                 + tokenizer + [String]transitiononfrozenlimit.Key() | 
|                                            | 
|       } | 
|     } | 
|     //Then, create other ocusps | 
|      | 
|     endcampaignhorizon := unit.CampaignTypeRequirementHorizon(); | 
|     //set the endpuzzle to take campaign horizon into account. Need the - 1 days here due to difference in inclusive / exclusive | 
|     endpuzzle := minvalue( endpuzzle, endcampaignhorizon - Duration::Days( 1 ) ); | 
|      | 
|     //the timeblock values will concatenated as follows: | 
|     //start time formatted to get correct sorting | 
|     //end time formatted to get correct sorting | 
|     //type of the timeblock (period, campaign or transition | 
|     //start time formatted as string that can be converted back to datetime using standard conversion | 
|     //end time formatted as string that can be converted back to datetime using standard conversion | 
|     //Reference key for fixed campaigns and transitions | 
|     periods := selectuniquevalues(  unit | 
|                                   , UnitPeriod | 
|                                   , up | 
|                                   , not up.IsFrozen() | 
|                                     and up.IsPlanning()  | 
|                                     and up.Start() <= endpuzzle  | 
|                                     and up.End() > startpuzzle | 
|                                     and not Unit::GetIsPeriodFrozen( unit, up.Period_MP() ) | 
|                                   , up.Start().Format( "YM2D2 H2:m:s" )  | 
|                                     + tokenizer + up.End().Format( "YM2D2 H2:m:s" )  | 
|                                     + tokenizer + OptCampaign::TypePeriod() | 
|                                     + tokenizer + [String]up.Start()  | 
|                                     + tokenizer + [String]up.End()  | 
|                                     + tokenizer + [String]up.Key() | 
|                                   ) | 
|      | 
|     fixedcampaigns := selectuniquevalues( unit | 
|                                          , Campaign_MP | 
|                                          , campaign | 
|                                          , campaign.IsFrozen()     //this should be changed to campagins being fixed | 
|                                            and campaign.Start() <= endpuzzle  | 
|                                            and campaign.End() > startpuzzle   | 
|                                          , campaign.Start().Format( "YM2D2 H2:m:s" )  | 
|                                            + tokenizer + campaign.End().Format( "YM2D2 H2:m:s" )  | 
|                                            + tokenizer + OptCampaign::TypeCampaign()  | 
|                                            + tokenizer + [String]campaign.Start()  | 
|                                            + tokenizer + [String]campaign.End()  | 
|                                            + tokenizer + [String]campaign.Key() | 
|                                          ) | 
|     fixedtransitions := selectuniquevalues( unit | 
|                                            , Transition_MP | 
|                                            , transition | 
|                                            , transition.IsFrozen()     //this should be changed to campagins being fixed | 
|                                              and transition.Start() <= endpuzzle   | 
|                                              and transition.End() > startpuzzle | 
|                                            , transition.Start().Format( "YM2D2 H2:m:s" )  | 
|                                              + tokenizer + transition.End().Format( "YM2D2 H2:m:s" )  | 
|                                              + tokenizer + OptCampaign::TypeTransition()  | 
|                                              + tokenizer + [String]transition.Start()  | 
|                                              + tokenizer + [String]transition.End()  | 
|                                              + tokenizer + [String]transition.Key() | 
|                                            ) | 
|      | 
|     if( not firstcampaignvalue = "" ) | 
|     { | 
|       fixedcampaigns := fixedcampaigns.Union( firstcampaignvalue ) | 
|     } | 
|     if( not firsttransitionvalue = "" ) | 
|     { | 
|       fixedtransitions := fixedtransitions.Union( firsttransitionvalue ) | 
|     } | 
|      | 
|     timeblocks := periods.Union( fixedcampaigns ).Union( fixedtransitions ); | 
|     timeblocks := timeblocks.Sort(); | 
|     //init variables | 
|     if(  timeblocks.Size() > 0 ) | 
|     { | 
|       //Get the values from the first element. This will prevent having to  | 
|       timeblockelements := timeblocks.Element( 0 ).Tokenize( tokenizer ); | 
|       subperiodstart := [DateTime]timeblockelements.Element( 3 ); //get the start that can be converted | 
|       subperiodend := [DateTime]timeblockelements.Element( 4 ); //get the end that can be converted | 
|       subperiodtype := timeblockelements.Element( 2 ); | 
|       subperiodrefkey := [Key]timeblockelements.Element( 5 ); | 
|       subperiodisfixed := subperiodtype <> "Period";  | 
|        | 
|       subperiodend := minvalue( subperiodend, endcampaignhorizon ); | 
|        | 
|       traverse( timeblocks, Elements, timeblock ) | 
|       { | 
|         timeblockelements := timeblock.Tokenize( tokenizer ); | 
|         timeblockstart := [DateTime]timeblockelements.Element( 3 );  | 
|         timeblockend := [DateTime]timeblockelements.Element( 4 );  | 
|         timeblocktype := timeblockelements.Element( 2 ); | 
|         timeblockrefkey := [Key]timeblockelements.Element( 5 ) | 
|         timeblockisfixed := timeblocktype <> "Period"; | 
|           | 
|         //If the next period starts earlier than the previous ends and its a fixed period  | 
|         //then keep creating fixed sub periods until the end of the fixed campaign/transition is reached | 
|         if( timeblockstart < subperiodend and subperiodisfixed ) | 
|         { | 
|           endsubperiodfixed := timeblockstart | 
|           //Close off the previous subperiod for the fixed block, but only if the subperiod has a duration | 
|           if( endsubperiodfixed > subperiodstart ) | 
|           { | 
|             OptCampaignUnitSubPeriod::Create( this, subperiodstart, endsubperiodfixed, subperiodtype, subperiodrefkey, subperiodisfixed );   | 
|           } | 
|           //Set the start time to the end of subperiod that was just created | 
|           subperiodstart := endsubperiodfixed; | 
|           //if the fixed period ends in this subperiod create an additional block for the end part of the fixed block  | 
|           if( subperiodend < timeblockend ) | 
|           { | 
|             OptCampaignUnitSubPeriod::Create( this, subperiodstart, subperiodend, subperiodtype, subperiodrefkey, subperiodisfixed );  | 
|             //now set the variables for the next subperiod | 
|             subperiodstart := subperiodend | 
|             subperiodend := timeblockend; | 
|             subperiodtype := timeblocktype; | 
|             subperiodrefkey := timeblockrefkey; | 
|             subperiodisfixed := timeblockisfixed; | 
|           } | 
|         } | 
|         else | 
|         { | 
|           //when we have a fixed supperiod then the end date is known, in other cases the enddate the previous period is the start of the next period | 
|           if( not subperiodisfixed ) | 
|           { | 
|             subperiodend := timeblockstart; | 
|           } | 
|           //Close off the previous timeblock, but only if the subperiod has a duration | 
|           if( subperiodend > subperiodstart ) | 
|           { | 
|             OptCampaignUnitSubPeriod::Create( this, subperiodstart, subperiodend, subperiodtype, subperiodrefkey, subperiodisfixed );   | 
|           } | 
|           //Setup the next period | 
|           //the start is the start of the period or the end of the previous period which could be later if it was a fixed period | 
|           subperiodstart := timeblockstart | 
|           subperiodend := minvalue( timeblockend, endcampaignhorizon ); | 
|           subperiodtype := timeblocktype; | 
|           subperiodrefkey := timeblockrefkey; | 
|           subperiodisfixed := timeblockisfixed; | 
|         } | 
|       } | 
|       //Close the last timeblock | 
|       if( subperiodend > subperiodstart )  | 
|       { | 
|         //Close off the previous timeblock | 
|         OptCampaignUnitSubPeriod::Create( this, subperiodstart, subperiodend, subperiodtype, subperiodrefkey, subperiodisfixed );   | 
|       } | 
|     } | 
|   *] | 
| } |