@seedtactics/insight-client 16.5.2 → 16.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/assets/ProgramHighlight-DPTeZ8Si.js +3 -0
  2. package/dist/assets/index-bPAFn3jp.js +364 -0
  3. package/dist/cell-status/basket-cycles.d.ts +21 -0
  4. package/dist/cell-status/basket-cycles.js +80 -0
  5. package/dist/cell-status/current-status.js +11 -3
  6. package/dist/cell-status/estimated-cycle-times.js +8 -4
  7. package/dist/cell-status/inspections.js +2 -2
  8. package/dist/cell-status/loading.js +4 -0
  9. package/dist/cell-status/material-details.d.ts +12 -4
  10. package/dist/cell-status/material-details.js +24 -13
  11. package/dist/cell-status/rebookings.js +15 -17
  12. package/dist/cell-status/scheduled-jobs.d.ts +1 -1
  13. package/dist/cell-status/scheduled-jobs.js +10 -3
  14. package/dist/cell-status/sim-production.js +3 -3
  15. package/dist/cell-status/sim-station-use.d.ts +1 -0
  16. package/dist/cell-status/sim-station-use.js +14 -8
  17. package/dist/cell-status/station-cycles.d.ts +29 -2
  18. package/dist/cell-status/station-cycles.js +81 -11
  19. package/dist/cell-status/tool-replacements.js +1 -1
  20. package/dist/cell-status/tool-usage.js +1 -1
  21. package/dist/components/App.js +101 -66
  22. package/dist/components/BarcodeScanning.js +12 -2
  23. package/dist/components/ErrorsAndLoading.js +10 -1
  24. package/dist/components/LogEntry.d.ts +0 -1
  25. package/dist/components/LogEntry.js +50 -26
  26. package/dist/components/Navigation.js +30 -4
  27. package/dist/components/analysis/AnalysisSelectToolbar.js +5 -1
  28. package/dist/components/analysis/BasketCycleCards.d.ts +1 -0
  29. package/dist/components/analysis/BasketCycleCards.js +145 -0
  30. package/dist/components/analysis/BufferChart.js +10 -4
  31. package/dist/components/analysis/CostPerPiece.js +28 -8
  32. package/dist/components/analysis/CycleChart.js +1 -1
  33. package/dist/components/analysis/DataTable.js +6 -4
  34. package/dist/components/analysis/HeatChart.js +27 -14
  35. package/dist/components/analysis/InspectionSankey.js +17 -6
  36. package/dist/components/analysis/PalletCycleCards.js +15 -4
  37. package/dist/components/analysis/PartCycleCards.js +62 -18
  38. package/dist/components/analysis/StationDataTable.js +14 -11
  39. package/dist/components/analysis/ToolReplacements.js +16 -10
  40. package/dist/components/log-entry-queue-filter.d.ts +2 -0
  41. package/dist/components/log-entry-queue-filter.js +21 -0
  42. package/dist/components/operations/AllMaterial.js +26 -10
  43. package/dist/components/operations/ChartRangeEdit.js +82 -4
  44. package/dist/components/operations/CloseoutReport.js +13 -4
  45. package/dist/components/operations/CompletedParts.js +23 -11
  46. package/dist/components/operations/CurrentWorkorders.js +31 -9
  47. package/dist/components/operations/OEEChart.js +8 -2
  48. package/dist/components/operations/Outliers.js +18 -7
  49. package/dist/components/operations/ProgramHighlight.js +4 -6
  50. package/dist/components/operations/Programs.js +9 -3
  51. package/dist/components/operations/Rebookings.js +8 -4
  52. package/dist/components/operations/RecentCycleChart.js +5 -3
  53. package/dist/components/operations/RecentProduction.js +30 -13
  54. package/dist/components/operations/RecentSchedules.js +6 -2
  55. package/dist/components/operations/RecentStationCycles.js +38 -11
  56. package/dist/components/operations/ShiftSettings.js +3 -3
  57. package/dist/components/operations/SimDayUsage.js +27 -4
  58. package/dist/components/operations/ToolReport.js +5 -1
  59. package/dist/components/operations/WorkorderGantt.js +22 -2
  60. package/dist/components/quality/QualityMaterial.js +11 -8
  61. package/dist/components/quality/RecentFailedInspections.js +3 -1
  62. package/dist/components/routes.d.ts +3 -0
  63. package/dist/components/routes.js +2 -0
  64. package/dist/components/station-monitor/BulkRawMaterial.js +11 -7
  65. package/dist/components/station-monitor/Closeout.js +14 -2
  66. package/dist/components/station-monitor/CustomStationMonitorDialog.js +1 -1
  67. package/dist/components/station-monitor/Inspection.js +23 -11
  68. package/dist/components/station-monitor/InvalidateCycle.js +3 -3
  69. package/dist/components/station-monitor/JobDetails.js +15 -2
  70. package/dist/components/station-monitor/LoadStation.js +274 -31
  71. package/dist/components/station-monitor/Material.js +71 -11
  72. package/dist/components/station-monitor/MoveMaterialArrows.js +4 -4
  73. package/dist/components/station-monitor/QuarantineButton.js +11 -0
  74. package/dist/components/station-monitor/Queues.js +28 -9
  75. package/dist/components/station-monitor/QueuesAddMaterial.js +8 -6
  76. package/dist/components/station-monitor/SelectInspType.js +1 -1
  77. package/dist/components/station-monitor/SelectWorkorder.js +1 -1
  78. package/dist/components/station-monitor/StationToolbar.js +17 -5
  79. package/dist/components/station-monitor/SystemOverview.d.ts +19 -1
  80. package/dist/components/station-monitor/SystemOverview.js +439 -20
  81. package/dist/components/station-monitor/Whiteboard.js +15 -7
  82. package/dist/data/all-material-bins.d.ts +7 -0
  83. package/dist/data/all-material-bins.js +47 -1
  84. package/dist/data/cost-per-piece.js +11 -8
  85. package/dist/data/current-cycles.d.ts +1 -1
  86. package/dist/data/current-cycles.js +62 -17
  87. package/dist/data/move-arrows.d.ts +5 -1
  88. package/dist/data/move-arrows.js +54 -4
  89. package/dist/data/part-summary.js +13 -13
  90. package/dist/data/path-lookup.js +6 -23
  91. package/dist/data/queue-material.d.ts +1 -1
  92. package/dist/data/queue-material.js +18 -17
  93. package/dist/data/results.completed-parts.js +4 -3
  94. package/dist/data/results.cycles.d.ts +15 -6
  95. package/dist/data/results.cycles.js +51 -30
  96. package/dist/data/results.inspection.js +11 -6
  97. package/dist/data/results.oee.js +8 -8
  98. package/dist/data/results.schedules.js +2 -11
  99. package/dist/data/tools-programs.js +5 -6
  100. package/dist/index.html +1 -1
  101. package/dist/network/api.d.ts +22 -4
  102. package/dist/network/api.js +40 -5
  103. package/dist/network/backend-mock.js +15 -8
  104. package/dist/network/backend.js +3 -3
  105. package/dist/network/websocket.js +15 -15
  106. package/dist/util/chart-helpers.d.ts +1 -1
  107. package/dist/util/chart-helpers.js +14 -8
  108. package/package.json +29 -31
  109. package/dist/assets/ProgramHighlight-LvRM40Qr.js +0 -3
  110. package/dist/assets/index-gAFi3Oss.js +0 -364
@@ -31,27 +31,35 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
31
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32
32
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
33
  */
34
- import { memo, useState } from "react";
34
+ import { memo, useMemo, useState } from "react";
35
35
  import { closestCenter, DndContext, DragOverlay, KeyboardSensor, PointerSensor, useSensor, useSensors, } from "@dnd-kit/core";
36
36
  import { rectSortingStrategy, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, } from "@dnd-kit/sortable";
37
37
  import { useAddExistingMaterialToQueue } from "../../cell-status/material-details.js";
38
38
  import { reorderQueuedMatInCurrentStatus } from "../../cell-status/current-status.js";
39
39
  import { currentOperator } from "../../data/operators.js";
40
40
  import { useAtomValue, useSetAtom } from "jotai";
41
+ function isSortableMatData(value) {
42
+ return typeof value === "object" && value !== null && "mat" in value;
43
+ }
44
+ function numericId(id) {
45
+ return typeof id === "number" ? id : null;
46
+ }
41
47
  export const SortableRegion = memo(function SortableRegion(props) {
42
48
  const [activeMat, setActiveMat] = useState(undefined);
43
49
  const [addExistingMatToQueue] = useAddExistingMaterialToQueue();
44
50
  const reorderQueuedMat = useSetAtom(reorderQueuedMatInCurrentStatus);
45
51
  const operator = useAtomValue(currentOperator);
52
+ const sortableItemIds = useMemo(() => props.matIds.slice(), [props.matIds]);
46
53
  const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
47
54
  coordinateGetter: sortableKeyboardCoordinates,
48
55
  }));
49
- return (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragStart: ({ active }) => setActiveMat(active.data.current.mat), onDragCancel: () => setActiveMat(undefined), onDragEnd: ({ active, over }) => {
50
- if (over && active.id !== over.id) {
51
- const activeMatId = active.id;
52
- const overIdx = props.matIds.indexOf(over.id);
56
+ return (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragStart: ({ active }) => setActiveMat(isSortableMatData(active.data.current) ? active.data.current.mat : undefined), onDragCancel: () => setActiveMat(undefined), onDragEnd: ({ active, over }) => {
57
+ const activeMatId = numericId(active.id);
58
+ const overMatId = over ? numericId(over.id) : null;
59
+ if (over && active.id !== over.id && activeMatId !== null && overMatId !== null) {
60
+ const overIdx = props.matIds.indexOf(overMatId);
53
61
  addExistingMatToQueue({
54
- materialId: active.id,
62
+ materialId: activeMatId,
55
63
  queue: props.queueName,
56
64
  queuePosition: overIdx,
57
65
  operator: operator,
@@ -63,5 +71,5 @@ export const SortableRegion = memo(function SortableRegion(props) {
63
71
  });
64
72
  }
65
73
  setActiveMat(undefined);
66
- }, children: _jsxs(SortableContext, { items: props.matIds, strategy: props.direction === "vertical" ? verticalListSortingStrategy : rectSortingStrategy, children: [props.children, _jsx(DragOverlay, { children: activeMat !== undefined ? props.renderDragOverlay(activeMat) : undefined })] }) }));
74
+ }, children: _jsxs(SortableContext, { items: sortableItemIds, strategy: props.direction === "vertical" ? verticalListSortingStrategy : rectSortingStrategy, children: [props.children, _jsx(DragOverlay, { children: activeMat !== undefined ? props.renderDragOverlay(activeMat) : undefined })] }) }));
67
75
  });
@@ -4,6 +4,7 @@ export type MaterialBinId = string;
4
4
  export declare const LoadStationBinId: MaterialBinId;
5
5
  export declare const PalletsBinId: MaterialBinId;
6
6
  export declare const ActiveQueuesBinId: MaterialBinId;
7
+ export declare const BasketsBinId: MaterialBinId;
7
8
  export interface MaterialBinState {
8
9
  readonly curBinOrder: ReadonlyArray<MaterialBinId>;
9
10
  }
@@ -13,6 +14,7 @@ export declare enum MaterialBinType {
13
14
  LoadStations = "Bin_LoadStations",
14
15
  Pallets = "Bin_Pallets",
15
16
  ActiveQueues = "Bin_ActiveQueues",
17
+ Baskets = "Bin_Baskets",
16
18
  QuarantineQueues = "Bin_Quarantine"
17
19
  }
18
20
  export type MaterialBin = {
@@ -27,6 +29,11 @@ export type MaterialBin = {
27
29
  readonly type: MaterialBinType.ActiveQueues;
28
30
  readonly binId: MaterialBinId;
29
31
  readonly byQueue: ReadonlyMap<string, MaterialList>;
32
+ } | {
33
+ readonly type: MaterialBinType.Baskets;
34
+ readonly binId: MaterialBinId;
35
+ readonly byBasket: ReadonlyMap<number, MaterialList>;
36
+ readonly basketLocations: ReadonlyMap<number, string>;
30
37
  } | {
31
38
  readonly type: MaterialBinType.QuarantineQueues;
32
39
  readonly binId: MaterialBinId;
@@ -36,6 +36,7 @@ import { atomWithStorage } from "jotai/utils";
36
36
  export const LoadStationBinId = "__FMS_INSIGHT_LOAD_STATION_BIN__";
37
37
  export const PalletsBinId = "__FMS_INSIGHT_PALLETS_BIN__";
38
38
  export const ActiveQueuesBinId = "__FMS_INSIGHT_ACTIVE_QUEUE_BIN__";
39
+ export const BasketsBinId = "__FMS_INSIGHT_BASKETS_BIN__";
39
40
  export const currentMaterialBinOrder = atomWithStorage("material-bins", []);
40
41
  export function moveMaterialBin(curBinOrder, oldIdx, newIdx) {
41
42
  const newBinOrder = Array.from(curBinOrder);
@@ -57,12 +58,27 @@ export var MaterialBinType;
57
58
  MaterialBinType["LoadStations"] = "Bin_LoadStations";
58
59
  MaterialBinType["Pallets"] = "Bin_Pallets";
59
60
  MaterialBinType["ActiveQueues"] = "Bin_ActiveQueues";
61
+ MaterialBinType["Baskets"] = "Bin_Baskets";
60
62
  MaterialBinType["QuarantineQueues"] = "Bin_Quarantine";
61
63
  })(MaterialBinType || (MaterialBinType = {}));
64
+ function basketLocation(st) {
65
+ let loc;
66
+ if (st.locationTitle && st.locationTitle.length > 0) {
67
+ loc = st.locationTitle;
68
+ }
69
+ else {
70
+ loc = st.location + " " + st.locationNum.toString();
71
+ }
72
+ if (st.slot) {
73
+ loc += `-${st.slot}`;
74
+ }
75
+ return loc;
76
+ }
62
77
  export function selectAllMaterialIntoBins(curSt, curBinOrder) {
63
78
  const loadStations = new Map();
64
79
  const pallets = new Map();
65
80
  const queues = new Map();
81
+ const baskets = new Map();
66
82
  const palLoc = LazySeq.ofObject(curSt.pallets).toRMap(([, st]) => [
67
83
  st.palletNum,
68
84
  st.palletNum.toString() +
@@ -115,6 +131,11 @@ export function selectAllMaterialIntoBins(curSt, curBinOrder) {
115
131
  break;
116
132
  }
117
133
  break;
134
+ case api.LocType.InBasket:
135
+ if (mat.location.basketId !== undefined) {
136
+ addToMap(baskets, mat.location.basketId, mat);
137
+ }
138
+ break;
118
139
  }
119
140
  }
120
141
  const activeQueues = LazySeq.ofObject(curSt.jobs)
@@ -135,10 +156,17 @@ export function selectAllMaterialIntoBins(curSt, curBinOrder) {
135
156
  const quarantineQueues = LazySeq.ofObject(curSt.queues)
136
157
  .filter(([qname, _]) => !activeQueues.has(qname))
137
158
  .toRSet(([qname, _]) => qname);
138
- const bins = curBinOrder.filter((b) => b === LoadStationBinId || b === PalletsBinId || b === ActiveQueuesBinId || quarantineQueues.has(b));
159
+ const bins = curBinOrder.filter((b) => b === LoadStationBinId ||
160
+ b === PalletsBinId ||
161
+ b === ActiveQueuesBinId ||
162
+ b === BasketsBinId ||
163
+ quarantineQueues.has(b));
139
164
  if (bins.indexOf(ActiveQueuesBinId) < 0) {
140
165
  bins.unshift(ActiveQueuesBinId);
141
166
  }
167
+ if (baskets.size > 0 && bins.indexOf(BasketsBinId) < 0) {
168
+ bins.unshift(BasketsBinId);
169
+ }
142
170
  if (bins.indexOf(PalletsBinId) < 0) {
143
171
  bins.unshift(PalletsBinId);
144
172
  }
@@ -168,6 +196,24 @@ export function selectAllMaterialIntoBins(curSt, curBinOrder) {
168
196
  }, (ms1, ms2) => ms1.concat(ms2)),
169
197
  };
170
198
  }
199
+ else if (binId === BasketsBinId) {
200
+ return {
201
+ type: MaterialBinType.Baskets,
202
+ binId: BasketsBinId,
203
+ byBasket: new Map(baskets),
204
+ basketLocations: LazySeq.ofObject(curSt.baskets ?? {})
205
+ .collect(([id, st]) => {
206
+ const basketId = parseInt(id);
207
+ if (basketId > 0) {
208
+ return [parseInt(id), basketLocation(st)];
209
+ }
210
+ else {
211
+ return null;
212
+ }
213
+ })
214
+ .toRMap((x) => x),
215
+ };
216
+ }
171
217
  else {
172
218
  const queueName = binId;
173
219
  const mat = queues.get(queueName) ?? [];
@@ -32,9 +32,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
32
  */
33
33
  import { addMonths, getDaysInMonth, addDays } from "date-fns";
34
34
  import copy from "copy-to-clipboard";
35
+ import { isLaborCycle, isMachineCycle, isPalletLoadCycle, } from "../cell-status/station-cycles.js";
35
36
  import { LazySeq } from "@seedtactics/immutable-collections";
36
37
  function isUnloadCycle(c) {
37
- return c.isLabor && c.operation === "UNLOAD";
38
+ return isPalletLoadCycle(c) && c.operation === "UNLOAD";
38
39
  }
39
40
  function isMonthType(type) {
40
41
  return Object.prototype.hasOwnProperty.call(type, "month");
@@ -51,10 +52,10 @@ export function compute_monthly_cost_percentages(cycles, partsToIgnore, matsById
51
52
  if (isUnloadCycle(c)) {
52
53
  totalPalletCycles += 1;
53
54
  }
54
- if (c.isLabor) {
55
+ if (isLaborCycle(c)) {
55
56
  totalLaborUseMinutes += c.activeMinutes;
56
57
  }
57
- else {
58
+ else if (isMachineCycle(c)) {
58
59
  totalStatUseMinutes.set(c.stationGroup, c.activeMinutes + (totalStatUseMinutes.get(c.stationGroup) ?? 0));
59
60
  const s = stationCount.get(c.stationGroup);
60
61
  if (s) {
@@ -81,16 +82,18 @@ export function compute_monthly_cost_percentages(cycles, partsToIgnore, matsById
81
82
  part: partName,
82
83
  parts_completed: completed.get(partName) ?? 0,
83
84
  machine: LazySeq.of(forPart)
84
- .filter((c) => !c.isLabor)
85
+ .filter((c) => isMachineCycle(c))
85
86
  .buildOrderedMap((c) => c.stationGroup, (old, c) => (old ?? 0) + c.activeMinutes)
86
87
  .mapValues((minutes, statGroup) => {
87
88
  const totalUse = totalStatUseMinutes.get(statGroup) ?? 1;
88
89
  return minutes / totalUse;
89
90
  }),
90
91
  labor: LazySeq.of(forPart)
91
- .filter((c) => c.isLabor)
92
+ .filter((c) => isLaborCycle(c))
92
93
  .sumBy((c) => c.activeMinutes / totalLaborUseMinutes),
93
- automation: forPart.reduce((acc, v) => (isUnloadCycle(v) ? acc + 1 : acc), 0) / totalPalletCycles,
94
+ automation: totalPalletCycles > 0
95
+ ? forPart.reduce((acc, v) => (isUnloadCycle(v) ? acc + 1 : acc), 0) / totalPalletCycles
96
+ : 0,
94
97
  }))
95
98
  .toRArray();
96
99
  const machineQuantities = LazySeq.of(stationCount).toOrderedMap(([statGroup, nums]) => [
@@ -147,7 +150,7 @@ export function buildCostPerPieceTable(costs) {
147
150
  return table;
148
151
  }
149
152
  export function copyCostPerPieceToClipboard(costs) {
150
- copy(buildCostPerPieceTable(costs));
153
+ void copy(buildCostPerPieceTable(costs));
151
154
  }
152
155
  export function buildCostBreakdownTable(costs) {
153
156
  let table = "<table>\n<thead><tr>";
@@ -179,5 +182,5 @@ export function buildCostBreakdownTable(costs) {
179
182
  return table;
180
183
  }
181
184
  export function copyCostBreakdownToClipboard(costs) {
182
- copy(buildCostBreakdownTable(costs));
185
+ void copy(buildCostBreakdownTable(costs));
183
186
  }
@@ -10,4 +10,4 @@ export type CurrentCycle = {
10
10
  readonly oper: string;
11
11
  }>;
12
12
  };
13
- export declare function currentCycles(currentSt: ICurrentStatus, estimated: EstimatedCycleTimes): ReadonlyArray<CurrentCycle>;
13
+ export declare function currentCycles(currentSt: ICurrentStatus, estimated: EstimatedCycleTimes, loadStationNames?: Readonly<Record<string, string>>): ReadonlyArray<CurrentCycle>;
@@ -33,10 +33,51 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
33
  import { LazySeq } from "@seedtactics/immutable-collections";
34
34
  import { addSeconds } from "date-fns";
35
35
  import { isOutlierAbove, PartAndStationOperation, splitElapsedTimeAmongChunk, } from "../cell-status/estimated-cycle-times.js";
36
- import { stat_name_and_num } from "../cell-status/station-cycles.js";
37
- import { ActionType } from "../network/api.js";
36
+ import { displayStationName } from "../cell-status/station-cycles.js";
37
+ import { ActionType, BasketLocationEnum } from "../network/api.js";
38
38
  import { durationToMinutes, durationToSeconds } from "../util/parseISODuration.js";
39
- function machiningCurrentCycles(currentSt, estimated, palToLoc) {
39
+ function basketsAtLoad(currentSt) {
40
+ return LazySeq.ofObject(currentSt.baskets ?? {})
41
+ .collect(([, basket]) => {
42
+ if (basket.location === BasketLocationEnum.LoadUnload ||
43
+ basket.location === BasketLocationEnum.LoadStationStaging) {
44
+ return [basket.basketId, { group: "L/U", num: basket.locationNum }];
45
+ }
46
+ return null;
47
+ })
48
+ .toRMap(([basketId, loc]) => [basketId, loc]);
49
+ }
50
+ function locationForLoadActivity(material, jobs, palToLoc, basketToLoad) {
51
+ if (material.action.type === ActionType.Loading) {
52
+ if (material.action.loadOntoPalletNum) {
53
+ return palToLoc.get(material.action.loadOntoPalletNum) ?? null;
54
+ }
55
+ if (material.action.loadFromBasketId) {
56
+ return basketToLoad.get(material.action.loadFromBasketId) ?? null;
57
+ }
58
+ // queue → basket: neither pallet nor source basket set. Find a basket at a load station
59
+ // that is a basket-load station for this job/process.
60
+ const proc = material.action.processAfterLoad ?? material.process;
61
+ const procInfo = jobs[material.jobUnique]?.procsAndPaths?.[proc - 1];
62
+ const basketLoadStations = procInfo?.basketLoadStations ?? [];
63
+ for (const [, loc] of basketToLoad) {
64
+ if (basketLoadStations.length === 0 || basketLoadStations.includes(loc.num)) {
65
+ return loc;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ else {
71
+ if (material.location.palletNum) {
72
+ return palToLoc.get(material.location.palletNum) ?? null;
73
+ }
74
+ if (material.location.basketId) {
75
+ return basketToLoad.get(material.location.basketId) ?? null;
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+ function machiningCurrentCycles(currentSt, estimated, palToLoc, loadStationNames) {
40
81
  return LazySeq.of(currentSt.material)
41
82
  .collect((m) => {
42
83
  if (m.action.type !== ActionType.Machining)
@@ -55,7 +96,7 @@ function machiningCurrentCycles(currentSt, estimated, palToLoc) {
55
96
  const elapsedSec = durationToSeconds(mats[0].mat.action.elapsedMachiningTime ?? "PT0S");
56
97
  const remainingSec = durationToSeconds(mats[0].mat.action.expectedRemainingMachiningTime ?? "PT0S");
57
98
  return {
58
- station: stat_name_and_num(statGroup, statNum),
99
+ station: displayStationName(statGroup, statNum, loadStationNames),
59
100
  start: addSeconds(currentSt.timeOfCurrentStatusUTC, -elapsedSec),
60
101
  expectedEnd: addSeconds(currentSt.timeOfCurrentStatusUTC, remainingSec),
61
102
  isOutlier: stats ? isOutlierAbove(stats, elapsedSec / 60 / mats.length) : false,
@@ -63,22 +104,19 @@ function machiningCurrentCycles(currentSt, estimated, palToLoc) {
63
104
  };
64
105
  });
65
106
  }
66
- function loadCurrentCycles(currentSt, estimated, palToLoc) {
107
+ function loadCurrentCycles(currentSt, estimated, palToLoc, loadStationNames) {
108
+ const basketToLoad = basketsAtLoad(currentSt);
67
109
  return LazySeq.of(currentSt.material)
68
110
  .collect((m) => {
69
111
  if (m.action.type === ActionType.UnloadToCompletedMaterial ||
70
112
  m.action.type === ActionType.UnloadToInProcess) {
71
- if (!m.location.palletNum)
72
- return null;
73
- const loc = palToLoc.get(m.location.palletNum);
113
+ const loc = locationForLoadActivity(m, currentSt.jobs, palToLoc, basketToLoad);
74
114
  if (!loc)
75
115
  return null;
76
116
  return { mat: m, material: [m], proc: m.process, path: m.path, loc };
77
117
  }
78
118
  else if (m.action.type === ActionType.Loading) {
79
- if (!m.action.loadOntoPalletNum)
80
- return null;
81
- const loc = palToLoc.get(m.action.loadOntoPalletNum);
119
+ const loc = locationForLoadActivity(m, currentSt.jobs, palToLoc, basketToLoad);
82
120
  if (!loc)
83
121
  return null;
84
122
  return {
@@ -93,12 +131,19 @@ function loadCurrentCycles(currentSt, estimated, palToLoc) {
93
131
  })
94
132
  .map((m) => {
95
133
  const job = currentSt.jobs[m.mat.jobUnique];
134
+ const procInfo = job?.procsAndPaths?.[m.proc - 1];
96
135
  const pathData = job?.procsAndPaths?.[m.proc - 1]?.paths?.[m.path - 1];
97
136
  if (m.mat.action.type === ActionType.Loading) {
98
- return { ...m, expectedLoadSecs: durationToSeconds(pathData?.expectedLoadTime ?? "PT0S") };
137
+ const expected = m.mat.action.loadOntoPalletNum !== null && m.mat.action.loadOntoPalletNum !== undefined
138
+ ? pathData?.expectedLoadTime
139
+ : (procInfo?.expectedBasketLoadTime ?? pathData?.expectedLoadTime);
140
+ return { ...m, expectedLoadSecs: durationToSeconds(expected ?? "PT0S") };
99
141
  }
100
142
  else {
101
- return { ...m, expectedLoadSecs: durationToSeconds(pathData?.expectedUnloadTime ?? "PT0S") };
143
+ const expected = m.mat.location.palletNum !== null && m.mat.location.palletNum !== undefined
144
+ ? pathData?.expectedUnloadTime
145
+ : (procInfo?.expectedBasketUnloadTime ?? pathData?.expectedUnloadTime);
146
+ return { ...m, expectedLoadSecs: durationToSeconds(expected ?? "PT0S") };
102
147
  }
103
148
  })
104
149
  .groupBy(({ loc }) => loc.group, ({ loc }) => loc.num)
@@ -122,7 +167,7 @@ function loadCurrentCycles(currentSt, estimated, palToLoc) {
122
167
  }
123
168
  const elapsedSec = durationToSeconds(mats[0].mat.action.elapsedLoadUnloadTime ?? "PT0S");
124
169
  return {
125
- station: stat_name_and_num(statGroup, statNum),
170
+ station: displayStationName(statGroup, statNum, loadStationNames),
126
171
  start: addSeconds(currentSt.timeOfCurrentStatusUTC, -elapsedSec),
127
172
  expectedEnd: addSeconds(currentSt.timeOfCurrentStatusUTC, -elapsedSec + expectedSecs),
128
173
  isOutlier: outlier,
@@ -136,9 +181,9 @@ function loadCurrentCycles(currentSt, estimated, palToLoc) {
136
181
  };
137
182
  });
138
183
  }
139
- export function currentCycles(currentSt, estimated) {
184
+ export function currentCycles(currentSt, estimated, loadStationNames) {
140
185
  const palToLoc = LazySeq.ofObject(currentSt.pallets).buildHashMap(([, p]) => p.palletNum, (_old, [, pal]) => pal.currentPalletLocation);
141
- return machiningCurrentCycles(currentSt, estimated, palToLoc)
142
- .concat(loadCurrentCycles(currentSt, estimated, palToLoc))
186
+ return machiningCurrentCycles(currentSt, estimated, palToLoc, loadStationNames)
187
+ .concat(loadCurrentCycles(currentSt, estimated, palToLoc, loadStationNames))
143
188
  .toRArray();
144
189
  }
@@ -6,7 +6,8 @@ export declare enum MoveMaterialNodeKindType {
6
6
  CompletedCollapsedMaterialZone = 2,
7
7
  CompletedExpandedMaterialZone = 3,
8
8
  PalletFaceZone = 4,
9
- QueueZone = 5
9
+ QueueZone = 5,
10
+ BasketZone = 6
10
11
  }
11
12
  export type MoveMaterialNodeKind = {
12
13
  readonly type: MoveMaterialNodeKindType.Material;
@@ -23,6 +24,9 @@ export type MoveMaterialNodeKind = {
23
24
  } | {
24
25
  readonly type: MoveMaterialNodeKindType.QueueZone;
25
26
  readonly queue: string;
27
+ } | {
28
+ readonly type: MoveMaterialNodeKindType.BasketZone;
29
+ readonly basketId: number;
26
30
  };
27
31
  export type MoveMaterialIdentifier = string;
28
32
  export declare function uniqueIdForNodeKind(kind: MoveMaterialNodeKind): MoveMaterialIdentifier;
@@ -40,6 +40,7 @@ export var MoveMaterialNodeKindType;
40
40
  MoveMaterialNodeKindType[MoveMaterialNodeKindType["CompletedExpandedMaterialZone"] = 3] = "CompletedExpandedMaterialZone";
41
41
  MoveMaterialNodeKindType[MoveMaterialNodeKindType["PalletFaceZone"] = 4] = "PalletFaceZone";
42
42
  MoveMaterialNodeKindType[MoveMaterialNodeKindType["QueueZone"] = 5] = "QueueZone";
43
+ MoveMaterialNodeKindType[MoveMaterialNodeKindType["BasketZone"] = 6] = "BasketZone";
43
44
  })(MoveMaterialNodeKindType || (MoveMaterialNodeKindType = {}));
44
45
  export function uniqueIdForNodeKind(kind) {
45
46
  switch (kind.type) {
@@ -55,6 +56,8 @@ export function uniqueIdForNodeKind(kind) {
55
56
  return "PalletFaceZone-" + kind.face.toString();
56
57
  case MoveMaterialNodeKindType.QueueZone:
57
58
  return "QueueZone-" + kind.queue;
59
+ case MoveMaterialNodeKindType.BasketZone:
60
+ return "BasketZone-" + kind.basketId.toString();
58
61
  }
59
62
  }
60
63
  export function memoPropsForNodeKind(kind) {
@@ -69,6 +72,8 @@ export function memoPropsForNodeKind(kind) {
69
72
  return [kind.type, kind.face];
70
73
  case MoveMaterialNodeKindType.QueueZone:
71
74
  return [kind.type, kind.queue];
75
+ case MoveMaterialNodeKindType.BasketZone:
76
+ return [kind.type, kind.basketId];
72
77
  }
73
78
  }
74
79
  function groupMatByKind(allNodes) {
@@ -76,6 +81,7 @@ function groupMatByKind(allNodes) {
76
81
  let completedMaterial;
77
82
  const faces = new Map();
78
83
  const queues = new Map();
84
+ const baskets = new Map();
79
85
  const material = new Array();
80
86
  for (const node of allNodes.values()) {
81
87
  switch (node.type) {
@@ -92,6 +98,9 @@ function groupMatByKind(allNodes) {
92
98
  case MoveMaterialNodeKindType.QueueZone:
93
99
  queues.set(node.queue, node.elem);
94
100
  break;
101
+ case MoveMaterialNodeKindType.BasketZone:
102
+ baskets.set(node.basketId, node.elem);
103
+ break;
95
104
  case MoveMaterialNodeKindType.Material:
96
105
  if (node.material) {
97
106
  material.push([node.elem, node.material]);
@@ -99,7 +108,7 @@ function groupMatByKind(allNodes) {
99
108
  break;
100
109
  }
101
110
  }
102
- return { freeMaterial, completedMaterial, faces, queues, material };
111
+ return { freeMaterial, completedMaterial, faces, queues, baskets, material };
103
112
  }
104
113
  export function computeArrows(container, allNodes) {
105
114
  if (!container) {
@@ -112,12 +121,25 @@ export function computeArrows(container, allNodes) {
112
121
  const arrows = [];
113
122
  const faceDestUsed = new Map();
114
123
  const queueDestUsed = new Map();
124
+ const basketDestUsed = new Map();
115
125
  let lastFreeUsed = 0;
116
- for (const [rect, mat] of LazySeq.of(byKind.material).sortBy(([rect]) => rect.left, ([rect]) => rect.top)) {
126
+ for (const [rect, mat] of LazySeq.of(byKind.material).sortBy(([box]) => box.left, ([box]) => box.top)) {
117
127
  switch (mat.action.type) {
118
128
  case api.ActionType.UnloadToCompletedMaterial:
119
129
  case api.ActionType.UnloadToInProcess:
120
- if (mat.action.type === api.ActionType.UnloadToCompletedMaterial &&
130
+ if (mat.action.unloadToBasketId) {
131
+ const dest = byKind.baskets.get(mat.action.unloadToBasketId);
132
+ const lastSlotUsed = basketDestUsed.get(mat.action.unloadToBasketId) ?? 0;
133
+ basketDestUsed.set(mat.action.unloadToBasketId, lastSlotUsed + 1);
134
+ arrows.push({
135
+ fromX: rect.left,
136
+ fromY: rect.top + rect.height / 2,
137
+ toX: dest !== undefined ? dest.right - 5 : container.left + 2,
138
+ toY: dest !== undefined ? dest.top + 20 * (lastSlotUsed + 1) : rect.top + rect.height / 2,
139
+ curveDirection: 1,
140
+ });
141
+ }
142
+ else if (mat.action.type === api.ActionType.UnloadToCompletedMaterial &&
121
143
  (!mat.action.unloadIntoQueue || mat.action.unloadIntoQueue === "")) {
122
144
  arrows.push({
123
145
  fromX: rect.right,
@@ -150,7 +172,35 @@ export function computeArrows(container, allNodes) {
150
172
  }
151
173
  break;
152
174
  case api.ActionType.Loading:
153
- if (mat.action.loadOntoFace) {
175
+ if (mat.action.loadFromBasketId && mat.action.loadOntoFace) {
176
+ // loading from basket onto pallet face
177
+ const face = byKind.faces.get(mat.action.loadOntoFace);
178
+ if (face !== undefined) {
179
+ const faceSpotsUsed = faceDestUsed.get(mat.action.loadOntoFace) ?? 0;
180
+ faceDestUsed.set(mat.action.loadOntoFace, faceSpotsUsed + 1);
181
+ arrows.push({
182
+ fromX: rect.right,
183
+ fromY: rect.top + rect.height / 2,
184
+ toX: face.left + 20,
185
+ toY: face.top + 50 + 20 * faceSpotsUsed,
186
+ curveDirection: -1,
187
+ });
188
+ }
189
+ }
190
+ else if (mat.action.loadFromBasketId) {
191
+ // basket-only load from a queue into the active basket
192
+ const dest = byKind.baskets.get(mat.action.loadFromBasketId);
193
+ const lastSlotUsed = basketDestUsed.get(mat.action.loadFromBasketId) ?? 0;
194
+ basketDestUsed.set(mat.action.loadFromBasketId, lastSlotUsed + 1);
195
+ arrows.push({
196
+ fromX: rect.right,
197
+ fromY: rect.top + rect.height / 2,
198
+ toX: dest !== undefined ? dest.left + 20 : container.right - 10,
199
+ toY: dest !== undefined ? dest.top + 50 + 20 * lastSlotUsed : rect.top + rect.height / 2,
200
+ curveDirection: -1,
201
+ });
202
+ }
203
+ else if (mat.action.loadOntoFace) {
154
204
  if (mat.location.type === api.LocType.OnPallet) {
155
205
  if (mat.location.palletNum === mat.action.loadOntoPalletNum &&
156
206
  mat.location.face === mat.action.loadOntoFace) {
@@ -33,12 +33,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
33
  import { LazySeq, OrderedMap, OrderedSet } from "@seedtactics/immutable-collections";
34
34
  import { last30MaterialSummary } from "../cell-status/material-summary";
35
35
  import { atom } from "jotai";
36
- import { last30StationCycles } from "../cell-status/station-cycles";
36
+ import { isLaborCycle, last30StationCycles } from "../cell-status/station-cycles";
37
37
  import { chartRangeAtom } from "./chart-times";
38
38
  function isAbnormal(m) {
39
39
  if (m.closeout_completed === undefined) {
40
40
  // no closeout has been done, so fall back to checking inspections and quarantined
41
- if (LazySeq.ofObject(m.completedInspections ?? {}).some(([, insp]) => insp.success === false)) {
41
+ if (LazySeq.ofObject(m.completedInspections ?? {}).some(([, insp]) => !insp.success)) {
42
42
  return true;
43
43
  }
44
44
  if (m.currently_quarantined) {
@@ -52,7 +52,7 @@ function isAbnormal(m) {
52
52
  }
53
53
  export const last30PartSummaryRange = chartRangeAtom("part-summary");
54
54
  export const last30PartSummary = atom((get) => {
55
- const mats = get(last30MaterialSummary);
55
+ const materialSummary = get(last30MaterialSummary);
56
56
  const cycles = get(last30StationCycles);
57
57
  const range = get(last30PartSummaryRange);
58
58
  const stationTimes = cycles
@@ -60,7 +60,7 @@ export const last30PartSummary = atom((get) => {
60
60
  .filter((c) => (range.startDate === null || c.endTime >= range.startDate) &&
61
61
  (range.endDate === null || c.endTime <= range.endDate))
62
62
  .toLookupOrderedMap((c) => c.part, (c) => c.stationGroup, (c) => ({
63
- isLoadUnload: c.isLabor,
63
+ isLoadUnload: isLaborCycle(c),
64
64
  elapsed: c.elapsedMinsPerMaterial * c.material.length,
65
65
  active: c.activeMinutes,
66
66
  medianElapsed: c.medianCycleMinutes,
@@ -70,26 +70,26 @@ export const last30PartSummary = atom((get) => {
70
70
  active: a.active + b.active,
71
71
  medianElapsed: a.medianElapsed + b.medianElapsed,
72
72
  }));
73
- return mats.matsById
73
+ return materialSummary.matsById
74
74
  .valuesToLazySeq()
75
75
  .filter((m) => Boolean(m.numProcesses &&
76
76
  m.unloaded_processes?.[m.numProcesses] &&
77
77
  (range.startDate === null || m.unloaded_processes[m.numProcesses] >= range.startDate) &&
78
78
  (range.endDate === null || m.unloaded_processes[m.numProcesses] <= range.endDate)))
79
79
  .toOrderedLookup((m) => m.partName)
80
- .mapValues((mats, partName) => ({
80
+ .mapValues((partMats, partName) => ({
81
81
  part: partName,
82
- completedQty: mats.length,
83
- abnormalQty: LazySeq.of(mats).sumBy((m) => (isAbnormal(m) ? 1 : 0)),
84
- mats: mats,
82
+ completedQty: partMats.length,
83
+ abnormalQty: LazySeq.of(partMats).sumBy((m) => (isAbnormal(m) ? 1 : 0)),
84
+ mats: partMats,
85
85
  stationMins: OrderedMap.empty(),
86
- workorders: LazySeq.of(mats)
86
+ workorders: LazySeq.of(partMats)
87
87
  .toOrderedSet((m) => m.workorderId ?? "")
88
88
  .delete(""),
89
89
  }))
90
- .adjust(stationTimes, (summary, stationTimes, partName) => {
90
+ .adjust(stationTimes, (summary, partStationTimes, partName) => {
91
91
  if (summary) {
92
- return { ...summary, stationMins: stationTimes };
92
+ return { ...summary, stationMins: partStationTimes };
93
93
  }
94
94
  else {
95
95
  return {
@@ -97,7 +97,7 @@ export const last30PartSummary = atom((get) => {
97
97
  completedQty: 0,
98
98
  abnormalQty: 0,
99
99
  mats: [],
100
- stationMins: stationTimes,
100
+ stationMins: partStationTimes,
101
101
  workorders: OrderedSet.empty(),
102
102
  };
103
103
  }
@@ -35,19 +35,21 @@ import { addDays } from "date-fns";
35
35
  import { convertLogToInspections, } from "../cell-status/inspections.js";
36
36
  import { HashMap, LazySeq } from "@seedtactics/immutable-collections";
37
37
  import { atom } from "jotai";
38
- import { loadable } from "jotai/utils";
38
+ import { unwrap } from "jotai/utils";
39
39
  export const pathLookupRange = atom(null);
40
40
  const localLogEntries = atom(async (get, { signal }) => {
41
41
  const range = get(pathLookupRange);
42
42
  if (range == null)
43
43
  return HashMap.empty();
44
+ if (typeof LogBackend === "undefined")
45
+ return HashMap.empty();
44
46
  const events = await LogBackend.get(range.curStart, range.curEnd, signal);
45
47
  return LazySeq.of(events)
46
48
  .flatMap(convertLogToInspections)
47
49
  .filter((e) => e.key.part === range.part)
48
50
  .toLookupMap((e) => e.key, (e) => e.entry.cntr, (e) => e.entry);
49
51
  });
50
- const localLogLoadable = loadable(localLogEntries);
52
+ const localLogEntriesUnwrapped = unwrap(localLogEntries, (prev) => prev ?? HashMap.empty());
51
53
  const otherLogEntries = atom(async (get) => {
52
54
  const range = get(pathLookupRange);
53
55
  if (range == null)
@@ -62,28 +64,9 @@ const otherLogEntries = atom(async (get) => {
62
64
  .filter((e) => e.key.part === range.part)
63
65
  .toLookupMap((e) => e.key, (e) => e.entry.cntr, (e) => e.entry);
64
66
  });
65
- const otherLogLoadable = loadable(otherLogEntries);
67
+ const otherLogEntriesUnwrapped = unwrap(otherLogEntries, (prev) => prev ?? HashMap.empty());
66
68
  export const inspectionLogEntries = atom((get) => {
67
- const localEvts = get(localLogLoadable);
68
- const otherEvts = get(otherLogLoadable);
69
- const localData = localEvts.state === "hasData" ? localEvts.data : null;
70
- const otherData = otherEvts.state === "hasData" ? otherEvts.data : null;
71
- if (localData) {
72
- if (!otherData) {
73
- return localData;
74
- }
75
- else {
76
- return HashMap.union((inspsByCntr1, inspsByCntr2) => inspsByCntr1.union(inspsByCntr2), localData, otherData);
77
- }
78
- }
79
- else {
80
- if (otherData) {
81
- return otherData;
82
- }
83
- else {
84
- return HashMap.empty();
85
- }
86
- }
69
+ return HashMap.union((inspsByCntr1, inspsByCntr2) => inspsByCntr1.union(inspsByCntr2), get(localLogEntriesUnwrapped), get(otherLogEntriesUnwrapped));
87
70
  });
88
71
  export function extendRange(numDays) {
89
72
  return (range) => {
@@ -44,4 +44,4 @@ export interface QueueData {
44
44
  readonly groupedRawMat?: ReadonlyArray<QueueRawMaterialGroup>;
45
45
  }
46
46
  export declare function selectQueueData(queuesToCheck: ReadonlyArray<string>, curSt: Readonly<api.ICurrentStatus>, rawMatQueues: ReadonlySet<string>, inProcQueues: ReadonlySet<string>): ReadonlyArray<QueueData>;
47
- export declare function loadRawMaterialEvents(material: ReadonlyArray<Readonly<api.IInProcessMaterial>>): Promise<ReadonlyArray<Readonly<api.ILogEntry>>>;
47
+ export declare function loadRawMaterialEvents(materials: ReadonlyArray<Readonly<api.IInProcessMaterial>>): Promise<ReadonlyArray<Readonly<api.ILogEntry>>>;