@things-factory/headless-twin 10.0.15 → 10.0.18
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/attention-digest.d.ts +4 -4
- package/dist-server/engine/attention-digest.js +7 -7
- package/dist-server/engine/attention-digest.js.map +1 -1
- package/dist-server/engine/canonical-ingest.d.ts +18 -82
- package/dist-server/engine/canonical-ingest.js +183 -18
- 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.js +1 -1
- package/dist-server/engine/declared-stimulus.js.map +1 -1
- package/dist-server/engine/energy-topology.js +4 -4
- package/dist-server/engine/energy-topology.js.map +1 -1
- package/dist-server/engine/ingest-dedupe.d.ts +38 -0
- package/dist-server/engine/ingest-dedupe.js +145 -0
- package/dist-server/engine/ingest-dedupe.js.map +1 -0
- package/dist-server/engine/ingest-health.d.ts +90 -10
- package/dist-server/engine/ingest-health.js +125 -16
- package/dist-server/engine/ingest-health.js.map +1 -1
- package/dist-server/engine/integration-probes.d.ts +3 -3
- package/dist-server/engine/integration-probes.js +6 -6
- package/dist-server/engine/integration-probes.js.map +1 -1
- package/dist-server/engine/kpi-baseline.js +4 -4
- package/dist-server/engine/kpi-baseline.js.map +1 -1
- package/dist-server/engine/kpi-fold.d.ts +212 -14
- package/dist-server/engine/kpi-fold.js +374 -21
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.d.ts +11 -11
- package/dist-server/engine/kpi-query.js +281 -36
- 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 +1 -1
- 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.map +1 -1
- package/dist-server/engine/local-declarations.d.ts +7 -7
- package/dist-server/engine/local-declarations.js +15 -14
- package/dist-server/engine/local-declarations.js.map +1 -1
- package/dist-server/engine/log.d.ts +1 -1
- package/dist-server/engine/log.js +1 -1
- package/dist-server/engine/log.js.map +1 -1
- package/dist-server/engine/measured-estimator.js +1 -1
- package/dist-server/engine/measured-estimator.js.map +1 -1
- package/dist-server/engine/measured-yield.js +1 -1
- package/dist-server/engine/measured-yield.js.map +1 -1
- package/dist-server/engine/model-basis.d.ts +1 -1
- package/dist-server/engine/model-basis.js +1 -1
- package/dist-server/engine/model-basis.js.map +1 -1
- package/dist-server/engine/model-gap.d.ts +3 -3
- package/dist-server/engine/model-gap.js +1 -1
- package/dist-server/engine/model-gap.js.map +1 -1
- package/dist-server/engine/model-vocabulary.js +3 -3
- package/dist-server/engine/model-vocabulary.js.map +1 -1
- package/dist-server/engine/oee-accumulator.d.ts +6 -5
- package/dist-server/engine/oee-accumulator.js +6 -5
- package/dist-server/engine/oee-accumulator.js.map +1 -1
- package/dist-server/engine/property-effects.js +2 -2
- package/dist-server/engine/property-effects.js.map +1 -1
- package/dist-server/engine/read-time.d.ts +22 -0
- package/dist-server/engine/read-time.js +81 -0
- package/dist-server/engine/read-time.js.map +1 -0
- package/dist-server/engine/restart-policy.js +1 -1
- package/dist-server/engine/restart-policy.js.map +1 -1
- 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 +2 -2
- package/dist-server/engine/spec-coverage.js +1 -1
- package/dist-server/engine/spec-coverage.js.map +1 -1
- package/dist-server/engine/stage-path.d.ts +4 -4
- package/dist-server/engine/stage-path.js +1 -1
- package/dist-server/engine/stage-path.js.map +1 -1
- package/dist-server/engine/state-axes.d.ts +1 -1
- package/dist-server/engine/state-axes.js +2 -2
- package/dist-server/engine/state-axes.js.map +1 -1
- package/dist-server/engine/travel-estimator.d.ts +2 -2
- package/dist-server/engine/travel-estimator.js +1 -1
- package/dist-server/engine/travel-estimator.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +307 -54
- package/dist-server/engine/twin-engine.js +822 -196
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/engine/warm-start.d.ts +2 -2
- package/dist-server/engine/warm-start.js +5 -5
- package/dist-server/engine/warm-start.js.map +1 -1
- package/dist-server/index.js +1 -1
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +1 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -1
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +3 -3
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -1
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +2 -2
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -1
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.d.ts +5 -0
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js +54 -0
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +1 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.d.ts +5 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js +44 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +1 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.d.ts +6 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js +84 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +1 -0
- package/dist-server/migrations/index.js +7 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/routes.d.ts +1 -0
- package/dist-server/routes.js +69 -5
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/index.d.ts +2 -2
- package/dist-server/service/index.js +30 -24
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/control-routing.d.ts +1 -1
- package/dist-server/service/reference/control-routing.js +1 -1
- package/dist-server/service/reference/control-routing.js.map +1 -1
- package/dist-server/service/reference/discovery-result.d.ts +2 -2
- package/dist-server/service/reference/discovery-result.js +3 -3
- package/dist-server/service/reference/discovery-result.js.map +1 -1
- package/dist-server/service/reference/hook-contract.d.ts +43 -0
- package/dist-server/service/reference/hook-contract.js +59 -0
- package/dist-server/service/reference/hook-contract.js.map +1 -0
- package/dist-server/service/reference/ingest-space.d.ts +1 -1
- package/dist-server/service/reference/ingest-space.js +4 -4
- package/dist-server/service/reference/ingest-space.js.map +1 -1
- package/dist-server/service/reference/knob-defaults.d.ts +1 -1
- package/dist-server/service/reference/knob-defaults.js +4 -4
- package/dist-server/service/reference/knob-defaults.js.map +1 -1
- package/dist-server/service/reference/reference-adapter.d.ts +116 -9
- package/dist-server/service/reference/reference-adapter.js +27 -0
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-hook.d.ts +20 -0
- package/dist-server/service/reference/reference-hook.js +133 -0
- package/dist-server/service/reference/reference-hook.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +2 -2
- package/dist-server/service/reference/reference-live.js +76 -13
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +25 -16
- package/dist-server/service/reference/reference-master.js +35 -13
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +1 -1
- package/dist-server/service/reference/reference-resolver.js +12 -12
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/template-registry.d.ts +1 -1
- package/dist-server/service/reference/template-registry.js.map +1 -1
- package/dist-server/service/reference/twin-reference.d.ts +3 -3
- package/dist-server/service/reference/twin-reference.js.map +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
- package/dist-server/service/twin-audit/command-audit.d.ts +1 -1
- package/dist-server/service/twin-audit/command-audit.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.d.ts +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-query.js +1 -1
- package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -1
- package/dist-server/service/twin-backfill/backfill-period-facts.d.ts +24 -0
- package/dist-server/service/twin-backfill/backfill-period-facts.js +96 -0
- package/dist-server/service/twin-backfill/backfill-period-facts.js.map +1 -0
- package/dist-server/service/twin-backfill/backfill-shape.d.ts +34 -0
- package/dist-server/service/twin-backfill/backfill-shape.js +75 -0
- package/dist-server/service/twin-backfill/backfill-shape.js.map +1 -0
- package/dist-server/service/twin-backfill/index.d.ts +2 -0
- package/dist-server/service/twin-backfill/index.js +6 -0
- package/dist-server/service/twin-backfill/index.js.map +1 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.d.ts +10 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js +44 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +1 -0
- package/dist-server/service/twin-control/twin-control-mutation.js +3 -3
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +1 -1
- package/dist-server/service/twin-event/twin-event-keys.js +2 -2
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-type.d.ts +1 -0
- package/dist-server/service/twin-event/twin-event-type.js +8 -1
- package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +2 -2
- package/dist-server/service/twin-event/twin-event.js +32 -4
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js +1 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -1
- package/dist-server/service/twin-forecast/gap-analytics.d.ts +1 -1
- package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +29 -11
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -1
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/event-type-split-shape.d.ts +50 -0
- package/dist-server/service/twin-journal/event-type-split-shape.js +111 -0
- package/dist-server/service/twin-journal/event-type-split-shape.js.map +1 -0
- package/dist-server/service/twin-journal/event-type-split.d.ts +15 -0
- package/dist-server/service/twin-journal/event-type-split.js +102 -0
- package/dist-server/service/twin-journal/event-type-split.js.map +1 -0
- package/dist-server/service/twin-journal/journal-order.d.ts +15 -0
- package/dist-server/service/twin-journal/journal-order.js +17 -0
- package/dist-server/service/twin-journal/journal-order.js.map +1 -0
- package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -5
- package/dist-server/service/twin-journal/twin-journal-query.js +232 -21
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/domain-catalog.js +2 -1
- package/dist-server/service/twin-lifecycle/domain-catalog.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +27 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +59 -3
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js +14 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -1
- package/dist-server/service/twin-model/epcis-coverage.js +34 -3
- package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
- package/dist-server/service/twin-model/isa95-coverage.d.ts +12 -9
- package/dist-server/service/twin-model/isa95-coverage.js +33 -9
- package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
- package/dist-server/service/twin-model/item-ref.js +2 -2
- package/dist-server/service/twin-model/item-ref.js.map +1 -1
- package/dist-server/service/twin-model/name-index.d.ts +1 -1
- package/dist-server/service/twin-model/name-index.js +2 -2
- package/dist-server/service/twin-model/name-index.js.map +1 -1
- package/dist-server/service/twin-model/project-structure.d.ts +1 -1
- package/dist-server/service/twin-model/project-structure.js +7 -6
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/twin-equipment.d.ts +2 -2
- package/dist-server/service/twin-model/twin-equipment.js +3 -3
- package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
- package/dist-server/service/twin-model/twin-lineage-query.js +16 -5
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-location.d.ts +1 -1
- package/dist-server/service/twin-model/twin-location.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.d.ts +3 -3
- package/dist-server/service/twin-model/twin-model-item-query.js +15 -15
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-mutation.d.ts +2 -2
- package/dist-server/service/twin-model/twin-model-mutation.js +4 -4
- package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-query.js +51 -30
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js +23 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-operation.d.ts +1 -1
- package/dist-server/service/twin-model/twin-operation.js +1 -1
- package/dist-server/service/twin-model/twin-operation.js.map +1 -1
- package/dist-server/service/twin-space/move-space.js +2 -2
- package/dist-server/service/twin-space/move-space.js.map +1 -1
- package/dist-server/service/twin-space/representation-role.d.ts +2 -0
- package/dist-server/service/twin-space/representation-role.js +18 -0
- package/dist-server/service/twin-space/representation-role.js.map +1 -0
- package/dist-server/service/twin-space/space-integrity.d.ts +1 -1
- package/dist-server/service/twin-space/space-integrity.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-representation.d.ts +2 -0
- package/dist-server/service/twin-space/twin-space-representation.js +16 -0
- package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
- package/dist-server/service/twin-space/twin-space-resolver.js +37 -8
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-server/service/twin-structure/twin-structure.js +3 -3
- package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
- package/dist-server/service/twin-subject/event-subjects.d.ts +43 -0
- package/dist-server/service/twin-subject/event-subjects.js +137 -0
- package/dist-server/service/twin-subject/event-subjects.js.map +1 -0
- package/dist-server/service/twin-subject/index.d.ts +9 -0
- package/dist-server/service/twin-subject/index.js +16 -0
- package/dist-server/service/twin-subject/index.js.map +1 -0
- package/dist-server/service/twin-subject/item-resolver.d.ts +10 -0
- package/dist-server/service/twin-subject/item-resolver.js +93 -0
- package/dist-server/service/twin-subject/item-resolver.js.map +1 -0
- package/dist-server/service/twin-subject/subject-query-shape.d.ts +22 -0
- package/dist-server/service/twin-subject/subject-query-shape.js +88 -0
- package/dist-server/service/twin-subject/subject-query-shape.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.d.ts +23 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.js +159 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-event.d.ts +14 -0
- package/dist-server/service/twin-subject/twin-subject-event.js +111 -0
- package/dist-server/service/twin-subject/twin-subject-event.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.d.ts +39 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.js +295 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-query.d.ts +27 -0
- package/dist-server/service/twin-subject/twin-subject-query.js +232 -0
- package/dist-server/service/twin-subject/twin-subject-query.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-writer.d.ts +27 -0
- package/dist-server/service/twin-subject/twin-subject-writer.js +166 -0
- package/dist-server/service/twin-subject/twin-subject-writer.js.map +1 -0
- package/dist-server/service/twin-target/twin-target-resolver.js +1 -1
- package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
- package/dist-shared/entity-delta.d.ts +1 -1
- package/dist-shared/entity-delta.js +4 -4
- package/dist-shared/entity-delta.js.map +1 -1
- package/dist-shared/touched-items.js +2 -2
- package/dist-shared/touched-items.js.map +1 -1
- package/dist-shared/twin-level.js +1 -1
- package/dist-shared/twin-level.js.map +1 -1
- package/package.json +7 -6
- package/server/engine/attention-digest.ts +9 -9
- package/server/engine/canonical-ingest.ts +202 -87
- package/server/engine/command-routing.ts +1 -1
- package/server/engine/declared-stimulus.ts +1 -1
- package/server/engine/energy-topology.ts +2 -2
- package/server/engine/ingest-dedupe.ts +149 -0
- package/server/engine/ingest-health.ts +171 -22
- package/server/engine/integration-probes.ts +7 -7
- package/server/engine/kpi-baseline.ts +4 -4
- package/server/engine/kpi-fold.ts +592 -41
- package/server/engine/kpi-query.ts +307 -48
- package/server/engine/live-feed-registry.ts +1 -1
- package/server/engine/load-meter.ts +1 -1
- package/server/engine/local-declarations.ts +15 -14
- package/server/engine/log.ts +1 -1
- package/server/engine/measured-estimator.ts +1 -1
- package/server/engine/measured-yield.ts +1 -1
- package/server/engine/model-basis.ts +2 -2
- package/server/engine/model-gap.ts +3 -3
- package/server/engine/model-vocabulary.ts +2 -2
- package/server/engine/oee-accumulator.ts +8 -6
- package/server/engine/property-effects.ts +2 -2
- package/server/engine/read-time.ts +77 -0
- package/server/engine/restart-policy.ts +1 -1
- package/server/engine/runtime-key.ts +1 -1
- package/server/engine/spec-coverage.ts +3 -3
- package/server/engine/stage-path.ts +5 -5
- package/server/engine/state-axes.ts +2 -2
- package/server/engine/travel-estimator.ts +2 -2
- package/server/engine/twin-engine.ts +906 -205
- package/server/engine/warm-start.ts +6 -6
- package/server/index.ts +1 -1
- package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +1 -1
- package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +3 -3
- package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +2 -2
- package/server/migrations/1786300000000-IndexEventTypeByTime.ts +48 -0
- package/server/migrations/1786400000000-IndexEventCreatedAt.ts +38 -0
- package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +93 -0
- package/server/migrations/index.ts +7 -1
- package/server/routes.ts +73 -5
- package/server/service/index.ts +7 -1
- package/server/service/reference/control-routing.ts +1 -1
- package/server/service/reference/discovery-result.ts +3 -3
- package/server/service/reference/hook-contract.ts +103 -0
- package/server/service/reference/ingest-space.ts +4 -4
- package/server/service/reference/knob-defaults.ts +4 -4
- package/server/service/reference/reference-adapter.ts +145 -10
- package/server/service/reference/reference-hook.ts +145 -0
- package/server/service/reference/reference-live.ts +87 -15
- package/server/service/reference/reference-master.ts +51 -20
- package/server/service/reference/reference-resolver.ts +11 -11
- package/server/service/reference/template-registry.ts +1 -1
- package/server/service/reference/twin-reference.ts +3 -3
- package/server/service/twin-attention/twin-attention-query.ts +1 -1
- package/server/service/twin-audit/command-audit.ts +1 -1
- package/server/service/twin-audit/twin-audit-event.ts +2 -2
- package/server/service/twin-audit/twin-audit-query.ts +1 -1
- package/server/service/twin-backfill/backfill-period-facts.ts +168 -0
- package/server/service/twin-backfill/backfill-shape.ts +86 -0
- package/server/service/twin-backfill/index.ts +3 -0
- package/server/service/twin-backfill/twin-backfill-resolver.ts +35 -0
- package/server/service/twin-control/twin-control-mutation.ts +2 -2
- package/server/service/twin-event/twin-event-keys.ts +2 -2
- package/server/service/twin-event/twin-event-type.ts +18 -1
- package/server/service/twin-event/twin-event.ts +32 -6
- package/server/service/twin-forecast/forecast-metrics.ts +1 -1
- package/server/service/twin-forecast/gap-analytics.ts +1 -1
- package/server/service/twin-forecast/twin-forecast-query.ts +30 -11
- package/server/service/twin-ingest-window/twin-ingest-window-query.ts +1 -1
- package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +1 -1
- package/server/service/twin-instance/twin-instance.ts +4 -4
- package/server/service/twin-journal/event-type-split-shape.ts +112 -0
- package/server/service/twin-journal/event-type-split.ts +144 -0
- package/server/service/twin-journal/journal-order.ts +20 -0
- package/server/service/twin-journal/twin-journal-query.ts +224 -20
- package/server/service/twin-lifecycle/domain-catalog.ts +2 -1
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +58 -4
- package/server/service/twin-metrics/twin-metrics-query.ts +14 -1
- package/server/service/twin-model/axis-journal-evidence.ts +1 -1
- package/server/service/twin-model/epcis-coverage.ts +34 -3
- package/server/service/twin-model/iec61850-coverage.ts +1 -1
- package/server/service/twin-model/isa95-coverage.ts +43 -16
- package/server/service/twin-model/item-ref.ts +2 -2
- package/server/service/twin-model/name-index.ts +2 -2
- package/server/service/twin-model/project-structure.ts +7 -6
- package/server/service/twin-model/twin-equipment.ts +16 -4
- package/server/service/twin-model/twin-lineage-query.ts +16 -5
- package/server/service/twin-model/twin-location.ts +1 -1
- package/server/service/twin-model/twin-model-item-query.ts +15 -15
- package/server/service/twin-model/twin-model-mutation.ts +4 -4
- package/server/service/twin-model/twin-model-query.ts +49 -28
- package/server/service/twin-model/twin-model-tree-query.ts +26 -2
- package/server/service/twin-model/twin-operation.ts +2 -2
- package/server/service/twin-space/move-space.ts +3 -3
- package/server/service/twin-space/representation-role.ts +15 -0
- package/server/service/twin-space/space-integrity.ts +1 -1
- package/server/service/twin-space/twin-space-representation.ts +37 -0
- package/server/service/twin-space/twin-space-resolver.ts +40 -8
- package/server/service/twin-structure/twin-structure.ts +3 -3
- package/server/service/twin-subject/event-subjects.ts +155 -0
- package/server/service/twin-subject/index.ts +10 -0
- package/server/service/twin-subject/item-resolver.ts +92 -0
- package/server/service/twin-subject/subject-query-shape.ts +84 -0
- package/server/service/twin-subject/twin-subject-backfill.ts +196 -0
- package/server/service/twin-subject/twin-subject-event.ts +101 -0
- package/server/service/twin-subject/twin-subject-fast-path.ts +346 -0
- package/server/service/twin-subject/twin-subject-query.ts +210 -0
- package/server/service/twin-subject/twin-subject-writer.ts +189 -0
- package/server/service/twin-target/twin-target-resolver.ts +1 -1
- package/shared/entity-delta.ts +4 -4
- package/shared/touched-items.ts +2 -2
- package/shared/twin-level.ts +1 -1
- package/test/adopt-structure-live.test.ts +3 -3
- package/test/aggregate-not-findone.test.ts +112 -0
- package/test/attention-digest.test.ts +6 -6
- package/test/axis-read.test.ts +6 -5
- package/test/backfill-shape.test.ts +89 -0
- package/test/boot-resume.test.ts +20 -7
- package/test/broadcast-cost-baseline.test.ts +2 -1
- package/test/broadcast-period.test.ts +2 -2
- package/test/broadcast-queue-overflow.test.ts +244 -0
- package/test/canonical-ingest-vocabularies.test.ts +259 -8
- package/test/canonical-quantity-door.test.ts +2 -2
- package/test/capability-mapping.test.ts +1 -1
- package/test/checkpoint-refuses-empty.test.ts +2 -2
- package/test/connector-capability-declaration.test.ts +88 -0
- package/test/contract-layer-guard.test.ts +48 -0
- package/test/control-capability.test.ts +1 -1
- package/test/cursor-stall-not-read-failure.test.ts +2 -2
- package/test/declaration-reaches-model.test.ts +7 -5
- package/test/declared-stimulus.test.ts +4 -4
- package/test/discovery-result.test.ts +2 -2
- package/test/entity-delta.test.ts +4 -4
- package/test/equipment-identity-reaches.test.ts +57 -0
- package/test/event-subjects.test.ts +147 -0
- package/test/event-time-column.test.ts +3 -3
- package/test/event-type-split.test.ts +155 -0
- package/test/fact-scope-wiring.test.ts +96 -0
- package/test/fold-gap-speaks.test.ts +53 -0
- package/test/forecast-metrics.test.ts +1 -1
- package/test/forecast-tuning.test.ts +1 -1
- package/test/generated-not-counted.test.ts +99 -0
- package/test/ingest-bench.test.ts +2 -1
- package/test/ingest-health-engine.test.ts +2 -2
- package/test/ingest-health-wiring.test.ts +7 -5
- package/test/ingest-health.test.ts +6 -6
- package/test/ingest-history.test.ts +2 -2
- package/test/ingest-idempotent.test.ts +132 -0
- package/test/ingest-space.test.ts +3 -3
- package/test/ingest-wiring-guard.test.ts +197 -0
- package/test/integration-probes.test.ts +3 -3
- package/test/integration-runner.test.ts +1 -1
- package/test/item-ref.test.ts +1 -1
- package/test/journal-order-axis.test.ts +80 -0
- package/test/journal-read-discipline.test.ts +16 -6
- package/test/journal-retention.test.ts +171 -29
- package/test/journal-sort-axis.test.ts +1 -1
- package/test/journal-write-door.test.ts +3 -3
- package/test/journal-write-trend.test.ts +1 -1
- package/test/kernel-kind-guard.test.ts +3 -3
- package/test/knob-defaults.test.ts +1 -1
- package/test/kpi-baseline-db.test.ts +2 -2
- package/test/kpi-fold.test.ts +266 -14
- package/test/kpi-query-bench.test.ts +3 -3
- package/test/live-cursor-wiring.test.ts +6 -6
- package/test/live-kernel-facts.test.ts +11 -11
- package/test/live-mirror-parity.test.ts +9 -6
- package/test/load-meter.test.ts +1 -1
- package/test/local-declarations.test.ts +13 -12
- package/test/log-stamp.test.ts +1 -1
- package/test/master-to-twin.test.ts +2 -2
- package/test/measured-yield.test.ts +2 -2
- package/test/merge-streams.test.ts +70 -0
- package/test/mirror-resumes-from-checkpoint.test.ts +13 -13
- package/test/model-basis.test.ts +2 -2
- package/test/model-gap.test.ts +4 -4
- package/test/model-vocabulary.test.ts +2 -2
- package/test/move-space.test.ts +1 -1
- package/test/oee-accumulator.test.ts +20 -5
- package/test/operational-vocabulary.test.ts +2 -2
- package/test/project-structure-db.test.ts +2 -2
- package/test/projection-reaches-screen.test.ts +7 -2
- package/test/projection-reads-declared.test.ts +109 -0
- package/test/property-effects.test.ts +10 -6
- package/test/read-failure-ledger.test.ts +106 -0
- package/test/read-failure-visible.test.ts +7 -7
- package/test/read-time.test.ts +118 -0
- package/test/reference-grounding.test.ts +1 -1
- package/test/reference-hook.test.ts +86 -0
- package/test/registry-key-guard.test.ts +1 -1
- package/test/rename-twin.test.ts +81 -0
- package/test/restart-policy.test.ts +3 -3
- package/test/resync-origin-site.test.ts +3 -3
- package/test/revision-axis.test.ts +14 -8
- package/test/runtime-key.test.ts +3 -3
- package/test/scale-twin-bench.test.ts +2 -1
- package/test/snapshot-freshness.test.ts +4 -4
- package/test/source-outcome-audit.test.ts +1 -1
- package/test/space-integrity.test.ts +1 -1
- package/test/space-representation.test.ts +66 -0
- package/test/space-scope-includes-stopped.test.ts +55 -0
- package/test/spec-coverage.test.ts +1 -1
- package/test/standard-coverage.test.ts +19 -21
- package/test/status-tally.test.ts +1 -1
- package/test/structure-revision-db.test.ts +7 -7
- package/test/subject-fast-path.test.ts +148 -0
- package/test/subject-read-model.test.ts +175 -0
- package/test/touched-items.test.ts +2 -2
- package/test/twin-audit.test.ts +1 -1
- package/test/twin-event-keys.test.ts +1 -1
- package/test/twin-model-item-db.test.ts +5 -5
- package/test/twin-model-tree-db.test.ts +35 -2
- package/test/twin-origin-resync.test.ts +1 -1
- package/test/vocabulary-guard.test.ts +1 -1
- package/test/warm-start-seam.test.ts +1 -1
- package/test/warm-start.test.ts +6 -6
- package/test/withheld-door.test.ts +2 -2
- package/test/yield-loop.test.ts +4 -4
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* 업무 KPI 폴드 — **이벤트 저널을
|
|
3
|
+
* 업무 KPI 폴드 — **이벤트 저널을 계산해** 시간창 지표를 만든다(순수).
|
|
4
4
|
*
|
|
5
5
|
* ── 왜 필요한가 ─────────────────────────────────────────────────────────────
|
|
6
6
|
* 지금 트윈이 답할 수 있는 것은 "이 순간의 값"(점유 92%)과 "인프라 부하"(초당 이벤트 수)뿐이다.
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* ── 원료는 이미 있다 ────────────────────────────────────────────────────────
|
|
11
11
|
* 저널(`TwinEvent`)에 `task.status`·`order.status`·`equipment.status` 전이가 `eventTime`(ISO)과 함께
|
|
12
|
-
* append-only 로 쌓인다. 그래서 새 계측을 심을 필요가 없다 —
|
|
13
|
-
* 같은 모양의 선례가 이 폴더에 있다(`oee-accumulator.ts` — equipment 전이를
|
|
12
|
+
* append-only 로 쌓인다. 그래서 새 계측을 심을 필요가 없다 — **계산만 하면 된다.**
|
|
13
|
+
* 같은 모양의 선례가 이 폴더에 있다(`oee-accumulator.ts` — equipment 전이를 계산해 OEE 카운터 생산).
|
|
14
14
|
*
|
|
15
15
|
* ── 이 파일이 하지 않는 것 ──────────────────────────────────────────────────
|
|
16
16
|
* · 목표와 비교하지 않는다. "95% 목표 대비" 는 목표를 저장하는 별개 층(경영 KPI)의 몫이고,
|
|
@@ -29,9 +29,10 @@ exports.foldKpiBuckets = foldKpiBuckets;
|
|
|
29
29
|
* 짝맞춤은 **커널이 소유한다**(`foldTaskRecords`). 완료는 작업당 하나·나중 것이 사실, 착수는 처음 것,
|
|
30
30
|
* 종류·오더는 어느 전이에서든 — 그 규칙은 커널 상태 기계에서 나오는 지식이다. 여기서 다시 적었더니
|
|
31
31
|
* 공정 타임라인과 **다른 답**을 냈다(재전송된 완료를 두 건으로 세어 처리량·점유가 부풀려졌다).
|
|
32
|
-
* 이 파일은 이제 "
|
|
32
|
+
* 이 파일은 이제 "계산한 기록 → 창 지표" 만 한다.
|
|
33
33
|
*/
|
|
34
34
|
const twin_kernel_1 = require("@operato/twin-kernel");
|
|
35
|
+
const ops_contract_1 = require("@operato/ops-contract");
|
|
35
36
|
/**
|
|
36
37
|
* 창의 전력량을 **배전 계통의 자리별로 나눈다** — 근거를 함께.
|
|
37
38
|
*
|
|
@@ -128,6 +129,221 @@ function feederShares(kWh, utilization, resourceFeeder) {
|
|
|
128
129
|
* 분기 계량 합이 뿌리보다 크면(회계가 다른 두 계량기) **그 사실을 낸다** — 값을 깎아 맞추면 계량을
|
|
129
130
|
* 우리가 고쳐 쓰는 것이다.
|
|
130
131
|
*/
|
|
132
|
+
/**
|
|
133
|
+
* 창의 **발전량** — 커널이 마감한 기간 사실을 합한다.
|
|
134
|
+
*
|
|
135
|
+
* ── 왜 합이 전부인가 ───────────────────────────────────────────────────────
|
|
136
|
+
* 기간 발전량은 커널이 이미 확정한 사실이다(적산의 종류마다 다른 규칙으로 얻는다). 여기서 다시
|
|
137
|
+
* 계산하면 규칙이 두 벌이 되고, 그때부터 두 화면이 다른 발전량을 말한다. 이 함수가 하는 일은
|
|
138
|
+
* **창에 든 것을 고르고, 설비별로 묶고, 근거가 얼마나 덮였는지 세는 것**이다.
|
|
139
|
+
*
|
|
140
|
+
* ── 걸친 기간은 세지 않는다 ─────────────────────────────────────────────────
|
|
141
|
+
* 구간 마감과 같은 규칙이다. 하루의 절반이 창에 들었다고 절반을 세면 그것은 관측이 아니라 우리가 나눈
|
|
142
|
+
* 값이고, 합이 창을 넘는다.
|
|
143
|
+
*
|
|
144
|
+
* ── 덜 덮인 기간을 센다 ─────────────────────────────────────────────────────
|
|
145
|
+
* 기간의 끝이 자정인데 마지막 표본이 저녁이었으면 그 사이는 재지 않은 것이다. 그 수를 함께 내야 화면이
|
|
146
|
+
* 「이 합은 이만큼 덜 잰 것」이라고 말할 수 있다. 얼마나 어긋나면 문제인지는 현장이 정한다.
|
|
147
|
+
*/
|
|
148
|
+
function generatedOfPeriods(periods, range,
|
|
149
|
+
/** 이용률의 분모 — 없으면 이용률을 내지 않는다(0 이나 짐작을 만들지 않는다). */
|
|
150
|
+
rated,
|
|
151
|
+
/** 기간마다 적용할 단가 — 기간을 덮는 것을 쓴다. 없으면 금액을 만들지 않는다. */
|
|
152
|
+
prices) {
|
|
153
|
+
const inside = periods.filter(g => g.startMs >= range.startMs && g.endMs <= range.endMs);
|
|
154
|
+
/*
|
|
155
|
+
* ── **걸친 기간을 조용히 버리지 않는다** (2026-08-30) ──────────────────────
|
|
156
|
+
*
|
|
157
|
+
* 더하는 것은 창에 **온전히 들어온 기간**뿐이다. 그것이 맞는 셈이다 — 걸친 기간을 더하면 다른 날의
|
|
158
|
+
* 값이 이 창의 값인 척한다.
|
|
159
|
+
*
|
|
160
|
+
* 그런데 버린 것을 말하지 않았다. 하루 단위 기간은 「지금부터 24시간 전」 창에 온전히 들어오는 일이
|
|
161
|
+
* 거의 없다(창이 한낮에서 시작하므로). 그래서 어제 발전량이 저널에 멀쩡히 있는데 화면이 비었고,
|
|
162
|
+
* 사용자는 그것을 **「발전량이 없다」**로 읽었다. 실측으로 그 일이 났다.
|
|
163
|
+
*
|
|
164
|
+
* 이제 걸친 것을 세어 함께 낸다. 화면이 「이 창에 다 들어오지 않은 기간이 있다」를 말할 수 있다.
|
|
165
|
+
*/
|
|
166
|
+
const straddling = periods.filter(g => !(g.startMs >= range.startMs && g.endMs <= range.endMs) && g.endMs > range.startMs && g.startMs < range.endMs);
|
|
167
|
+
const notCounted = straddling.length
|
|
168
|
+
? {
|
|
169
|
+
periods: straddling.length,
|
|
170
|
+
kWh: straddling.reduce((a, g) => a + g.kWh, 0),
|
|
171
|
+
earliestStart: new Date(Math.min(...straddling.map(g => g.startMs))).toISOString(),
|
|
172
|
+
latestEnd: new Date(Math.max(...straddling.map(g => g.endMs))).toISOString()
|
|
173
|
+
}
|
|
174
|
+
: undefined;
|
|
175
|
+
if (!inside.length) {
|
|
176
|
+
/* 온전히 들어온 것이 없다 — 그래도 걸친 것이 있으면 그 사실을 낸다(빈 값과 「없음」은 다르다). */
|
|
177
|
+
return notCounted ? { generated: { kWh: 0, counted: 0, partial: 0, byEquipment: [], notCounted } } : {};
|
|
178
|
+
}
|
|
179
|
+
const byEquipment = new Map();
|
|
180
|
+
let partial = 0;
|
|
181
|
+
for (const g of inside) {
|
|
182
|
+
byEquipment.set(g.equipmentId, (byEquipment.get(g.equipmentId) ?? 0) + g.kWh);
|
|
183
|
+
const coveredFrom = Number.isFinite(g.observedFromMs) ? g.observedFromMs <= g.startMs : false;
|
|
184
|
+
const coveredTo = Number.isFinite(g.observedToMs) ? g.observedToMs >= g.endMs : false;
|
|
185
|
+
if (!coveredFrom || !coveredTo)
|
|
186
|
+
partial++;
|
|
187
|
+
}
|
|
188
|
+
const kWh = inside.reduce((a, g) => a + g.kWh, 0);
|
|
189
|
+
/*
|
|
190
|
+
* 이용률의 분모는 **센 기간들의 길이**다. 창 길이로 나누면, 하루치 하나만 센 24시간 창에서 그 하루가
|
|
191
|
+
* 창 전체에 걸쳐 있었던 것처럼 작아진다.
|
|
192
|
+
*/
|
|
193
|
+
const hours = inside.reduce((a, g) => a + (g.endMs - g.startMs) / 3_600_000, 0);
|
|
194
|
+
const capacityFactor = rated && rated.kW > 0 && hours > 0
|
|
195
|
+
? { value: kWh / (rated.kW * hours), ratedKW: rated.kW, basis: rated.basis, from: rated.from, hours }
|
|
196
|
+
: undefined;
|
|
197
|
+
/*
|
|
198
|
+
* 낸 것의 값 — 기간마다 **그 기간을 덮는 단가**로 매긴다. 걸친 단가는 쓰지 않는다: 하루 단가로
|
|
199
|
+
* 이틀치를 매기면 그 절반은 다른 날의 값이 된다.
|
|
200
|
+
*
|
|
201
|
+
* 모든 기간에 단가가 있을 때만 금액을 만든다. 일부만 매기고 더하면 그 합이 창 전체의 값인 척한다.
|
|
202
|
+
*/
|
|
203
|
+
let amount = 0;
|
|
204
|
+
let priced = 0;
|
|
205
|
+
let currency;
|
|
206
|
+
for (const g of inside) {
|
|
207
|
+
const p = (prices ?? []).find(x => x.startMs <= g.startMs && x.endMs >= g.endMs);
|
|
208
|
+
if (!p)
|
|
209
|
+
continue;
|
|
210
|
+
amount += g.kWh * p.unitPrice;
|
|
211
|
+
priced++;
|
|
212
|
+
currency = currency ?? p.currency;
|
|
213
|
+
}
|
|
214
|
+
const unpriced = inside.length - priced;
|
|
215
|
+
const revenue = priced > 0 && unpriced === 0 && currency ? { amount, currency, priced, unpriced } : undefined;
|
|
216
|
+
return {
|
|
217
|
+
generated: {
|
|
218
|
+
kWh,
|
|
219
|
+
counted: inside.length,
|
|
220
|
+
partial,
|
|
221
|
+
byEquipment: [...byEquipment.entries()]
|
|
222
|
+
.map(([id, kWh]) => ({ id, kWh }))
|
|
223
|
+
.sort((a, b) => b.kWh - a.kWh),
|
|
224
|
+
...(capacityFactor ? { capacityFactor } : {}),
|
|
225
|
+
...(revenue ? { revenue } : {}),
|
|
226
|
+
/* 이 창에 다 들어오지 않아 더하지 못한 기간 — 있으면 화면이 그것을 말한다. */
|
|
227
|
+
...(notCounted ? { notCounted } : {})
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 밖에서 마감되어 온 사용 구간을 합한다 — **구간 마감과 더하지 않는다.**
|
|
233
|
+
*
|
|
234
|
+
* 두 회계가 같은 전기를 말한다. 더하면 두 번 세고, 둘 다 있는 현장에서는 계량 쪽이 강한 근거이므로
|
|
235
|
+
* 그쪽을 쓴다. 여기서는 이쪽 몫만 계산하고, 무엇을 쓸지는 부르는 쪽이 정한다.
|
|
236
|
+
*
|
|
237
|
+
* 단가는 **모든 구간이 실었을 때만** 요금을 만든다. 일부만 실었는데 있는 것만 더하면 그 합이 창의
|
|
238
|
+
* 요금인 척한다 — 못 매긴 구간 수를 함께 낸다.
|
|
239
|
+
*/
|
|
240
|
+
function usageOfPeriods(periods, range) {
|
|
241
|
+
const inside = periods.filter(u => u.startMs >= range.startMs && u.endMs <= range.endMs);
|
|
242
|
+
if (!inside.length)
|
|
243
|
+
return undefined;
|
|
244
|
+
const byBasis = new Map();
|
|
245
|
+
let kWh = 0;
|
|
246
|
+
let charge = 0;
|
|
247
|
+
let unpriced = 0;
|
|
248
|
+
let currency;
|
|
249
|
+
for (const u of inside) {
|
|
250
|
+
kWh += u.kWh;
|
|
251
|
+
byBasis.set(u.basis, (byBasis.get(u.basis) ?? 0) + 1);
|
|
252
|
+
if (Number.isFinite(u.unitPrice)) {
|
|
253
|
+
charge += u.kWh * u.unitPrice;
|
|
254
|
+
if (!currency)
|
|
255
|
+
currency = u.currency;
|
|
256
|
+
}
|
|
257
|
+
else
|
|
258
|
+
unpriced++;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
kWh,
|
|
262
|
+
counted: inside.length,
|
|
263
|
+
...(unpriced === 0 && currency ? { charge, currency } : {}),
|
|
264
|
+
unpriced,
|
|
265
|
+
byBasis: [...byBasis.entries()].map(([basis, counted]) => ({ basis, counted })).sort((a, b) => b.counted - a.counted)
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* **요금적용전력을 고른다 — 청구서가 먼저다.**
|
|
270
|
+
*
|
|
271
|
+
* 순서에 뜻이 있다. 청구서는 그 기간에 실제로 매겨진 값이고, 선언은 사람이 옮겨 적은 값이다.
|
|
272
|
+
* 그 값이 매달 바뀌므로 옮겨 적은 것은 곧 낡는다.
|
|
273
|
+
*/
|
|
274
|
+
function billingDemandOf(billsInWindow, tariffBasis, lastBill, declaredKW,
|
|
275
|
+
/** 이 창에서 관측한 최고 수요와 그 값이 언제까지의 것인지 — 사용 구간의 `maxKW` 에서 온다. */
|
|
276
|
+
observedPeak) {
|
|
277
|
+
const declared = Number.isFinite(Number(declaredKW)) ? Number(declaredKW) : undefined;
|
|
278
|
+
const withValue = billsInWindow.filter(b => Number.isFinite(b.billingDemandKW));
|
|
279
|
+
const fromBill = withValue.length ? withValue.reduce((a, x) => (x.endMs > a.endMs ? x : a)).billingDemandKW : undefined;
|
|
280
|
+
const carried = Number.isFinite(Number(lastBill?.billingDemandKW)) ? Number(lastBill?.billingDemandKW) : undefined;
|
|
281
|
+
/* 이 주기에 적용되는 기준이 가장 먼저다 — 청구서는 끝난 기간의 것이다. */
|
|
282
|
+
const fromBasis = Number.isFinite(Number(tariffBasis?.billingDemandKW)) ? Number(tariffBasis?.billingDemandKW) : undefined;
|
|
283
|
+
const picked = fromBasis !== undefined
|
|
284
|
+
? { kW: fromBasis, from: 'tariff-basis' }
|
|
285
|
+
: fromBill !== undefined
|
|
286
|
+
? { kW: fromBill, from: 'bill' }
|
|
287
|
+
: carried !== undefined
|
|
288
|
+
? { kW: carried, from: 'carried-forward' }
|
|
289
|
+
: declared !== undefined
|
|
290
|
+
? { kW: declared, from: 'declaration' }
|
|
291
|
+
: undefined;
|
|
292
|
+
if (!picked)
|
|
293
|
+
return undefined;
|
|
294
|
+
/* 선언이 있는데 청구서와 다르면 그 사실을 낸다 — 낡은 선언은 스스로 말해야 한다. */
|
|
295
|
+
const stale = picked.from !== 'declaration' && declared !== undefined && declared !== picked.kW;
|
|
296
|
+
const exceeded = observedPeak && observedPeak.kW > picked.kW
|
|
297
|
+
? {
|
|
298
|
+
peakKW: observedPeak.kW,
|
|
299
|
+
overKW: observedPeak.kW - picked.kW,
|
|
300
|
+
observedTo: new Date(observedPeak.toMs).toISOString()
|
|
301
|
+
}
|
|
302
|
+
: undefined;
|
|
303
|
+
return {
|
|
304
|
+
...picked,
|
|
305
|
+
...(declared !== undefined ? { declaredKW: declared } : {}),
|
|
306
|
+
...(stale ? { stale: true } : {}),
|
|
307
|
+
...(exceeded ? { exceeded } : {})
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* 창에 온전히 든 청구서 하나 — 가장 늦게 끝난 것.
|
|
312
|
+
*
|
|
313
|
+
* **창이 청구 주기와 같을 때만 견준다.** 한 달 청구서를 하루 창의 값과 빼면 그 차이는 아무것도 뜻하지
|
|
314
|
+
* 않는다. 그래서 같은지를 값과 함께 낸다.
|
|
315
|
+
*/
|
|
316
|
+
function billOfWindow(bills, range, ours) {
|
|
317
|
+
const inside = bills.filter(b => b.startMs >= range.startMs && b.endMs <= range.endMs);
|
|
318
|
+
if (!inside.length)
|
|
319
|
+
return undefined;
|
|
320
|
+
const b = inside.reduce((a, x) => (x.endMs > a.endMs ? x : a));
|
|
321
|
+
const matchesWindow = b.startMs === range.startMs && b.endMs === range.endMs;
|
|
322
|
+
/* 양쪽에 있는 항목만 견준다 — 한쪽이 없는 것을 0 으로 두면 그 차이가 통째로 어긋남처럼 보인다. */
|
|
323
|
+
const gap = (mine, billed) => Number.isFinite(Number(mine)) && Number.isFinite(billed) ? Number(mine) - billed : undefined;
|
|
324
|
+
const diff = matchesWindow
|
|
325
|
+
? {
|
|
326
|
+
...(gap(ours?.total, b.total) !== undefined ? { total: gap(ours?.total, b.total) } : {}),
|
|
327
|
+
...(gap(ours?.demandCharge, b.demandCharge) !== undefined
|
|
328
|
+
? { demandCharge: gap(ours?.demandCharge, b.demandCharge) }
|
|
329
|
+
: {}),
|
|
330
|
+
...(gap(ours?.energyCharge, b.energyCharge) !== undefined
|
|
331
|
+
? { energyCharge: gap(ours?.energyCharge, b.energyCharge) }
|
|
332
|
+
: {})
|
|
333
|
+
}
|
|
334
|
+
: {};
|
|
335
|
+
return {
|
|
336
|
+
from: b.from,
|
|
337
|
+
to: b.to,
|
|
338
|
+
...(Number.isFinite(b.total) ? { total: b.total } : {}),
|
|
339
|
+
...(Number.isFinite(b.energyCharge) ? { energyCharge: b.energyCharge } : {}),
|
|
340
|
+
...(Number.isFinite(b.demandCharge) ? { demandCharge: b.demandCharge } : {}),
|
|
341
|
+
...(b.currency ? { currency: b.currency } : {}),
|
|
342
|
+
...(Number.isFinite(b.billingDemandKW) ? { billingDemandKW: b.billingDemandKW } : {}),
|
|
343
|
+
matchesWindow,
|
|
344
|
+
...(Object.keys(diff).length ? { diff } : {})
|
|
345
|
+
};
|
|
346
|
+
}
|
|
131
347
|
function measuredFeeders(windows, range, totalKWh, meterFeeder) {
|
|
132
348
|
if (!meterFeeder || !Object.keys(meterFeeder).length)
|
|
133
349
|
return undefined;
|
|
@@ -197,7 +413,29 @@ function measuredFeeders(windows, range, totalKWh, meterFeeder) {
|
|
|
197
413
|
...(remainder < 0 ? { submeterExcessKWh: -remainder } : {})
|
|
198
414
|
};
|
|
199
415
|
}
|
|
200
|
-
/**
|
|
416
|
+
/** 폴드 옵션 — 관점 축과, 그 축을 만들기 위해 이벤트 밖에서 와야 하는 지도. */
|
|
417
|
+
/*
|
|
418
|
+
* ── 같은 사실이 두 벌 있어도 한 번만 센다 (2026-08-30) ──────────────────────
|
|
419
|
+
*
|
|
420
|
+
* 마감된 구간 사실은 정체성이 내용에 있다(커널 0.7.81). 그런데 저널에 이미 여러 벌 적힌 것이 있다 —
|
|
421
|
+
* 쓰는 자리에서 막기 전에 들어간 것들이다(실측: 어느 날의 발전 단가가 세 벌).
|
|
422
|
+
*
|
|
423
|
+
* 지난 기록을 지우지 않는다 — 저널은 일어난 일의 기록이다. 대신 **읽을 때 한 번만 센다.**
|
|
424
|
+
* 그러지 않으면 그 구간의 수익·사용량이 그 횟수만큼 세어진다.
|
|
425
|
+
*/
|
|
426
|
+
function onceById(rows) {
|
|
427
|
+
const seen = new Set();
|
|
428
|
+
return rows.filter(r => {
|
|
429
|
+
const id = r?.payload?.eventId;
|
|
430
|
+
if (typeof id !== 'string' || !id)
|
|
431
|
+
return true;
|
|
432
|
+
if (seen.has(id))
|
|
433
|
+
return false;
|
|
434
|
+
seen.add(id);
|
|
435
|
+
return true;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
/** 이 기록이 요청한 축에서 어느 값에 속하는가. 값이 없으면 'unknown'(알리지 않고 버리지 않는다). */
|
|
201
439
|
function facetKey(rec, options) {
|
|
202
440
|
const f = rec.facets;
|
|
203
441
|
switch (options.groupBy) {
|
|
@@ -227,8 +465,8 @@ function facetKey(rec, options) {
|
|
|
227
465
|
if (!options.shifts?.length)
|
|
228
466
|
return 'unknown';
|
|
229
467
|
/* 시각까지 넘긴다 — **휴일에 일어난 일은 어느 교대에도 속하지 않는다.** 되풀이만 보면 그날 서지도
|
|
230
|
-
않은 교대에 집계돼, 교대별 성과가
|
|
231
|
-
return (0,
|
|
468
|
+
않은 교대에 집계돼, 교대별 성과가 알리지 않고 거짓이 된다. */
|
|
469
|
+
return (0, ops_contract_1.activeShiftAt)(options.shifts, rec.at, options.utcOffsetMinutes) ?? 'unknown';
|
|
232
470
|
default:
|
|
233
471
|
return 'unknown';
|
|
234
472
|
}
|
|
@@ -256,7 +494,7 @@ function qualityOf(rows) {
|
|
|
256
494
|
* 기록을 축으로 묶어 축별 통계를 낸다 — **전체와 같은 규칙**으로(같은 stats 함수, 같은 창).
|
|
257
495
|
*
|
|
258
496
|
* 축의 값이 아주 많을 수 있다(오더 축은 수천 개). 그래서 건수 큰 것부터 상한까지만 돌려주고
|
|
259
|
-
* **잘라낸 수를 함께 알린다** —
|
|
497
|
+
* **잘라낸 수를 함께 알린다** — 들어온 것이 없는 절단은 "이게 전부" 라는 거짓을 만든다.
|
|
260
498
|
*/
|
|
261
499
|
function groupRecords(records, options) {
|
|
262
500
|
const by = options.groupBy;
|
|
@@ -320,11 +558,11 @@ function stats(values) {
|
|
|
320
558
|
};
|
|
321
559
|
}
|
|
322
560
|
/**
|
|
323
|
-
* 이벤트를
|
|
561
|
+
* 이벤트를 계산해 시간창 KPI 를 만든다.
|
|
324
562
|
*
|
|
325
563
|
* **호출부 규약**: `events` 는 창보다 **넉넉히 앞까지** 담아 넘긴다(예: 창 시작 − 리드타임 상한).
|
|
326
564
|
* 완료된 작업의 시작 이벤트가 창 밖에 있을 수 있기 때문이다. 못 찾은 것은 `unpaired` 로 알린다 —
|
|
327
|
-
* 평균에서
|
|
565
|
+
* 평균에서 오류 없이 빠지면 지표가 거짓이 된다.
|
|
328
566
|
*
|
|
329
567
|
* 시간축은 `eventTime`(ISO) 차분이다. sim 클록이 실제 시각과 오프셋이 있어도 **차분은 정확**하다
|
|
330
568
|
* (고정 오프셋은 상쇄된다). 파싱 불가한 줄은 버린다.
|
|
@@ -352,7 +590,7 @@ function foldKpi(events, window, options = {}) {
|
|
|
352
590
|
* **끝났나는 커널이 답한다** (2026-08-23) — `isOrderTerminal`.
|
|
353
591
|
*
|
|
354
592
|
* 예전에는 여기서 **동의어 목록**으로 추론했다(`completed`·`fulfilled`·`done`·`finished`).
|
|
355
|
-
* 그 방식이 낸 대가가 둘이었고 둘 다
|
|
593
|
+
* 그 방식이 낸 대가가 둘이었고 둘 다 들어온 것이 없었다.
|
|
356
594
|
*
|
|
357
595
|
* ① 원본의 낱말이 목록에 없으면 완료가 0 이 된다. 실제로 그랬다 — 첫 실 시스템의 `FINISHED`
|
|
358
596
|
* 가 빠져 있었고, 그때 목록에 낱말을 더한 것은 진짜 빈 자리(그 커넥터가 자기 상태 표를
|
|
@@ -365,11 +603,11 @@ function foldKpi(events, window, options = {}) {
|
|
|
365
603
|
* 판정의 1차 근거는 **양**이고 낱말은 양으로 말할 수 없는 종결(취소·중도 종료)에만 쓴다.
|
|
366
604
|
*/
|
|
367
605
|
const status = String(d.status ?? '').toLowerCase();
|
|
368
|
-
const done = (0,
|
|
606
|
+
const done = (0, ops_contract_1.isOrderTerminal)({ status: d.status, requested: d.requested, fulfilled: d.fulfilled });
|
|
369
607
|
if (at >= window.fromMs && at <= window.toMs) {
|
|
370
608
|
/*
|
|
371
609
|
* **오더당 한 번만 센다.** 폴링하는 원본은 같은 오더의 종결을 여러 주기에 걸쳐 다시 보낼 수
|
|
372
|
-
* 있고(그것은 결함이 아니라 폴링의 성질이다), 사건마다 세면 처리량이
|
|
610
|
+
* 있고(그것은 결함이 아니라 폴링의 성질이다), 사건마다 세면 처리량이 알리지 않고 부풀려진다.
|
|
373
611
|
* 작업 쪽은 커널의 짝맞춤이 이미 그 규율을 갖고 있다(「완료는 작업당 하나」) — 오더만 없었다.
|
|
374
612
|
*/
|
|
375
613
|
if (done) {
|
|
@@ -379,7 +617,7 @@ function foldKpi(events, window, options = {}) {
|
|
|
379
617
|
completedOrders++;
|
|
380
618
|
}
|
|
381
619
|
else if (!id)
|
|
382
|
-
completedOrders++; // 정체를 모르면 셈에서 빼지 않는다(
|
|
620
|
+
completedOrders++; // 정체를 모르면 셈에서 빼지 않는다(알리지 않고 줄이지 않는다)
|
|
383
621
|
}
|
|
384
622
|
/*
|
|
385
623
|
* **완료로 세지 않은 낱말의 인구조사.** 「못 읽었다」가 아니다 — `picking` 처럼 완료가 아닌
|
|
@@ -398,7 +636,7 @@ function foldKpi(events, window, options = {}) {
|
|
|
398
636
|
* 사건이 하나도 없으면 이 트윈에는 에너지가 없는 것이므로 블록 자체를 내지 않는다.
|
|
399
637
|
*/
|
|
400
638
|
const demandWindows = events
|
|
401
|
-
.filter(e => e.eventType ===
|
|
639
|
+
.filter(e => e.eventType === ops_contract_1.ENERGY_EVENT.demandWindow)
|
|
402
640
|
.map(e => {
|
|
403
641
|
const d = e.payload?.data ?? e.payload ?? {};
|
|
404
642
|
/* 최대(`maxKW`)도 함께 넘긴다 — 요금이 걸리는 수는 평균이 아니라 최대다.
|
|
@@ -421,6 +659,72 @@ function foldKpi(events, window, options = {}) {
|
|
|
421
659
|
};
|
|
422
660
|
})
|
|
423
661
|
.filter(w => Number.isFinite(w.startMs) && Number.isFinite(w.endMs));
|
|
662
|
+
/*
|
|
663
|
+
* 발전 기간 사실 — 커널이 기간을 마감할 때 낸 것이다. 구간 마감과 같은 규칙으로 **창에 온전히 든
|
|
664
|
+
* 것만** 센다(걸친 기간을 반만 세면 합이 창을 넘는다).
|
|
665
|
+
*
|
|
666
|
+
* 관측 구간(`observedFrom`·`observedTo`)을 함께 읽는다 — 값의 근거가 얼마나 덮였는지가 그 둘에 있다.
|
|
667
|
+
*/
|
|
668
|
+
const generationPeriods = onceById(events.filter(e => e.eventType === ops_contract_1.ENERGY_EVENT.generationPeriod))
|
|
669
|
+
.map(e => {
|
|
670
|
+
const d = e.payload?.data ?? e.payload ?? {};
|
|
671
|
+
return {
|
|
672
|
+
equipmentId: String(d.equipmentId ?? ''),
|
|
673
|
+
kWh: Number(d.kWh),
|
|
674
|
+
startMs: Date.parse(String(d.periodStart ?? '')),
|
|
675
|
+
endMs: Date.parse(String(d.periodEnd ?? '')),
|
|
676
|
+
observedFromMs: Date.parse(String(d.observedFrom ?? '')),
|
|
677
|
+
observedToMs: Date.parse(String(d.observedTo ?? ''))
|
|
678
|
+
};
|
|
679
|
+
})
|
|
680
|
+
.filter(g => g.equipmentId && Number.isFinite(g.kWh) && Number.isFinite(g.startMs) && Number.isFinite(g.endMs));
|
|
681
|
+
/*
|
|
682
|
+
* 밖에서 마감되어 온 사용 구간 — 구간 마감과 같은 규칙으로 **창에 온전히 든 것만** 센다.
|
|
683
|
+
*/
|
|
684
|
+
const usagePeriods = onceById(events.filter(e => e.eventType === ops_contract_1.ENERGY_EVENT.usagePeriod))
|
|
685
|
+
.map(e => {
|
|
686
|
+
const d = e.payload?.data ?? e.payload ?? {};
|
|
687
|
+
return {
|
|
688
|
+
meterId: String(d.meterId ?? ''),
|
|
689
|
+
kWh: Number(d.kWh),
|
|
690
|
+
startMs: Date.parse(String(d.from ?? '')),
|
|
691
|
+
endMs: Date.parse(String(d.to ?? '')),
|
|
692
|
+
unitPrice: Number(d.unitPrice),
|
|
693
|
+
maxKW: Number(d.maxKW),
|
|
694
|
+
currency: d.currency ? String(d.currency) : undefined,
|
|
695
|
+
basis: d.basis ? String(d.basis) : 'unstated'
|
|
696
|
+
};
|
|
697
|
+
})
|
|
698
|
+
.filter(u => u.meterId && Number.isFinite(u.kWh) && Number.isFinite(u.startMs) && Number.isFinite(u.endMs));
|
|
699
|
+
/* 청구서 — 창에 온전히 든 것 중 가장 늦게 끝난 것 하나. */
|
|
700
|
+
const bills = onceById(events.filter(e => e.eventType === ops_contract_1.ENERGY_EVENT.bill))
|
|
701
|
+
.map(e => {
|
|
702
|
+
const d = e.payload?.data ?? e.payload ?? {};
|
|
703
|
+
return {
|
|
704
|
+
from: String(d.from ?? ''),
|
|
705
|
+
to: String(d.to ?? ''),
|
|
706
|
+
startMs: Date.parse(String(d.from ?? '')),
|
|
707
|
+
endMs: Date.parse(String(d.to ?? '')),
|
|
708
|
+
total: Number(d.total),
|
|
709
|
+
energyCharge: Number(d.energyCharge),
|
|
710
|
+
demandCharge: Number(d.demandCharge),
|
|
711
|
+
currency: d.currency ? String(d.currency) : undefined,
|
|
712
|
+
billingDemandKW: Number(d.billingDemandKW)
|
|
713
|
+
};
|
|
714
|
+
})
|
|
715
|
+
.filter(b => Number.isFinite(b.startMs) && Number.isFinite(b.endMs));
|
|
716
|
+
/* 발전 단가 — 기간마다 그때의 값이다(날마다 바뀐다). */
|
|
717
|
+
const generationPrices = onceById(events.filter(e => e.eventType === ops_contract_1.ENERGY_EVENT.generationPrice))
|
|
718
|
+
.map(e => {
|
|
719
|
+
const d = e.payload?.data ?? e.payload ?? {};
|
|
720
|
+
return {
|
|
721
|
+
startMs: Date.parse(String(d.from ?? '')),
|
|
722
|
+
endMs: Date.parse(String(d.to ?? '')),
|
|
723
|
+
unitPrice: Number(d.unitPrice),
|
|
724
|
+
currency: String(d.currency ?? '')
|
|
725
|
+
};
|
|
726
|
+
})
|
|
727
|
+
.filter(p => Number.isFinite(p.startMs) && Number.isFinite(p.endMs) && Number.isFinite(p.unitPrice) && p.currency);
|
|
424
728
|
const leads = [];
|
|
425
729
|
const works = [];
|
|
426
730
|
const waits = [];
|
|
@@ -466,18 +770,48 @@ function foldKpi(events, window, options = {}) {
|
|
|
466
770
|
return {
|
|
467
771
|
window: { fromMs: window.fromMs, toMs: window.toMs },
|
|
468
772
|
throughput: { tasks: tasksInWindow, orders: completedOrders },
|
|
469
|
-
|
|
773
|
+
/* 발전만 있는 현장(계량 없는 발전소)도 답이 나와야 한다 — 둘 중 하나만 있어도 블록을 낸다. */
|
|
774
|
+
...(demandWindows.length || generationPeriods.length || usagePeriods.length || bills.length
|
|
470
775
|
? (() => {
|
|
471
776
|
const e = (0, twin_kernel_1.energyOfWindows)(demandWindows, { startMs: window.fromMs, endMs: window.toMs });
|
|
777
|
+
const usage = usageOfPeriods(usagePeriods, { startMs: window.fromMs, endMs: window.toMs });
|
|
778
|
+
/*
|
|
779
|
+
* 어느 회계를 쓸지 정한다 — **더하지 않는다.** 계량 표본에서 우리가 마감한 값이 있으면 그것을
|
|
780
|
+
* 쓰고(강한 근거), 없으면 밖에서 마감되어 온 값을 쓴다. 무엇을 썼는지 함께 낸다.
|
|
781
|
+
*/
|
|
782
|
+
const usedKWh = e.kWh !== undefined ? e.kWh : usage?.kWh;
|
|
783
|
+
const kWhBasis = e.kWh !== undefined ? 'demand-window' : usage ? 'usage-period' : undefined;
|
|
472
784
|
/* 분모는 **완료 오더**다(대당 전력). 창에 완료가 없으면 커널 규칙이 「정의되지 않음」으로 답한다. */
|
|
473
785
|
const i = (0, twin_kernel_1.energyIntensity)({
|
|
474
|
-
kWh:
|
|
786
|
+
kWh: usedKWh,
|
|
475
787
|
energyWindow: e.window,
|
|
476
788
|
denominator: { kind: 'output', value: completedOrders, window: e.window }
|
|
477
789
|
});
|
|
790
|
+
/*
|
|
791
|
+
* 요금은 **구간마다 제 단가로** 매긴 값이 있으면 그것을 쓴다. 단일 단가로 접으면 시간대마다
|
|
792
|
+
* 다른 단가가 사라지고, 「피크를 깎아 얼마를 아끼나」가 그 차이 위에 선다.
|
|
793
|
+
*/
|
|
794
|
+
/* 요금적용전력은 청구서가 먼저다 — 선언은 청구가 오지 않는 현장의 몫이다. */
|
|
795
|
+
/* 이 창의 최고 수요 — 사용 구간이 실어 온 값 중 가장 큰 것(원본 계량기가 잰 값이다). */
|
|
796
|
+
const peaks = usagePeriods
|
|
797
|
+
.filter(u => u.startMs >= window.fromMs && u.endMs <= window.toMs && Number.isFinite(u.maxKW))
|
|
798
|
+
.map(u => ({ kW: u.maxKW, toMs: u.endMs }));
|
|
799
|
+
const observedPeak = peaks.length ? peaks.reduce((a, x) => (x.kW > a.kW ? x : a)) : undefined;
|
|
800
|
+
const billingDemand = billingDemandOf(bills.filter(b => Number.isFinite(b.billingDemandKW)).map(b => ({ endMs: b.endMs, billingDemandKW: b.billingDemandKW })), options.tariffBasis, options.lastBill, options.tariff?.billingDemandKW, observedPeak);
|
|
801
|
+
const cost = options.tariff
|
|
802
|
+
? (0, twin_kernel_1.electricityCost)({
|
|
803
|
+
kWh: usedKWh,
|
|
804
|
+
peakKW: e.peakKW,
|
|
805
|
+
tariff: { ...options.tariff, ...(billingDemand ? { billingDemandKW: billingDemand.kW } : {}) },
|
|
806
|
+
...(usage?.charge !== undefined ? { energyChargeFromPeriods: usage.charge } : {})
|
|
807
|
+
})
|
|
808
|
+
: undefined;
|
|
478
809
|
return {
|
|
479
810
|
energy: {
|
|
480
|
-
...(
|
|
811
|
+
...(usedKWh !== undefined ? { kWh: usedKWh } : {}),
|
|
812
|
+
...(kWhBasis ? { kWhBasis } : {}),
|
|
813
|
+
...(billingDemand ? { billingDemand } : {}),
|
|
814
|
+
...(usage ? { usage } : {}),
|
|
481
815
|
/* 이 창의 최대수요 — 계약 대비 판정이 보는 수다(전력량과 같은 구간 집합에서 왔다). */
|
|
482
816
|
...(e.peakKW !== undefined ? { peakKW: e.peakKW, peakStartMs: e.peakStartMs } : {}),
|
|
483
817
|
/* 깎인 양은 **같은 구간의 두 수**의 차이다 — 여기서 한 번만 계산해 두 곳이 갈라지지 않게. */
|
|
@@ -493,7 +827,26 @@ function foldKpi(events, window, options = {}) {
|
|
|
493
827
|
...(measuredFeeders(demandWindows, { startMs: window.fromMs, endMs: window.toMs }, e.kWh, options.meterFeeder) ??
|
|
494
828
|
feederShares(e.kWh, utilization, options.resourceFeeder)),
|
|
495
829
|
/* 금액은 같은 창의 전력량·최대수요에서 나온다 — 창이 하나뿐이라 어긋날 수 없다. */
|
|
496
|
-
...(
|
|
830
|
+
...(cost ? { cost } : {}),
|
|
831
|
+
...generatedOfPeriods(generationPeriods, { startMs: window.fromMs, endMs: window.toMs }, options.generationRated,
|
|
832
|
+
/* 창 안의 단가 사실이 먼저이고, 없으면 이어받은 마지막 단가를 쓴다. */
|
|
833
|
+
generationPrices.length
|
|
834
|
+
? generationPrices
|
|
835
|
+
: options.generationPrice
|
|
836
|
+
? [
|
|
837
|
+
{
|
|
838
|
+
startMs: Date.parse(options.generationPrice.from),
|
|
839
|
+
endMs: Date.parse(options.generationPrice.to),
|
|
840
|
+
unitPrice: options.generationPrice.unitPrice,
|
|
841
|
+
currency: options.generationPrice.currency
|
|
842
|
+
}
|
|
843
|
+
]
|
|
844
|
+
: []),
|
|
845
|
+
/* 청구서는 사실이다 — 우리 계산을 덮지 않고 나란히 둔다(§`billOfWindow`). */
|
|
846
|
+
...(() => {
|
|
847
|
+
const b = billOfWindow(bills, { startMs: window.fromMs, endMs: window.toMs }, cost);
|
|
848
|
+
return b ? { bill: b } : {};
|
|
849
|
+
})()
|
|
497
850
|
}
|
|
498
851
|
};
|
|
499
852
|
})()
|
|
@@ -529,7 +882,7 @@ function crossGroups(now, prev) {
|
|
|
529
882
|
}
|
|
530
883
|
/* ── 시간 비교 ──────────────────────────────────────────────────────────────
|
|
531
884
|
* 숫자 하나로는 좋아졌는지 나빠졌는지 알 수 없다("29초" 가 개선인지 악화인지). 같은 길이의 창을 옮겨
|
|
532
|
-
* 한 번 더
|
|
885
|
+
* 한 번 더 계산하면 되므로(폴드는 순수) 값싸다. 창 옮기기와 차이 계산은 계산 규칙이라 여기 있다 —
|
|
533
886
|
* 조회 층에 두면 테스트가 DB 를 끌고 와야 한다.
|
|
534
887
|
*/
|
|
535
888
|
/**
|
|
@@ -553,7 +906,7 @@ function deltaP50(now, prev) {
|
|
|
553
906
|
return now.count > 0 && prev.count > 0 ? now.p50Ms - prev.p50Ms : undefined;
|
|
554
907
|
}
|
|
555
908
|
/**
|
|
556
|
-
* 창을 N 등분해 구간별로
|
|
909
|
+
* 창을 N 등분해 구간별로 처리한다 — **같은 이벤트를 창만 바꿔** 다시 계산한다(조회 추가 없음).
|
|
557
910
|
*
|
|
558
911
|
* 마지막 구간은 나머지를 흡수한다(부동소수 나눗셈으로 끝이 밀리면 마지막 완료가 빠진다).
|
|
559
912
|
* `count <= 1` 이면 쪼갤 것이 없으므로 빈 배열 — 호출부가 "추세 없음" 으로 다룬다.
|