LotSizingDataModel.Checker 2.0.1
Reusable validation engine for structural, mathematical and objective consistency checks.
Loading...
Searching...
No Matches
MacroMicroSchedulingStructuralValidator.cs
Go to the documentation of this file.
3using LotSizingDataModel.Core.DecisionModel.Scheduling;
4using LotSizingDataModel.Core.PhysicalModel;
5using LotSizingDataModel.Core.Relationships;
6using LotSizingDataModel.Instance;
7using LotSizingDataModel.Solution;
8using LotSizingDataModel.Solution.Decisions;
9
11
13{
14 public IReadOnlyList<SolutionCheckIssue> Validate(
15 LotSizingInstance instance,
16 LotSizingSolution solution)
17 {
18 ArgumentNullException.ThrowIfNull(instance);
19 ArgumentNullException.ThrowIfNull(solution);
20 var issues = new List<SolutionCheckIssue>();
21
22 var expected = instance.SupplyChain.Plants
23 .SelectMany(plant => plant.WorkCenters
24 .Where(workCenter => workCenter.SchedulingProfile?.BucketMode == SchedulingBucketMode.MacroMicro)
25 .Select(workCenter => (PlantId: plant.Id, WorkCenter: workCenter)))
26 .ToArray();
27
28 foreach (var entry in expected)
29 {
30 WorkCenterSchedulingDecision? schedule =
31 solution.WorkCenterSchedulingDecisions.SingleOrDefault(candidate =>
32 candidate.WorkCenter.PlantId == entry.PlantId &&
33 candidate.WorkCenter.WorkCenterId == entry.WorkCenter.Id);
34
35 if (schedule is null)
36 {
37 issues.Add(Error(
38 $"DECSCHED005: Missing macro/micro schedule for Plant:{entry.PlantId}/WorkCenter:{entry.WorkCenter.Id}."));
39 continue;
40 }
41
42 ProductionSchedulingProfile profile = entry.WorkCenter.SchedulingProfile!;
43 foreach (ProductionMicroPeriodReference microPeriod in profile.EnumerateMicroPeriods())
44 {
45 if (!schedule.MicroPeriods.Any(decision =>
46 decision.MicroPeriod.RefersToSameMicroPeriod(microPeriod)))
47 {
48 issues.Add(Error(
49 $"DECSCHED006: Missing decision for micro-period {microPeriod} on {schedule.WorkCenter}."));
50 }
51 }
52 }
53
54 foreach (WorkCenterSchedulingDecision schedule in solution.WorkCenterSchedulingDecisions)
55 {
56 WorkCenter? workCenter = instance.SupplyChain.Plants
57 .Where(plant => plant.Id == schedule.WorkCenter.PlantId)
58 .SelectMany(plant => plant.WorkCenters)
59 .SingleOrDefault(candidate => candidate.Id == schedule.WorkCenter.WorkCenterId);
60
61 if (workCenter is null)
62 {
63 issues.Add(Error($"REFSCHED001: Unknown scheduling work center {schedule.WorkCenter}."));
64 continue;
65 }
66
67 ProductionSchedulingProfile? profile = workCenter.SchedulingProfile;
68 if (profile is null ||
69 profile.BucketMode != SchedulingBucketMode.MacroMicro ||
70 profile.MicroPeriodCount is null)
71 {
72 issues.Add(Error(
73 $"DECSCHED001: Work center {schedule.WorkCenter} does not expose an explicit macro/micro scheduling grid."));
74 continue;
75 }
76
77 foreach (ProductionMicroPeriodDecision decision in schedule.MicroPeriods)
78 {
79 int macro = decision.MicroPeriod.MacroPeriod;
80 int micro = decision.MicroPeriod.MicroPeriodIndex;
81
82 if (macro <= 0 || macro > instance.PlanningHorizon ||
83 micro <= 0 || micro > profile.MicroPeriodCount.GetCount(macro))
84 {
85 issues.Add(Error(
86 $"DECSCHED002: Micro-period {decision.MicroPeriod} is outside the instance scheduling grid."));
87 continue;
88 }
89
90 if (decision.SetupItemId <= 0 ||
91 !instance.SupplyChain.Items.Any(item => item.Id == decision.SetupItemId))
92 {
93 issues.Add(Error(
94 $"REFSCHED002: Unknown or missing setup item {decision.SetupItemId} at {decision.MicroPeriod}."));
95 }
96
97 if (decision.RoutingId <= 0)
98 {
99 if (decision.Quantity > 0.0)
100 {
101 issues.Add(Error(
102 $"DECSCHED007: Positive production at {decision.MicroPeriod} has no routing."));
103 }
104 continue;
105 }
106
107 ProductionRouting? routing = instance.SupplyChain.ProductionRoutings
108 .SingleOrDefault(candidate => candidate.Id == decision.RoutingId);
109
110 if (routing is null)
111 {
112 issues.Add(Error(
113 $"REFSCHED003: Unknown production routing {decision.RoutingId} at {decision.MicroPeriod}."));
114 continue;
115 }
116
117 if (routing.ItemId != decision.SetupItemId)
118 {
119 issues.Add(Error(
120 $"DECSCHED003: Routing {routing.Id} produces item {routing.ItemId}, not setup item {decision.SetupItemId}."));
121 }
122
123 if (!routing.WorkCenters.Any(reference =>
124 reference.RefersToSameWorkCenter(schedule.WorkCenter)))
125 {
126 issues.Add(Error(
127 $"DECSCHED004: Routing {routing.Id} does not use work center {schedule.WorkCenter}."));
128 }
129 }
130 }
131
132 return issues;
133 }
134
135 private static SolutionCheckIssue Error(string message) => new()
136 {
137 Severity = SolutionCheckSeverity.Error,
138 Kind = SolutionCheckIssueKind.Structural,
139 Message = message
140 };
141}
Describes one issue detected by a lot-sizing solution checker.
IReadOnlyList< SolutionCheckIssue > Validate(LotSizingInstance instance, LotSizingSolution solution)
SolutionCheckSeverity
Defines the severity of a solution-check issue.
SolutionCheckIssueKind
Identifies the category of an issue detected while checking a solution.