@things-factory/headless-twin 10.0.11 → 10.0.13
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.
- package/dist-server/engine/canonical-ingest.d.ts +81 -3
- package/dist-server/engine/canonical-ingest.js +89 -10
- package/dist-server/engine/canonical-ingest.js.map +1 -1
- package/dist-server/engine/command-routing.js +1 -1
- package/dist-server/engine/command-routing.js.map +1 -1
- package/dist-server/engine/declared-stimulus.d.ts +43 -0
- package/dist-server/engine/declared-stimulus.js +57 -0
- package/dist-server/engine/declared-stimulus.js.map +1 -0
- package/dist-server/engine/index.d.ts +5 -0
- package/dist-server/engine/index.js +6 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/ingest-health.d.ts +335 -0
- package/dist-server/engine/ingest-health.js +434 -0
- package/dist-server/engine/ingest-health.js.map +1 -0
- package/dist-server/engine/integration-coverage.d.ts +76 -0
- package/dist-server/engine/integration-coverage.js +73 -0
- package/dist-server/engine/integration-coverage.js.map +1 -0
- package/dist-server/engine/integration-probes.d.ts +65 -0
- package/dist-server/engine/integration-probes.js +100 -0
- package/dist-server/engine/integration-probes.js.map +1 -0
- package/dist-server/engine/integration-runner.d.ts +45 -0
- package/dist-server/engine/integration-runner.js +59 -0
- package/dist-server/engine/integration-runner.js.map +1 -0
- package/dist-server/engine/integration-target-profile.d.ts +57 -0
- package/dist-server/engine/integration-target-profile.js +79 -0
- package/dist-server/engine/integration-target-profile.js.map +1 -0
- package/dist-server/engine/kpi-fold.d.ts +39 -0
- package/dist-server/engine/kpi-fold.js +68 -5
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.d.ts +3 -3
- package/dist-server/engine/kpi-query.js +132 -18
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/live-feed-registry.d.ts +1 -1
- package/dist-server/engine/live-feed-registry.js +3 -17
- package/dist-server/engine/live-feed-registry.js.map +1 -1
- package/dist-server/engine/load-meter.d.ts +1 -1
- package/dist-server/engine/load-meter.js +12 -3
- package/dist-server/engine/load-meter.js.map +1 -1
- package/dist-server/engine/local-declarations.d.ts +3 -6
- package/dist-server/engine/local-declarations.js +97 -16
- package/dist-server/engine/local-declarations.js.map +1 -1
- package/dist-server/engine/log.d.ts +18 -0
- package/dist-server/engine/log.js +80 -0
- package/dist-server/engine/log.js.map +1 -0
- package/dist-server/engine/loop-lag.d.ts +54 -0
- package/dist-server/engine/loop-lag.js +87 -0
- package/dist-server/engine/loop-lag.js.map +1 -0
- package/dist-server/engine/measured-yield.d.ts +42 -0
- package/dist-server/engine/measured-yield.js +75 -0
- package/dist-server/engine/measured-yield.js.map +1 -0
- package/dist-server/engine/model-gap.d.ts +108 -0
- package/dist-server/engine/model-gap.js +95 -0
- package/dist-server/engine/model-gap.js.map +1 -0
- package/dist-server/engine/operation-basis.d.ts +16 -0
- package/dist-server/engine/operation-basis.js +20 -2
- package/dist-server/engine/operation-basis.js.map +1 -1
- package/dist-server/engine/property-effects.js +17 -0
- package/dist-server/engine/property-effects.js.map +1 -1
- package/dist-server/engine/restart-policy.d.ts +13 -0
- package/dist-server/engine/restart-policy.js +52 -0
- package/dist-server/engine/restart-policy.js.map +1 -0
- package/dist-server/engine/runtime-key.js +1 -1
- package/dist-server/engine/runtime-key.js.map +1 -1
- package/dist-server/engine/spec-coverage.d.ts +8 -0
- package/dist-server/engine/spec-coverage.js +3 -1
- package/dist-server/engine/spec-coverage.js.map +1 -1
- package/dist-server/engine/stage-path.d.ts +83 -0
- package/dist-server/engine/stage-path.js +118 -0
- package/dist-server/engine/stage-path.js.map +1 -0
- package/dist-server/engine/twin-engine.d.ts +400 -31
- package/dist-server/engine/twin-engine.js +1198 -187
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/index.js +35 -3
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.d.ts +5 -0
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +58 -0
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -0
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.d.ts +5 -0
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +55 -0
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -0
- package/dist-server/migrations/index.js +7 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/service/index.d.ts +5 -2
- package/dist-server/service/index.js +18 -7
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/control-routing.d.ts +14 -0
- package/dist-server/service/reference/control-routing.js +64 -0
- package/dist-server/service/reference/control-routing.js.map +1 -0
- package/dist-server/service/reference/discovery-result.d.ts +1 -1
- package/dist-server/service/reference/discovery-result.js +1 -1
- package/dist-server/service/reference/discovery-result.js.map +1 -1
- package/dist-server/service/reference/index.d.ts +5 -1
- package/dist-server/service/reference/index.js +8 -1
- package/dist-server/service/reference/index.js.map +1 -1
- package/dist-server/service/reference/reference-adapter.d.ts +251 -3
- package/dist-server/service/reference/reference-adapter.js +43 -1
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-assessment.d.ts +68 -0
- package/dist-server/service/reference/reference-assessment.js +136 -0
- package/dist-server/service/reference/reference-assessment.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +12 -1
- package/dist-server/service/reference/reference-live.js +119 -14
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +47 -1
- package/dist-server/service/reference/reference-master.js +34 -4
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-probe.d.ts +92 -0
- package/dist-server/service/reference/reference-probe.js +186 -0
- package/dist-server/service/reference/reference-probe.js.map +1 -0
- package/dist-server/service/reference/reference-progress-subscription.d.ts +17 -0
- package/dist-server/service/reference/reference-progress-subscription.js +94 -0
- package/dist-server/service/reference/reference-progress-subscription.js.map +1 -0
- package/dist-server/service/reference/reference-progress.d.ts +38 -0
- package/dist-server/service/reference/reference-progress.js +71 -0
- package/dist-server/service/reference/reference-progress.js.map +1 -0
- package/dist-server/service/reference/reference-resolver.d.ts +3 -3
- package/dist-server/service/reference/reference-resolver.js +242 -58
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/twin-reference.d.ts +32 -0
- package/dist-server/service/reference/twin-reference.js +10 -0
- package/dist-server/service/reference/twin-reference.js.map +1 -1
- package/dist-server/service/twin-audit/command-audit.d.ts +34 -0
- package/dist-server/service/twin-audit/command-audit.js +15 -1
- package/dist-server/service/twin-audit/command-audit.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.d.ts +3 -0
- package/dist-server/service/twin-audit/twin-audit-event.js +10 -0
- package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.d.ts +15 -2
- package/dist-server/service/twin-control/twin-control-mutation.js +88 -36
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/journal-count.d.ts +21 -0
- package/dist-server/service/twin-event/journal-count.js +26 -0
- package/dist-server/service/twin-event/journal-count.js.map +1 -0
- package/dist-server/service/twin-event/twin-event-keys.d.ts +9 -0
- package/dist-server/service/twin-event/twin-event-keys.js +15 -18
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-type.d.ts +3 -1
- package/dist-server/service/twin-event/twin-event-type.js +18 -1
- package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +1 -0
- package/dist-server/service/twin-event/twin-event.js +9 -1
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +2 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/index.d.ts +5 -0
- package/dist-server/service/twin-ingest-window/index.js +10 -0
- package/dist-server/service/twin-ingest-window/index.js.map +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +12 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +123 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.d.ts +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +51 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window.d.ts +16 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window.js +108 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +1 -0
- package/dist-server/service/twin-instance/twin-instance.d.ts +1 -1
- package/dist-server/service/twin-instance/twin-instance.js +2 -2
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/twin-journal-query.js +58 -5
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +14 -0
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +74 -3
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.d.ts +1 -0
- package/dist-server/service/twin-metrics/twin-metrics-query.js +78 -3
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.d.ts +2 -0
- package/dist-server/service/twin-model/axis-journal-evidence.js +89 -0
- package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -0
- package/dist-server/service/twin-model/epcis-coverage.d.ts +1 -1
- package/dist-server/service/twin-model/epcis-coverage.js +19 -7
- package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.d.ts +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js +18 -7
- package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
- package/dist-server/service/twin-model/isa95-coverage.d.ts +14 -0
- package/dist-server/service/twin-model/isa95-coverage.js +23 -9
- package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
- package/dist-server/service/twin-model/item-ref.d.ts +24 -0
- package/dist-server/service/twin-model/item-ref.js +88 -0
- package/dist-server/service/twin-model/item-ref.js.map +1 -0
- package/dist-server/service/twin-model/name-index.d.ts +36 -0
- package/dist-server/service/twin-model/name-index.js +116 -0
- package/dist-server/service/twin-model/name-index.js.map +1 -0
- package/dist-server/service/twin-model/project-structure.js +1 -1
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/status-tally.d.ts +9 -0
- package/dist-server/service/twin-model/status-tally.js +37 -0
- package/dist-server/service/twin-model/status-tally.js.map +1 -0
- package/dist-server/service/twin-model/twin-lineage-query.js +40 -12
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.js +38 -39
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-query.js +157 -5
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js +7 -0
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
- package/dist-server/service/twin-readiness/index.d.ts +2 -0
- package/dist-server/service/twin-readiness/index.js +6 -0
- package/dist-server/service/twin-readiness/index.js.map +1 -0
- package/dist-server/service/twin-readiness/twin-readiness-query.d.ts +3 -0
- package/dist-server/service/twin-readiness/twin-readiness-query.js +103 -0
- package/dist-server/service/twin-readiness/twin-readiness-query.js.map +1 -0
- package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
- package/dist-server/service/twin-space/twin-space-resolver.js +5 -4
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-shared/entity-delta.d.ts +23 -3
- package/dist-shared/entity-delta.js +12 -8
- package/dist-shared/entity-delta.js.map +1 -1
- package/dist-shared/kpi-broadcast.js +1 -1
- package/dist-shared/kpi-broadcast.js.map +1 -1
- package/dist-shared/touched-items.d.ts +7 -0
- package/dist-shared/touched-items.js +80 -0
- package/dist-shared/touched-items.js.map +1 -0
- package/package.json +7 -7
- package/server/engine/canonical-ingest.ts +164 -15
- package/server/engine/command-routing.ts +1 -1
- package/server/engine/declared-stimulus.ts +66 -0
- package/server/engine/index.ts +6 -0
- package/server/engine/ingest-health.ts +704 -0
- package/server/engine/integration-coverage.ts +147 -0
- package/server/engine/integration-probes.ts +144 -0
- package/server/engine/integration-runner.ts +95 -0
- package/server/engine/integration-target-profile.ts +103 -0
- package/server/engine/kpi-fold.ts +101 -5
- package/server/engine/kpi-query.ts +137 -23
- package/server/engine/live-feed-registry.ts +4 -3
- package/server/engine/load-meter.ts +12 -3
- package/server/engine/local-declarations.ts +97 -19
- package/server/engine/log.ts +72 -0
- package/server/engine/loop-lag.ts +120 -0
- package/server/engine/measured-yield.ts +89 -0
- package/server/engine/model-gap.ts +168 -0
- package/server/engine/operation-basis.ts +33 -2
- package/server/engine/property-effects.ts +17 -0
- package/server/engine/restart-policy.ts +55 -0
- package/server/engine/runtime-key.ts +1 -1
- package/server/engine/spec-coverage.ts +23 -3
- package/server/engine/stage-path.ts +172 -0
- package/server/engine/twin-engine.ts +1318 -186
- package/server/index.ts +36 -3
- package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +54 -0
- package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +53 -0
- package/server/migrations/index.ts +7 -1
- package/server/service/index.ts +11 -0
- package/server/service/reference/control-routing.ts +62 -0
- package/server/service/reference/discovery-result.ts +1 -1
- package/server/service/reference/index.ts +7 -1
- package/server/service/reference/reference-adapter.ts +275 -5
- package/server/service/reference/reference-assessment.ts +215 -0
- package/server/service/reference/reference-live.ts +126 -14
- package/server/service/reference/reference-master.ts +64 -4
- package/server/service/reference/reference-probe.ts +264 -0
- package/server/service/reference/reference-progress-subscription.ts +73 -0
- package/server/service/reference/reference-progress.ts +95 -0
- package/server/service/reference/reference-resolver.ts +246 -19
- package/server/service/reference/twin-reference.ts +39 -1
- package/server/service/twin-audit/command-audit.ts +34 -1
- package/server/service/twin-audit/twin-audit-event.ts +20 -0
- package/server/service/twin-control/twin-control-mutation.ts +78 -30
- package/server/service/twin-event/journal-count.ts +53 -0
- package/server/service/twin-event/twin-event-keys.ts +16 -1
- package/server/service/twin-event/twin-event-type.ts +26 -2
- package/server/service/twin-event/twin-event.ts +7 -0
- package/server/service/twin-forecast/twin-forecast-query.ts +2 -1
- package/server/service/twin-ingest-window/index.ts +7 -0
- package/server/service/twin-ingest-window/twin-ingest-window-query.ts +125 -0
- package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +57 -0
- package/server/service/twin-ingest-window/twin-ingest-window.ts +113 -0
- package/server/service/twin-instance/twin-instance.ts +16 -10
- package/server/service/twin-journal/twin-journal-query.ts +59 -5
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +79 -4
- package/server/service/twin-metrics/twin-metrics-query.ts +81 -3
- package/server/service/twin-model/axis-journal-evidence.ts +57 -0
- package/server/service/twin-model/epcis-coverage.ts +2 -8
- package/server/service/twin-model/iec61850-coverage.ts +2 -8
- package/server/service/twin-model/isa95-coverage.ts +22 -9
- package/server/service/twin-model/item-ref.ts +80 -0
- package/server/service/twin-model/name-index.ts +94 -0
- package/server/service/twin-model/project-structure.ts +1 -1
- package/server/service/twin-model/status-tally.ts +37 -0
- package/server/service/twin-model/twin-lineage-query.ts +38 -9
- package/server/service/twin-model/twin-model-item-query.ts +26 -27
- package/server/service/twin-model/twin-model-query.ts +156 -5
- package/server/service/twin-model/twin-model-tree-query.ts +7 -0
- package/server/service/twin-readiness/index.ts +3 -0
- package/server/service/twin-readiness/twin-readiness-query.ts +96 -0
- package/server/service/twin-space/twin-space-resolver.ts +5 -4
- package/shared/entity-delta.ts +31 -7
- package/shared/kpi-broadcast.ts +1 -1
- package/shared/touched-items.ts +73 -0
- package/test/adopt-structure-live.test.ts +7 -7
- package/test/axis-journal-evidence.test.ts +71 -0
- package/test/axis-read.test.ts +101 -1
- package/test/boot-resume.test.ts +93 -27
- package/test/broadcast-cost-baseline.test.ts +200 -0
- package/test/broadcast-period.test.ts +58 -0
- package/test/canonical-ingest-vocabularies.test.ts +36 -1
- package/test/canonical-quantity-door.test.ts +61 -0
- package/test/command-routing.test.ts +1 -1
- package/test/control-capability.test.ts +103 -0
- package/test/declared-location-types.test.ts +89 -0
- package/test/declared-stimulus.test.ts +88 -0
- package/test/discovery-result.test.ts +1 -1
- package/test/duration-estimators.test.ts +1 -1
- package/test/entity-delta.test.ts +2 -2
- package/test/ingest-bench.test.ts +3 -3
- package/test/ingest-health-engine.test.ts +248 -0
- package/test/ingest-health-wiring.test.ts +119 -0
- package/test/ingest-health.test.ts +306 -0
- package/test/ingest-history.test.ts +247 -0
- package/test/ingest-running-guard.test.ts +5 -5
- package/test/instance-cache-lifecycle.test.ts +1 -1
- package/test/integration-probes.test.ts +103 -0
- package/test/integration-runner.test.ts +95 -0
- package/test/item-ref.test.ts +78 -0
- package/test/journal-read-discipline.test.ts +177 -0
- package/test/journal-retention.test.ts +133 -0
- package/test/journal-sort-axis.test.ts +142 -0
- package/test/journal-write-door.test.ts +110 -0
- package/test/journal-write-trend.test.ts +115 -0
- package/test/kernel-kind-guard.test.ts +4 -4
- package/test/kpi-baseline-db.test.ts +1 -1
- package/test/kpi-fold.test.ts +90 -3
- package/test/kpi-query-bench.test.ts +3 -3
- package/test/lineage-survives-restart.test.ts +20 -4
- package/test/live-cursor-wiring.test.ts +87 -0
- package/test/live-feed-registry.test.ts +15 -7
- package/test/live-kernel-facts.test.ts +7 -7
- package/test/live-mirror-parity.test.ts +6 -0
- package/test/load-meter.test.ts +29 -15
- package/test/local-declarations.test.ts +109 -2
- package/test/log-stamp.test.ts +59 -0
- package/test/loop-lag.test.ts +82 -0
- package/test/measured-yield.test.ts +90 -0
- package/test/model-gap.test.ts +153 -0
- package/test/oee-accumulator.test.ts +4 -0
- package/test/operation-basis.test.ts +28 -1
- package/test/operational-vocabulary.test.ts +108 -0
- package/test/operations-capability-db.test.ts +4 -4
- package/test/project-structure-db.test.ts +1 -1
- package/test/projection-reaches-screen.test.ts +1 -1
- package/test/property-effects.test.ts +28 -0
- package/test/read-failure-visible.test.ts +145 -0
- package/test/reference-grounding.test.ts +70 -0
- package/test/resolve-ts-siblings.mjs +52 -0
- package/test/restart-policy.test.ts +111 -0
- package/test/resync-origin-site.test.ts +7 -1
- package/test/revision-axis.test.ts +93 -0
- package/test/runtime-key.test.ts +2 -2
- package/test/scale-twin-bench.test.ts +2 -2
- package/test/source-outcome-audit.test.ts +104 -0
- package/test/spec-coverage.test.ts +1 -1
- package/test/stage-path.test.ts +95 -0
- package/test/standard-coverage.test.ts +15 -3
- package/test/status-tally.test.ts +55 -0
- package/test/structure-revision-db.test.ts +31 -27
- package/test/tenant-registry-db.test.ts +3 -3
- package/test/time-range.test.ts +152 -0
- package/test/touched-items.test.ts +61 -0
- package/test/twin-event-keys.test.ts +10 -2
- package/test/twin-model-item-db.test.ts +5 -3
- package/test/twin-model-tree-db.test.ts +7 -7
- package/test/twin-origin-resync.test.ts +4 -4
- package/test/warm-start-seam.test.ts +4 -0
- package/test/yield-loop.test.ts +199 -0
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import type { ReferenceAdapter, SiteDescriptor } from './reference-adapter.js'
|
|
2
|
+
import type { ReferenceMaster } from './reference-master.js'
|
|
3
|
+
import { capabilitiesOf } from './reference-adapter.js'
|
|
4
|
+
import { REFERENCE_STEPS, type ReferenceStep } from './reference-progress.js'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* **커넥터 점검 하네스** — 실 원본에 붙여 「무엇이 채워지고 무엇이 비는가」를 한 번에 본다.
|
|
8
|
+
*
|
|
9
|
+
* ── 왜 필요한가 (2026-08-21) ────────────────────────────────────────────────
|
|
10
|
+
* chef v1 커넥터를 만들 때 실 인스턴스(포천)에 붙이는 확인을 매번 손으로 했다 — curl 로 필드를 묻고,
|
|
11
|
+
* 일회용 스크립트로 마스터를 읽고, 결과를 눈으로 셌다. 그 방식은 세 가지를 잃는다:
|
|
12
|
+
* · 같은 확인을 다음 커넥터에서 처음부터 다시 만든다.
|
|
13
|
+
* · 무엇을 확인했는지가 어디에도 남지 않는다.
|
|
14
|
+
* · **앱을 띄워야 알 수 있는 것**이 되어 버린다 — dev 서버가 남의 것이면 확인 자체가 막힌다.
|
|
15
|
+
*
|
|
16
|
+
* 그래서 어댑터 규약만 아는 자리에 둔다. 어댑터는 `testConnection` · `discoverSites` · `fetchMaster` ·
|
|
17
|
+
* `openLiveFeed` 를 갖는 객체이고, 그 넷을 순서대로 부르는 일에 커넥터별 지식이 필요하지 않다.
|
|
18
|
+
*
|
|
19
|
+
* ── 무엇을 하지 않는가 ──────────────────────────────────────────────────────
|
|
20
|
+
* · **판단하지 않는다.** 「합격」을 매기지 않고 센 것을 그대로 낸다 — 무엇이 충분한지는 커넥터마다
|
|
21
|
+
* 다르고, 그 판단은 체크리스트 문서가 한다.
|
|
22
|
+
* · **저장하지 않는다.** 트윈을 만들지도, 커서를 남기지도 않는다. 원본을 읽기만 한다.
|
|
23
|
+
* · **라이브는 부르지 않는 것이 기본이다.** 첫 주기는 전량 읽기이므로 실 운영 원본에 부담이 된다
|
|
24
|
+
* (포천은 로트 99,095 · 투입 290,688 · 재고 141,358건이다). 부를 때는 호출부가 명시한다.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/** 마스터 한 벌에서 센 것 — 커넥터와 무관한 자리 이름으로만 센다. */
|
|
28
|
+
export interface MasterCounts {
|
|
29
|
+
locations: number
|
|
30
|
+
areas: number
|
|
31
|
+
equipment: number
|
|
32
|
+
materialDefinitions: number
|
|
33
|
+
operations: number
|
|
34
|
+
/** 생산 선언이 없으면 `undefined` — 0 과 구별한다(선언 자체가 없는 것과 비어 있는 것은 다르다). */
|
|
35
|
+
materials?: number
|
|
36
|
+
materialsWithHome?: number
|
|
37
|
+
routes?: number
|
|
38
|
+
recipes?: number
|
|
39
|
+
recipesWithRoute?: number
|
|
40
|
+
locationTypes?: number
|
|
41
|
+
resourceTypes?: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ProbeStep {
|
|
45
|
+
key: ReferenceStep | string
|
|
46
|
+
detail?: string
|
|
47
|
+
/** 그 단계가 끝난 시각까지 걸린 밀리초 — 어느 단계가 느린지 보이게 한다. */
|
|
48
|
+
elapsedMs: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ProbeReport {
|
|
52
|
+
adapter: string
|
|
53
|
+
/** 어댑터가 선언하고 **구현까지 있는** 능력. 선언만 있는 것은 여기 나오지 않는다. */
|
|
54
|
+
capabilities: string[]
|
|
55
|
+
connection?: { ok: boolean; readiness?: string; notes?: string[]; error?: string }
|
|
56
|
+
sites?: { siteId: string; name?: string }[]
|
|
57
|
+
/** 사이트마다 한 벌 — 마스터는 사이트 단위다. */
|
|
58
|
+
masters: {
|
|
59
|
+
siteId: string
|
|
60
|
+
steps: ProbeStep[]
|
|
61
|
+
counts?: MasterCounts
|
|
62
|
+
warnings: string[]
|
|
63
|
+
space?: unknown
|
|
64
|
+
identity?: unknown
|
|
65
|
+
/** 표본 — 눈으로 확인할 몇 건. 전체를 싣지 않는다(원본이 수천 건이다). */
|
|
66
|
+
samples?: { recipes?: unknown[]; routes?: unknown[]; locations?: unknown[] }
|
|
67
|
+
elapsedMs: number
|
|
68
|
+
error?: string
|
|
69
|
+
}[]
|
|
70
|
+
/** 라이브를 부른 경우에만 — 한 주기가 낸 기록을 종류별로 센다. */
|
|
71
|
+
live?: { records: number; byShape: Record<string, number>; samples: unknown[]; elapsedMs: number; error?: string }
|
|
72
|
+
/** 선언한 단계 중 **한 번도 보고되지 않은** 것. 선언해 두고 보내지 않으면 화면의 진행이 멈춘 것처럼 보인다. */
|
|
73
|
+
silentSteps?: string[]
|
|
74
|
+
/** 선언하지 않은 이름으로 보고한 단계. 표준 낱말 밖의 값은 화면이 번역할 수 없다. */
|
|
75
|
+
unknownSteps?: string[]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 기록의 모양을 이름 붙인다 — 커널 낱말로만 가린다(커넥터를 알 필요가 없다). */
|
|
79
|
+
function shapeOf(r: any): string {
|
|
80
|
+
if (r?.inputQuantityList || r?.outputQuantityList) return 'transformation'
|
|
81
|
+
if (r?.taskId) return 'task'
|
|
82
|
+
if (r?.orderId) return 'order'
|
|
83
|
+
if (r?.quantityList || r?.epcList) return 'observation'
|
|
84
|
+
return 'other'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function countMaster(ref: ReferenceMaster): MasterCounts {
|
|
88
|
+
const m: any = ref
|
|
89
|
+
const d = m.productionSpec?.definition
|
|
90
|
+
return {
|
|
91
|
+
locations: m.locations?.length ?? 0,
|
|
92
|
+
areas: (m.locations ?? []).filter((l: any) => l.role === 'area').length,
|
|
93
|
+
equipment: m.equipment?.length ?? 0,
|
|
94
|
+
materialDefinitions: m.materialDefinitions?.length ?? 0,
|
|
95
|
+
operations: m.operations?.length ?? 0,
|
|
96
|
+
...(d
|
|
97
|
+
? {
|
|
98
|
+
materials: d.materials?.length ?? 0,
|
|
99
|
+
materialsWithHome: (d.materials ?? []).filter((x: any) => x.locationType).length,
|
|
100
|
+
routes: d.routes?.length ?? 0,
|
|
101
|
+
recipes: d.recipes?.length ?? 0,
|
|
102
|
+
recipesWithRoute: (d.recipes ?? []).filter((x: any) => x.route).length,
|
|
103
|
+
locationTypes: d.locationTypes?.length ?? 0,
|
|
104
|
+
resourceTypes: d.resourceTypes?.length ?? 0
|
|
105
|
+
}
|
|
106
|
+
: {})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const messageOf = (w: unknown): string => (typeof w === 'string' ? w : `${(w as any)?.code}: ${(w as any)?.message}`)
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 사람이 읽는 줄로 자른다 — **자랐다는 사실을 함께 적는다.**
|
|
114
|
+
*
|
|
115
|
+
* 경고에 값 목록이 붙는다(어느 자리가 걸렀는지). 88건이면 UUID 88개가 한 줄에 쏟아져 보고를 읽을 수
|
|
116
|
+
* 없게 된다. 그래서 사람용 출력에서만 자르고, 전체는 보고 객체(`--json`)에 그대로 남긴다.
|
|
117
|
+
*/
|
|
118
|
+
const brief = (text: string, max = 300): string =>
|
|
119
|
+
text.length <= max ? text : `${text.slice(0, max)}… (${text.length}자 중 앞 ${max}자 — 전체는 보고 객체에 있다)`
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 어댑터 한 벌을 실 원본에 붙여 본다.
|
|
123
|
+
*
|
|
124
|
+
* 실패를 던지지 않는다 — **어디까지 갔는지가 보고의 내용**이다. 연결이 안 되면 그 사실이, 마스터가
|
|
125
|
+
* 실패하면 어느 단계에서 실패했는지가 보고에 남는다(예외로 끝나면 그 정보가 사라진다).
|
|
126
|
+
*/
|
|
127
|
+
export async function probeReference(opts: {
|
|
128
|
+
adapter: ReferenceAdapter
|
|
129
|
+
config: any
|
|
130
|
+
/** 특정 사이트만 볼 때. 없으면 `discoverSites` 가 낸 전부. */
|
|
131
|
+
siteId?: string
|
|
132
|
+
/**
|
|
133
|
+
* 라이브 한 주기를 부를지. **기본은 부르지 않는다** — 첫 주기가 전량 읽기다.
|
|
134
|
+
* `waitMs` 만큼 기다린 뒤 멈춘다(주기가 끝나지 않았으면 그때까지 받은 것만 센다).
|
|
135
|
+
*/
|
|
136
|
+
live?: { waitMs: number; sampleCount?: number }
|
|
137
|
+
/** 표본 건수 — 기본 3건. */
|
|
138
|
+
sampleCount?: number
|
|
139
|
+
}): Promise<ProbeReport> {
|
|
140
|
+
const { adapter, config } = opts
|
|
141
|
+
const sampleCount = opts.sampleCount ?? 3
|
|
142
|
+
const report: ProbeReport = { adapter: adapter.type, capabilities: capabilitiesOf(adapter), masters: [] }
|
|
143
|
+
|
|
144
|
+
if (adapter.testConnection) {
|
|
145
|
+
try {
|
|
146
|
+
report.connection = (await adapter.testConnection(config)) as any
|
|
147
|
+
} catch (e: any) {
|
|
148
|
+
report.connection = { ok: false, error: e?.message ?? String(e) }
|
|
149
|
+
}
|
|
150
|
+
/* 연결이 안 되면 더 가지 않는다 — 그 뒤 실패는 모두 같은 원인의 반복이다. */
|
|
151
|
+
if (report.connection && report.connection.ok === false) return report
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let sites: SiteDescriptor[] = []
|
|
155
|
+
try {
|
|
156
|
+
sites = adapter.discoverSites ? await adapter.discoverSites(config) : []
|
|
157
|
+
} catch (e: any) {
|
|
158
|
+
report.sites = []
|
|
159
|
+
report.masters.push({ siteId: '(사이트를 찾지 못했다)', steps: [], warnings: [], elapsedMs: 0, error: e?.message ?? String(e) })
|
|
160
|
+
return report
|
|
161
|
+
}
|
|
162
|
+
if (opts.siteId) sites = sites.filter(s => s.siteId === opts.siteId)
|
|
163
|
+
report.sites = sites.map(s => ({ siteId: s.siteId, name: s.name }))
|
|
164
|
+
|
|
165
|
+
const declared = new Set<string>(adapter.masterSteps ?? [])
|
|
166
|
+
const reported = new Set<string>()
|
|
167
|
+
const unknown = new Set<string>()
|
|
168
|
+
|
|
169
|
+
for (const site of sites) {
|
|
170
|
+
const t0 = Date.now()
|
|
171
|
+
const steps: ProbeStep[] = []
|
|
172
|
+
try {
|
|
173
|
+
const ref = await adapter.fetchMaster!(config, site, s => {
|
|
174
|
+
reported.add(String(s.key))
|
|
175
|
+
if (!(REFERENCE_STEPS as readonly string[]).includes(String(s.key))) unknown.add(String(s.key))
|
|
176
|
+
steps.push({ key: s.key, detail: s.detail, elapsedMs: Date.now() - t0 })
|
|
177
|
+
})
|
|
178
|
+
const m: any = ref
|
|
179
|
+
const d = m.productionSpec?.definition
|
|
180
|
+
report.masters.push({
|
|
181
|
+
siteId: site.siteId,
|
|
182
|
+
steps,
|
|
183
|
+
counts: countMaster(ref),
|
|
184
|
+
warnings: (m.warnings ?? []).map(messageOf),
|
|
185
|
+
space: m.space,
|
|
186
|
+
identity: m.identity,
|
|
187
|
+
samples: {
|
|
188
|
+
recipes: (d?.recipes ?? []).slice(0, sampleCount),
|
|
189
|
+
routes: (d?.routes ?? []).slice(0, sampleCount),
|
|
190
|
+
locations: (m.locations ?? []).slice(0, sampleCount)
|
|
191
|
+
},
|
|
192
|
+
elapsedMs: Date.now() - t0
|
|
193
|
+
})
|
|
194
|
+
} catch (e: any) {
|
|
195
|
+
/* 어느 단계까지 갔는지를 남긴다 — 그것이 다음에 볼 자리다. */
|
|
196
|
+
report.masters.push({ siteId: site.siteId, steps, warnings: [], elapsedMs: Date.now() - t0, error: e?.message ?? String(e) })
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const silent = [...declared].filter(s => !reported.has(s))
|
|
201
|
+
if (silent.length) report.silentSteps = silent
|
|
202
|
+
if (unknown.size) report.unknownSteps = [...unknown]
|
|
203
|
+
|
|
204
|
+
if (opts.live && adapter.openLiveFeed && sites.length) {
|
|
205
|
+
const t0 = Date.now()
|
|
206
|
+
const got: unknown[] = []
|
|
207
|
+
try {
|
|
208
|
+
const stop = adapter.openLiveFeed(config, sites[0], rs => got.push(...rs))
|
|
209
|
+
await new Promise(r => setTimeout(r, opts.live.waitMs))
|
|
210
|
+
stop()
|
|
211
|
+
const byShape: Record<string, number> = {}
|
|
212
|
+
for (const r of got) byShape[shapeOf(r)] = (byShape[shapeOf(r)] ?? 0) + 1
|
|
213
|
+
report.live = {
|
|
214
|
+
records: got.length,
|
|
215
|
+
byShape,
|
|
216
|
+
samples: got.slice(0, opts.live.sampleCount ?? sampleCount),
|
|
217
|
+
elapsedMs: Date.now() - t0
|
|
218
|
+
}
|
|
219
|
+
} catch (e: any) {
|
|
220
|
+
report.live = { records: got.length, byShape: {}, samples: [], elapsedMs: Date.now() - t0, error: e?.message ?? String(e) }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return report
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** 보고를 사람이 읽는 줄로 — 화면이 아니라 터미널·로그가 읽는다. */
|
|
228
|
+
export function formatProbeReport(r: ProbeReport): string {
|
|
229
|
+
const out: string[] = []
|
|
230
|
+
out.push(`커넥터 ${r.adapter} · 능력 ${r.capabilities.length ? r.capabilities.join(',') : '(없음)'}`)
|
|
231
|
+
if (r.connection) {
|
|
232
|
+
out.push(`연결 ${r.connection.ok ? 'ok' : '실패'}${r.connection.readiness ? ` · 읽기준비 ${r.connection.readiness}` : ''}`)
|
|
233
|
+
for (const n of r.connection.notes ?? []) out.push(` · ${brief(n)}`)
|
|
234
|
+
if (r.connection.error) out.push(` · ${r.connection.error}`)
|
|
235
|
+
}
|
|
236
|
+
for (const m of r.masters) {
|
|
237
|
+
out.push(`사이트 ${m.siteId} — ${(m.elapsedMs / 1000).toFixed(1)}초`)
|
|
238
|
+
for (const s of m.steps) out.push(` [${(s.elapsedMs / 1000).toFixed(1)}s] ${s.key}${s.detail ? ` — ${s.detail}` : ''}`)
|
|
239
|
+
if (m.error) out.push(` 실패: ${m.error}`)
|
|
240
|
+
if (m.counts) {
|
|
241
|
+
const c = m.counts
|
|
242
|
+
out.push(
|
|
243
|
+
` 로케이션 ${c.locations}(존 ${c.areas}) · 설비 ${c.equipment} · 자재정의 ${c.materialDefinitions} · 공정 ${c.operations}`
|
|
244
|
+
)
|
|
245
|
+
if (c.materials !== undefined) {
|
|
246
|
+
out.push(
|
|
247
|
+
` 선언자재 ${c.materials}(보관 로케이션 있는 것 ${c.materialsWithHome}) · 경로 ${c.routes} · 레시피 ${c.recipes}(경로 붙은 것 ${c.recipesWithRoute})`
|
|
248
|
+
)
|
|
249
|
+
} else {
|
|
250
|
+
out.push(' 생산 선언 없음 — 레시피가 하나도 성립하지 않았다')
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const w of m.warnings) out.push(` 경고 ${brief(w)}`)
|
|
254
|
+
}
|
|
255
|
+
if (r.silentSteps?.length) out.push(`선언했으나 보고되지 않은 단계: ${r.silentSteps.join(', ')}`)
|
|
256
|
+
if (r.unknownSteps?.length) out.push(`표준 낱말 밖의 단계 이름: ${r.unknownSteps.join(', ')}`)
|
|
257
|
+
if (r.live) {
|
|
258
|
+
out.push(
|
|
259
|
+
`라이브 한 주기 — 기록 ${r.live.records}건 (${Object.entries(r.live.byShape).map(([k, v]) => `${k} ${v}`).join(' · ') || '없음'}) · ${(r.live.elapsedMs / 1000).toFixed(1)}초`
|
|
260
|
+
)
|
|
261
|
+
if (r.live.error) out.push(` 실패: ${r.live.error}`)
|
|
262
|
+
}
|
|
263
|
+
return out.join('\n')
|
|
264
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 유입 진행 구독 — **트윈을 만드는 동안 무엇을 하고 있는지 화면이 안다.**
|
|
3
|
+
*
|
|
4
|
+
* 마스터 읽기는 원본에 여러 질의를 보내는 긴 작업이고, 그 사이 화면은 아무것도 알 수 없었다. 단계를
|
|
5
|
+
* 아는 것은 어댑터뿐이므로 어댑터가 **표준 낱말**로 말하고(`REFERENCE_STEPS`) 호스트가 자기 두 단계를
|
|
6
|
+
* 더해 이 채널로 흘린다.
|
|
7
|
+
*
|
|
8
|
+
* 낱말이 표준이므로 화면은 **단계마다 번역 하나**만 두면 모든 커넥터를 보여 준다.
|
|
9
|
+
*/
|
|
10
|
+
import { Arg, Field, Int, ObjectType, Resolver, Root, Subscription } from 'type-graphql'
|
|
11
|
+
import { pubsub } from '@things-factory/shell'
|
|
12
|
+
import { filter, pipe } from 'graphql-yoga'
|
|
13
|
+
|
|
14
|
+
import { REFERENCE_STEPS } from './reference-progress.js'
|
|
15
|
+
|
|
16
|
+
@ObjectType({ description: 'One step of progress while a reference is being imported into twins.' })
|
|
17
|
+
export class TwinReferenceProgressMessage {
|
|
18
|
+
@Field({ description: 'Which reference (source) this belongs to.' })
|
|
19
|
+
source: string
|
|
20
|
+
|
|
21
|
+
@Field({ description: 'Which site is being built.' })
|
|
22
|
+
siteId: string
|
|
23
|
+
|
|
24
|
+
@Field({ description: `Standard step name — one of: ${REFERENCE_STEPS.join(' | ')}.` })
|
|
25
|
+
step: string
|
|
26
|
+
|
|
27
|
+
@Field(type => Int, { description: 'Steps finished for this site.' })
|
|
28
|
+
done: number
|
|
29
|
+
|
|
30
|
+
@Field(type => Int, {
|
|
31
|
+
nullable: true,
|
|
32
|
+
description: 'Total steps for this site. Absent when the adapter did not declare its steps — the UI then shows no percentage rather than inventing one.'
|
|
33
|
+
})
|
|
34
|
+
total?: number
|
|
35
|
+
|
|
36
|
+
@Field(type => Int, { description: 'Index of the site being built (0-based).' })
|
|
37
|
+
siteIndex: number
|
|
38
|
+
|
|
39
|
+
@Field(type => Int, { description: 'How many sites are being built.' })
|
|
40
|
+
siteCount: number
|
|
41
|
+
|
|
42
|
+
@Field({ nullable: true, description: 'A fact within the step, such as how many rows were read.' })
|
|
43
|
+
detail?: string
|
|
44
|
+
|
|
45
|
+
@Field({ nullable: true, description: 'True when this step failed. The UI keeps the progress panel and marks this step.' })
|
|
46
|
+
failed?: boolean
|
|
47
|
+
|
|
48
|
+
@Field({ nullable: true, description: 'Why it failed. Present only on failure.' })
|
|
49
|
+
error?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Resolver()
|
|
53
|
+
export class TwinReferenceProgressSubscription {
|
|
54
|
+
@Subscription(returns => TwinReferenceProgressMessage, {
|
|
55
|
+
subscribe: ({ args, context }) => {
|
|
56
|
+
const { source } = args
|
|
57
|
+
const domainId = (context as any)?.state?.domain?.id
|
|
58
|
+
/* 테넌트 격리 — 도메인이 없으면 빈 구독이다(다른 테넌트의 진행을 흘리지 않는다). */
|
|
59
|
+
if (!domainId) return pipe(pubsub.subscribe('twin-reference-progress'), filter(() => false))
|
|
60
|
+
return pipe(
|
|
61
|
+
pubsub.subscribe('twin-reference-progress'),
|
|
62
|
+
filter((p: { twinReferenceProgress: TwinReferenceProgressMessage }) => p.twinReferenceProgress.source === source)
|
|
63
|
+
)
|
|
64
|
+
},
|
|
65
|
+
description: 'Progress while importing a reference — standard step names, so one translation per step covers every connector.'
|
|
66
|
+
})
|
|
67
|
+
twinReferenceProgress(
|
|
68
|
+
@Root() payload: { twinReferenceProgress: TwinReferenceProgressMessage },
|
|
69
|
+
@Arg('source') source: string
|
|
70
|
+
): TwinReferenceProgressMessage {
|
|
71
|
+
return payload.twinReferenceProgress
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 유입 진행 단계 — **모든 커넥터가 같은 낱말로 말한다.**
|
|
3
|
+
*
|
|
4
|
+
* ── 왜 표준화하나 (2026-08-21) ──────────────────────────────────────────────
|
|
5
|
+
* 마스터 읽기는 원본에 여러 질의를 보내는 긴 작업이고, 그 사이 화면은 아무것도 알 수 없었다. 커넥터마다
|
|
6
|
+
* 자기 낱말로 말하게 두면 화면이 커넥터 수만큼 문구를 알아야 하고, 새 커넥터가 생기면 화면에 낯선 글자가
|
|
7
|
+
* 나온다.
|
|
8
|
+
*
|
|
9
|
+
* 그래서 낱말을 고정한다. 그리고 그 낱말은 **트윈의 자리 이름**이다 — 커넥터가 지금 채우는 자리가 곧
|
|
10
|
+
* 지금 하는 일이다(`design/integration/connector-checklist.md` 의 마스터 축과 같은 이름).
|
|
11
|
+
*
|
|
12
|
+
* 그러면 셋이 함께 얻어진다.
|
|
13
|
+
* · 화면은 **단계마다 하나의 번역**만 두면 모든 커넥터를 보여 준다.
|
|
14
|
+
* · 사람은 원본이 달라도 **같은 말로** 진행을 읽는다.
|
|
15
|
+
* · 실패했을 때 「어느 자리를 읽다 실패했다」가 곧 「트윈의 무엇이 비었다」가 된다.
|
|
16
|
+
*
|
|
17
|
+
* ── 퍼센트를 만들려면 분모가 있어야 한다 ────────────────────────────────────
|
|
18
|
+
* 어댑터가 **자기가 보낼 단계를 미리 선언**한다(`ReferenceAdapter.masterSteps`). 그것이 분모다. 선언하지
|
|
19
|
+
* 않으면 화면은 퍼센트를 보이지 않고 단계 이름만 보인다 — **모르는 진행률을 지어내지 않는다.**
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 표준 단계. 순서는 대개 이 차례이지만 **강제하지 않는다** — 원본에 따라 없는 자리가 있고, 그때는 그
|
|
24
|
+
* 단계를 선언하지도 보내지도 않는다.
|
|
25
|
+
*/
|
|
26
|
+
export const REFERENCE_STEPS = [
|
|
27
|
+
/** 원본 스키마 확인 — 무엇을 물을 수 있는지 먼저 본다(판이 다를 수 있다). */
|
|
28
|
+
'schema',
|
|
29
|
+
/** 공간·좌표 — 자리가 놓일 좌표계. */
|
|
30
|
+
'space',
|
|
31
|
+
/** 자리 — 물건이 놓이고 작업이 일어나는 곳. */
|
|
32
|
+
'locations',
|
|
33
|
+
/** 설비 — 일하는 것. */
|
|
34
|
+
'equipment',
|
|
35
|
+
/** 공정 — 무슨 일을 하는가. */
|
|
36
|
+
'operations',
|
|
37
|
+
/** 자재 — 무엇이 흐르는가. */
|
|
38
|
+
'materials',
|
|
39
|
+
/** 경로 — 공정의 순서. */
|
|
40
|
+
'routes',
|
|
41
|
+
/** 레시피 — 투입과 산출. */
|
|
42
|
+
'recipes',
|
|
43
|
+
/** 시험 명세 — 무엇을 어떻게 검사하는가. */
|
|
44
|
+
'testSpecifications',
|
|
45
|
+
/** 인원. */
|
|
46
|
+
'persons',
|
|
47
|
+
/** 물리자산. */
|
|
48
|
+
'assets',
|
|
49
|
+
/** 트윈 생성 — 여기부터는 호스트가 하는 일이다(어댑터가 선언하지 않는다). */
|
|
50
|
+
'provision',
|
|
51
|
+
/** 기동. */
|
|
52
|
+
'start'
|
|
53
|
+
] as const
|
|
54
|
+
|
|
55
|
+
export type ReferenceStep = (typeof REFERENCE_STEPS)[number]
|
|
56
|
+
|
|
57
|
+
/** 호스트가 마스터 읽기 뒤에 하는 단계 — 어댑터의 선언에 이만큼을 더해 분모를 만든다. */
|
|
58
|
+
export const HOST_STEPS: ReferenceStep[] = ['provision', 'start']
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 한 단계의 진행 — 구독으로 흐르는 메시지의 몸통.
|
|
62
|
+
*
|
|
63
|
+
* `detail` 은 **그 단계 안에서 사람이 알아야 하는 것**이다(읽은 건수 등). 비어 있어도 된다 — 지어내지
|
|
64
|
+
* 않는다.
|
|
65
|
+
*/
|
|
66
|
+
export interface ReferenceProgress {
|
|
67
|
+
/** 어느 사이트를 만드는 중인가. */
|
|
68
|
+
siteId: string
|
|
69
|
+
step: ReferenceStep
|
|
70
|
+
/** 이 사이트에서 끝낸 단계 수. */
|
|
71
|
+
done: number
|
|
72
|
+
/** 이 사이트의 전체 단계 수 — 어댑터가 선언하지 않으면 없다(그때 퍼센트를 만들지 않는다). */
|
|
73
|
+
total?: number
|
|
74
|
+
/** 여러 사이트를 한 번에 만들 때, 지금 몇 번째인가. */
|
|
75
|
+
siteIndex: number
|
|
76
|
+
siteCount: number
|
|
77
|
+
/** 그 단계 안의 사실 — 「자리 128건」처럼. */
|
|
78
|
+
detail?: string
|
|
79
|
+
/** 이 단계에서 실패했는가. 실패해도 화면이 그 자리를 그대로 보여 줄 수 있어야 한다. */
|
|
80
|
+
failed?: boolean
|
|
81
|
+
/** 실패 이유 — 실패했을 때만. */
|
|
82
|
+
error?: string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 전체 진행률 — **분모를 알 때만 낸다.**
|
|
87
|
+
*
|
|
88
|
+
* 사이트가 여럿이면 사이트 사이의 진행도 함께 센다: 앞선 사이트는 끝난 것으로 본다.
|
|
89
|
+
*/
|
|
90
|
+
export function referencePercent(p: ReferenceProgress): number | undefined {
|
|
91
|
+
if (!p.total || p.total <= 0 || p.siteCount <= 0) return undefined
|
|
92
|
+
const perSite = 1 / p.siteCount
|
|
93
|
+
const within = Math.min(p.done, p.total) / p.total
|
|
94
|
+
return Math.round((p.siteIndex * perSite + within * perSite) * 100)
|
|
95
|
+
}
|