lazhen
2025-01-09 8afe90b633046db39042aada36b88193062f8cff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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 );  
      }
    }
  *]
}