2using System.Collections.Generic;
3using System.ComponentModel;
5using System.Xml.Serialization;
6using LotSizingDataModel.Core;
7using LotSizingDataModel.Core.Common;
8using LotSizingDataModel.Core.PhysicalModel;
30[XmlRoot(
"lotSizingSolution")]
31[XmlType(TypeName =
"lotSizingSolution")]
39 private string _name =
42 private string _description =
45 private string _instanceIdentifier =
48 private string _instanceFingerprint =
51 private int _planningHorizon;
56 private SolutionGenerationMetadata _generationMetadata =
59 private SolutionEvaluation _evaluation =
71 SubscribeToObject(_generationMetadata);
72 SubscribeToObject(_evaluation);
85 if (planningHorizon <= 0)
87 throw new ArgumentOutOfRangeException(
88 nameof(planningHorizon),
90 "The planning horizon must be strictly positive.");
110 string instanceIdentifier,
112 : this(planningHorizon)
142 SupplyChain supplyChain,
143 string instanceIdentifier =
"",
145 string instanceFingerprint =
"",
146 bool validateSupplyChain =
true)
153 validateSupplyChain);
172 [XmlAttribute(
"name")]
178 value?.Trim() ??
string.Empty);
185 [XmlElement(
"description")]
191 value ??
string.Empty);
202 [XmlAttribute(
"instanceIdentifier")]
205 get => _instanceIdentifier;
207 ref _instanceIdentifier,
208 value?.Trim() ??
string.Empty);
222 [XmlAttribute(
"instanceFingerprint")]
225 get => _instanceFingerprint;
227 ref _instanceFingerprint,
228 value?.Trim() ??
string.Empty);
239 [XmlAttribute(
"planningHorizon")]
242 get => _planningHorizon;
250 [XmlAttribute(
"completeness")]
253 get => _completeness;
263 [XmlElement(
"generationMetadata")]
266 get => _generationMetadata;
269 ArgumentNullException.ThrowIfNull(value);
278 UnsubscribeFromObject(
279 _generationMetadata);
282 ref _generationMetadata,
286 _generationMetadata);
294 [XmlElement(
"evaluation")]
300 ArgumentNullException.ThrowIfNull(value);
309 UnsubscribeFromObject(_evaluation);
315 SubscribeToObject(_evaluation);
322 [XmlArray(
"productionDecisions")]
323 [XmlArrayItem(
"productionDecision")]
332 [XmlArray(
"inventoryDecisions")]
333 [XmlArrayItem(
"inventoryDecision")]
342 [XmlArray(
"transportDecisions")]
343 [XmlArrayItem(
"transportDecision")]
352 [XmlArray(
"purchaseDecisions")]
353 [XmlArrayItem(
"purchaseDecision")]
362 [XmlArray(
"distributionDecisions")]
363 [XmlArrayItem(
"distributionDecision")]
373 [XmlArray(
"workCenterCapacityDecisions")]
374 [XmlArrayItem(
"workCenterCapacityDecision")]
375 public List<WorkCenterCapacityDecision>
385 [XmlArray(
"warehouseCapacityDecisions")]
386 [XmlArrayItem(
"warehouseCapacityDecision")]
387 public List<WarehouseCapacityDecision>
397 [XmlArray(
"transportResourceCapacityDecisions")]
398 [XmlArrayItem(
"transportResourceCapacityDecision")]
399 public List<TransportResourceCapacityDecision>
411 ProductionDecisions.Count +
412 WorkCenterSchedulingDecisions.Count +
413 InventoryDecisions.Count +
414 TransportDecisions.Count +
415 PurchaseDecisions.Count +
416 DistributionDecisions.Count +
417 WorkCenterCapacityDecisions.Count +
418 WarehouseCapacityDecisions.Count +
437 decision.PlanningHorizon ==
442 decision.PlanningHorizon ==
447 decision.PlanningHorizon ==
452 decision.PlanningHorizon ==
457 decision.PlanningHorizon ==
462 decision.PlanningHorizon ==
467 decision.PlanningHorizon ==
472 decision.PlanningHorizon ==
477 decision.PlanningHorizon ==
488 decision.IsInternallyValid) &&
492 decision.IsInternallyValid) &&
496 decision.IsInternallyValid) &&
500 decision.IsInternallyValid) &&
504 decision.IsInternallyValid) &&
508 decision.IsInternallyValid) &&
512 decision.IsInternallyValid) &&
516 decision.IsInternallyValid) &&
520 decision.IsInternallyValid);
534 ProductionDecisions.Count &&
537 WorkCenterSchedulingDecisions.Count &&
544 decision.Warehouse.Kind,
545 decision.Warehouse.ReferenceId
549 InventoryDecisions.Count &&
556 decision.TransportResourceId,
557 decision.Origin.Kind,
558 decision.Origin.ReferenceId,
559 decision.Destination.Kind,
560 decision.Destination.ReferenceId
564 TransportDecisions.Count &&
572 decision.DestinationWarehouse.Kind,
573 decision.DestinationWarehouse.ReferenceId
577 PurchaseDecisions.Count &&
583 decision.DistributionCenterId,
585 decision.Warehouse.Kind,
586 decision.Warehouse.ReferenceId
590 DistributionDecisions.Count &&
596 decision.WorkCenter.PlantId,
597 decision.WorkCenter.WorkCenterId
601 WorkCenterCapacityDecisions.Count &&
607 decision.Warehouse.Kind,
608 decision.Warehouse.ReferenceId
612 WarehouseCapacityDecisions.Count &&
617 decision.TransportResourceId)
652 ArgumentNullException.ThrowIfNull(decision);
661 throw new ArgumentException(
662 "The production decision is not " +
669 existing.RoutingId ==
672 throw new InvalidOperationException(
673 $
"A production decision already exists " +
674 $
"for routing {decision.RoutingId}.");
678 SubscribeToObject(decision);
680 NotifyDecisionProperties();
692 ArgumentNullException.ThrowIfNull(decision);
701 throw new ArgumentException(
702 "The inventory decision is not " +
715 throw new InvalidOperationException(
716 "An inventory decision already exists " +
717 "for this item and warehouse.");
721 SubscribeToObject(decision);
723 NotifyDecisionProperties();
735 ArgumentNullException.ThrowIfNull(decision);
744 throw new ArgumentException(
745 "The transport decision is not " +
758 throw new InvalidOperationException(
759 "A transport decision already exists " +
760 "for this item, resource and lane.");
764 SubscribeToObject(decision);
766 NotifyDecisionProperties();
778 ArgumentNullException.ThrowIfNull(decision);
787 throw new ArgumentException(
788 "The purchase decision is not " +
800 throw new InvalidOperationException(
801 "A purchase decision already exists " +
802 "for this supplier, item and warehouse.");
806 SubscribeToObject(decision);
808 NotifyDecisionProperties();
820 ArgumentNullException.ThrowIfNull(decision);
829 throw new ArgumentException(
830 "The distribution decision is not " +
842 throw new InvalidOperationException(
843 "A distribution decision already exists " +
844 "for this distribution center, item " +
849 SubscribeToObject(decision);
851 NotifyDecisionProperties();
863 ArgumentNullException.ThrowIfNull(decision);
872 throw new ArgumentException(
873 "The work-center capacity decision is not " +
883 throw new InvalidOperationException(
884 "A capacity decision already exists " +
885 "for this work center.");
889 SubscribeToObject(decision);
891 NotifyDecisionProperties();
903 ArgumentNullException.ThrowIfNull(decision);
912 throw new ArgumentException(
913 "The warehouse-capacity decision is not " +
923 throw new InvalidOperationException(
924 "A capacity decision already exists " +
925 "for this warehouse.");
929 SubscribeToObject(decision);
931 NotifyDecisionProperties();
944 ArgumentNullException.ThrowIfNull(decision);
953 throw new ArgumentException(
954 "The transport-resource capacity decision " +
955 "is not internally valid.",
964 throw new InvalidOperationException(
965 "A capacity decision already exists " +
966 "for this transport resource.");
970 SubscribeToObject(decision);
972 NotifyDecisionProperties();
990 throw new ArgumentOutOfRangeException(
993 "The period count cannot be negative.");
997 ref _planningHorizon,
1055 NotifyDecisionProperties();
1063 UnsubscribeFromObjects(
1066 UnsubscribeFromObjects(
1069 UnsubscribeFromObjects(
1072 UnsubscribeFromObjects(
1075 UnsubscribeFromObjects(
1078 UnsubscribeFromObjects(
1081 UnsubscribeFromObjects(
1084 UnsubscribeFromObjects(
1087 UnsubscribeFromObjects(
1103 NotifyDecisionProperties();
1109 string solutionName =
1110 string.IsNullOrWhiteSpace(
Name)
1115 $
"{solutionName}: " +
1116 $
"{DecisionCount} decisions; " +
1117 $
"horizon {PlanningHorizon}; " +
1123 ReconnectObject(_generationMetadata);
1124 ReconnectObject(_evaluation);
1153 NotifyDecisionProperties();
1156 private void PrepareDecision(
1157 int decisionPlanningHorizon,
1158 Action<int> resizeAction,
1159 string parameterName)
1161 ArgumentNullException.ThrowIfNull(resizeAction);
1165 if (decisionPlanningHorizon <= 0)
1167 throw new ArgumentException(
1168 "Neither the solution nor the decision " +
1169 "defines a valid planning horizon.",
1174 decisionPlanningHorizon);
1179 if (decisionPlanningHorizon == 0)
1186 if (decisionPlanningHorizon !=
1189 throw new ArgumentException(
1190 $
"The decision planning horizon " +
1191 $
"({decisionPlanningHorizon}) differs from " +
1192 $
"the solution planning horizon " +
1193 $
"({PlanningHorizon}).",
1198 private void SubscribeToObject(
1199 ModelObject modelObject)
1201 modelObject.PropertyChanged +=
1202 OnNestedPropertyChanged;
1205 private void UnsubscribeFromObject(
1206 ModelObject modelObject)
1208 modelObject.PropertyChanged -=
1209 OnNestedPropertyChanged;
1212 private void ReconnectObject(
1213 ModelObject modelObject)
1215 UnsubscribeFromObject(modelObject);
1216 SubscribeToObject(modelObject);
1219 private void ReconnectObjects<T>(
1220 IEnumerable<T> modelObjects)
1221 where T : ModelObject
1223 foreach (T modelObject
in modelObjects)
1225 ReconnectObject(modelObject);
1229 private void UnsubscribeFromObjects<T>(
1230 IEnumerable<T> modelObjects)
1231 where T : ModelObject
1233 foreach (T modelObject
in modelObjects)
1235 UnsubscribeFromObject(modelObject);
1239 private void OnNestedPropertyChanged(
1241 PropertyChangedEventArgs eventArgs)
1243 NotifyDecisionProperties();
1246 private void NotifyDecisionProperties()
1267 private static bool SameWarehouse(
1268 WarehouseReference first,
1269 WarehouseReference second)
1271 return first.Kind ==
1273 first.ReferenceId ==
Creates lot-sizing solutions whose decision structure matches a supply-chain instance.
static LotSizingSolution Create(SupplyChain supplyChain, string instanceIdentifier="", string name="", string instanceFingerprint="", bool validateSupplyChain=true)
Creates a zero-initialized solution whose decision structure matches the specified supply-chain insta...
Stores the distribution decisions associated with one distribution center, one item and one warehouse...
int PlanningHorizon
Gets the number of planning periods represented by the delivered-quantity series.
WarehouseReference Warehouse
Gets or sets the warehouse from which the item is delivered.
int ItemId
Gets or sets the identifier of the distributed item.
void ResizeTimeSeries(int periodCount)
Resizes every decision series to the specified planning horizon.
int DistributionCenterId
Gets or sets the identifier of the distribution center.
bool IsInternallyValid
Gets a value indicating whether the distribution decision is internally consistent.
Stores the inventory decisions associated with one item and one warehouse over the complete planning ...
int ItemId
Gets or sets the identifier of the stored item.
WarehouseReference Warehouse
Gets or sets the reference to the warehouse associated with the inventory decision.
void ResizeTimeSeries(int periodCount)
Resizes every decision series to the specified planning horizon.
int PlanningHorizon
Gets the number of planning periods represented by the inventory-level series.
bool IsInternallyValid
Gets a value indicating whether the inventory decision is internally consistent.
Stores the production decisions associated with one production routing over the complete planning hor...
int PlanningHorizon
Gets the number of planning periods represented by the production-quantity series.
int RoutingId
Gets or sets the identifier of the production routing associated with the decision.
bool IsInternallyValid
Gets a value indicating whether the production decision is internally consistent.
void ResizeTimeSeries(int periodCount)
Resizes every decision series to the specified planning horizon.
Stores the purchase decisions associated with one supplier, one item and one destination warehouse ov...
int PlanningHorizon
Gets the number of planning periods represented by the purchased-quantity series.
WarehouseReference DestinationWarehouse
Gets or sets the warehouse to which the purchased item is delivered.
int ItemId
Gets or sets the identifier of the purchased item.
int SupplierId
Gets or sets the identifier of the supplier associated with the purchase decision.
void ResizeTimeSeries(int periodCount)
Resizes the purchased-quantity series to the specified planning horizon.
bool IsInternallyValid
Gets a value indicating whether the purchase decision is internally consistent.
Stores the transport decisions associated with one item, one transport resource and one directed ware...
WarehouseReference Destination
Gets or sets the destination warehouse reference.
bool IsInternallyValid
Gets a value indicating whether the transport decision is internally consistent.
int TransportResourceId
Gets or sets the identifier of the transport resource used by the decision.
void ResizeTimeSeries(int periodCount)
Resizes every decision series to the specified planning horizon.
int PlanningHorizon
Gets the number of planning periods represented by the transported-quantity series.
WarehouseReference Origin
Gets or sets the origin warehouse reference.
int ItemId
Gets or sets the identifier of the transported item.
Stores the global capacity decisions associated with one transport resource over the complete plannin...
void ResizeTimeSeries(int periodCount)
Resizes every decision series to the specified planning horizon.
int PlanningHorizon
Gets the number of planning periods represented by the activation series.
bool IsInternallyValid
Gets a value indicating whether the transport-resource capacity decision is internally consistent.
int TransportResourceId
Gets or sets the identifier of the transport resource associated with the capacity decision.
Stores the global capacity decisions associated with one warehouse over the complete planning horizon...
WarehouseReference Warehouse
Gets or sets the warehouse associated with the capacity decision.
bool IsInternallyValid
Gets a value indicating whether the warehouse-capacity decision is internally consistent.
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.
Stores the global capacity decisions associated with one work center over the complete planning horiz...
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.
WorkCenterReference WorkCenter
Gets or sets the reference to the work center associated with the capacity decision.
bool IsInternallyValid
Gets a value indicating whether the work-center capacity decision is internally consistent.
Describes the independent evaluation of a lot-sizing solution candidate.
Represents one candidate solution for a lot-sizing supply-chain instance.
void AddPurchaseDecision(PurchaseDecision decision)
Adds a purchase decision to the solution.
void AddWarehouseCapacityDecision(WarehouseCapacityDecision decision)
Adds a warehouse-capacity decision to the solution.
LotSizingSolution(string name, string instanceIdentifier, int planningHorizon)
Initializes a named lot-sizing solution for an instance.
static LotSizingSolution CreateFor(SupplyChain supplyChain, string instanceIdentifier="", string name="", string instanceFingerprint="", bool validateSupplyChain=true)
Creates a zero-initialized solution whose decision structure matches the specified supply-chain insta...
List< ProductionDecision > ProductionDecisions
Gets the production decisions contained in the solution.
LotSizingSolution()
Initializes an empty lot-sizing solution.
List< WorkCenterCapacityDecision > WorkCenterCapacityDecisions
Gets the global work-center capacity decisions contained in the solution.
SolutionCompleteness Completeness
Gets or sets whether the solution contains all decisions expected for the associated instance.
bool HasInternallyValidDecisions
Gets a value indicating whether every decision object is internally valid.
override string ToString()
void ClearDecisions()
Removes every decision object from the solution.
bool HasConsistentPlanningHorizon
Gets a value indicating whether every decision uses the same planning horizon as the solution.
SolutionGenerationMetadata GenerationMetadata
Gets or sets the metadata describing how the solution was generated.
string Description
Gets or sets an optional human-readable description of the solution.
void ResizeTimeSeries(int periodCount)
Resizes every decision time series to the specified planning horizon.
int PlanningHorizon
Gets or sets the number of planning periods represented by the solution.
List< TransportDecision > TransportDecisions
Gets the transport decisions contained in the solution.
string InstanceIdentifier
Gets or sets the identifier of the supply-chain instance associated with the solution.
Guid Id
Gets or sets the unique identifier of the solution.
List< PurchaseDecision > PurchaseDecisions
Gets the purchase decisions contained in the solution.
bool HasUniqueDecisionKeys
Gets a value indicating whether every decision key is unique within its decision family.
void AddInventoryDecision(InventoryDecision decision)
Adds an inventory decision to the solution.
string InstanceFingerprint
Gets or sets an optional fingerprint of the associated supply-chain instance.
List< DistributionDecision > DistributionDecisions
Gets the distribution decisions contained in the solution.
List< WorkCenterSchedulingDecision > WorkCenterSchedulingDecisions
int DecisionCount
Gets the total number of decision objects contained in the solution.
void AddTransportDecision(TransportDecision decision)
Adds a transport decision to the solution.
void AddWorkCenterCapacityDecision(WorkCenterCapacityDecision decision)
Adds a work-center capacity decision to the solution.
void AddDistributionDecision(DistributionDecision decision)
Adds a distribution decision to the solution.
bool IsInternallyValid
Gets a value indicating whether the solution is internally consistent.
List< WarehouseCapacityDecision > WarehouseCapacityDecisions
Gets the global warehouse-capacity decisions contained in the solution.
void AddTransportResourceCapacityDecision(TransportResourceCapacityDecision decision)
Adds a transport-resource capacity decision to the solution.
SolutionEvaluation Evaluation
Gets or sets the independent evaluation of the solution.
void AddProductionDecision(ProductionDecision decision)
Adds a production decision to the solution.
LotSizingSolution(int planningHorizon)
Initializes a lot-sizing solution for a planning horizon.
bool HasDecisions
Gets a value indicating whether the solution contains at least one decision object.
List< TransportResourceCapacityDecision > TransportResourceCapacityDecisions
Gets the global transport-resource capacity decisions contained in the solution.
void ReconnectNestedNotifications()
string Name
Gets or sets the human-readable name of the solution.
List< InventoryDecision > InventoryDecisions
Gets the inventory decisions contained in the solution.
SolutionCompleteness
Indicates whether a lot-sizing solution contains all decision values expected for its associated inst...