2using System.Collections.Generic;
32 throw new ArgumentNullException(
33 nameof(supplyChain))))
44 throw new ArgumentNullException(nameof(index));
76 #region Production load
89 IEnumerable<ItemResourceActivity> activities)
91 ArgumentNullException.ThrowIfNull(
94 ValidatePeriod(period);
97 Index.GetRequiredWorkCenter(
100 IReadOnlyList<NormalizedActivity>
101 normalizedActivities =
102 NormalizeActivities(activities);
105 new List<ItemLoadResult>(
106 normalizedActivities.Count);
108 foreach (NormalizedActivity activity
109 in normalizedActivities)
112 Queries.GetRequiredProductionCharacteristic(
114 workCenterReference);
116 double unitCapacity =
117 characteristic.UnitCapacityConsumption
118 ?.GetConsumption(period) ??
121 double setupCapacity =
122 activity.SetupActivated
123 ? characteristic.SetupTime
124 ?.GetSetupTime(period) ??
133 itemSpecificRegularCapacity:
null,
134 itemSpecificAdditionalCapacity:
null));
140 $
"{workCenterReference.PlantId}:" +
141 $
"{workCenterReference.WorkCenterId}",
142 resourceName: workCenter.Name,
145 globalRegularCapacity:
146 workCenter.CapacityConstraint
147 ?.GetMaximumCapacity(period),
148 globalAdditionalCapacity:
149 workCenter.AdditionalCapacity
150 ?.GetMaximumAdditionalCapacity(period));
155 #region Warehouse load
167 IEnumerable<ItemResourceActivity> activities)
169 ArgumentNullException.ThrowIfNull(
172 ValidatePeriod(period);
175 Index.GetRequiredWarehouse(
178 IReadOnlyList<NormalizedActivity>
179 normalizedActivities =
180 NormalizeActivities(activities);
183 new List<ItemLoadResult>(
184 normalizedActivities.Count);
186 foreach (NormalizedActivity activity
187 in normalizedActivities)
194 double unitCapacity =
195 inventory.UnitCapacityConsumption
196 ?.GetConsumption(period) ??
199 double setupCapacity =
200 activity.SetupActivated
201 ? inventory.SetupTime
202 ?.GetSetupTime(period) ??
211 itemSpecificRegularCapacity:
213 ?.GetMaximumCapacity(period),
214 itemSpecificAdditionalCapacity:
216 ?.GetMaximumAdditionalCapacity(
223 $
"{warehouseReference.Kind}:" +
224 $
"{warehouseReference.ReferenceId}",
225 resourceName: warehouse.Name,
228 globalRegularCapacity:
229 warehouse.CapacityConstraint
230 ?.GetMaximumCapacity(period),
231 globalAdditionalCapacity:
232 warehouse.AdditionalCapacity
233 ?.GetMaximumAdditionalCapacity(period));
238 #region Transport load
249 int transportResourceId,
251 IEnumerable<ItemResourceActivity> activities)
253 ValidatePeriod(period);
256 Index.GetRequiredTransportResource(
257 transportResourceId);
259 IReadOnlyList<NormalizedActivity>
260 normalizedActivities =
261 NormalizeActivities(activities);
264 new List<ItemLoadResult>(
265 normalizedActivities.Count);
267 foreach (NormalizedActivity activity
268 in normalizedActivities)
271 Queries.GetRequiredTransportCharacteristic(
273 transportResourceId);
275 double unitCapacity =
276 characteristic.UnitCapacityConsumption
277 ?.GetConsumption(period) ??
280 double setupCapacity =
281 activity.SetupActivated
282 ? characteristic.SetupTime
283 ?.GetSetupTime(period) ??
292 itemSpecificRegularCapacity:
294 ?.GetMaximumCapacity(period),
295 itemSpecificAdditionalCapacity:
297 ?.GetMaximumAdditionalCapacity(
303 resourceKey: transportResourceId.ToString(),
304 resourceName: resource.Name,
307 globalRegularCapacity:
308 resource.CapacityConstraint
309 ?.GetMaximumCapacity(period),
310 globalAdditionalCapacity:
311 resource.AdditionalCapacity
312 ?.GetMaximumAdditionalCapacity(period));
317 #region Private calculations
319 private ResourceLoadReport CreateReport(
324 IEnumerable<ItemLoadResult> itemLoads,
325 double? globalRegularCapacity,
326 double? globalAdditionalCapacity)
328 ValidateOptionalCapacityConfiguration(
329 globalRegularCapacity,
330 globalAdditionalCapacity,
331 "global resource capacity");
333 return new ResourceLoadReport(
339 globalRegularCapacity,
340 globalAdditionalCapacity);
343 private static ItemLoadResult CreateItemLoad(
344 NormalizedActivity activity,
346 double setupCapacity,
347 double? itemSpecificRegularCapacity,
348 double? itemSpecificAdditionalCapacity)
350 ValidateNonNegativeFiniteValue(
352 nameof(unitCapacity));
354 ValidateNonNegativeFiniteValue(
356 nameof(setupCapacity));
358 ValidateOptionalCapacityConfiguration(
359 itemSpecificRegularCapacity,
360 itemSpecificAdditionalCapacity,
361 "item-specific capacity");
363 double quantityCapacity =
364 activity.Quantity * unitCapacity;
366 if (!
double.IsFinite(quantityCapacity))
368 throw new InvalidOperationException(
369 "The calculated unit-capacity requirement " +
370 "exceeds the supported numerical range.");
373 double requiredCapacity =
374 quantityCapacity + setupCapacity;
376 if (!
double.IsFinite(requiredCapacity))
378 throw new InvalidOperationException(
379 "The calculated resource load exceeds the " +
380 "supported numerical range.");
383 return new ItemLoadResult(
386 activity.SetupActivated,
390 itemSpecificRegularCapacity,
391 itemSpecificAdditionalCapacity);
394 private IReadOnlyList<NormalizedActivity>
396 IEnumerable<ItemResourceActivity> activities)
398 ArgumentNullException.ThrowIfNull(activities);
401 new Dictionary<int, NormalizedActivity>();
406 if (activity is
null)
408 throw new ArgumentException(
409 "The activity collection cannot contain null.",
413 Index.GetRequiredItem(activity.ItemId);
415 ValidateNonNegativeFiniteValue(
417 nameof(activity.Quantity));
419 if (!normalized.TryGetValue(
421 out NormalizedActivity? existing))
425 new NormalizedActivity(
428 activity.SetupActivated));
433 double totalQuantity =
437 if (!
double.IsFinite(totalQuantity))
439 throw new InvalidOperationException(
440 $
"The aggregated quantity for item " +
441 $
"{activity.ItemId} exceeds the supported " +
445 normalized[activity.ItemId] =
446 new NormalizedActivity(
449 existing.SetupActivated ||
450 activity.SetupActivated);
453 return normalized.Values
454 .OrderBy(activity => activity.ItemId)
458 private void ValidatePeriod(
int period)
463 throw new ArgumentOutOfRangeException(
466 $
"The period must be between 1 and " +
467 $
"{SupplyChain.PlanningHorizon}.");
472 ValidateOptionalCapacityConfiguration(
473 double? regularCapacity,
474 double? additionalCapacity,
477 if (regularCapacity.HasValue)
479 ValidateNonNegativeFiniteValue(
480 regularCapacity.Value,
481 nameof(regularCapacity));
484 if (additionalCapacity.HasValue)
486 ValidateNonNegativeFiniteValue(
487 additionalCapacity.Value,
488 nameof(additionalCapacity));
490 if (!regularCapacity.HasValue)
492 throw new InvalidOperationException(
493 $
"The {capacityName} defines additional " +
494 "capacity without regular capacity.");
499 private static void ValidateNonNegativeFiniteValue(
501 string parameterName)
503 if (!
double.IsFinite(value) ||
506 throw new ArgumentOutOfRangeException(
509 "The value must be finite and non-negative.");
563 throw new ArgumentOutOfRangeException(
566 "The item identifier must be strictly positive.");
569 ValidateNonNegativeFiniteValue(
598 private sealed class NormalizedActivity
600 public NormalizedActivity(
607 SetupActivated = setupActivated;
610 public int ItemId {
get; }
612 public double Quantity {
get; }
614 public bool SetupActivated {
get; }
657 double setupCapacity,
658 double requiredCapacity,
659 double? maximumRegularCapacity,
660 double? maximumAdditionalCapacity)
662 ValidateNonNegativeFiniteValue(
666 ValidateNonNegativeFiniteValue(
668 nameof(unitCapacity));
670 ValidateNonNegativeFiniteValue(
672 nameof(setupCapacity));
674 ValidateNonNegativeFiniteValue(
676 nameof(requiredCapacity));
678 ValidateOptionalCapacityConfiguration(
679 maximumRegularCapacity,
680 maximumAdditionalCapacity,
681 "item-specific capacity");
691 maximumRegularCapacity;
694 maximumAdditionalCapacity;
750 MaximumRegularCapacity.HasValue
751 ? MaximumRegularCapacity.Value +
760 MaximumRegularCapacity.HasValue
772 !MaximumRegularCapacity.HasValue ||
781 !MaximumTotalCapacity.HasValue ||
790 MaximumTotalCapacity.HasValue
803 $
"Item {ItemId}: required " +
804 $
"{RequiredCapacity}; no item-specific limit";
808 $
"Item {ItemId}: required {RequiredCapacity}; " +
809 $
"maximum {MaximumTotalCapacity}; " +
810 $
"feasible {FitsTotalCapacity}";
863 IEnumerable<ItemLoadResult> itemLoads,
864 double? maximumRegularCapacity,
865 double? maximumAdditionalCapacity)
867 ArgumentException.ThrowIfNullOrWhiteSpace(
870 ArgumentNullException.ThrowIfNull(itemLoads);
874 throw new ArgumentOutOfRangeException(
877 "The period must be strictly positive.");
880 ValidateOptionalCapacityConfiguration(
881 maximumRegularCapacity,
882 maximumAdditionalCapacity,
883 "global resource capacity");
890 _itemLoads = itemLoads
891 .OrderBy(load => load.ItemId)
895 maximumRegularCapacity;
898 maximumAdditionalCapacity;
901 CalculateTotalRequiredCapacity(
929 Array.AsReadOnly(_itemLoads);
962 MaximumRegularCapacity.HasValue
963 ? MaximumRegularCapacity.Value +
972 MaximumRegularCapacity.HasValue
984 !MaximumRegularCapacity.HasValue ||
993 !MaximumTotalCapacity.HasValue ||
1001 MaximumTotalCapacity.HasValue
1014 load => load.FitsTotalCapacity);
1020 public IReadOnlyList<ItemLoadResult>
1025 !load.FitsTotalCapacity)
1045 string capacityDescription =
1047 ? $
"maximum {MaximumTotalCapacity}"
1048 :
"no global capacity limit";
1051 $
"{ResourceKind} resource {ResourceKey}, " +
1052 $
"period {Period}: required " +
1053 $
"{TotalRequiredCapacity}, " +
1054 $
"{capacityDescription}, " +
1055 $
"feasible {IsFeasible}";
1058 private static double
1059 CalculateTotalRequiredCapacity(
1060 IEnumerable<ItemLoadResult> loads)
1068 if (!
double.IsFinite(total))
1070 throw new InvalidOperationException(
1071 "The aggregated resource load exceeds " +
1072 "the supported numerical range.");
Represents the load generated by one item on a resource.
double? MaximumRegularCapacity
Gets the item-specific regular-capacity limit.
override string ToString()
ItemLoadResult(int itemId, double quantity, bool setupActivated, double unitCapacity, double setupCapacity, double requiredCapacity, double? maximumRegularCapacity, double? maximumAdditionalCapacity)
Initializes the capacity-load result of one item on a resource during one planning period.
double Quantity
Gets the item quantity.
bool FitsTotalCapacity
Gets a value indicating whether the item load fits its regular plus additional item-specific capacity...
double? MaximumTotalCapacity
Gets the item-specific total maximum capacity.
double UnitCapacity
Gets the capacity consumed per item unit.
double RequiredCapacity
Gets the total capacity required by the item.
bool SetupActivated
Gets a value indicating whether setup is activated.
bool FitsRegularCapacity
Gets a value indicating whether the item load fits its regular item-specific capacity.
bool HasItemSpecificCapacity
Gets a value indicating whether an item-specific capacity constraint exists.
int ItemId
Gets the item identifier.
double SetupCapacity
Gets the setup capacity included in the load.
double RequiredAdditionalCapacity
Gets the additional capacity required beyond the regular item-specific capacity.
double? MaximumAdditionalCapacity
Gets the item-specific additional-capacity limit.
double CapacityExcess
Gets the amount exceeding the item-specific maximum capacity.
Represents the activity of one item on a resource during one planning period.
double Quantity
Gets the processed, stored or transported quantity.
ItemResourceActivity(int itemId, double quantity, bool setupActivated)
Initializes an item-resource activity.
int ItemId
Gets the item identifier.
bool SetupActivated
Gets a value indicating whether setup is activated.
Represents the aggregated load of one resource during one planning period.
override string ToString()
Returns a textual summary of the resource load, its available capacity and its feasibility.
bool HasGlobalCapacityConstraint
Gets a value indicating whether the resource has a global capacity constraint.
ResourceLoadReport(ResourceLoadKind resourceKind, string resourceKey, string resourceName, int period, IEnumerable< ItemLoadResult > itemLoads, double? maximumRegularCapacity, double? maximumAdditionalCapacity)
Initializes a report describing the aggregated load of a resource during one planning period.
int Period
Gets the analyzed planning period.
double RequiredAdditionalCapacity
Gets the additional capacity required beyond the global regular capacity.
double CapacityExcess
Gets the amount exceeding the global maximum capacity.
int ItemCount
Gets the number of loaded item types.
bool AllItemSpecificCapacitiesSatisfied
Gets a value indicating whether all item-specific capacity constraints are respected.
double TotalRequiredCapacity
Gets the aggregated required capacity.
IReadOnlyList< ItemLoadResult > ItemLoads
Gets the individual item loads.
bool FitsTotalCapacity
Gets a value indicating whether the aggregated load fits the regular plus additional global capacity.
bool IsFeasible
Gets a value indicating whether both the global capacity and all item-specific capacities are respect...
string ResourceKey
Gets the technical resource key.
string ResourceName
Gets the resource name.
IReadOnlyList< ItemLoadResult > ItemSpecificCapacityViolations
Gets the item loads exceeding an item-specific capacity limit.
double? MaximumRegularCapacity
Gets the global regular-capacity limit.
bool FitsRegularCapacity
Gets a value indicating whether the aggregated load fits the regular global capacity.
double? MaximumTotalCapacity
Gets the global total maximum capacity.
double? MaximumAdditionalCapacity
Gets the global additional-capacity limit.
ResourceLoadKind ResourceKind
Gets the resource type.
ResourceLoadReport AnalyzeWorkCenterLoad(WorkCenterReference workCenterReference, int period, IEnumerable< ItemResourceActivity > activities)
Aggregates the production loads generated by several items on the same work center during one period.
SupplyChainQueries Queries
Gets the query service used to resolve relationships.
SupplyChainIndex Index
Gets the entity index used by the analyzer.
void RebuildIndex()
Rebuilds the underlying entity index.
ResourceLoadAnalyzer(SupplyChain supplyChain)
Initializes a resource-load analyzer and creates a new supply-chain index.
ResourceLoadAnalyzer(SupplyChainIndex index)
Initializes a resource-load analyzer using an existing supply-chain index.
SupplyChain SupplyChain
Gets the analyzed supply chain.
ResourceLoadReport AnalyzeTransportResourceLoad(int transportResourceId, int period, IEnumerable< ItemResourceActivity > activities)
Aggregates the transport loads generated by several items using the same transport resource during on...
ResourceLoadReport AnalyzeWarehouseLoad(WarehouseReference warehouseReference, int period, IEnumerable< ItemResourceActivity > activities)
Aggregates the storage loads generated by several items in the same warehouse during one period.
ResourceLoadKind
Identifies the type of resource represented by a load report.
@ Production
Production resource such as a work center.
@ Storage
Storage resource such as a warehouse.
@ Transport
Transport resource.
Provides fast access to the entities contained in a supply chain.
SupplyChain SupplyChain
Gets the indexed supply chain.
A transport resource with its own shared capacities and costs.
Represents a warehouse in the supply chain.
Represents a serializable reference to a warehouse.
Represents a production work center belonging to a plant.
Represents a serializable reference to a work center.
Provides high-level read-only queries over a supply-chain model.
Represents the inventory of an item in a warehouse.
AdditionalCapacity? AdditionalCapacity
Gets or sets the maximum additional storage capacity available specifically for this item.
CapacityConstraint? CapacityConstraint
Gets or sets the maximum storage capacity reserved for this item during each planning period.
Represents the production characteristics of an item on a specific work center.
Represents the transport characteristics of an item for a specific transport resource.
AdditionalCapacity? AdditionalCapacity
Gets or sets the maximum additional transport capacity available specifically for this item.
CapacityConstraint? CapacityConstraint
Gets or sets the maximum transport capacity available specifically for this item during each planning...