@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
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.IEC61850_CONCEPTS = void 0;
|
|
4
4
|
exports.iec61850Coverage = iec61850Coverage;
|
|
5
|
+
/*
|
|
6
|
+
* IEC 61850 적합성 기준 — **에너지 트윈이 표준의 어디를 채우고 있나.**
|
|
7
|
+
*
|
|
8
|
+
* ── 왜 따로 있나 ────────────────────────────────────────────────────────────
|
|
9
|
+
* 인스펙션의 적합성 패널은 트윈 종류와 무관하게 ISA-95 하나만 말하고 있었다. 에너지 트윈에서 그것은
|
|
10
|
+
* 거짓은 아니지만(설비·계층·속성은 에너지에서도 ISA-95 자원이다) **부족하다** — 계량기·차단기·태양광·
|
|
11
|
+
* 축전지의 어휘를 주는 것은 IEC 61850 이고, 무엇이 비었는지도 그 목록으로만 셀 수 있다.
|
|
12
|
+
*
|
|
13
|
+
* ── 주장 수준을 못 박는다 ───────────────────────────────────────────────────
|
|
14
|
+
* 여기서 재는 것은 **어휘와 모델**이다. 프로토콜(MMS·GOOSE·샘플드값)을 구현했다는 뜻이 아니며,
|
|
15
|
+
* 그 주장은 적합성 시험이 뒷받침해야 한다. 표 이름을 「IEC 61850 (vocabulary)」로 쓰는 이유다.
|
|
16
|
+
*
|
|
17
|
+
* ── 값은 관측이 아니라 선언이다 ─────────────────────────────────────────────
|
|
18
|
+
* ISA-95 표와 같은 규율이다(`isa95-coverage.ts` 머리말): 사람이 판단해 적고, 틀리면 여기를 고친다.
|
|
19
|
+
* 자동으로 알 수 있는 것 하나만 자동으로 한다 — 축 이름이 커널 축 목록에 없으면 구조가 없는 것이다.
|
|
20
|
+
*/
|
|
21
|
+
const isa95_coverage_js_1 = require("./isa95-coverage.js");
|
|
5
22
|
/*
|
|
6
23
|
* Part 7-4 — 논리 노드 클래스. 이 프로파일이 쓰는 것만 적는다(표준 전체를 옮기지 않는다):
|
|
7
24
|
* 계량·계측·개폐·발전·저장.
|
|
@@ -80,13 +97,7 @@ const LOGICAL_NODES = [
|
|
|
80
97
|
];
|
|
81
98
|
exports.IEC61850_CONCEPTS = [...LOGICAL_NODES]; // vocabulary-guard: allow — 표준 용어(위 주석)
|
|
82
99
|
function iec61850Coverage(axes) {
|
|
83
|
-
const
|
|
84
|
-
const concepts = exports.IEC61850_CONCEPTS.map(c => {
|
|
85
|
-
const axisMissing = !!c.axis && !known.has(c.axis);
|
|
86
|
-
const shownOn = c.shownOn?.filter(a => known.has(a));
|
|
87
|
-
const fixed = shownOn && shownOn.length !== c.shownOn?.length ? { ...c, shownOn } : c;
|
|
88
|
-
return axisMissing ? { ...fixed, structure: 'none', axisMissing } : fixed;
|
|
89
|
-
});
|
|
100
|
+
const concepts = exports.IEC61850_CONCEPTS.map(c => (0, isa95_coverage_js_1.reconcileAxes)(c, new Set(axes)));
|
|
90
101
|
const count = (k) => concepts.filter(c => c[k] === 'full').length;
|
|
91
102
|
return {
|
|
92
103
|
/* 이름에 (vocabulary) 를 달아 둔다 — 프로토콜 적합성으로 읽히지 않게. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iec61850-coverage.js","sourceRoot":"","sources":["../../../server/service/twin-model/iec61850-coverage.ts"],"names":[],"mappings":";;;AAiGA,
|
|
1
|
+
{"version":3,"file":"iec61850-coverage.js","sourceRoot":"","sources":["../../../server/service/twin-model/iec61850-coverage.ts"],"names":[],"mappings":";;;AAiGA,4CAaC;AA9GD;;;;;;;;;;;;;;;GAeG;AACH,2DAA0F;AAE1F;;;GAGG;AACH,MAAM,aAAa,GAAmB;IACpC;;;OAGG;IACH;QACE,GAAG,EAAE,MAAM;QACX,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,yBAAyB;KAChC;IACD;;;OAGG;IACH;QACE,GAAG,EAAE,MAAM;QACX,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,eAAe;QACrB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,yBAAyB;KAChC;IACD;;;;;;;;OAQG;IACH;QACE,GAAG,EAAE,MAAM;QACX,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,CAAC,WAAW,CAAC;QACtB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,yBAAyB;KAChC;IACD;QACE,GAAG,EAAE,MAAM;QACX,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,CAAC,WAAW,CAAC;QACtB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,yBAAyB;KAChC;IACD;QACE,GAAG,EAAE,MAAM;QACX,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,CAAC,WAAW,CAAC;QACtB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,yBAAyB;KAChC;CACF,CAAA;AAEY,QAAA,iBAAiB,GAAmB,CAAC,GAAG,aAAa,CAAC,CAAA,CAAC,wCAAwC;AAE5G,SAAgB,gBAAgB,CAAC,IAAc;IAK7C,MAAM,QAAQ,GAAG,yBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAA,iCAAa,EAAC,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC5E,MAAM,KAAK,GAAG,CAAC,CAAuC,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,MAAM,CAAA;IACvG,OAAO;QACL,mDAAmD;QACnD,KAAK,EAAE,wBAAwB;QAC/B,QAAQ;QACR,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE;KAC7H,CAAA;AACH,CAAC","sourcesContent":["/*\n * IEC 61850 적합성 기준 — **에너지 트윈이 표준의 어디를 채우고 있나.**\n *\n * ── 왜 따로 있나 ────────────────────────────────────────────────────────────\n * 인스펙션의 적합성 패널은 트윈 종류와 무관하게 ISA-95 하나만 말하고 있었다. 에너지 트윈에서 그것은\n * 거짓은 아니지만(설비·계층·속성은 에너지에서도 ISA-95 자원이다) **부족하다** — 계량기·차단기·태양광·\n * 축전지의 어휘를 주는 것은 IEC 61850 이고, 무엇이 비었는지도 그 목록으로만 셀 수 있다.\n *\n * ── 주장 수준을 못 박는다 ───────────────────────────────────────────────────\n * 여기서 재는 것은 **어휘와 모델**이다. 프로토콜(MMS·GOOSE·샘플드값)을 구현했다는 뜻이 아니며,\n * 그 주장은 적합성 시험이 뒷받침해야 한다. 표 이름을 「IEC 61850 (vocabulary)」로 쓰는 이유다.\n *\n * ── 값은 관측이 아니라 선언이다 ─────────────────────────────────────────────\n * ISA-95 표와 같은 규율이다(`isa95-coverage.ts` 머리말): 사람이 판단해 적고, 틀리면 여기를 고친다.\n * 자동으로 알 수 있는 것 하나만 자동으로 한다 — 축 이름이 커널 축 목록에 없으면 구조가 없는 것이다.\n */\nimport { reconcileAxes, type Isa95Concept, type CoverageLevel } from './isa95-coverage.js'\n\n/*\n * Part 7-4 — 논리 노드 클래스. 이 프로파일이 쓰는 것만 적는다(표준 전체를 옮기지 않는다):\n * 계량·계측·개폐·발전·저장.\n */\nconst LOGICAL_NODES: Isa95Concept[] = [ // vocabulary-guard: allow — IEC 61850 의 「논리 노드(logical node)」는 표준 용어다(우리가 은퇴시킨 node 어휘가 아니다)\n /*\n * 계량(적산) — 커넥터가 누적 전력량을 실어 오고 상태가 지점별로 들고 있다. 화면은 계량 컴포넌트가\n * 지점의 값을 보인다.\n */\n {\n std: 'MMTR',\n part: '7-4',\n label: 'twin.iec61850.MMTR',\n axis: 'equipment',\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.MMTR'\n },\n /*\n * 계측(유효전력·역률) — 표본이 수요 구간으로 접히고, 구간·피크·계약 대비가 그 위에 선다.\n * 이 프로파일에서 가장 두껍게 채운 자리다.\n */\n {\n std: 'MMXU',\n part: '7-4',\n label: 'twin.iec61850.MMXU',\n axis: 'demandWindows',\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.MMXU'\n },\n /*\n * ── 셋이 함께 올라갔다 (2026-08-15) ──────────────────────────────────────\n * 개폐·발전·저장은 오래 「구조만 있음」이었다. 능력이 상태 필드를 선언해 두었는데 **값을 받는 문이\n * 없었기 때문**이다. 이제 셋 다 실제로 돈다: 설비 상태가 계약에 서고(`EquipmentState`), 커널이\n * `energy.equipment` 로 받아 그 설비에 적고, 씬 컴포넌트가 그린다.\n *\n * 세 층을 함께 올리는 이유: 값이 없는데 화면만 만들면 영원히 「—」인 카드가 되고, 화면이 없는데\n * 기능만 올리면 아무도 그 값을 보지 못한다. 「값이 먼저, 컴포넌트는 그 다음」의 결과다.\n */\n {\n std: 'XCBR',\n part: '7-4',\n label: 'twin.iec61850.XCBR',\n axis: null,\n shownOn: ['equipment'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.XCBR'\n },\n {\n std: 'DPVA',\n part: '7-4',\n label: 'twin.iec61850.DPVA',\n axis: null,\n shownOn: ['equipment'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.DPVA'\n },\n {\n std: 'ZBAT',\n part: '7-4',\n label: 'twin.iec61850.ZBAT',\n axis: null,\n shownOn: ['equipment'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.ZBAT'\n }\n]\n\nexport const IEC61850_CONCEPTS: Isa95Concept[] = [...LOGICAL_NODES] // vocabulary-guard: allow — 표준 용어(위 주석)\n\nexport function iec61850Coverage(axes: string[]): {\n model: string\n concepts: (Isa95Concept & { axisMissing?: boolean })[]\n totals: { concepts: number; structure: number; behavior: number; surface: number }\n} {\n const concepts = IEC61850_CONCEPTS.map(c => reconcileAxes(c, new Set(axes)))\n const count = (k: 'structure' | 'behavior' | 'surface') => concepts.filter(c => c[k] === 'full').length\n return {\n /* 이름에 (vocabulary) 를 달아 둔다 — 프로토콜 적합성으로 읽히지 않게. */\n model: 'IEC 61850 (vocabulary)',\n concepts,\n totals: { concepts: concepts.length, structure: count('structure'), behavior: count('behavior'), surface: count('surface') }\n }\n}\n"]}
|
|
@@ -34,6 +34,20 @@ export declare const ISA95_CONCEPTS: Isa95Concept[];
|
|
|
34
34
|
* `axis` 를 커널 축 목록과 맞춰 본다: 축이 있다고 적혀 있는데 커널에 없으면 그 선언이 낡은 것이므로
|
|
35
35
|
* **구조 없음으로 낮추고 그 사실을 말한다**(조용히 통과시키면 이 표가 거짓이 된다).
|
|
36
36
|
*/
|
|
37
|
+
/**
|
|
38
|
+
* 선언한 축을 커널의 축 목록과 맞춘다 — **어긋난 것을 조용히 지우지 않는다.**
|
|
39
|
+
*
|
|
40
|
+
* 두 자리가 어긋날 수 있다.
|
|
41
|
+
* · `axis` — 그 개념 자신의 문. 커널에 없으면 구조를 「없음」으로 낮추고 그 사실을 말한다.
|
|
42
|
+
* · `shownOn` — 그 사실을 들고 있는 다른 축. 예전에는 없는 축을 **말없이 걸러 냈다.** 그러면 그 줄은
|
|
43
|
+
* 「개념 축이 없습니다」로 보이고, 사람은 그것을 **원래 그런 것**으로 읽는다. 실제로 그 일이 있었다:
|
|
44
|
+
* EPCIS 표의 일곱 줄이 커널에 없는 축(`items`)에 기대고 있었는데, 경고가 뜬 것은 두 줄뿐이었다.
|
|
45
|
+
*
|
|
46
|
+
* 둘 중 하나라도 어긋나면 `axisMissing` 이다 — 표가 스스로 「최신이 아니다」라고 말하게 한다.
|
|
47
|
+
*/
|
|
48
|
+
export declare function reconcileAxes(c: Isa95Concept, known: Set<string>): Isa95Concept & {
|
|
49
|
+
axisMissing?: boolean;
|
|
50
|
+
};
|
|
37
51
|
export declare function isa95Coverage(axes: string[]): {
|
|
38
52
|
model: string;
|
|
39
53
|
concepts: (Isa95Concept & {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
32
|
exports.ISA95_CONCEPTS = void 0;
|
|
33
|
+
exports.reconcileAxes = reconcileAxes;
|
|
33
34
|
exports.isa95Coverage = isa95Coverage;
|
|
34
35
|
/*
|
|
35
36
|
* 가용 능력(2026-08-12, 커널 0.6.8) — **판정과 이유가 상태에 실려 온다**(`capability {available, reason}`).
|
|
@@ -134,16 +135,29 @@ exports.ISA95_CONCEPTS = [...PART2, ...PART4];
|
|
|
134
135
|
* `axis` 를 커널 축 목록과 맞춰 본다: 축이 있다고 적혀 있는데 커널에 없으면 그 선언이 낡은 것이므로
|
|
135
136
|
* **구조 없음으로 낮추고 그 사실을 말한다**(조용히 통과시키면 이 표가 거짓이 된다).
|
|
136
137
|
*/
|
|
138
|
+
/**
|
|
139
|
+
* 선언한 축을 커널의 축 목록과 맞춘다 — **어긋난 것을 조용히 지우지 않는다.**
|
|
140
|
+
*
|
|
141
|
+
* 두 자리가 어긋날 수 있다.
|
|
142
|
+
* · `axis` — 그 개념 자신의 문. 커널에 없으면 구조를 「없음」으로 낮추고 그 사실을 말한다.
|
|
143
|
+
* · `shownOn` — 그 사실을 들고 있는 다른 축. 예전에는 없는 축을 **말없이 걸러 냈다.** 그러면 그 줄은
|
|
144
|
+
* 「개념 축이 없습니다」로 보이고, 사람은 그것을 **원래 그런 것**으로 읽는다. 실제로 그 일이 있었다:
|
|
145
|
+
* EPCIS 표의 일곱 줄이 커널에 없는 축(`items`)에 기대고 있었는데, 경고가 뜬 것은 두 줄뿐이었다.
|
|
146
|
+
*
|
|
147
|
+
* 둘 중 하나라도 어긋나면 `axisMissing` 이다 — 표가 스스로 「최신이 아니다」라고 말하게 한다.
|
|
148
|
+
*/
|
|
149
|
+
function reconcileAxes(c, known) {
|
|
150
|
+
const ownMissing = !!c.axis && !known.has(c.axis);
|
|
151
|
+
const shownOn = c.shownOn?.filter(a => known.has(a));
|
|
152
|
+
const lostShown = !!c.shownOn && (shownOn?.length ?? 0) !== c.shownOn.length;
|
|
153
|
+
const fixed = lostShown ? { ...c, shownOn } : c;
|
|
154
|
+
if (!ownMissing && !lostShown)
|
|
155
|
+
return fixed;
|
|
156
|
+
/* 구조를 낮추는 것은 **자기 문이 없어진 경우**다 — 남의 축에서 보이던 것은 구조가 아니라 표시의 문제다. */
|
|
157
|
+
return ownMissing ? { ...fixed, structure: 'none', axisMissing: true } : { ...fixed, axisMissing: true };
|
|
158
|
+
}
|
|
137
159
|
function isa95Coverage(axes) {
|
|
138
|
-
const
|
|
139
|
-
const concepts = exports.ISA95_CONCEPTS.map(c => {
|
|
140
|
-
const axisMissing = !!c.axis && !known.has(c.axis);
|
|
141
|
-
/* 가리키는 곳이 없어진 축은 **가리키지 않는다** — 죽은 문으로 보내면 화면이 거짓말을 한다.
|
|
142
|
-
`axis` 와 같은 규율이고, 여기가 비면 그 줄은 다시 "축이 없다" 로 정직하게 돌아간다. */
|
|
143
|
-
const shownOn = c.shownOn?.filter(a => known.has(a));
|
|
144
|
-
const fixed = shownOn && shownOn.length !== c.shownOn?.length ? { ...c, shownOn } : c;
|
|
145
|
-
return axisMissing ? { ...fixed, structure: 'none', axisMissing } : fixed;
|
|
146
|
-
});
|
|
160
|
+
const concepts = exports.ISA95_CONCEPTS.map(c => reconcileAxes(c, new Set(axes)));
|
|
147
161
|
/* 채운 수는 `full` 만 센다 — 부분을 채운 것으로 세면 기준이 스스로를 속인다. */
|
|
148
162
|
const count = (k) => concepts.filter(c => c[k] === 'full').length;
|
|
149
163
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"isa95-coverage.js","sourceRoot":"","sources":["../../../server/service/twin-model/isa95-coverage.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * ISA-95 적합성 기준 — **표준이 요구하는 것의 목록.**\n *\n * ── 왜 이 파일이 필요한가 ──────────────────────────────────────────────────────\n * 지금까지 화면은 \"우리가 가진 축\" 만 보였다. 그러면 채운 것은 세어지지만 **빠진 것은 셀 수 없다** —\n * 없는 것은 화면에 없으므로 아무도 그것을 묻지 않는다. 표준 준수는 \"우리 축에 표준 이름을 붙였다\"\n * 가 아니라 **표준의 목록을 기준으로 빠진 것이 없다**는 말이어야 한다. 기준이 코드로 있어야 그\n * 문장을 검사할 수 있다.\n *\n * ── 세 층에서 본다 ────────────────────────────────────────────────────────────\n * 표준 준수는 한 층의 일이 아니다. 개념마다 셋을 따로 본다:\n *\n * · `structure` — **데이터구조**가 있나(커널 축·호스트 표에 이 개념의 자리가 있나)\n * · `behavior` — **기능**이 그것을 쓰나(커널이 계획·집행·집계에서 실제로 다루나)\n * · `surface` — **UI 노출**이 있나(사용자가 그것을 볼 수 있나)\n *\n * 셋을 합치면 \"구조는 있는데 아무도 안 쓰는 컬럼\" 과 \"커널은 쓰는데 화면에 없는 사실\" 이 같은\n * 값으로 보인다. 실제로 이 프로젝트에서 둘 다 나왔다(인력 명세는 구조·기능이 있었지만 화면에\n * 없었고, `properties` 는 구조만 있다).\n *\n * ── 값은 관측이 아니라 선언이다 ────────────────────────────────────────────────\n * 여기 적힌 것은 **사람이 판단해 적은 사실**이다(자동 판정이 아니다). 그래서 틀릴 수 있고, 틀리면\n * 여기를 고쳐야 한다 — 화면은 이 선언을 그대로 보이고 지어내지 않는다. 자동으로 알 수 있는 것\n * 하나는 자동으로 한다: `axis` 가 커널 축 목록에 없으면 그것은 **구조가 없는 것**이다(오타 방지).\n *\n * ── 이 표가 커널에 없는 이유 ──────────────────────────────────────────────────\n * 커널은 **런타임 모델의 SSOT** 다(무엇이 있는가). 이 표는 **참조 모델의 목록**이다(무엇이 있어야\n * 하는가). 둘은 다른 종류의 사실이고, 후자는 커널을 배포하지 않고도 늘어야 한다.\n */\n\n/** 한 층의 상태 — 있음 · 부분 · 없음. \"부분\" 을 두는 이유: 절반 있는 것을 있다고 하면 기준이 죽는다. */\nexport type CoverageLevel = 'full' | 'partial' | 'none'\n\nexport interface Isa95Concept {\n /** 표준이 부르는 이름 — 이 표의 키다(우리 어휘가 아니라 표준 어휘). */\n std: string\n /** 표준의 어느 부분인가 — Part 2(자원 모델) · Part 4(일정·실적) 등. */\n part: string\n /** 사람이 읽는 이름(i18n 키). 화면이 번역한다. */\n label: string\n /** 우리 커널 축 — 있으면 데이터구조가 있는 것이다. 없으면 `null`. */\n axis: string | null\n /** 세 층의 상태. `structure` 는 `axis` 로도 검증된다. */\n structure: CoverageLevel\n behavior: CoverageLevel\n surface: CoverageLevel\n /** 왜 그 수준인지 — **모르는 것을 감추지 않기 위해** 한 줄로 적는다(i18n 키). */\n note?: string\n /**\n * 자기 축이 없을 때 **어디서 보이나** — 그 사실을 들고 있는 축들.\n *\n * 「어디에」칸이 두 가지를 합쳐고 있었다: *아직 축이 없다(부채)* 와 *축일 수 없다(설계)*. 가용 능력은\n * 개체가 아니라 자원의 지금 상태이므로 축이 될 수 없는데, 세 층이 다 완전한 줄이 \"개념 축이 없습니다\"\n * 라고만 말하면 **표가 스스로 모순된 말을 한다**(다 됐다면서 없다고 한다).\n *\n * 그래서 축일 수 없는 것은 갈 곳을 가리킨다. 여기가 비어 있고 축도 없으면 그것은 **진짜 빈칸**이다.\n */\n shownOn?: string[]\n}\n\n/*\n * 가용 능력(2026-08-12, 커널 0.6.8) — **판정과 이유가 상태에 실려 온다**(`capability {available, reason}`).\n *\n * 셋 다 완전인 근거: 상태가 판정을 들고 오고(시뮬·미러가 같은 함수로 파생한다), 배정이 같은 판정을\n * 쓰고, 두 화면이 같은 낱말로 그 이유를 보인다(자원 탭 배지 · 인스펙션 관측 열).\n *\n * 왜 축이 없는가 — 가용 능력은 개체가 아니라 **자원의 지금 상태**다. 개념 카드로 세우면 사람·설비와\n * 나란히 놓여 \"능력이 몇 개인가\" 를 세게 되는데, 그건 물을 일이 아니다.\n */\n\n/*\n * 생산 능력(2026-08-13, 커널 0.6.12) — **구간의 양을 표준 종류로 구분한다.**\n *\n * `structure` 완전: 계약이 보고서를 낸다(`operationsCapabilityOf`) — 구간(StartTime·EndTime) · 표준\n * 열거값(Committed·Available·Unattainable) · 공정별 분해 · 제약하는 축. 낱말은 B2MML 대조 후 표준 것을\n * 그대로 쓴다(우리 낱말을 만들지 않았다).\n * `behavior` 완전: 약정은 오더에서, 달성불가는 자원 판정(`capabilityOf`)에서 온다 — 그리고 그 판정은\n * 배정이 쓰는 것과 같은 한 벌이다. 표준의 `Unattainable` 정의가 그 판정과 겹친다.\n * `surface` 완전: 두 자리에서 보인다 — 성과 화면이 현장 전체의 \"앞으로 더 받을 수 있는 몫\" 을, 공정\n * 상세가 그 공정 하나의 분해(상한·약정·달성불가·가용)와 제약하는 축을 보인다. 두 화면은 **같은 낱말**을\n * 쓴다(같은 i18n 키) — 같은 사실을 다르게 부르면 어느 쪽이 맞는지 아무도 모른다.\n *\n * 남은 것 — 표준은 **게시된(약속된) 능력**도 담는다(계산이 아니라 선언). 값을 실을 소스가 아직\n * 침묵하므로 빈 축을 먼저 만들지 않는다(상태 쪽 `workCalendar` 를 지운 이유와 같다).\n *\n * 과거 분해(Used·Unused)는 이 보고서에 없다 — 그것은 실적이고 이미 두 곳에 있다(설비 OEE 의 가동/유휴,\n * 성과 화면의 구간 가동률). 표준이 그 둘을 다른 종류로 부르므로 **합치지 않는다.**\n */\n\n/*\n * Part 2 — 자원 모델. 네 자원 각각이 **등급/정의 ↔ 개체** 이중구조를 갖는 것이 이 부분의 핵심이고,\n * 그 이중성이 이 트윈의 뼈대이기도 하다.\n */\nconst PART2: Isa95Concept[] = [\n { std: 'PersonnelClass', part: '2', label: 'twin.isa95.PersonnelClass', axis: 'personnelClasses', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'Person', part: '2', label: 'twin.isa95.Person', axis: 'persons', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'PersonnelCapability', part: '2', label: 'twin.isa95.PersonnelCapability', axis: null, shownOn: ['persons'], structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.capability' },\n { std: 'EquipmentClass', part: '2', label: 'twin.isa95.EquipmentClass', axis: 'equipmentClasses', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'Equipment', part: '2', label: 'twin.isa95.Equipment', axis: 'equipment', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'EquipmentCapability', part: '2', label: 'twin.isa95.EquipmentCapability', axis: null, shownOn: ['equipment'], structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.capability' },\n { std: 'PhysicalAssetClass', part: '2', label: 'twin.isa95.PhysicalAssetClass', axis: 'assetClasses', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'PhysicalAsset', part: '2', label: 'twin.isa95.PhysicalAsset', axis: 'assets', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'MaterialClass', part: '2', label: 'twin.isa95.MaterialClass', axis: 'materialClasses', structure: 'full', behavior: 'partial', surface: 'full' },\n { std: 'MaterialDefinition', part: '2', label: 'twin.isa95.MaterialDefinition', axis: 'materialDefinitions', structure: 'full', behavior: 'full', surface: 'full' },\n /* 로트·서브로트는 개체 축이 아니라 **관측**으로 존재한다(EPCIS 개체) — 구조는 저널이 갖는다. */\n { std: 'MaterialLot', part: '2', label: 'twin.isa95.MaterialLot', axis: null, structure: 'partial', behavior: 'full', surface: 'partial', note: 'twin.isa95.note.lot' },\n { std: 'MaterialSublot', part: '2', label: 'twin.isa95.MaterialSublot', axis: null, structure: 'none', behavior: 'none', surface: 'none' },\n { std: 'ProcessSegment', part: '2', label: 'twin.isa95.ProcessSegment', axis: 'operations', structure: 'full', behavior: 'full', surface: 'full' },\n /*\n * 속성은 자원마다 붙는 확장이라 **자기 축이 아니다** — 그래서 `axis` 는 없지만 구조는 완전하다:\n * 계약(`ResourceProperty`: 값·데이터형·단위·하위·등급속성)이 표준을 그대로 담고, 인제스트·상태·\n * 화면이 그것을 통과시킨다.\n *\n * 기능은 **부분**이다: 호스트의 주행 추정기가 `speed` 를 읽어 이동 시간을 만드는 것이 유일한 소비처다\n * (다른 속성은 아직 아무도 읽지 않는다). 그 사실을 `full` 로 올리면 표가 거짓이 된다.\n */\n { std: 'Property', part: '2', label: 'twin.isa95.Property', axis: null, structure: 'full', behavior: 'partial', surface: 'full', note: 'twin.isa95.note.property' }\n]\n\n/*\n * Part 4 — 일정과 실적.\n *\n * **2026-08-11 이전에는 이 부분이 거의 비어 있었다.** 오더·작업은 런타임과 저널에 실재하는데 조회\n * 모델의 축이 아니었고, 그래서 \"이 트윈이 표준의 어디를 채우나\" 라는 질문에 절반만 답했다.\n *\n * 커널이 계약을 넓혀(`source: 'state'`) 두 축이 추가되었다 — 이제 요구(`orders`)와 응답(`tasks`)을 개념으로\n * 가리켜 걸어 들어갈 수 있다. 남은 것은 **과거로 다시 계산하는 길**(저널 커서 페이징)과 능력·검사 명세다.\n */\nconst PART4: Isa95Concept[] = [\n { std: 'OperationsDefinition', part: '4', label: 'twin.isa95.OperationsDefinition', axis: 'productionSpec', structure: 'full', behavior: 'full', surface: 'partial', note: 'twin.isa95.note.opsdef' },\n /*\n * 2026-08-11 — **축이 추가되었다**(커널 0.6.3 · `source: 'state'`). 요구는 `orders`, 응답은 `tasks` 다.\n *\n * `structure` 를 `full` 로 올린다: 담을 자리가 계약에 있다(상태 계약 + 축 선언). **테이블 투영이\n * 없는 것은 결손이 아니다** — 이 축의 정본은 저널이고, 투영하지 않는 것이 결정이다(ADR-0032-D).\n *\n * `surface` 도 `full` 이다(같은 날 두 번째 단계): 목록·상세·관계에 더해 **과거로 다시 계산하는 길**이 화면에\n * 있다 — 시각 커서를 옮기면 그 시점의 사실이 나오고, 무엇을 근거로 답했는지도 화면이 말한다\n * (라이브=권위 · 저널=파생 · 접은 리비전). 실측: 이른 시각 0건 · 하루 뒤 504건.\n */\n { std: 'OperationsSchedule', part: '4', label: 'twin.isa95.OperationsSchedule', axis: 'orders', structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.schedule' },\n { std: 'OperationsPerformance', part: '4', label: 'twin.isa95.OperationsPerformance', axis: 'tasks', structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.performance' },\n { std: 'OperationsCapability', part: '4', label: 'twin.isa95.OperationsCapability', axis: null, shownOn: ['operations'], structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.opscapability' },\n /*\n * 2026-08-12 — **축이 추가되었다**(커널 0.6.5). 자원 아홉이 가리키던 참조에 대상이 생겼고, 없는 명세를\n * 가리키면 선 채움이 끊어진 참조로 센다.\n *\n * 기능이 **`partial` 로 살아났다**(커널 0.6.6): 결과(`TestResult`)가 계약에 들어오면서 커널이 배정에서\n * 그것을 본다 — 만료·불합격이면 그 자격은 성립하지 않는다(실측: 만료된 용접사는 용접 작업 5건 중 한\n * 번도 배정되지 않았다).\n *\n * `full` 이 아닌 이유는 둘이다: **사람만 판정한다**(설비·자산의 검사 결과는 아직 배정에 쓰지 않는다),\n * 그리고 결과가 없으면 판정하지 않는다(선언한 것만 제약이 된다 — 그건 결손이 아니라 규율이다).\n */\n { std: 'TestSpecification', part: '4', label: 'twin.isa95.TestSpecification', axis: 'testSpecifications', structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.test' },\n { std: 'WorkMaster', part: '4', label: 'twin.isa95.WorkMaster', axis: 'recipes', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'WorkDirective', part: '4', label: 'twin.isa95.WorkDirective', axis: null, structure: 'none', behavior: 'none', surface: 'none' }\n]\n\n/** 표준이 요구하는 것 전부 — 이 배열이 곧 기준이다. */\nexport const ISA95_CONCEPTS: Isa95Concept[] = [...PART2, ...PART4]\n\n/**\n * 적합성 보고 — **채운 것과 빠진 것을 함께.**\n *\n * `axis` 를 커널 축 목록과 맞춰 본다: 축이 있다고 적혀 있는데 커널에 없으면 그 선언이 낡은 것이므로\n * **구조 없음으로 낮추고 그 사실을 말한다**(조용히 통과시키면 이 표가 거짓이 된다).\n */\nexport function isa95Coverage(axes: string[]): {\n model: string\n concepts: (Isa95Concept & { axisMissing?: boolean })[]\n totals: { concepts: number; structure: number; behavior: number; surface: number }\n} {\n const known = new Set(axes)\n const concepts = ISA95_CONCEPTS.map(c => {\n const axisMissing = !!c.axis && !known.has(c.axis)\n /* 가리키는 곳이 없어진 축은 **가리키지 않는다** — 죽은 문으로 보내면 화면이 거짓말을 한다.\n `axis` 와 같은 규율이고, 여기가 비면 그 줄은 다시 \"축이 없다\" 로 정직하게 돌아간다. */\n const shownOn = c.shownOn?.filter(a => known.has(a))\n const fixed = shownOn && shownOn.length !== c.shownOn?.length ? { ...c, shownOn } : c\n return axisMissing ? { ...fixed, structure: 'none' as CoverageLevel, axisMissing } : fixed\n })\n /* 채운 수는 `full` 만 센다 — 부분을 채운 것으로 세면 기준이 스스로를 속인다. */\n const count = (k: 'structure' | 'behavior' | 'surface') => concepts.filter(c => c[k] === 'full').length\n return {\n model: 'ISA-95',\n concepts,\n totals: { concepts: concepts.length, structure: count('structure'), behavior: count('behavior'), surface: count('surface') }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"isa95-coverage.js","sourceRoot":"","sources":["../../../server/service/twin-model/isa95-coverage.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * ISA-95 적합성 기준 — **표준이 요구하는 것의 목록.**\n *\n * ── 왜 이 파일이 필요한가 ──────────────────────────────────────────────────────\n * 지금까지 화면은 \"우리가 가진 축\" 만 보였다. 그러면 채운 것은 세어지지만 **빠진 것은 셀 수 없다** —\n * 없는 것은 화면에 없으므로 아무도 그것을 묻지 않는다. 표준 준수는 \"우리 축에 표준 이름을 붙였다\"\n * 가 아니라 **표준의 목록을 기준으로 빠진 것이 없다**는 말이어야 한다. 기준이 코드로 있어야 그\n * 문장을 검사할 수 있다.\n *\n * ── 세 층에서 본다 ────────────────────────────────────────────────────────────\n * 표준 준수는 한 층의 일이 아니다. 개념마다 셋을 따로 본다:\n *\n * · `structure` — **데이터구조**가 있나(커널 축·호스트 표에 이 개념의 자리가 있나)\n * · `behavior` — **기능**이 그것을 쓰나(커널이 계획·집행·집계에서 실제로 다루나)\n * · `surface` — **UI 노출**이 있나(사용자가 그것을 볼 수 있나)\n *\n * 셋을 합치면 \"구조는 있는데 아무도 안 쓰는 컬럼\" 과 \"커널은 쓰는데 화면에 없는 사실\" 이 같은\n * 값으로 보인다. 실제로 이 프로젝트에서 둘 다 나왔다(인력 명세는 구조·기능이 있었지만 화면에\n * 없었고, `properties` 는 구조만 있다).\n *\n * ── 값은 관측이 아니라 선언이다 ────────────────────────────────────────────────\n * 여기 적힌 것은 **사람이 판단해 적은 사실**이다(자동 판정이 아니다). 그래서 틀릴 수 있고, 틀리면\n * 여기를 고쳐야 한다 — 화면은 이 선언을 그대로 보이고 지어내지 않는다. 자동으로 알 수 있는 것\n * 하나는 자동으로 한다: `axis` 가 커널 축 목록에 없으면 그것은 **구조가 없는 것**이다(오타 방지).\n *\n * ── 이 표가 커널에 없는 이유 ──────────────────────────────────────────────────\n * 커널은 **런타임 모델의 SSOT** 다(무엇이 있는가). 이 표는 **참조 모델의 목록**이다(무엇이 있어야\n * 하는가). 둘은 다른 종류의 사실이고, 후자는 커널을 배포하지 않고도 늘어야 한다.\n */\n\n/** 한 층의 상태 — 있음 · 부분 · 없음. \"부분\" 을 두는 이유: 절반 있는 것을 있다고 하면 기준이 죽는다. */\nexport type CoverageLevel = 'full' | 'partial' | 'none'\n\nexport interface Isa95Concept {\n /** 표준이 부르는 이름 — 이 표의 키다(우리 어휘가 아니라 표준 어휘). */\n std: string\n /** 표준의 어느 부분인가 — Part 2(자원 모델) · Part 4(일정·실적) 등. */\n part: string\n /** 사람이 읽는 이름(i18n 키). 화면이 번역한다. */\n label: string\n /** 우리 커널 축 — 있으면 데이터구조가 있는 것이다. 없으면 `null`. */\n axis: string | null\n /** 세 층의 상태. `structure` 는 `axis` 로도 검증된다. */\n structure: CoverageLevel\n behavior: CoverageLevel\n surface: CoverageLevel\n /** 왜 그 수준인지 — **모르는 것을 감추지 않기 위해** 한 줄로 적는다(i18n 키). */\n note?: string\n /**\n * 자기 축이 없을 때 **어디서 보이나** — 그 사실을 들고 있는 축들.\n *\n * 「어디에」칸이 두 가지를 합쳐고 있었다: *아직 축이 없다(부채)* 와 *축일 수 없다(설계)*. 가용 능력은\n * 개체가 아니라 자원의 지금 상태이므로 축이 될 수 없는데, 세 층이 다 완전한 줄이 \"개념 축이 없습니다\"\n * 라고만 말하면 **표가 스스로 모순된 말을 한다**(다 됐다면서 없다고 한다).\n *\n * 그래서 축일 수 없는 것은 갈 곳을 가리킨다. 여기가 비어 있고 축도 없으면 그것은 **진짜 빈칸**이다.\n */\n shownOn?: string[]\n}\n\n/*\n * 가용 능력(2026-08-12, 커널 0.6.8) — **판정과 이유가 상태에 실려 온다**(`capability {available, reason}`).\n *\n * 셋 다 완전인 근거: 상태가 판정을 들고 오고(시뮬·미러가 같은 함수로 파생한다), 배정이 같은 판정을\n * 쓰고, 두 화면이 같은 낱말로 그 이유를 보인다(자원 탭 배지 · 인스펙션 관측 열).\n *\n * 왜 축이 없는가 — 가용 능력은 개체가 아니라 **자원의 지금 상태**다. 개념 카드로 세우면 사람·설비와\n * 나란히 놓여 \"능력이 몇 개인가\" 를 세게 되는데, 그건 물을 일이 아니다.\n */\n\n/*\n * 생산 능력(2026-08-13, 커널 0.6.12) — **구간의 양을 표준 종류로 구분한다.**\n *\n * `structure` 완전: 계약이 보고서를 낸다(`operationsCapabilityOf`) — 구간(StartTime·EndTime) · 표준\n * 열거값(Committed·Available·Unattainable) · 공정별 분해 · 제약하는 축. 낱말은 B2MML 대조 후 표준 것을\n * 그대로 쓴다(우리 낱말을 만들지 않았다).\n * `behavior` 완전: 약정은 오더에서, 달성불가는 자원 판정(`capabilityOf`)에서 온다 — 그리고 그 판정은\n * 배정이 쓰는 것과 같은 한 벌이다. 표준의 `Unattainable` 정의가 그 판정과 겹친다.\n * `surface` 완전: 두 자리에서 보인다 — 성과 화면이 현장 전체의 \"앞으로 더 받을 수 있는 몫\" 을, 공정\n * 상세가 그 공정 하나의 분해(상한·약정·달성불가·가용)와 제약하는 축을 보인다. 두 화면은 **같은 낱말**을\n * 쓴다(같은 i18n 키) — 같은 사실을 다르게 부르면 어느 쪽이 맞는지 아무도 모른다.\n *\n * 남은 것 — 표준은 **게시된(약속된) 능력**도 담는다(계산이 아니라 선언). 값을 실을 소스가 아직\n * 침묵하므로 빈 축을 먼저 만들지 않는다(상태 쪽 `workCalendar` 를 지운 이유와 같다).\n *\n * 과거 분해(Used·Unused)는 이 보고서에 없다 — 그것은 실적이고 이미 두 곳에 있다(설비 OEE 의 가동/유휴,\n * 성과 화면의 구간 가동률). 표준이 그 둘을 다른 종류로 부르므로 **합치지 않는다.**\n */\n\n/*\n * Part 2 — 자원 모델. 네 자원 각각이 **등급/정의 ↔ 개체** 이중구조를 갖는 것이 이 부분의 핵심이고,\n * 그 이중성이 이 트윈의 뼈대이기도 하다.\n */\nconst PART2: Isa95Concept[] = [\n { std: 'PersonnelClass', part: '2', label: 'twin.isa95.PersonnelClass', axis: 'personnelClasses', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'Person', part: '2', label: 'twin.isa95.Person', axis: 'persons', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'PersonnelCapability', part: '2', label: 'twin.isa95.PersonnelCapability', axis: null, shownOn: ['persons'], structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.capability' },\n { std: 'EquipmentClass', part: '2', label: 'twin.isa95.EquipmentClass', axis: 'equipmentClasses', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'Equipment', part: '2', label: 'twin.isa95.Equipment', axis: 'equipment', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'EquipmentCapability', part: '2', label: 'twin.isa95.EquipmentCapability', axis: null, shownOn: ['equipment'], structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.capability' },\n { std: 'PhysicalAssetClass', part: '2', label: 'twin.isa95.PhysicalAssetClass', axis: 'assetClasses', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'PhysicalAsset', part: '2', label: 'twin.isa95.PhysicalAsset', axis: 'assets', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'MaterialClass', part: '2', label: 'twin.isa95.MaterialClass', axis: 'materialClasses', structure: 'full', behavior: 'partial', surface: 'full' },\n { std: 'MaterialDefinition', part: '2', label: 'twin.isa95.MaterialDefinition', axis: 'materialDefinitions', structure: 'full', behavior: 'full', surface: 'full' },\n /* 로트·서브로트는 개체 축이 아니라 **관측**으로 존재한다(EPCIS 개체) — 구조는 저널이 갖는다. */\n { std: 'MaterialLot', part: '2', label: 'twin.isa95.MaterialLot', axis: null, structure: 'partial', behavior: 'full', surface: 'partial', note: 'twin.isa95.note.lot' },\n { std: 'MaterialSublot', part: '2', label: 'twin.isa95.MaterialSublot', axis: null, structure: 'none', behavior: 'none', surface: 'none' },\n { std: 'ProcessSegment', part: '2', label: 'twin.isa95.ProcessSegment', axis: 'operations', structure: 'full', behavior: 'full', surface: 'full' },\n /*\n * 속성은 자원마다 붙는 확장이라 **자기 축이 아니다** — 그래서 `axis` 는 없지만 구조는 완전하다:\n * 계약(`ResourceProperty`: 값·데이터형·단위·하위·등급속성)이 표준을 그대로 담고, 인제스트·상태·\n * 화면이 그것을 통과시킨다.\n *\n * 기능은 **부분**이다: 호스트의 주행 추정기가 `speed` 를 읽어 이동 시간을 만드는 것이 유일한 소비처다\n * (다른 속성은 아직 아무도 읽지 않는다). 그 사실을 `full` 로 올리면 표가 거짓이 된다.\n */\n { std: 'Property', part: '2', label: 'twin.isa95.Property', axis: null, structure: 'full', behavior: 'partial', surface: 'full', note: 'twin.isa95.note.property' }\n]\n\n/*\n * Part 4 — 일정과 실적.\n *\n * **2026-08-11 이전에는 이 부분이 거의 비어 있었다.** 오더·작업은 런타임과 저널에 실재하는데 조회\n * 모델의 축이 아니었고, 그래서 \"이 트윈이 표준의 어디를 채우나\" 라는 질문에 절반만 답했다.\n *\n * 커널이 계약을 넓혀(`source: 'state'`) 두 축이 추가되었다 — 이제 요구(`orders`)와 응답(`tasks`)을 개념으로\n * 가리켜 걸어 들어갈 수 있다. 남은 것은 **과거로 다시 계산하는 길**(저널 커서 페이징)과 능력·검사 명세다.\n */\nconst PART4: Isa95Concept[] = [\n { std: 'OperationsDefinition', part: '4', label: 'twin.isa95.OperationsDefinition', axis: 'productionSpec', structure: 'full', behavior: 'full', surface: 'partial', note: 'twin.isa95.note.opsdef' },\n /*\n * 2026-08-11 — **축이 추가되었다**(커널 0.6.3 · `source: 'state'`). 요구는 `orders`, 응답은 `tasks` 다.\n *\n * `structure` 를 `full` 로 올린다: 담을 자리가 계약에 있다(상태 계약 + 축 선언). **테이블 투영이\n * 없는 것은 결손이 아니다** — 이 축의 정본은 저널이고, 투영하지 않는 것이 결정이다(ADR-0032-D).\n *\n * `surface` 도 `full` 이다(같은 날 두 번째 단계): 목록·상세·관계에 더해 **과거로 다시 계산하는 길**이 화면에\n * 있다 — 시각 커서를 옮기면 그 시점의 사실이 나오고, 무엇을 근거로 답했는지도 화면이 말한다\n * (라이브=권위 · 저널=파생 · 접은 리비전). 실측: 이른 시각 0건 · 하루 뒤 504건.\n */\n { std: 'OperationsSchedule', part: '4', label: 'twin.isa95.OperationsSchedule', axis: 'orders', structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.schedule' },\n { std: 'OperationsPerformance', part: '4', label: 'twin.isa95.OperationsPerformance', axis: 'tasks', structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.performance' },\n { std: 'OperationsCapability', part: '4', label: 'twin.isa95.OperationsCapability', axis: null, shownOn: ['operations'], structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.opscapability' },\n /*\n * 2026-08-12 — **축이 추가되었다**(커널 0.6.5). 자원 아홉이 가리키던 참조에 대상이 생겼고, 없는 명세를\n * 가리키면 선 채움이 끊어진 참조로 센다.\n *\n * 기능이 **`partial` 로 살아났다**(커널 0.6.6): 결과(`TestResult`)가 계약에 들어오면서 커널이 배정에서\n * 그것을 본다 — 만료·불합격이면 그 자격은 성립하지 않는다(실측: 만료된 용접사는 용접 작업 5건 중 한\n * 번도 배정되지 않았다).\n *\n * `full` 이 아닌 이유는 둘이다: **사람만 판정한다**(설비·자산의 검사 결과는 아직 배정에 쓰지 않는다),\n * 그리고 결과가 없으면 판정하지 않는다(선언한 것만 제약이 된다 — 그건 결손이 아니라 규율이다).\n */\n { std: 'TestSpecification', part: '4', label: 'twin.isa95.TestSpecification', axis: 'testSpecifications', structure: 'full', behavior: 'full', surface: 'full', note: 'twin.isa95.note.test' },\n { std: 'WorkMaster', part: '4', label: 'twin.isa95.WorkMaster', axis: 'recipes', structure: 'full', behavior: 'full', surface: 'full' },\n { std: 'WorkDirective', part: '4', label: 'twin.isa95.WorkDirective', axis: null, structure: 'none', behavior: 'none', surface: 'none' }\n]\n\n/** 표준이 요구하는 것 전부 — 이 배열이 곧 기준이다. */\nexport const ISA95_CONCEPTS: Isa95Concept[] = [...PART2, ...PART4]\n\n/**\n * 적합성 보고 — **채운 것과 빠진 것을 함께.**\n *\n * `axis` 를 커널 축 목록과 맞춰 본다: 축이 있다고 적혀 있는데 커널에 없으면 그 선언이 낡은 것이므로\n * **구조 없음으로 낮추고 그 사실을 말한다**(조용히 통과시키면 이 표가 거짓이 된다).\n */\n/**\n * 선언한 축을 커널의 축 목록과 맞춘다 — **어긋난 것을 조용히 지우지 않는다.**\n *\n * 두 자리가 어긋날 수 있다.\n * · `axis` — 그 개념 자신의 문. 커널에 없으면 구조를 「없음」으로 낮추고 그 사실을 말한다.\n * · `shownOn` — 그 사실을 들고 있는 다른 축. 예전에는 없는 축을 **말없이 걸러 냈다.** 그러면 그 줄은\n * 「개념 축이 없습니다」로 보이고, 사람은 그것을 **원래 그런 것**으로 읽는다. 실제로 그 일이 있었다:\n * EPCIS 표의 일곱 줄이 커널에 없는 축(`items`)에 기대고 있었는데, 경고가 뜬 것은 두 줄뿐이었다.\n *\n * 둘 중 하나라도 어긋나면 `axisMissing` 이다 — 표가 스스로 「최신이 아니다」라고 말하게 한다.\n */\nexport function reconcileAxes(c: Isa95Concept, known: Set<string>): Isa95Concept & { axisMissing?: boolean } {\n const ownMissing = !!c.axis && !known.has(c.axis)\n const shownOn = c.shownOn?.filter(a => known.has(a))\n const lostShown = !!c.shownOn && (shownOn?.length ?? 0) !== c.shownOn.length\n const fixed = lostShown ? { ...c, shownOn } : c\n if (!ownMissing && !lostShown) return fixed\n /* 구조를 낮추는 것은 **자기 문이 없어진 경우**다 — 남의 축에서 보이던 것은 구조가 아니라 표시의 문제다. */\n return ownMissing ? { ...fixed, structure: 'none' as CoverageLevel, axisMissing: true } : { ...fixed, axisMissing: true }\n}\n\nexport function isa95Coverage(axes: string[]): {\n model: string\n concepts: (Isa95Concept & { axisMissing?: boolean })[]\n totals: { concepts: number; structure: number; behavior: number; surface: number }\n} {\n const concepts = ISA95_CONCEPTS.map(c => reconcileAxes(c, new Set(axes)))\n /* 채운 수는 `full` 만 센다 — 부분을 채운 것으로 세면 기준이 스스로를 속인다. */\n const count = (k: 'structure' | 'behavior' | 'surface') => concepts.filter(c => c[k] === 'full').length\n return {\n model: 'ISA-95',\n concepts,\n totals: { concepts: concepts.length, structure: count('structure'), behavior: count('behavior'), surface: count('surface') }\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 개체 식별자 → **품목 참조**. 대응 표의 값과 견줄 수 있는 모양으로 돌려준다.
|
|
3
|
+
*
|
|
4
|
+
* SGTIN 은 표준이 자리를 정해 두었으므로 그 자리에서 품목 참조를 꺼낸다. 그 밖의 모양은 **손대지
|
|
5
|
+
* 않는다** — 어느 부호가 개체를 가리키는지는 원본마다 다르고, 커널 계약이 그 구분자를 정한 적이 없다.
|
|
6
|
+
* 앞서 이 자리에 「`@` 뒤는 개체다」를 넣었는데, 그것은 한 원본(chef)의 모양을 규칙으로 굳힌 것이었다.
|
|
7
|
+
*
|
|
8
|
+
* 그래서 자르는 일은 여기서 하지 않고, **그 트윈의 선언과 견주어** 판정한다(§`resolveByDeclaration`).
|
|
9
|
+
* 선언이 답을 주지 못하면 `null` 이다 — 짐작으로 이으면 남의 레시피와 견주게 되고, 그것이 「설계와
|
|
10
|
+
* 다르다」는 거짓 진단이 된다.
|
|
11
|
+
*/
|
|
12
|
+
export declare function itemRefOf(epc: unknown): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* 이 식별자가 가리키는 **선언 항목**을 찾는다 — 원본의 정체성 관행을 알지 않고, 구분자도 모른다.
|
|
15
|
+
*
|
|
16
|
+
* 세 가지를 차례로 본다. 어느 단계든 **후보가 둘 이상이면 답하지 않는다**(모르는 것이 틀린 답보다 낫다).
|
|
17
|
+
*
|
|
18
|
+
* ① 그대로 같다
|
|
19
|
+
* ② 관측된 식별자가 선언을 **확장**한다 — 개체는 자기 클래스로 시작한다(`…/product-M1` + 개체 표시)
|
|
20
|
+
* ③ 화면이 **짧게 줄인** 식별자를 건넸다 — 선언이 그것으로 끝난다(`net/class/product-M1`)
|
|
21
|
+
*
|
|
22
|
+
* ②③은 부호를 모른다. 그래서 원본이 `@`·`#`·`/` 무엇을 쓰든, 또는 아무것도 쓰지 않든 같은 규칙이 선다.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveByDeclaration<T>(ref: string | null, declared: Map<string, T>): T | undefined;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* **개체에서 품목으로** — 정체성 모양이 하나가 아니다 (2026-08-23).
|
|
4
|
+
*
|
|
5
|
+
* ── 무엇이 조용히 비어 있었나 ───────────────────────────────────────────────
|
|
6
|
+
* 화면이 「이 개체는 선언된 자재가 아닙니다(라인 내부의 중간 산물일 수 있습니다)」라고 말했는데, 그
|
|
7
|
+
* 품목은 원본의 선언에 이름까지 있었다(「냉동그린빈스」). 대응 표(`productionSpec.binding`)도 멀쩡했다.
|
|
8
|
+
*
|
|
9
|
+
* 원인은 **개체에서 품목을 꺼내는 규칙**이었다. 그 규칙이 GS1 SGTIN URN 하나만 알았다.
|
|
10
|
+
*
|
|
11
|
+
* urn:epc:id:sgtin:0614141.077101.42 → 077101 (알던 모양)
|
|
12
|
+
* https://…/class/product-M0000438@M2R0001 → null (모르던 모양)
|
|
13
|
+
*
|
|
14
|
+
* 웹 URI 로 정체성을 말하는 원본에서는 언제나 `null` 이 나왔고, 그래서 **개체를 자재에 이을 수 없었다.**
|
|
15
|
+
* 그 결과가 화면 두 곳에서 조용한 빈칸이었다(계보 적합성 · 오더가 무엇을 만드나). 「없으면 붙이지
|
|
16
|
+
* 않는다」는 규율을 지키기 때문에 아무 오류도 나지 않았다.
|
|
17
|
+
*
|
|
18
|
+
* ── 무엇을 돌려주나 ─────────────────────────────────────────────────────────
|
|
19
|
+
* **그 트윈의 대응 표가 쓰는 것과 같은 모양**이다. 두 세계가 서로 다른 것을 값으로 쓴다.
|
|
20
|
+
*
|
|
21
|
+
* SGTIN 세계 binding 값 = 품목 참조 숫자(`077101`) → 숫자를 돌려준다
|
|
22
|
+
* URI 세계 binding 값 = 클래스 URI(`…/product-M…`) → 클래스 URI 를 돌려준다
|
|
23
|
+
*
|
|
24
|
+
* 그래서 두 세계를 한 함수가 답한다 — 부르는 자리가 원본의 정체성 방식을 알지 않아도 된다.
|
|
25
|
+
*
|
|
26
|
+
* ── 무엇을 지어내지 않나 ────────────────────────────────────────────────────
|
|
27
|
+
* **어느 부호가 개체를 가리키는지 모른다.** 그것은 원본마다 다르고 커널 계약이 정한 적이 없다 — 한
|
|
28
|
+
* 원본(chef)이 쓰는 `@` 를 규칙으로 굳히면 이 화면이 그 레거시 위에 서게 된다. 자르는 판단은 그 트윈의
|
|
29
|
+
* **선언**이 하고(§`resolveByDeclaration`), 후보가 둘 이상이면 답하지 않는다. 짐작으로 이으면 남의
|
|
30
|
+
* 레시피와 견주게 되고, 그것이 「설계와 다르다」는 거짓 진단이 된다.
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.itemRefOf = itemRefOf;
|
|
34
|
+
exports.resolveByDeclaration = resolveByDeclaration;
|
|
35
|
+
/**
|
|
36
|
+
* 개체 식별자 → **품목 참조**. 대응 표의 값과 견줄 수 있는 모양으로 돌려준다.
|
|
37
|
+
*
|
|
38
|
+
* SGTIN 은 표준이 자리를 정해 두었으므로 그 자리에서 품목 참조를 꺼낸다. 그 밖의 모양은 **손대지
|
|
39
|
+
* 않는다** — 어느 부호가 개체를 가리키는지는 원본마다 다르고, 커널 계약이 그 구분자를 정한 적이 없다.
|
|
40
|
+
* 앞서 이 자리에 「`@` 뒤는 개체다」를 넣었는데, 그것은 한 원본(chef)의 모양을 규칙으로 굳힌 것이었다.
|
|
41
|
+
*
|
|
42
|
+
* 그래서 자르는 일은 여기서 하지 않고, **그 트윈의 선언과 견주어** 판정한다(§`resolveByDeclaration`).
|
|
43
|
+
* 선언이 답을 주지 못하면 `null` 이다 — 짐작으로 이으면 남의 레시피와 견주게 되고, 그것이 「설계와
|
|
44
|
+
* 다르다」는 거짓 진단이 된다.
|
|
45
|
+
*/
|
|
46
|
+
function itemRefOf(epc) {
|
|
47
|
+
if (typeof epc !== 'string')
|
|
48
|
+
return null;
|
|
49
|
+
const s = epc.trim();
|
|
50
|
+
if (!s)
|
|
51
|
+
return null;
|
|
52
|
+
/* GS1 SGTIN — 회사 접두사와 일련번호 사이의 품목 참조. `idpat` 의 `*` 도 같은 자리에서 걸린다. */
|
|
53
|
+
const sgtin = /sgtin:[^.]+\.([^.]+)\./.exec(s);
|
|
54
|
+
if (sgtin)
|
|
55
|
+
return sgtin[1];
|
|
56
|
+
/* 그 밖의 정체성(웹 URI 등)은 그대로 낸다 — 자르는 판단은 선언이 한다. */
|
|
57
|
+
return s;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 이 식별자가 가리키는 **선언 항목**을 찾는다 — 원본의 정체성 관행을 알지 않고, 구분자도 모른다.
|
|
61
|
+
*
|
|
62
|
+
* 세 가지를 차례로 본다. 어느 단계든 **후보가 둘 이상이면 답하지 않는다**(모르는 것이 틀린 답보다 낫다).
|
|
63
|
+
*
|
|
64
|
+
* ① 그대로 같다
|
|
65
|
+
* ② 관측된 식별자가 선언을 **확장**한다 — 개체는 자기 클래스로 시작한다(`…/product-M1` + 개체 표시)
|
|
66
|
+
* ③ 화면이 **짧게 줄인** 식별자를 건넸다 — 선언이 그것으로 끝난다(`net/class/product-M1`)
|
|
67
|
+
*
|
|
68
|
+
* ②③은 부호를 모른다. 그래서 원본이 `@`·`#`·`/` 무엇을 쓰든, 또는 아무것도 쓰지 않든 같은 규칙이 선다.
|
|
69
|
+
*/
|
|
70
|
+
function resolveByDeclaration(ref, declared) {
|
|
71
|
+
if (!ref)
|
|
72
|
+
return undefined;
|
|
73
|
+
const exact = declared.get(ref);
|
|
74
|
+
if (exact !== undefined)
|
|
75
|
+
return exact;
|
|
76
|
+
let hit;
|
|
77
|
+
let found = 0;
|
|
78
|
+
for (const [id, value] of declared) {
|
|
79
|
+
if (!(ref.startsWith(id) || id.endsWith(ref)))
|
|
80
|
+
continue;
|
|
81
|
+
found++;
|
|
82
|
+
if (found > 1)
|
|
83
|
+
return undefined;
|
|
84
|
+
hit = value;
|
|
85
|
+
}
|
|
86
|
+
return hit;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=item-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-ref.js","sourceRoot":"","sources":["../../../server/service/twin-model/item-ref.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;AAaH,8BAWC;AAaD,oDAaC;AAhDD;;;;;;;;;;GAUG;AACH,SAAgB,SAAS,CAAC,GAAY;IACpC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACxC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IACpB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAEnB,sEAAsE;IACtE,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC9C,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAA;IAE1B,iDAAiD;IACjD,OAAO,CAAC,CAAA;AACV,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,oBAAoB,CAAI,GAAkB,EAAE,QAAwB;IAClF,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAA;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACrC,IAAI,GAAkB,CAAA;IACtB,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAAE,SAAQ;QACvD,KAAK,EAAE,CAAA;QACP,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO,SAAS,CAAA;QAC/B,GAAG,GAAG,KAAK,CAAA;IACb,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["/*\n * **개체에서 품목으로** — 정체성 모양이 하나가 아니다 (2026-08-23).\n *\n * ── 무엇이 조용히 비어 있었나 ───────────────────────────────────────────────\n * 화면이 「이 개체는 선언된 자재가 아닙니다(라인 내부의 중간 산물일 수 있습니다)」라고 말했는데, 그\n * 품목은 원본의 선언에 이름까지 있었다(「냉동그린빈스」). 대응 표(`productionSpec.binding`)도 멀쩡했다.\n *\n * 원인은 **개체에서 품목을 꺼내는 규칙**이었다. 그 규칙이 GS1 SGTIN URN 하나만 알았다.\n *\n * urn:epc:id:sgtin:0614141.077101.42 → 077101 (알던 모양)\n * https://…/class/product-M0000438@M2R0001 → null (모르던 모양)\n *\n * 웹 URI 로 정체성을 말하는 원본에서는 언제나 `null` 이 나왔고, 그래서 **개체를 자재에 이을 수 없었다.**\n * 그 결과가 화면 두 곳에서 조용한 빈칸이었다(계보 적합성 · 오더가 무엇을 만드나). 「없으면 붙이지\n * 않는다」는 규율을 지키기 때문에 아무 오류도 나지 않았다.\n *\n * ── 무엇을 돌려주나 ─────────────────────────────────────────────────────────\n * **그 트윈의 대응 표가 쓰는 것과 같은 모양**이다. 두 세계가 서로 다른 것을 값으로 쓴다.\n *\n * SGTIN 세계 binding 값 = 품목 참조 숫자(`077101`) → 숫자를 돌려준다\n * URI 세계 binding 값 = 클래스 URI(`…/product-M…`) → 클래스 URI 를 돌려준다\n *\n * 그래서 두 세계를 한 함수가 답한다 — 부르는 자리가 원본의 정체성 방식을 알지 않아도 된다.\n *\n * ── 무엇을 지어내지 않나 ────────────────────────────────────────────────────\n * **어느 부호가 개체를 가리키는지 모른다.** 그것은 원본마다 다르고 커널 계약이 정한 적이 없다 — 한\n * 원본(chef)이 쓰는 `@` 를 규칙으로 굳히면 이 화면이 그 레거시 위에 서게 된다. 자르는 판단은 그 트윈의\n * **선언**이 하고(§`resolveByDeclaration`), 후보가 둘 이상이면 답하지 않는다. 짐작으로 이으면 남의\n * 레시피와 견주게 되고, 그것이 「설계와 다르다」는 거짓 진단이 된다.\n */\n\n/**\n * 개체 식별자 → **품목 참조**. 대응 표의 값과 견줄 수 있는 모양으로 돌려준다.\n *\n * SGTIN 은 표준이 자리를 정해 두었으므로 그 자리에서 품목 참조를 꺼낸다. 그 밖의 모양은 **손대지\n * 않는다** — 어느 부호가 개체를 가리키는지는 원본마다 다르고, 커널 계약이 그 구분자를 정한 적이 없다.\n * 앞서 이 자리에 「`@` 뒤는 개체다」를 넣었는데, 그것은 한 원본(chef)의 모양을 규칙으로 굳힌 것이었다.\n *\n * 그래서 자르는 일은 여기서 하지 않고, **그 트윈의 선언과 견주어** 판정한다(§`resolveByDeclaration`).\n * 선언이 답을 주지 못하면 `null` 이다 — 짐작으로 이으면 남의 레시피와 견주게 되고, 그것이 「설계와\n * 다르다」는 거짓 진단이 된다.\n */\nexport function itemRefOf(epc: unknown): string | null {\n if (typeof epc !== 'string') return null\n const s = epc.trim()\n if (!s) return null\n\n /* GS1 SGTIN — 회사 접두사와 일련번호 사이의 품목 참조. `idpat` 의 `*` 도 같은 자리에서 걸린다. */\n const sgtin = /sgtin:[^.]+\\.([^.]+)\\./.exec(s)\n if (sgtin) return sgtin[1]\n\n /* 그 밖의 정체성(웹 URI 등)은 그대로 낸다 — 자르는 판단은 선언이 한다. */\n return s\n}\n\n/**\n * 이 식별자가 가리키는 **선언 항목**을 찾는다 — 원본의 정체성 관행을 알지 않고, 구분자도 모른다.\n *\n * 세 가지를 차례로 본다. 어느 단계든 **후보가 둘 이상이면 답하지 않는다**(모르는 것이 틀린 답보다 낫다).\n *\n * ① 그대로 같다\n * ② 관측된 식별자가 선언을 **확장**한다 — 개체는 자기 클래스로 시작한다(`…/product-M1` + 개체 표시)\n * ③ 화면이 **짧게 줄인** 식별자를 건넸다 — 선언이 그것으로 끝난다(`net/class/product-M1`)\n *\n * ②③은 부호를 모른다. 그래서 원본이 `@`·`#`·`/` 무엇을 쓰든, 또는 아무것도 쓰지 않든 같은 규칙이 선다.\n */\nexport function resolveByDeclaration<T>(ref: string | null, declared: Map<string, T>): T | undefined {\n if (!ref) return undefined\n const exact = declared.get(ref)\n if (exact !== undefined) return exact\n let hit: T | undefined\n let found = 0\n for (const [id, value] of declared) {\n if (!(ref.startsWith(id) || id.endsWith(ref))) continue\n found++\n if (found > 1) return undefined\n hit = value\n }\n return hit\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** `productionSpec.definition.recipes` 같은 경로를 문서에서 따라간다. */
|
|
2
|
+
export declare const at: (o: any, path: string) => any;
|
|
3
|
+
/**
|
|
4
|
+
* 문서 항목의 **식별자** — 축마다 이름이 다르다(자리는 `id`, 공정은 `key`, 레시피도 `key`).
|
|
5
|
+
*
|
|
6
|
+
* 도메인 모양을 코드가 아는 것이 아니라 **식별자 관례**를 아는 것이다. 후보를 순서대로 보고 첫
|
|
7
|
+
* 값을 쓴다. 하나도 없으면 그 항목은 가리킬 수 없으므로 `null` — 목록에서 빼지 않고 **id 없음**으로
|
|
8
|
+
* 낸다(조용히 빠지면 건수가 안 맞는다).
|
|
9
|
+
*/
|
|
10
|
+
export declare const idOf: (o: any, info?: {
|
|
11
|
+
idField?: string | string[];
|
|
12
|
+
}) => string | null;
|
|
13
|
+
/**
|
|
14
|
+
* **id → 이름**, 모든 축을 한 표로.
|
|
15
|
+
*
|
|
16
|
+
* ── 왜 이 표가 필요한가 ──────────────────────────────────────────────────────
|
|
17
|
+
* 행은 다른 것을 **id 로** 가리킨다(사람의 `personnelClassIds`, 설비의 `homeLocationId`, 공정의
|
|
18
|
+
* `locationType`). 그 id 의 이름은 **가리켜진 축의 행**에 있으므로, 화면이 그것을 알려면 축을
|
|
19
|
+
* 하나씩 더 읽어야 한다 — 그래서 지금까지 표면마다 그냥 id 를 찍었다(`cutter-op`, `cut-station`).
|
|
20
|
+
* 표면을 하나씩 고치는 것은 끝이 없다: 새 화면은 처음부터 다시 id 를 찍는다.
|
|
21
|
+
*
|
|
22
|
+
* 그래서 **서버가 붙인다.** 서버는 이미 모델을 손에 들고 있고, 어느 축이 있는지는 커널 카탈로그가
|
|
23
|
+
* 선언한다(`TWIN_AXES`) — 도메인이 늘어도 이 함수는 그대로다.
|
|
24
|
+
*
|
|
25
|
+
* 값은 그 항목이 **자기 이름이라고 말한 것**이다(`name`·`label`·`description`). 그중 어떤 것은
|
|
26
|
+
* i18n 키이므로(등급의 `description`) 번역은 화면의 일로 남긴다. 이름이 없으면 표에 넣지 않는다 —
|
|
27
|
+
* 없는 것을 지어내지 않고, 그 경우 화면은 id 를 그대로 보인다.
|
|
28
|
+
*/
|
|
29
|
+
export declare function nameIndex(model: any): Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* 이 응답이 **실제로 가리킨 것만** 골라 낸다.
|
|
32
|
+
*
|
|
33
|
+
* 모델 전체 표를 그대로 실으면 자리가 10만인 현장에서 목록 한 페이지에 10만 줄짜리 이름표가
|
|
34
|
+
* 따라온다. 나온 값에 걸리는 것만 싣는다 — 규모에서 성립하는 유일한 방식이다.
|
|
35
|
+
*/
|
|
36
|
+
export declare function namesFor(rows: any[], index: Record<string, string>): Record<string, string>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.idOf = exports.at = void 0;
|
|
4
|
+
exports.nameIndex = nameIndex;
|
|
5
|
+
exports.namesFor = namesFor;
|
|
6
|
+
/*
|
|
7
|
+
* 항목의 **이름**을 붙이는 자리 — 화면이 id 만 보이지 않게. (2026-08-22 공용으로 옮김)
|
|
8
|
+
*
|
|
9
|
+
* 이 둘은 항목 질의 안에 사적으로 있었다. 그런데 **전개(BOM 나무) 화면도 같은 이름이 필요하다** —
|
|
10
|
+
* 사용자가 「각 노드의 식별자가 반복되어서 이해가 어렵다. 식별자와 이름이 보이면」이라고 했다.
|
|
11
|
+
* 두 벌을 만들면 한쪽만 고쳐지므로 한 곳에 둔다(재발명 금지).
|
|
12
|
+
*/
|
|
13
|
+
/* 커널 런타임 로드 — 어느 축이 있는지는 카탈로그가 선언한다(이 파일은 도메인 모양을 알지 않는다). */
|
|
14
|
+
const { TWIN_AXES, axisSource } = require('@operato/twin-kernel');
|
|
15
|
+
/** `productionSpec.definition.recipes` 같은 경로를 문서에서 따라간다. */
|
|
16
|
+
const at = (o, path) => path.split('.').reduce((a, k) => (a == null ? undefined : a[k]), o);
|
|
17
|
+
exports.at = at;
|
|
18
|
+
/**
|
|
19
|
+
* 문서 항목의 **식별자** — 축마다 이름이 다르다(자리는 `id`, 공정은 `key`, 레시피도 `key`).
|
|
20
|
+
*
|
|
21
|
+
* 도메인 모양을 코드가 아는 것이 아니라 **식별자 관례**를 아는 것이다. 후보를 순서대로 보고 첫
|
|
22
|
+
* 값을 쓴다. 하나도 없으면 그 항목은 가리킬 수 없으므로 `null` — 목록에서 빼지 않고 **id 없음**으로
|
|
23
|
+
* 낸다(조용히 빠지면 건수가 안 맞는다).
|
|
24
|
+
*/
|
|
25
|
+
const idOf = (o, info) => {
|
|
26
|
+
if (typeof o === 'string')
|
|
27
|
+
return o;
|
|
28
|
+
/*
|
|
29
|
+
* **무엇이 이 항목을 가리키는지는 선언이 말한다**(§`TwinAxisInfo.idField`).
|
|
30
|
+
*
|
|
31
|
+
* 예전에는 `id`·`key`·`gtin` 을 짐작했다. 그래서 `id` 가 없는 축(수요 구간 — 정체성이 구간 시작
|
|
32
|
+
* 시각이다)의 항목이 전부 「식별자 없음 — 참조할 수 없는 항목」으로 보였다. 값은 실재하는데
|
|
33
|
+
* 가리킬 수 없다고 말한 것이다.
|
|
34
|
+
*/
|
|
35
|
+
/*
|
|
36
|
+
* 선언이 **차례**를 말할 수 있다(2026-08-22) — 정체성이 한 칸으로 정해지지 않는 축이 있다. 물품이
|
|
37
|
+
* 그렇다: 직렬 물품은 `epc` 가 유일하지만 비직렬 로트가 자리마다 나뉘면 `subLotId` 가 개체를
|
|
38
|
+
* 구별한다(같은 로트의 두 부분은 같은 `epc` 를 갖는다). 앞에서 값이 있는 첫 칸이 이긴다.
|
|
39
|
+
*/
|
|
40
|
+
for (const f of info?.idField ? (Array.isArray(info.idField) ? info.idField : [info.idField]) : []) {
|
|
41
|
+
const v = o?.[f];
|
|
42
|
+
if (v !== undefined && v !== null)
|
|
43
|
+
return String(v);
|
|
44
|
+
}
|
|
45
|
+
return o?.id ?? o?.key ?? o?.gtin ?? null;
|
|
46
|
+
};
|
|
47
|
+
exports.idOf = idOf;
|
|
48
|
+
/**
|
|
49
|
+
* **id → 이름**, 모든 축을 한 표로.
|
|
50
|
+
*
|
|
51
|
+
* ── 왜 이 표가 필요한가 ──────────────────────────────────────────────────────
|
|
52
|
+
* 행은 다른 것을 **id 로** 가리킨다(사람의 `personnelClassIds`, 설비의 `homeLocationId`, 공정의
|
|
53
|
+
* `locationType`). 그 id 의 이름은 **가리켜진 축의 행**에 있으므로, 화면이 그것을 알려면 축을
|
|
54
|
+
* 하나씩 더 읽어야 한다 — 그래서 지금까지 표면마다 그냥 id 를 찍었다(`cutter-op`, `cut-station`).
|
|
55
|
+
* 표면을 하나씩 고치는 것은 끝이 없다: 새 화면은 처음부터 다시 id 를 찍는다.
|
|
56
|
+
*
|
|
57
|
+
* 그래서 **서버가 붙인다.** 서버는 이미 모델을 손에 들고 있고, 어느 축이 있는지는 커널 카탈로그가
|
|
58
|
+
* 선언한다(`TWIN_AXES`) — 도메인이 늘어도 이 함수는 그대로다.
|
|
59
|
+
*
|
|
60
|
+
* 값은 그 항목이 **자기 이름이라고 말한 것**이다(`name`·`label`·`description`). 그중 어떤 것은
|
|
61
|
+
* i18n 키이므로(등급의 `description`) 번역은 화면의 일로 남긴다. 이름이 없으면 표에 넣지 않는다 —
|
|
62
|
+
* 없는 것을 지어내지 않고, 그 경우 화면은 id 를 그대로 보인다.
|
|
63
|
+
*/
|
|
64
|
+
function nameIndex(model) {
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const info of TWIN_AXES) {
|
|
67
|
+
/* 관측 축(상태·저널)은 문서에 없다 — 경로가 없는 축을 읽으면 모델 루트를 훑는다. */
|
|
68
|
+
if (axisSource(info.axis) !== 'document')
|
|
69
|
+
continue;
|
|
70
|
+
const raw = (0, exports.at)(model ?? {}, info.path);
|
|
71
|
+
const list = Array.isArray(raw) ? raw : raw ? [raw] : [];
|
|
72
|
+
for (const o of list) {
|
|
73
|
+
const id = (0, exports.idOf)(o);
|
|
74
|
+
if (!id || typeof o !== 'object')
|
|
75
|
+
continue;
|
|
76
|
+
const label = o.name ?? o.label ?? o.description;
|
|
77
|
+
/* 이름이 id 와 같으면 표에 넣지 않는다 — 같은 값을 두 번 보이는 셈이다. */
|
|
78
|
+
if (typeof label === 'string' && label && label !== id && !out[id])
|
|
79
|
+
out[id] = label;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 이 응답이 **실제로 가리킨 것만** 골라 낸다.
|
|
86
|
+
*
|
|
87
|
+
* 모델 전체 표를 그대로 실으면 자리가 10만인 현장에서 목록 한 페이지에 10만 줄짜리 이름표가
|
|
88
|
+
* 따라온다. 나온 값에 걸리는 것만 싣는다 — 규모에서 성립하는 유일한 방식이다.
|
|
89
|
+
*/
|
|
90
|
+
function namesFor(rows, index) {
|
|
91
|
+
const out = {};
|
|
92
|
+
const take = (v, depth) => {
|
|
93
|
+
if (v == null || depth > 4)
|
|
94
|
+
return;
|
|
95
|
+
if (typeof v === 'string') {
|
|
96
|
+
if (index[v])
|
|
97
|
+
out[v] = index[v];
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(v)) {
|
|
101
|
+
for (const x of v)
|
|
102
|
+
take(x, depth + 1);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (typeof v === 'object')
|
|
106
|
+
for (const x of Object.values(v))
|
|
107
|
+
take(x, depth + 1);
|
|
108
|
+
};
|
|
109
|
+
for (const r of rows) {
|
|
110
|
+
take(r?.fields ?? r, 0);
|
|
111
|
+
if (typeof r?.id === 'string' && index[r.id])
|
|
112
|
+
out[r.id] = index[r.id];
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=name-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"name-index.js","sourceRoot":"","sources":["../../../server/service/twin-model/name-index.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 항목의 **이름**을 붙이는 자리 — 화면이 id 만 보이지 않게. (2026-08-22 공용으로 옮김)\n *\n * 이 둘은 항목 질의 안에 사적으로 있었다. 그런데 **전개(BOM 나무) 화면도 같은 이름이 필요하다** —\n * 사용자가 「각 노드의 식별자가 반복되어서 이해가 어렵다. 식별자와 이름이 보이면」이라고 했다.\n * 두 벌을 만들면 한쪽만 고쳐지므로 한 곳에 둔다(재발명 금지).\n */\n/* 커널 런타임 로드 — 어느 축이 있는지는 카탈로그가 선언한다(이 파일은 도메인 모양을 알지 않는다). */\nconst { TWIN_AXES, axisSource } = require('@operato/twin-kernel')\n\n/** `productionSpec.definition.recipes` 같은 경로를 문서에서 따라간다. */\nexport const at = (o: any, path: string) => path.split('.').reduce((a: any, k: string) => (a == null ? undefined : a[k]), o)\n\n/**\n * 문서 항목의 **식별자** — 축마다 이름이 다르다(자리는 `id`, 공정은 `key`, 레시피도 `key`).\n *\n * 도메인 모양을 코드가 아는 것이 아니라 **식별자 관례**를 아는 것이다. 후보를 순서대로 보고 첫\n * 값을 쓴다. 하나도 없으면 그 항목은 가리킬 수 없으므로 `null` — 목록에서 빼지 않고 **id 없음**으로\n * 낸다(조용히 빠지면 건수가 안 맞는다).\n */\nexport const idOf = (o: any, info?: { idField?: string | string[] }): string | null => {\n if (typeof o === 'string') return o\n /*\n * **무엇이 이 항목을 가리키는지는 선언이 말한다**(§`TwinAxisInfo.idField`).\n *\n * 예전에는 `id`·`key`·`gtin` 을 짐작했다. 그래서 `id` 가 없는 축(수요 구간 — 정체성이 구간 시작\n * 시각이다)의 항목이 전부 「식별자 없음 — 참조할 수 없는 항목」으로 보였다. 값은 실재하는데\n * 가리킬 수 없다고 말한 것이다.\n */\n /*\n * 선언이 **차례**를 말할 수 있다(2026-08-22) — 정체성이 한 칸으로 정해지지 않는 축이 있다. 물품이\n * 그렇다: 직렬 물품은 `epc` 가 유일하지만 비직렬 로트가 자리마다 나뉘면 `subLotId` 가 개체를\n * 구별한다(같은 로트의 두 부분은 같은 `epc` 를 갖는다). 앞에서 값이 있는 첫 칸이 이긴다.\n */\n for (const f of info?.idField ? (Array.isArray(info.idField) ? info.idField : [info.idField]) : []) {\n const v = o?.[f]\n if (v !== undefined && v !== null) return String(v)\n }\n return o?.id ?? o?.key ?? o?.gtin ?? null\n}\n\n\n/**\n * **id → 이름**, 모든 축을 한 표로.\n *\n * ── 왜 이 표가 필요한가 ──────────────────────────────────────────────────────\n * 행은 다른 것을 **id 로** 가리킨다(사람의 `personnelClassIds`, 설비의 `homeLocationId`, 공정의\n * `locationType`). 그 id 의 이름은 **가리켜진 축의 행**에 있으므로, 화면이 그것을 알려면 축을\n * 하나씩 더 읽어야 한다 — 그래서 지금까지 표면마다 그냥 id 를 찍었다(`cutter-op`, `cut-station`).\n * 표면을 하나씩 고치는 것은 끝이 없다: 새 화면은 처음부터 다시 id 를 찍는다.\n *\n * 그래서 **서버가 붙인다.** 서버는 이미 모델을 손에 들고 있고, 어느 축이 있는지는 커널 카탈로그가\n * 선언한다(`TWIN_AXES`) — 도메인이 늘어도 이 함수는 그대로다.\n *\n * 값은 그 항목이 **자기 이름이라고 말한 것**이다(`name`·`label`·`description`). 그중 어떤 것은\n * i18n 키이므로(등급의 `description`) 번역은 화면의 일로 남긴다. 이름이 없으면 표에 넣지 않는다 —\n * 없는 것을 지어내지 않고, 그 경우 화면은 id 를 그대로 보인다.\n */\nexport function nameIndex(model: any): Record<string, string> {\n const out: Record<string, string> = {}\n for (const info of TWIN_AXES as any[]) {\n /* 관측 축(상태·저널)은 문서에 없다 — 경로가 없는 축을 읽으면 모델 루트를 훑는다. */\n if (axisSource(info.axis) !== 'document') continue\n const raw = at(model ?? {}, info.path)\n const list = Array.isArray(raw) ? raw : raw ? [raw] : []\n for (const o of list) {\n const id = idOf(o)\n if (!id || typeof o !== 'object') continue\n const label = o.name ?? o.label ?? o.description\n /* 이름이 id 와 같으면 표에 넣지 않는다 — 같은 값을 두 번 보이는 셈이다. */\n if (typeof label === 'string' && label && label !== id && !out[id]) out[id] = label\n }\n }\n return out\n}\n\n\n/**\n * 이 응답이 **실제로 가리킨 것만** 골라 낸다.\n *\n * 모델 전체 표를 그대로 실으면 자리가 10만인 현장에서 목록 한 페이지에 10만 줄짜리 이름표가\n * 따라온다. 나온 값에 걸리는 것만 싣는다 — 규모에서 성립하는 유일한 방식이다.\n */\nexport function namesFor(rows: any[], index: Record<string, string>): Record<string, string> {\n const out: Record<string, string> = {}\n const take = (v: any, depth: number): void => {\n if (v == null || depth > 4) return\n if (typeof v === 'string') { if (index[v]) out[v] = index[v]; return }\n if (Array.isArray(v)) { for (const x of v) take(x, depth + 1); return }\n if (typeof v === 'object') for (const x of Object.values(v)) take(x, depth + 1)\n }\n for (const r of rows) { take(r?.fields ?? r, 0); if (typeof r?.id === 'string' && index[r.id]) out[r.id] = index[r.id] }\n return out\n}\n"]}
|
|
@@ -19,7 +19,7 @@ async function projectStructure(domainId, instanceId, model, sourceRef) {
|
|
|
19
19
|
if (!instance)
|
|
20
20
|
throw new Error(`cannot project structure: twin instance "${instanceId}" is not registered`);
|
|
21
21
|
/*
|
|
22
|
-
* **같은 id 가 둘이면 그 사실을 말한다** — 제약 위반 문구를 사용자에게
|
|
22
|
+
* **같은 id 가 둘이면 그 사실을 말한다** — 제약 위반 문구를 사용자에게 보이지 않는다.
|
|
23
23
|
*
|
|
24
24
|
* 실측(2026-08-18): 현장 값을 그 현장의 트윈 전부에 얹다가 한 트윈에서 투영이 터졌다. 올라온 것은
|
|
25
25
|
* `SQLITE_CONSTRAINT: UNIQUE constraint failed: twin_equipment...` 뿐이라 **무엇이 잘못됐는지 아무도
|