LotSizingDataModel.Solution 2.0.1
Solution objects for production, setup, inventory and related decisions.
Loading...
Searching...
No Matches
WorkCenterCapacityDecision.cs
Go to the documentation of this file.
1using System;
2using System.ComponentModel;
3using System.Linq;
4using System.Xml.Serialization;
5using LotSizingDataModel.Core.Common;
6using LotSizingDataModel.Core.PhysicalModel;
7
9
10/// <summary>
11/// Stores the global capacity decisions associated with one
12/// work center over the complete planning horizon.
13/// </summary>
14/// <remarks>
15/// Period numbers are one-based.
16///
17/// This class stores decisions that apply to the complete
18/// work center, independently of individual items or
19/// production routings.
20///
21/// Item-specific production quantities and setup decisions
22/// are stored separately in production decisions.
23/// </remarks>
24[Serializable]
25[XmlType(TypeName = "workCenterCapacityDecision")]
26public sealed class WorkCenterCapacityDecision :
27 ModelObject,
28 IPlanningHorizonAware
29{
30 private WorkCenterReference _workCenter =
31 new();
32
33 private IntegerTimeSeries _activations =
34 new();
35
36 private DoubleTimeSeries _additionalCapacityUsed =
37 new();
38
39 /// <summary>
40 /// Initializes an empty work-center capacity decision.
41 /// </summary>
42 /// <remarks>
43 /// This constructor is required by
44 /// <see cref="XmlSerializer"/>.
45 /// </remarks>
47 {
48 SubscribeToObject(_workCenter);
49 SubscribeToObject(_activations);
50 SubscribeToObject(_additionalCapacityUsed);
51 }
52
53 /// <summary>
54 /// Initializes a capacity decision for a work center
55 /// and a planning horizon.
56 /// </summary>
57 /// <param name="workCenter">
58 /// Reference to the work center.
59 /// </param>
60 /// <param name="planningHorizon">
61 /// Strictly positive number of planning periods.
62 /// </param>
64 WorkCenterReference workCenter,
65 int planningHorizon)
66 : this()
67 {
68 ArgumentNullException.ThrowIfNull(workCenter);
69
70 if (workCenter.PlantId <= 0)
71 {
72 throw new ArgumentException(
73 "The plant identifier must be strictly positive.",
74 nameof(workCenter));
75 }
76
77 if (workCenter.WorkCenterId <= 0)
78 {
79 throw new ArgumentException(
80 "The work-center identifier must be " +
81 "strictly positive.",
82 nameof(workCenter));
83 }
84
85 if (planningHorizon <= 0)
86 {
87 throw new ArgumentOutOfRangeException(
88 nameof(planningHorizon),
89 planningHorizon,
90 "The planning horizon must be strictly positive.");
91 }
92
93 WorkCenter = workCenter;
94
95 ResizeTimeSeries(planningHorizon);
96 }
97
98 /// <summary>
99 /// Gets or sets the reference to the work center
100 /// associated with the capacity decision.
101 /// </summary>
102 [XmlElement("workCenter")]
103 public WorkCenterReference WorkCenter
104 {
105 get => _workCenter;
106 set
107 {
108 ArgumentNullException.ThrowIfNull(value);
109
110 if (ReferenceEquals(
111 _workCenter,
112 value))
113 {
114 return;
115 }
116
117 UnsubscribeFromObject(_workCenter);
118
119 SetProperty(
120 ref _workCenter,
121 value);
122
123 SubscribeToObject(_workCenter);
124
125 NotifyDerivedProperties();
126 }
127 }
128
129 /// <summary>
130 /// Gets or sets the binary global work-center
131 /// activation decisions.
132 /// </summary>
133 /// <remarks>
134 /// Each value must be zero or one.
135 ///
136 /// An activation value of one indicates that the work
137 /// center is globally available or used during the period.
138 ///
139 /// When no fixed work-center usage decision is required,
140 /// the series may remain filled with zeros.
141 /// </remarks>
142 [XmlElement("activations")]
143 public IntegerTimeSeries Activations
144 {
145 get => _activations;
146 set
147 {
148 ArgumentNullException.ThrowIfNull(value);
149
150 if (ReferenceEquals(
151 _activations,
152 value))
153 {
154 return;
155 }
156
157 UnsubscribeFromObject(_activations);
158
159 SetProperty(
160 ref _activations,
161 value);
162
163 SubscribeToObject(_activations);
164
165 NotifyDerivedProperties();
166 }
167 }
168
169 /// <summary>
170 /// Gets or sets the global additional capacity used
171 /// by the work center during each planning period.
172 /// </summary>
173 /// <remarks>
174 /// Values must be finite and non-negative.
175 ///
176 /// The maximum available additional capacity belongs
177 /// to the supply-chain instance and is not duplicated
178 /// in this solution object.
179 /// </remarks>
180 [XmlElement("additionalCapacityUsed")]
181 public DoubleTimeSeries AdditionalCapacityUsed
182 {
183 get => _additionalCapacityUsed;
184 set
185 {
186 ArgumentNullException.ThrowIfNull(value);
187
188 if (ReferenceEquals(
189 _additionalCapacityUsed,
190 value))
191 {
192 return;
193 }
194
195 UnsubscribeFromObject(
196 _additionalCapacityUsed);
197
198 SetProperty(
199 ref _additionalCapacityUsed,
200 value);
201
202 SubscribeToObject(
203 _additionalCapacityUsed);
204
205 NotifyDerivedProperties();
206 }
207 }
208
209 /// <summary>
210 /// Gets the number of planning periods represented
211 /// by the activation series.
212 /// </summary>
213 [XmlIgnore]
214 public int PlanningHorizon =>
215 Activations.PeriodCount;
216
217 /// <summary>
218 /// Gets a value indicating whether every decision series
219 /// uses the same planning horizon.
220 /// </summary>
221 [XmlIgnore]
223 AdditionalCapacityUsed.PeriodCount ==
225
226 /// <summary>
227 /// Gets a value indicating whether the work-center
228 /// reference is initialized.
229 /// </summary>
230 [XmlIgnore]
232 WorkCenter.PlantId > 0 &&
233 WorkCenter.WorkCenterId > 0;
234
235 /// <summary>
236 /// Gets a value indicating whether every activation value
237 /// is equal to zero or one.
238 /// </summary>
239 [XmlIgnore]
241 Activations.All(
242 activation =>
243 activation is 0 or 1);
244
245 /// <summary>
246 /// Gets a value indicating whether every additional-capacity
247 /// value is finite and non-negative.
248 /// </summary>
249 [XmlIgnore]
252 capacity =>
253 double.IsFinite(capacity) &&
254 capacity >= 0.0);
255
256 /// <summary>
257 /// Gets a value indicating whether the work-center
258 /// capacity decision is internally consistent.
259 /// </summary>
260 /// <remarks>
261 /// This property does not verify that the referenced
262 /// work center exists in a particular supply-chain
263 /// instance or that the used additional capacity does
264 /// not exceed the available amount.
265 /// </remarks>
266 [XmlIgnore]
267 public bool IsInternallyValid =>
268 PlanningHorizon > 0 &&
273
274 /// <summary>
275 /// Determines whether the work center is activated
276 /// during a planning period.
277 /// </summary>
278 /// <param name="period">
279 /// One-based planning period.
280 /// </param>
281 /// <returns>
282 /// True when the activation value is one;
283 /// otherwise, false.
284 /// </returns>
285 public bool IsActivated(int period)
286 {
287 return Activations[period] == 1;
288 }
289
290 /// <summary>
291 /// Sets the global work-center activation decision
292 /// for a planning period.
293 /// </summary>
294 /// <param name="period">
295 /// One-based planning period.
296 /// </param>
297 /// <param name="isActivated">
298 /// True to activate the work center;
299 /// otherwise, false.
300 /// </param>
301 public void SetActivated(
302 int period,
303 bool isActivated)
304 {
305 Activations[period] =
306 isActivated ? 1 : 0;
307 }
308
309 /// <summary>
310 /// Gets the global additional capacity used during
311 /// a planning period.
312 /// </summary>
313 /// <param name="period">
314 /// One-based planning period.
315 /// </param>
316 /// <returns>
317 /// Non-negative additional-capacity quantity.
318 /// </returns>
320 int period)
321 {
322 return AdditionalCapacityUsed[period];
323 }
324
325 /// <summary>
326 /// Sets the global additional capacity used during
327 /// a planning period.
328 /// </summary>
329 /// <param name="period">
330 /// One-based planning period.
331 /// </param>
332 /// <param name="capacity">
333 /// Finite and non-negative additional-capacity quantity.
334 /// </param>
336 int period,
337 double capacity)
338 {
339 if (!double.IsFinite(capacity) ||
340 capacity < 0.0)
341 {
342 throw new ArgumentOutOfRangeException(
343 nameof(capacity),
344 capacity,
345 "The additional capacity must be finite " +
346 "and non-negative.");
347 }
348
349 AdditionalCapacityUsed[period] =
350 capacity;
351 }
352
353 /// <summary>
354 /// Determines whether this decision refers to the
355 /// specified work center.
356 /// </summary>
357 /// <param name="workCenter">
358 /// Work-center reference to compare.
359 /// </param>
360 /// <returns>
361 /// True when the plant and work-center identifiers
362 /// match; otherwise, false.
363 /// </returns>
364 public bool Matches(
365 WorkCenterReference workCenter)
366 {
367 ArgumentNullException.ThrowIfNull(workCenter);
368
369 return WorkCenter.PlantId ==
370 workCenter.PlantId &&
371 WorkCenter.WorkCenterId ==
372 workCenter.WorkCenterId;
373 }
374
375 /// <summary>
376 /// Resizes every decision series to the specified
377 /// planning horizon.
378 /// </summary>
379 /// <param name="periodCount">
380 /// Non-negative number of planning periods.
381 /// </param>
382 /// <remarks>
383 /// Existing values are preserved whenever possible.
384 /// New periods are initialized with zero.
385 /// </remarks>
386 public void ResizeTimeSeries(int periodCount)
387 {
388 if (periodCount < 0)
389 {
390 throw new ArgumentOutOfRangeException(
391 nameof(periodCount),
392 periodCount,
393 "The period count cannot be negative.");
394 }
395
396 Activations.Resize(
397 periodCount,
398 defaultValue: 0);
399
401 periodCount,
402 defaultValue: 0.0);
403
404 NotifyDerivedProperties();
405 }
406
407 /// <summary>
408 /// Resets every work-center capacity decision value
409 /// to zero.
410 /// </summary>
411 public void Clear()
412 {
413 Activations.Fill(0);
414 AdditionalCapacityUsed.Fill(0.0);
415
416 NotifyDerivedProperties();
417 }
418
419 /// <inheritdoc/>
420 public override string ToString()
421 {
422 int activatedPeriodCount =
423 Activations.Count(
424 activation =>
425 activation == 1);
426
427 double totalAdditionalCapacity =
429
430 return
431 $"Work center {WorkCenter}: " +
432 $"activated periods {activatedPeriodCount}; " +
433 $"additional capacity " +
434 $"{totalAdditionalCapacity}";
435 }
436
437 private void SubscribeToObject(
438 ModelObject modelObject)
439 {
440 modelObject.PropertyChanged +=
441 OnNestedPropertyChanged;
442 }
443
444 private void UnsubscribeFromObject(
445 ModelObject modelObject)
446 {
447 modelObject.PropertyChanged -=
448 OnNestedPropertyChanged;
449 }
450
451 private void OnNestedPropertyChanged(
452 object? sender,
453 PropertyChangedEventArgs eventArgs)
454 {
455 NotifyDerivedProperties();
456 }
457
458 private void NotifyDerivedProperties()
459 {
460 OnPropertyChanged(
461 nameof(PlanningHorizon));
462
463 OnPropertyChanged(
465
466 OnPropertyChanged(
468
469 OnPropertyChanged(
471
472 OnPropertyChanged(
474
475 OnPropertyChanged(
476 nameof(IsInternallyValid));
477 }
478}
bool Matches(WorkCenterReference workCenter)
Determines whether this decision refers to the specified work center.
WorkCenterCapacityDecision(WorkCenterReference workCenter, int planningHorizon)
Initializes a capacity decision for a work center and a planning horizon.
void Clear()
Resets every work-center capacity decision value to zero.
int PlanningHorizon
Gets the number of planning periods represented by the activation series.
void ResizeTimeSeries(int periodCount)
Resizes every decision series to the specified planning horizon.
bool HasConsistentPlanningHorizon
Gets a value indicating whether every decision series uses the same planning horizon.
WorkCenterReference WorkCenter
Gets or sets the reference to the work center associated with the capacity decision.
void SetAdditionalCapacityUsed(int period, double capacity)
Sets the global additional capacity used during a planning period.
bool HasValidAdditionalCapacityValues
Gets a value indicating whether every additional-capacity value is finite and non-negative.
bool HasValidActivationValues
Gets a value indicating whether every activation value is equal to zero or one.
double GetAdditionalCapacityUsed(int period)
Gets the global additional capacity used during a planning period.
bool IsInternallyValid
Gets a value indicating whether the work-center capacity decision is internally consistent.
bool HasValidWorkCenterReference
Gets a value indicating whether the work-center reference is initialized.
bool IsActivated(int period)
Determines whether the work center is activated during a planning period.
void SetActivated(int period, bool isActivated)
Sets the global work-center activation decision for a planning period.
IntegerTimeSeries Activations
Gets or sets the binary global work-center activation decisions.
WorkCenterCapacityDecision()
Initializes an empty work-center capacity decision.
DoubleTimeSeries AdditionalCapacityUsed
Gets or sets the global additional capacity used by the work center during each planning period.