LotSizingDataModel.Core 2.0.1
Core domain model, shared abstractions and XML-serializable entities.
Loading...
Searching...
No Matches
SupplyChainModelBuilder.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
9
11
12/// <summary>
13/// Provides a fluent and controlled way to construct
14/// a complete supply-chain model.
15///
16/// The builder:
17/// - preserves the global planning horizon;
18/// - checks references before adding relationships;
19/// - prevents the use of unknown entities;
20/// - delegates duplicate detection to SupplyChain;
21/// - validates the complete model before returning it.
22/// </summary>
23public sealed class SupplyChainModelBuilder
24{
25 private readonly SupplyChainValidator _validator;
26 private readonly SupplyChainIndex _index;
27
28 /// <summary>
29 /// Initializes an empty supply-chain builder.
30 /// </summary>
31 /// <param name="planningHorizon">
32 /// Strictly positive number of planning periods.
33 /// </param>
34 public SupplyChainModelBuilder(int planningHorizon)
35 : this(
36 new SupplyChain(planningHorizon),
38 {
39 }
40
41 /// <summary>
42 /// Initializes a builder for an existing supply chain.
43 /// </summary>
44 /// <param name="supplyChain">
45 /// Supply chain to extend.
46 /// </param>
48 SupplyChain supplyChain)
49 : this(
50 supplyChain,
52 {
53 }
54
55 /// <summary>
56 /// Initializes a builder with a supply chain
57 /// and a custom validator.
58 /// </summary>
60 SupplyChain supplyChain,
61 SupplyChainValidator validator)
62 {
63 SupplyChain = supplyChain ??
64 throw new ArgumentNullException(
65 nameof(supplyChain));
66
67 _validator = validator ??
68 throw new ArgumentNullException(
69 nameof(validator));
70
71 if (SupplyChain.PlanningHorizon <= 0)
72 {
73 throw new ArgumentOutOfRangeException(
74 nameof(supplyChain),
75 SupplyChain.PlanningHorizon,
76 "The planning horizon must be strictly positive.");
77 }
78
79 _index = new SupplyChainIndex(SupplyChain);
80 }
81
82 /// <summary>
83 /// Gets the supply chain currently being constructed.
84 /// </summary>
85 public SupplyChain SupplyChain { get; }
86
87 /// <summary>
88 /// Gets the current global planning horizon.
89 /// </summary>
90 public int PlanningHorizon =>
91 SupplyChain.PlanningHorizon;
92
93 /// <summary>
94 /// Changes the global planning horizon.
95 ///
96 /// Every active time series is automatically resized.
97 /// </summary>
99 int planningHorizon)
100 {
101 if (planningHorizon <= 0)
102 {
103 throw new ArgumentOutOfRangeException(
104 nameof(planningHorizon),
105 planningHorizon,
106 "The planning horizon must be strictly positive.");
107 }
108
110 planningHorizon;
111
112 return this;
113 }
114
115 #region Logical subsystem
116
117 /// <summary>
118 /// Adds an item to the logical subsystem.
119 /// </summary>
121 Item item)
122 {
123 ArgumentNullException.ThrowIfNull(item);
124
125 SupplyChain.AddItem(item);
126 RebuildIndex();
127
128 return this;
129 }
130
131 /// <summary>
132 /// Creates and adds an item.
133 /// </summary>
135 int id,
136 string name,
137 int billOfMaterialsLevel = 0)
138 {
139 return AddItem(
140 new Item(
141 id,
142 name,
143 billOfMaterialsLevel));
144 }
145
146 /// <summary>
147 /// Adds a bill-of-material component requirement.
148 ///
149 /// Both items must already exist.
150 /// </summary>
153 ComponentRequirement requirement)
154 {
155 ArgumentNullException.ThrowIfNull(requirement);
156
157 _index.GetRequiredItem(
158 requirement.ParentItemId);
159
160 _index.GetRequiredItem(
161 requirement.ComponentItemId);
162
163 if (requirement.ParentItemId ==
164 requirement.ComponentItemId)
165 {
166 throw new InvalidOperationException(
167 "An item cannot be its own component.");
168 }
169
170 if (requirement.Quantity <= 0.0)
171 {
172 throw new ArgumentOutOfRangeException(
173 nameof(requirement),
174 requirement.Quantity,
175 "A component quantity must be strictly positive.");
176 }
177
178 SupplyChain.AddComponentRequirement(
179 requirement);
180
181 return this;
182 }
183
184 /// <summary>
185 /// Creates and adds a bill-of-material requirement.
186 /// </summary>
188 int parentItemId,
189 int componentItemId,
190 int quantity)
191 {
194 {
195 ParentItemId = parentItemId,
196 ComponentItemId = componentItemId,
197 Quantity = quantity
198 });
199 }
200
201 #endregion
202
203 #region Physical subsystem
204
205 /// <summary>
206 /// Adds a plant.
207 ///
208 /// Work centers may already be contained in the plant
209 /// or may be added later through AddWorkCenter.
210 /// </summary>
212 Plant plant)
213 {
214 ArgumentNullException.ThrowIfNull(plant);
215
216 SupplyChain.AddPlant(plant);
217 RebuildIndex();
218
219 return this;
220 }
221
222 /// <summary>
223 /// Creates and adds an empty plant.
224 /// </summary>
226 int id,
227 string name)
228 {
229 if (string.IsNullOrWhiteSpace(name))
230 {
231 throw new ArgumentException(
232 "The plant name cannot be empty.",
233 nameof(name));
234 }
235
236 return AddPlant(
237 new Plant
238 {
239 Id = id,
240 Name = name
241 });
242 }
243
244 /// <summary>
245 /// Adds a work center to an existing plant.
246 /// </summary>
248 int plantId,
249 WorkCenter workCenter)
250 {
251 ArgumentNullException.ThrowIfNull(workCenter);
252
253 Plant plant =
254 _index.GetRequiredPlant(plantId);
255
256 plant.AddWorkCenter(workCenter);
257
258 workCenter.ResizeTimeSeries(
260
261 RebuildIndex();
262
263 return this;
264 }
265
266 /// <summary>
267 /// Creates and adds a work center to an existing plant.
268 /// </summary>
270 int plantId,
271 int workCenterId,
272 string name)
273 {
274 return AddWorkCenter(
275 plantId,
276 new WorkCenter(
277 workCenterId,
278 name));
279 }
280
281 /// <summary>
282 /// Adds a standalone warehouse.
283 /// </summary>
286 StandaloneWarehouse warehouse)
287 {
288 ArgumentNullException.ThrowIfNull(warehouse);
289
290 SupplyChain.AddStandaloneWarehouse(
291 warehouse);
292
293 RebuildIndex();
294
295 return this;
296 }
297
298 /// <summary>
299 /// Creates and adds a standalone warehouse.
300 /// </summary>
303 int id,
304 string name)
305 {
307 new StandaloneWarehouse(id, name));
308 }
309
310 /// <summary>
311 /// Adds a supplier.
312 /// </summary>
314 Supplier supplier)
315 {
316 ArgumentNullException.ThrowIfNull(supplier);
317
318 SupplyChain.AddSupplier(supplier);
319 RebuildIndex();
320
321 return this;
322 }
323
324 /// <summary>
325 /// Creates and adds a supplier.
326 /// </summary>
328 int id,
329 string name)
330 {
331 return AddSupplier(
332 new Supplier(id, name));
333 }
334
335 /// <summary>
336 /// Adds a distribution center.
337 /// </summary>
340 DistributionCenter distributionCenter)
341 {
342 ArgumentNullException.ThrowIfNull(
343 distributionCenter);
344
345 SupplyChain.AddDistributionCenter(
346 distributionCenter);
347
348 RebuildIndex();
349
350 return this;
351 }
352
353 /// <summary>
354 /// Creates and adds a distribution center.
355 /// </summary>
358 int id,
359 string name)
360 {
362 new DistributionCenter(id, name));
363 }
364
365 /// <summary>
366 /// Adds a transport resource.
367 ///
368 /// Resources may be added before any lane assignment.
369 /// </summary>
372 TransportResource transportResource)
373 {
374 ArgumentNullException.ThrowIfNull(
375 transportResource);
376
377SupplyChain.AddTransportResource(
378 transportResource);
379
380 RebuildIndex();
381
382 return this;
383 }
384
385 /// <summary>
386 /// Creates and adds an empty transport resource.
387 /// </summary>
390 int id,
391 string name)
392 {
394 new TransportResource(id, name));
395 }
396
397 /// <summary>
398 /// Adds a central directed lane.
399 ///
400 /// Both warehouses must already exist.
401 /// </summary>
403 {
404 ValidateTransportLane(lane);
405 SupplyChain.AddTransportLane(lane);
406 RebuildIndex();
407 return this;
408 }
409
410 public SupplyChainModelBuilder AddTransportAssignment(int laneId, int resourceId, int leadTime)
411 {
412 SupplyChain.AddTransportAssignment(new TransportAssignment(laneId, resourceId, leadTime));
413 RebuildIndex();
414 return this;
415 }
416
417 #endregion
418
419 #region Production relationships
420
421 /// <summary>
422 /// Adds a production routing.
423 ///
424 /// The item, plant and every referenced work center
425 /// must already exist.
426 /// </summary>
429 ProductionRouting routing)
430 {
431 ArgumentNullException.ThrowIfNull(routing);
432
433 _index.GetRequiredItem(routing.ItemId);
434
435 _index.GetRequiredPlant(routing.PlantId);
436
437 foreach (WorkCenterReference reference
438 in routing.WorkCenters)
439 {
440 if (reference.PlantId != routing.PlantId)
441 {
442 throw new InvalidOperationException(
443 "Every work center referenced by a routing " +
444 "must belong to the routing plant.");
445 }
446
447 _index.GetRequiredWorkCenter(reference);
448 }
449
450 SupplyChain.AddProductionRouting(routing);
451
452 return this;
453 }
454
455 /// <summary>
456 /// Adds an item-work-center production characteristic.
457 ///
458 /// The item and work center must already exist.
459 /// </summary>
462 ProductionCharacteristic characteristic)
463 {
464 ArgumentNullException.ThrowIfNull(
465 characteristic);
466
467 _index.GetRequiredItem(
468 characteristic.ItemId);
469
470 if (characteristic.WorkCenter is null)
471 {
472 throw new InvalidOperationException(
473 "A production characteristic must reference " +
474 "a work center.");
475 }
476
477 _index.GetRequiredWorkCenter(
478 characteristic.WorkCenter);
479
480 SupplyChain.AddProductionCharacteristic(
481 characteristic);
482
483 return this;
484 }
485
486 #endregion
487
488 #region Inventory relationships
489
490 /// <summary>
491 /// Adds an item-warehouse inventory.
492 ///
493 /// The item and warehouse must already exist.
494 /// </summary>
496 Inventory inventory)
497 {
498 ArgumentNullException.ThrowIfNull(inventory);
499
500 _index.GetRequiredItem(inventory.ItemId);
501
502 ValidateWarehouseReference(
503 inventory.Warehouse);
504
505 SupplyChain.AddInventory(inventory);
506
507 return this;
508 }
509
510 #endregion
511
512 #region Transport relationships
513
514 /// <summary>
515 /// Adds an item-transport-resource characteristic.
516 ///
517 /// The item and transport resource must already exist.
518 /// </summary>
521 TransportCharacteristic characteristic)
522 {
523 ArgumentNullException.ThrowIfNull(
524 characteristic);
525
526 _index.GetRequiredItem(
527 characteristic.ItemId);
528
529 _index.GetRequiredTransportResource(
530 characteristic.TransportResourceId);
531
532 SupplyChain.AddTransportCharacteristic(
533 characteristic);
534
535 return this;
536 }
537
538 #endregion
539
540 #region Demand and distribution relationships
541
542 /// <summary>
543 /// Adds a demand.
544 ///
545 /// The item and distribution center must already exist.
546 /// </summary>
548 Demand demand)
549 {
550 ArgumentNullException.ThrowIfNull(demand);
551
552 _index.GetRequiredItem(demand.ItemId);
553
554 _index.GetRequiredDistributionCenter(
555 demand.DistributionCenterId);
556
557 SupplyChain.AddDemand(demand);
558
559 return this;
560 }
561
562 /// <summary>
563 /// Adds a distribution-center sourcing relationship.
564 ///
565 /// The item, distribution center, warehouse and corresponding
566 /// inventory must already exist.
567 /// </summary>
571 {
572 ArgumentNullException.ThrowIfNull(sourcing);
573
574 _index.GetRequiredItem(sourcing.ItemId);
575
576 _index.GetRequiredDistributionCenter(
577 sourcing.DistributionCenterId);
578
579 ValidateWarehouseReference(
580 sourcing.Warehouse);
581
582 EnsureInventoryExists(
583 sourcing.ItemId,
584 sourcing.Warehouse);
585
586 SupplyChain.AddDistributionCenterSourcing(
587 sourcing);
588
589 return this;
590 }
591
592 #endregion
593
594 #region Supplier relationships
595
596 /// <summary>
597 /// Adds a supplier-delivery relationship.
598 ///
599 /// The supplier, item, warehouse and corresponding inventory
600 /// must already exist.
601 /// </summary>
604 SupplierDelivery delivery)
605 {
606 ArgumentNullException.ThrowIfNull(delivery);
607
608 _index.GetRequiredSupplier(
609 delivery.SupplierId);
610
611 _index.GetRequiredItem(
612 delivery.ItemId);
613
614 ValidateWarehouseReference(
615 delivery.Warehouse);
616
617 EnsureInventoryExists(
618 delivery.ItemId,
619 delivery.Warehouse);
620
621 SupplyChain.AddSupplierDelivery(
622 delivery);
623
624 return this;
625 }
626
627 #endregion
628
629 #region Validation and build
630
631 /// <summary>
632 /// Validates the current state of the model.
633 /// </summary>
634 public IReadOnlyList<
636 {
637 return _validator.Validate(SupplyChain);
638 }
639
640 /// <summary>
641 /// Gets a value indicating whether the current model
642 /// contains no validation error.
643 /// </summary>
644 public bool IsValid()
645 {
646 return _validator.IsValid(SupplyChain);
647 }
648
649 /// <summary>
650 /// Synchronizes all time series and returns the model.
651 ///
652 /// The model is validated by default.
653 /// </summary>
654 /// <param name="validate">
655 /// Indicates whether validation must be performed.
656 /// </param>
657 public SupplyChain Build(bool validate = true)
658 {
659 SupplyChain.SynchronizePlanningHorizon();
660
661 RebuildIndex();
662
663 if (validate)
664 {
665 _validator.ThrowIfInvalid(SupplyChain);
666 }
667
668 return SupplyChain;
669 }
670
671 /// <summary>
672 /// Synchronizes, validates and returns the model.
673 /// </summary>
675 {
676 return Build(validate: true);
677 }
678
679 #endregion
680
681 #region Private helpers
682
683 private void RebuildIndex()
684 {
685 _index.Rebuild();
686 }
687
688 private void ValidateTransportLane(
689 TransportLane lane)
690 {
691 ArgumentNullException.ThrowIfNull(lane);
692
693 ValidateWarehouseReference(lane.Origin);
694 ValidateWarehouseReference(lane.Destination);
695
696 if (SameWarehouse(
697 lane.Origin,
698 lane.Destination))
699 {
700 throw new InvalidOperationException(
701 "A transport lane must connect two " +
702 "different warehouses.");
703 }
704
705 }
706
707 private void ValidateWarehouseReference(
708 WarehouseReference? reference)
709 {
710 if (reference is null)
711 {
712 throw new InvalidOperationException(
713 "A warehouse reference is required.");
714 }
715
716 _index.GetRequiredWarehouse(reference);
717 }
718
719 private void EnsureInventoryExists(
720 int itemId,
721 WarehouseReference warehouse)
722 {
723 bool inventoryExists =
724 SupplyChain.Inventories.Any(
725 inventory =>
726 inventory.ItemId == itemId &&
727 SameWarehouse(
728 inventory.Warehouse,
729 warehouse));
730
731 if (!inventoryExists)
732 {
733 throw new InvalidOperationException(
734 $"No inventory exists for item {itemId} " +
735 $"and warehouse {FormatWarehouse(warehouse)}.");
736 }
737 }
738
739 private static bool SameWarehouse(
740 WarehouseReference? first,
741 WarehouseReference? second)
742 {
743 if (first is null || second is null)
744 {
745 return first is null &&
746 second is null;
747 }
748
749 return first.Kind == second.Kind &&
750 first.ReferenceId == second.ReferenceId;
751 }
752
753 private static string FormatWarehouse(
754 WarehouseReference warehouse)
755 {
756 return
757 $"{warehouse.Kind}:{warehouse.ReferenceId}";
758 }
759
760 #endregion
761}
Provides a fluent and controlled way to construct a complete supply-chain model.
SupplyChainModelBuilder AddComponentRequirement(ComponentRequirement requirement)
Adds a bill-of-material component requirement.
SupplyChainModelBuilder AddDistributionCenterSourcing(DistributionCenterSourcing sourcing)
Adds a distribution-center sourcing relationship.
SupplyChainModelBuilder(SupplyChain supplyChain)
Initializes a builder for an existing supply chain.
SupplyChainModelBuilder AddPlant(int id, string name)
Creates and adds an empty plant.
SupplyChainModelBuilder SetPlanningHorizon(int planningHorizon)
Changes the global planning horizon.
SupplyChainModelBuilder AddTransportLane(TransportLane lane)
Adds a central directed lane.
SupplyChainModelBuilder AddTransportCharacteristic(TransportCharacteristic characteristic)
Adds an item-transport-resource characteristic.
SupplyChainModelBuilder AddProductionRouting(ProductionRouting routing)
Adds a production routing.
int PlanningHorizon
Gets the current global planning horizon.
SupplyChain BuildValidated()
Synchronizes, validates and returns the model.
SupplyChainModelBuilder AddProductionCharacteristic(ProductionCharacteristic characteristic)
Adds an item-work-center production characteristic.
SupplyChainModelBuilder(int planningHorizon)
Initializes an empty supply-chain builder.
SupplyChainModelBuilder AddDistributionCenter(int id, string name)
Creates and adds a distribution center.
SupplyChainModelBuilder AddSupplier(Supplier supplier)
Adds a supplier.
SupplyChainModelBuilder AddItem(Item item)
Adds an item to the logical subsystem.
SupplyChainModelBuilder AddSupplierDelivery(SupplierDelivery delivery)
Adds a supplier-delivery relationship.
SupplyChainModelBuilder AddInventory(Inventory inventory)
Adds an item-warehouse inventory.
SupplyChainModelBuilder AddPlant(Plant plant)
Adds a plant.
SupplyChainModelBuilder AddStandaloneWarehouse(StandaloneWarehouse warehouse)
Adds a standalone warehouse.
SupplyChainModelBuilder AddDistributionCenter(DistributionCenter distributionCenter)
Adds a distribution center.
SupplyChainModelBuilder AddSupplier(int id, string name)
Creates and adds a supplier.
SupplyChainModelBuilder(SupplyChain supplyChain, SupplyChainValidator validator)
Initializes a builder with a supply chain and a custom validator.
SupplyChain SupplyChain
Gets the supply chain currently being constructed.
SupplyChainModelBuilder AddItem(int id, string name, int billOfMaterialsLevel=0)
Creates and adds an item.
IReadOnlyList< SupplyChainValidator.ValidationIssue > Validate()
Validates the current state of the model.
SupplyChainModelBuilder AddComponentRequirement(int parentItemId, int componentItemId, int quantity)
Creates and adds a bill-of-material requirement.
SupplyChainModelBuilder AddTransportAssignment(int laneId, int resourceId, int leadTime)
SupplyChainModelBuilder AddWorkCenter(int plantId, int workCenterId, string name)
Creates and adds a work center to an existing plant.
SupplyChainModelBuilder AddDemand(Demand demand)
Adds a demand.
SupplyChainModelBuilder AddStandaloneWarehouse(int id, string name)
Creates and adds a standalone warehouse.
SupplyChainModelBuilder AddTransportResource(TransportResource transportResource)
Adds a transport resource.
bool IsValid()
Gets a value indicating whether the current model contains no validation error.
SupplyChainModelBuilder AddTransportResource(int id, string name)
Creates and adds an empty transport resource.
SupplyChainModelBuilder AddWorkCenter(int plantId, WorkCenter workCenter)
Adds a work center to an existing plant.
SupplyChain Build(bool validate=true)
Synchronizes all time series and returns the model.
Provides fast access to the entities contained in a supply chain.
Represents a bill-of-materials relationship between two items.
int ParentItemId
Gets or sets the identifier of the item being manufactured.
int ComponentItemId
Gets or sets the identifier of the required component.
int Quantity
Gets or sets the quantity of the component required to manufacture one unit of the parent item.
Represents a finished or semi-finished item handled by the supply chain.
Definition Item.cs:15
Represents a distribution center in the supply chain.
Represents a production plant.
Definition Plant.cs:19
void AddWorkCenter(WorkCenter workCenter)
Adds a work center to the plant.
Definition Plant.cs:118
Represents a standalone warehouse that is not attached to a plant.
Represents a supplier in the supply chain.
Definition Supplier.cs:16
An authorized lane-resource pair, with its delay in periods.
Represents a transport lane between two warehouses.
WarehouseReference Destination
Gets or sets the destination warehouse.
WarehouseReference Origin
Gets or sets the origin warehouse.
A transport resource with its own shared capacities and costs.
Represents a serializable reference to a warehouse.
int ReferenceId
Gets or sets the referenced identifier.
Represents a production work center belonging to a plant.
Definition WorkCenter.cs:15
void ResizeTimeSeries(int periodCount)
Resizes every active period-dependent parameter.
Represents a serializable reference to a work center.
int PlantId
Gets or sets the identifier of the plant owning the referenced work center.
Represents the demand for an item issued by a distribution center.
Definition Demand.cs:22
int ItemId
Gets or sets the identifier of the requested item.
Definition Demand.cs:70
int DistributionCenterId
Gets or sets the identifier of the distribution center issuing the demand.
Definition Demand.cs:96
Represents the ability of a distribution center to source a given item from a given warehouse invento...
int DistributionCenterId
Gets or sets the identifier of the distribution center that can obtain the item from the referenced i...
WarehouseReference Warehouse
Gets or sets the warehouse holding the inventory from which the distribution center can obtain the it...
int ItemId
Gets or sets the identifier of the item contained in the referenced inventory.
Represents the inventory of an item in a warehouse.
Definition Inventory.cs:20
int ItemId
Gets or sets the identifier of the item stored in the warehouse.
Definition Inventory.cs:115
WarehouseReference Warehouse
Gets or sets the warehouse storing the item.
Definition Inventory.cs:141
Represents the production characteristics of an item on a specific work center.
WorkCenterReference WorkCenter
Gets or sets the referenced work center.
int ItemId
Gets or sets the identifier of the item manufactured on the referenced work center.
Represents a production routing used to manufacture an item in a plant through one or more work cente...
int ItemId
Gets or sets the identifier of the item manufactured through this routing.
int PlantId
Gets or sets the identifier of the plant in which this routing is available.
Represents the ability of a supplier to deliver a given item to a given warehouse inventory.
int SupplierId
Gets or sets the identifier of the supplier.
int ItemId
Gets or sets the identifier of the delivered item.
WarehouseReference Warehouse
Gets or sets the warehouse receiving the item.
Represents the transport characteristics of an item for a specific transport resource.
int ItemId
Gets or sets the identifier of the item transported by the referenced transport resource.
int TransportResourceId
Gets or sets the identifier of the transport resource associated with the item.
int PlanningHorizon
Gets or sets the number of periods in the global planning horizon.
Validates the structural, referential and decision-model consistency of a complete supply chain.
@ StandaloneWarehouse
The reference identifies a standalone warehouse through its own warehouse identifier e.