@things-factory/headless-twin 10.0.14 โ†’ 10.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/dist-server/engine/attention-digest.d.ts +4 -4
  2. package/dist-server/engine/attention-digest.js +7 -7
  3. package/dist-server/engine/attention-digest.js.map +1 -1
  4. package/dist-server/engine/canonical-ingest.d.ts +11 -2
  5. package/dist-server/engine/canonical-ingest.js +137 -8
  6. package/dist-server/engine/canonical-ingest.js.map +1 -1
  7. package/dist-server/engine/command-routing.js +1 -1
  8. package/dist-server/engine/command-routing.js.map +1 -1
  9. package/dist-server/engine/declared-stimulus.js +1 -1
  10. package/dist-server/engine/declared-stimulus.js.map +1 -1
  11. package/dist-server/engine/energy-topology.js +1 -1
  12. package/dist-server/engine/energy-topology.js.map +1 -1
  13. package/dist-server/engine/ingest-dedupe.d.ts +38 -0
  14. package/dist-server/engine/ingest-dedupe.js +145 -0
  15. package/dist-server/engine/ingest-dedupe.js.map +1 -0
  16. package/dist-server/engine/ingest-health.d.ts +124 -9
  17. package/dist-server/engine/ingest-health.js +169 -15
  18. package/dist-server/engine/ingest-health.js.map +1 -1
  19. package/dist-server/engine/integration-probes.d.ts +3 -3
  20. package/dist-server/engine/integration-probes.js +6 -6
  21. package/dist-server/engine/integration-probes.js.map +1 -1
  22. package/dist-server/engine/kpi-baseline.js +4 -4
  23. package/dist-server/engine/kpi-baseline.js.map +1 -1
  24. package/dist-server/engine/kpi-fold.d.ts +122 -12
  25. package/dist-server/engine/kpi-fold.js +247 -18
  26. package/dist-server/engine/kpi-fold.js.map +1 -1
  27. package/dist-server/engine/kpi-query.d.ts +11 -11
  28. package/dist-server/engine/kpi-query.js +185 -28
  29. package/dist-server/engine/kpi-query.js.map +1 -1
  30. package/dist-server/engine/live-feed-registry.d.ts +1 -1
  31. package/dist-server/engine/live-feed-registry.js +1 -1
  32. package/dist-server/engine/live-feed-registry.js.map +1 -1
  33. package/dist-server/engine/load-meter.d.ts +1 -1
  34. package/dist-server/engine/load-meter.js.map +1 -1
  35. package/dist-server/engine/local-declarations.d.ts +7 -7
  36. package/dist-server/engine/local-declarations.js +9 -9
  37. package/dist-server/engine/local-declarations.js.map +1 -1
  38. package/dist-server/engine/log.d.ts +1 -1
  39. package/dist-server/engine/log.js +1 -1
  40. package/dist-server/engine/log.js.map +1 -1
  41. package/dist-server/engine/measured-estimator.js +1 -1
  42. package/dist-server/engine/measured-estimator.js.map +1 -1
  43. package/dist-server/engine/measured-yield.js +1 -1
  44. package/dist-server/engine/measured-yield.js.map +1 -1
  45. package/dist-server/engine/model-basis.d.ts +1 -1
  46. package/dist-server/engine/model-basis.js +1 -1
  47. package/dist-server/engine/model-basis.js.map +1 -1
  48. package/dist-server/engine/model-gap.d.ts +3 -3
  49. package/dist-server/engine/model-gap.js +1 -1
  50. package/dist-server/engine/model-gap.js.map +1 -1
  51. package/dist-server/engine/model-vocabulary.js +1 -1
  52. package/dist-server/engine/model-vocabulary.js.map +1 -1
  53. package/dist-server/engine/oee-accumulator.d.ts +4 -4
  54. package/dist-server/engine/oee-accumulator.js +3 -3
  55. package/dist-server/engine/oee-accumulator.js.map +1 -1
  56. package/dist-server/engine/property-effects.js +2 -2
  57. package/dist-server/engine/property-effects.js.map +1 -1
  58. package/dist-server/engine/read-time.d.ts +22 -0
  59. package/dist-server/engine/read-time.js +81 -0
  60. package/dist-server/engine/read-time.js.map +1 -0
  61. package/dist-server/engine/restart-policy.js +1 -1
  62. package/dist-server/engine/restart-policy.js.map +1 -1
  63. package/dist-server/engine/runtime-key.js +1 -1
  64. package/dist-server/engine/runtime-key.js.map +1 -1
  65. package/dist-server/engine/spec-coverage.d.ts +2 -2
  66. package/dist-server/engine/spec-coverage.js +1 -1
  67. package/dist-server/engine/spec-coverage.js.map +1 -1
  68. package/dist-server/engine/stage-path.d.ts +4 -4
  69. package/dist-server/engine/stage-path.js +1 -1
  70. package/dist-server/engine/stage-path.js.map +1 -1
  71. package/dist-server/engine/state-axes.d.ts +1 -1
  72. package/dist-server/engine/state-axes.js +2 -2
  73. package/dist-server/engine/state-axes.js.map +1 -1
  74. package/dist-server/engine/travel-estimator.d.ts +2 -2
  75. package/dist-server/engine/travel-estimator.js +1 -1
  76. package/dist-server/engine/travel-estimator.js.map +1 -1
  77. package/dist-server/engine/twin-engine.d.ts +243 -53
  78. package/dist-server/engine/twin-engine.js +690 -191
  79. package/dist-server/engine/twin-engine.js.map +1 -1
  80. package/dist-server/engine/warm-start.d.ts +2 -2
  81. package/dist-server/engine/warm-start.js +5 -5
  82. package/dist-server/engine/warm-start.js.map +1 -1
  83. package/dist-server/index.js +1 -1
  84. package/dist-server/index.js.map +1 -1
  85. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +1 -1
  86. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -1
  87. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +3 -3
  88. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -1
  89. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +2 -2
  90. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -1
  91. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.d.ts +5 -0
  92. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js +54 -0
  93. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +1 -0
  94. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.d.ts +5 -0
  95. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js +44 -0
  96. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +1 -0
  97. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.d.ts +6 -0
  98. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js +84 -0
  99. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +1 -0
  100. package/dist-server/migrations/index.js +7 -1
  101. package/dist-server/migrations/index.js.map +1 -1
  102. package/dist-server/routes.d.ts +1 -0
  103. package/dist-server/routes.js +69 -5
  104. package/dist-server/routes.js.map +1 -1
  105. package/dist-server/service/index.d.ts +2 -2
  106. package/dist-server/service/index.js +27 -24
  107. package/dist-server/service/index.js.map +1 -1
  108. package/dist-server/service/reference/control-routing.d.ts +1 -1
  109. package/dist-server/service/reference/control-routing.js +1 -1
  110. package/dist-server/service/reference/control-routing.js.map +1 -1
  111. package/dist-server/service/reference/discovery-result.d.ts +2 -2
  112. package/dist-server/service/reference/discovery-result.js +3 -3
  113. package/dist-server/service/reference/discovery-result.js.map +1 -1
  114. package/dist-server/service/reference/hook-contract.d.ts +43 -0
  115. package/dist-server/service/reference/hook-contract.js +59 -0
  116. package/dist-server/service/reference/hook-contract.js.map +1 -0
  117. package/dist-server/service/reference/ingest-space.d.ts +1 -1
  118. package/dist-server/service/reference/ingest-space.js +4 -4
  119. package/dist-server/service/reference/ingest-space.js.map +1 -1
  120. package/dist-server/service/reference/knob-defaults.d.ts +1 -1
  121. package/dist-server/service/reference/knob-defaults.js +4 -4
  122. package/dist-server/service/reference/knob-defaults.js.map +1 -1
  123. package/dist-server/service/reference/reference-adapter.d.ts +140 -8
  124. package/dist-server/service/reference/reference-adapter.js +27 -0
  125. package/dist-server/service/reference/reference-adapter.js.map +1 -1
  126. package/dist-server/service/reference/reference-hook.d.ts +20 -0
  127. package/dist-server/service/reference/reference-hook.js +133 -0
  128. package/dist-server/service/reference/reference-hook.js.map +1 -0
  129. package/dist-server/service/reference/reference-live.d.ts +2 -2
  130. package/dist-server/service/reference/reference-live.js +88 -13
  131. package/dist-server/service/reference/reference-live.js.map +1 -1
  132. package/dist-server/service/reference/reference-master.d.ts +14 -14
  133. package/dist-server/service/reference/reference-master.js +34 -12
  134. package/dist-server/service/reference/reference-master.js.map +1 -1
  135. package/dist-server/service/reference/reference-resolver.d.ts +1 -1
  136. package/dist-server/service/reference/reference-resolver.js +10 -10
  137. package/dist-server/service/reference/reference-resolver.js.map +1 -1
  138. package/dist-server/service/reference/twin-reference.d.ts +3 -3
  139. package/dist-server/service/reference/twin-reference.js.map +1 -1
  140. package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -1
  141. package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
  142. package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
  143. package/dist-server/service/twin-audit/command-audit.d.ts +1 -1
  144. package/dist-server/service/twin-audit/command-audit.js.map +1 -1
  145. package/dist-server/service/twin-audit/twin-audit-event.d.ts +1 -1
  146. package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
  147. package/dist-server/service/twin-audit/twin-audit-query.js +1 -1
  148. package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -1
  149. package/dist-server/service/twin-control/twin-control-mutation.js +1 -1
  150. package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
  151. package/dist-server/service/twin-event/twin-event-keys.d.ts +22 -2
  152. package/dist-server/service/twin-event/twin-event-keys.js +37 -5
  153. package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
  154. package/dist-server/service/twin-event/twin-event-type.d.ts +1 -0
  155. package/dist-server/service/twin-event/twin-event-type.js +8 -1
  156. package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
  157. package/dist-server/service/twin-event/twin-event.d.ts +2 -2
  158. package/dist-server/service/twin-event/twin-event.js +32 -4
  159. package/dist-server/service/twin-event/twin-event.js.map +1 -1
  160. package/dist-server/service/twin-forecast/forecast-metrics.js +1 -1
  161. package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -1
  162. package/dist-server/service/twin-forecast/gap-analytics.d.ts +1 -1
  163. package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
  164. package/dist-server/service/twin-forecast/twin-forecast-query.js +29 -11
  165. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
  166. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +1 -1
  167. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +1 -1
  168. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -1
  169. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +1 -1
  170. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -1
  171. package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
  172. package/dist-server/service/twin-journal/event-type-split-shape.d.ts +50 -0
  173. package/dist-server/service/twin-journal/event-type-split-shape.js +111 -0
  174. package/dist-server/service/twin-journal/event-type-split-shape.js.map +1 -0
  175. package/dist-server/service/twin-journal/event-type-split.d.ts +15 -0
  176. package/dist-server/service/twin-journal/event-type-split.js +102 -0
  177. package/dist-server/service/twin-journal/event-type-split.js.map +1 -0
  178. package/dist-server/service/twin-journal/journal-order.d.ts +15 -0
  179. package/dist-server/service/twin-journal/journal-order.js +17 -0
  180. package/dist-server/service/twin-journal/journal-order.js.map +1 -0
  181. package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -5
  182. package/dist-server/service/twin-journal/twin-journal-query.js +232 -21
  183. package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
  184. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +27 -1
  185. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +59 -3
  186. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
  187. package/dist-server/service/twin-metrics/twin-metrics-query.js +14 -1
  188. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
  189. package/dist-server/service/twin-model/axis-journal-evidence.js +1 -1
  190. package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -1
  191. package/dist-server/service/twin-model/epcis-coverage.js +36 -1
  192. package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
  193. package/dist-server/service/twin-model/iec61850-coverage.js +1 -1
  194. package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
  195. package/dist-server/service/twin-model/isa95-coverage.d.ts +15 -2
  196. package/dist-server/service/twin-model/isa95-coverage.js +32 -8
  197. package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
  198. package/dist-server/service/twin-model/item-ref.js +2 -2
  199. package/dist-server/service/twin-model/item-ref.js.map +1 -1
  200. package/dist-server/service/twin-model/name-index.d.ts +1 -1
  201. package/dist-server/service/twin-model/name-index.js +1 -1
  202. package/dist-server/service/twin-model/name-index.js.map +1 -1
  203. package/dist-server/service/twin-model/project-structure.d.ts +1 -1
  204. package/dist-server/service/twin-model/project-structure.js +3 -3
  205. package/dist-server/service/twin-model/project-structure.js.map +1 -1
  206. package/dist-server/service/twin-model/twin-equipment.d.ts +1 -1
  207. package/dist-server/service/twin-model/twin-equipment.js +1 -1
  208. package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
  209. package/dist-server/service/twin-model/twin-lineage-query.js +16 -5
  210. package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
  211. package/dist-server/service/twin-model/twin-location.d.ts +1 -1
  212. package/dist-server/service/twin-model/twin-location.js.map +1 -1
  213. package/dist-server/service/twin-model/twin-model-item-query.d.ts +3 -3
  214. package/dist-server/service/twin-model/twin-model-item-query.js +14 -14
  215. package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
  216. package/dist-server/service/twin-model/twin-model-mutation.d.ts +2 -2
  217. package/dist-server/service/twin-model/twin-model-mutation.js +4 -4
  218. package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -1
  219. package/dist-server/service/twin-model/twin-model-query.js +117 -28
  220. package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
  221. package/dist-server/service/twin-model/twin-model-tree-query.js +22 -0
  222. package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
  223. package/dist-server/service/twin-model/twin-operation.d.ts +1 -1
  224. package/dist-server/service/twin-model/twin-operation.js +1 -1
  225. package/dist-server/service/twin-model/twin-operation.js.map +1 -1
  226. package/dist-server/service/twin-space/move-space.js +2 -2
  227. package/dist-server/service/twin-space/move-space.js.map +1 -1
  228. package/dist-server/service/twin-space/representation-role.d.ts +2 -0
  229. package/dist-server/service/twin-space/representation-role.js +18 -0
  230. package/dist-server/service/twin-space/representation-role.js.map +1 -0
  231. package/dist-server/service/twin-space/space-integrity.d.ts +1 -1
  232. package/dist-server/service/twin-space/space-integrity.js.map +1 -1
  233. package/dist-server/service/twin-space/twin-space-representation.d.ts +2 -0
  234. package/dist-server/service/twin-space/twin-space-representation.js +16 -0
  235. package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
  236. package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
  237. package/dist-server/service/twin-space/twin-space-resolver.js +36 -7
  238. package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
  239. package/dist-server/service/twin-structure/twin-structure.js +3 -3
  240. package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
  241. package/dist-server/service/twin-subject/event-subjects.d.ts +43 -0
  242. package/dist-server/service/twin-subject/event-subjects.js +137 -0
  243. package/dist-server/service/twin-subject/event-subjects.js.map +1 -0
  244. package/dist-server/service/twin-subject/index.d.ts +9 -0
  245. package/dist-server/service/twin-subject/index.js +16 -0
  246. package/dist-server/service/twin-subject/index.js.map +1 -0
  247. package/dist-server/service/twin-subject/item-resolver.d.ts +10 -0
  248. package/dist-server/service/twin-subject/item-resolver.js +93 -0
  249. package/dist-server/service/twin-subject/item-resolver.js.map +1 -0
  250. package/dist-server/service/twin-subject/subject-query-shape.d.ts +22 -0
  251. package/dist-server/service/twin-subject/subject-query-shape.js +88 -0
  252. package/dist-server/service/twin-subject/subject-query-shape.js.map +1 -0
  253. package/dist-server/service/twin-subject/twin-subject-backfill.d.ts +23 -0
  254. package/dist-server/service/twin-subject/twin-subject-backfill.js +159 -0
  255. package/dist-server/service/twin-subject/twin-subject-backfill.js.map +1 -0
  256. package/dist-server/service/twin-subject/twin-subject-event.d.ts +14 -0
  257. package/dist-server/service/twin-subject/twin-subject-event.js +111 -0
  258. package/dist-server/service/twin-subject/twin-subject-event.js.map +1 -0
  259. package/dist-server/service/twin-subject/twin-subject-fast-path.d.ts +39 -0
  260. package/dist-server/service/twin-subject/twin-subject-fast-path.js +295 -0
  261. package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +1 -0
  262. package/dist-server/service/twin-subject/twin-subject-query.d.ts +27 -0
  263. package/dist-server/service/twin-subject/twin-subject-query.js +232 -0
  264. package/dist-server/service/twin-subject/twin-subject-query.js.map +1 -0
  265. package/dist-server/service/twin-subject/twin-subject-writer.d.ts +27 -0
  266. package/dist-server/service/twin-subject/twin-subject-writer.js +166 -0
  267. package/dist-server/service/twin-subject/twin-subject-writer.js.map +1 -0
  268. package/dist-server/service/twin-target/twin-target-resolver.js +1 -1
  269. package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
  270. package/dist-shared/entity-delta.d.ts +1 -1
  271. package/dist-shared/entity-delta.js +3 -3
  272. package/dist-shared/entity-delta.js.map +1 -1
  273. package/dist-shared/touched-items.js +2 -2
  274. package/dist-shared/touched-items.js.map +1 -1
  275. package/dist-shared/twin-level.js +1 -1
  276. package/dist-shared/twin-level.js.map +1 -1
  277. package/package.json +3 -3
  278. package/server/engine/attention-digest.ts +9 -9
  279. package/server/engine/canonical-ingest.ts +170 -13
  280. package/server/engine/command-routing.ts +1 -1
  281. package/server/engine/declared-stimulus.ts +1 -1
  282. package/server/engine/energy-topology.ts +1 -1
  283. package/server/engine/ingest-dedupe.ts +149 -0
  284. package/server/engine/ingest-health.ts +234 -20
  285. package/server/engine/integration-probes.ts +7 -7
  286. package/server/engine/kpi-baseline.ts +4 -4
  287. package/server/engine/kpi-fold.ts +395 -27
  288. package/server/engine/kpi-query.ts +212 -46
  289. package/server/engine/live-feed-registry.ts +1 -1
  290. package/server/engine/load-meter.ts +1 -1
  291. package/server/engine/local-declarations.ts +13 -13
  292. package/server/engine/log.ts +1 -1
  293. package/server/engine/measured-estimator.ts +1 -1
  294. package/server/engine/measured-yield.ts +1 -1
  295. package/server/engine/model-basis.ts +2 -2
  296. package/server/engine/model-gap.ts +3 -3
  297. package/server/engine/model-vocabulary.ts +1 -1
  298. package/server/engine/oee-accumulator.ts +4 -4
  299. package/server/engine/property-effects.ts +2 -2
  300. package/server/engine/read-time.ts +77 -0
  301. package/server/engine/restart-policy.ts +1 -1
  302. package/server/engine/runtime-key.ts +1 -1
  303. package/server/engine/spec-coverage.ts +3 -3
  304. package/server/engine/stage-path.ts +5 -5
  305. package/server/engine/state-axes.ts +2 -2
  306. package/server/engine/travel-estimator.ts +2 -2
  307. package/server/engine/twin-engine.ts +764 -201
  308. package/server/engine/warm-start.ts +6 -6
  309. package/server/index.ts +1 -1
  310. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +1 -1
  311. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +3 -3
  312. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +2 -2
  313. package/server/migrations/1786300000000-IndexEventTypeByTime.ts +48 -0
  314. package/server/migrations/1786400000000-IndexEventCreatedAt.ts +38 -0
  315. package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +93 -0
  316. package/server/migrations/index.ts +7 -1
  317. package/server/routes.ts +68 -5
  318. package/server/service/index.ts +4 -1
  319. package/server/service/reference/control-routing.ts +1 -1
  320. package/server/service/reference/discovery-result.ts +3 -3
  321. package/server/service/reference/hook-contract.ts +103 -0
  322. package/server/service/reference/ingest-space.ts +4 -4
  323. package/server/service/reference/knob-defaults.ts +4 -4
  324. package/server/service/reference/reference-adapter.ts +166 -9
  325. package/server/service/reference/reference-hook.ts +145 -0
  326. package/server/service/reference/reference-live.ts +92 -14
  327. package/server/service/reference/reference-master.ts +39 -17
  328. package/server/service/reference/reference-resolver.ts +10 -10
  329. package/server/service/reference/twin-reference.ts +3 -3
  330. package/server/service/twin-attention/twin-attention-query.ts +1 -1
  331. package/server/service/twin-audit/command-audit.ts +1 -1
  332. package/server/service/twin-audit/twin-audit-event.ts +2 -2
  333. package/server/service/twin-audit/twin-audit-query.ts +1 -1
  334. package/server/service/twin-control/twin-control-mutation.ts +1 -1
  335. package/server/service/twin-event/twin-event-keys.ts +37 -5
  336. package/server/service/twin-event/twin-event-type.ts +18 -1
  337. package/server/service/twin-event/twin-event.ts +32 -6
  338. package/server/service/twin-forecast/forecast-metrics.ts +1 -1
  339. package/server/service/twin-forecast/gap-analytics.ts +1 -1
  340. package/server/service/twin-forecast/twin-forecast-query.ts +30 -11
  341. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +1 -1
  342. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +1 -1
  343. package/server/service/twin-instance/twin-instance.ts +4 -4
  344. package/server/service/twin-journal/event-type-split-shape.ts +112 -0
  345. package/server/service/twin-journal/event-type-split.ts +144 -0
  346. package/server/service/twin-journal/journal-order.ts +20 -0
  347. package/server/service/twin-journal/twin-journal-query.ts +224 -20
  348. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +57 -3
  349. package/server/service/twin-metrics/twin-metrics-query.ts +14 -1
  350. package/server/service/twin-model/axis-journal-evidence.ts +1 -1
  351. package/server/service/twin-model/epcis-coverage.ts +36 -1
  352. package/server/service/twin-model/iec61850-coverage.ts +1 -1
  353. package/server/service/twin-model/isa95-coverage.ts +45 -8
  354. package/server/service/twin-model/item-ref.ts +2 -2
  355. package/server/service/twin-model/name-index.ts +1 -1
  356. package/server/service/twin-model/project-structure.ts +4 -4
  357. package/server/service/twin-model/twin-equipment.ts +2 -2
  358. package/server/service/twin-model/twin-lineage-query.ts +16 -5
  359. package/server/service/twin-model/twin-location.ts +1 -1
  360. package/server/service/twin-model/twin-model-item-query.ts +14 -14
  361. package/server/service/twin-model/twin-model-mutation.ts +4 -4
  362. package/server/service/twin-model/twin-model-query.ts +117 -28
  363. package/server/service/twin-model/twin-model-tree-query.ts +25 -1
  364. package/server/service/twin-model/twin-operation.ts +2 -2
  365. package/server/service/twin-space/move-space.ts +3 -3
  366. package/server/service/twin-space/representation-role.ts +15 -0
  367. package/server/service/twin-space/space-integrity.ts +1 -1
  368. package/server/service/twin-space/twin-space-representation.ts +37 -0
  369. package/server/service/twin-space/twin-space-resolver.ts +39 -7
  370. package/server/service/twin-structure/twin-structure.ts +3 -3
  371. package/server/service/twin-subject/event-subjects.ts +155 -0
  372. package/server/service/twin-subject/index.ts +10 -0
  373. package/server/service/twin-subject/item-resolver.ts +92 -0
  374. package/server/service/twin-subject/subject-query-shape.ts +84 -0
  375. package/server/service/twin-subject/twin-subject-backfill.ts +196 -0
  376. package/server/service/twin-subject/twin-subject-event.ts +101 -0
  377. package/server/service/twin-subject/twin-subject-fast-path.ts +346 -0
  378. package/server/service/twin-subject/twin-subject-query.ts +210 -0
  379. package/server/service/twin-subject/twin-subject-writer.ts +189 -0
  380. package/server/service/twin-target/twin-target-resolver.ts +1 -1
  381. package/shared/entity-delta.ts +3 -3
  382. package/shared/touched-items.ts +2 -2
  383. package/shared/twin-level.ts +1 -1
  384. package/test/adopt-structure-live.test.ts +3 -3
  385. package/test/aggregate-not-findone.test.ts +112 -0
  386. package/test/attention-digest.test.ts +6 -6
  387. package/test/axis-read.test.ts +2 -2
  388. package/test/boot-resume.test.ts +20 -7
  389. package/test/broadcast-period.test.ts +2 -2
  390. package/test/broadcast-queue-overflow.test.ts +244 -0
  391. package/test/canonical-ingest-vocabularies.test.ts +179 -7
  392. package/test/canonical-quantity-door.test.ts +2 -2
  393. package/test/checkpoint-refuses-empty.test.ts +2 -2
  394. package/test/connector-capability-declaration.test.ts +88 -0
  395. package/test/control-capability.test.ts +1 -1
  396. package/test/cursor-stall-not-read-failure.test.ts +2 -2
  397. package/test/declaration-reaches-model.test.ts +5 -5
  398. package/test/declared-stimulus.test.ts +4 -4
  399. package/test/discovery-result.test.ts +2 -2
  400. package/test/entity-delta.test.ts +4 -4
  401. package/test/event-subjects.test.ts +147 -0
  402. package/test/event-time-column.test.ts +3 -3
  403. package/test/event-type-split.test.ts +155 -0
  404. package/test/fold-gap-speaks.test.ts +53 -0
  405. package/test/forecast-metrics.test.ts +1 -1
  406. package/test/forecast-tuning.test.ts +1 -1
  407. package/test/ingest-health-engine.test.ts +2 -2
  408. package/test/ingest-health-wiring.test.ts +7 -5
  409. package/test/ingest-health.test.ts +6 -6
  410. package/test/ingest-history.test.ts +2 -2
  411. package/test/ingest-idempotent.test.ts +132 -0
  412. package/test/ingest-space.test.ts +3 -3
  413. package/test/integration-probes.test.ts +3 -3
  414. package/test/integration-runner.test.ts +1 -1
  415. package/test/item-ref.test.ts +1 -1
  416. package/test/journal-order-axis.test.ts +80 -0
  417. package/test/journal-read-discipline.test.ts +16 -6
  418. package/test/journal-retention.test.ts +171 -29
  419. package/test/journal-sort-axis.test.ts +1 -1
  420. package/test/journal-write-door.test.ts +3 -3
  421. package/test/journal-write-trend.test.ts +1 -1
  422. package/test/kernel-kind-guard.test.ts +3 -3
  423. package/test/knob-defaults.test.ts +1 -1
  424. package/test/kpi-baseline-db.test.ts +2 -2
  425. package/test/kpi-fold.test.ts +185 -14
  426. package/test/kpi-query-bench.test.ts +3 -3
  427. package/test/live-cursor-wiring.test.ts +6 -6
  428. package/test/live-kernel-facts.test.ts +11 -11
  429. package/test/live-mirror-parity.test.ts +3 -3
  430. package/test/load-meter.test.ts +1 -1
  431. package/test/local-declarations.test.ts +10 -10
  432. package/test/log-stamp.test.ts +1 -1
  433. package/test/measured-yield.test.ts +2 -2
  434. package/test/merge-streams.test.ts +70 -0
  435. package/test/mirror-resumes-from-checkpoint.test.ts +13 -13
  436. package/test/model-basis.test.ts +2 -2
  437. package/test/model-gap.test.ts +4 -4
  438. package/test/model-vocabulary.test.ts +2 -2
  439. package/test/move-space.test.ts +1 -1
  440. package/test/oee-accumulator.test.ts +2 -2
  441. package/test/operational-vocabulary.test.ts +2 -2
  442. package/test/project-structure-db.test.ts +2 -2
  443. package/test/projection-reaches-screen.test.ts +2 -2
  444. package/test/property-effects.test.ts +3 -3
  445. package/test/read-failure-ledger.test.ts +106 -0
  446. package/test/read-failure-visible.test.ts +7 -7
  447. package/test/read-time.test.ts +118 -0
  448. package/test/reference-grounding.test.ts +1 -1
  449. package/test/reference-hook.test.ts +86 -0
  450. package/test/registry-key-guard.test.ts +1 -1
  451. package/test/rename-twin.test.ts +81 -0
  452. package/test/restart-policy.test.ts +3 -3
  453. package/test/resync-origin-site.test.ts +3 -3
  454. package/test/revision-axis.test.ts +14 -8
  455. package/test/runtime-key.test.ts +3 -3
  456. package/test/snapshot-freshness.test.ts +4 -4
  457. package/test/source-outcome-audit.test.ts +1 -1
  458. package/test/space-integrity.test.ts +1 -1
  459. package/test/space-representation.test.ts +66 -0
  460. package/test/space-scope-includes-stopped.test.ts +55 -0
  461. package/test/spec-coverage.test.ts +1 -1
  462. package/test/standard-coverage.test.ts +43 -1
  463. package/test/status-tally.test.ts +1 -1
  464. package/test/structure-revision-db.test.ts +7 -7
  465. package/test/subject-fast-path.test.ts +148 -0
  466. package/test/subject-read-model.test.ts +175 -0
  467. package/test/touched-items.test.ts +2 -2
  468. package/test/twin-audit.test.ts +1 -1
  469. package/test/twin-event-keys.test.ts +49 -3
  470. package/test/twin-model-item-db.test.ts +5 -5
  471. package/test/twin-model-tree-db.test.ts +35 -2
  472. package/test/twin-origin-resync.test.ts +1 -1
  473. package/test/warm-start-seam.test.ts +1 -1
  474. package/test/warm-start.test.ts +6 -6
  475. package/test/withheld-door.test.ts +95 -0
  476. package/test/yield-loop.test.ts +4 -4
  477. package/tsconfig.shared.tsbuildinfo +1 -1
  478. package/tsconfig.tsbuildinfo +1 -1
@@ -15,6 +15,8 @@
15
15
  * ์–ด๋–ค ๊ธฐ์ค€์„ ์ผ๋Š”์ง€๋Š” ๊ฒฐ๊ณผ์˜ `window.basis` ๋กœ ๋ฐํžŒ๋‹ค โ€” ์ˆจ๊ธฐ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ˆซ์ž๋ฅผ ์˜คํ•ดํ•œ๋‹ค.
16
16
  */
17
17
  import { twinWarn } from './log.js'
18
+ import { mergeStreams } from '../service/twin-journal/event-type-split-shape.js'
19
+ import { hydratedDate } from './read-time.js'
18
20
  import { Between, In } from 'typeorm'
19
21
 
20
22
  import { hierarchyOf, levelOfLocationType, electricalUpstreamOf, EMS_LOCATION_TYPES, EMS_PROPERTY, type TariffDeclaration } from '@operato/twin-kernel'
@@ -49,20 +51,29 @@ import { MAX_BASELINE_PERIODS, baselineWindows, summarizeBaseline, type Baseline
49
51
  * ๋‚˜๋ˆˆ ๊ฐ’์ด๋ผ, ๋‘ ์‚ฌ์‹ค์ด ํ•œ ํด๋“œ์— ๋“ค์–ด์™€์•ผ ์ฐฝ์ด ์–ด๊ธ‹๋‚  ์ˆ˜ ์—†๋‹ค. ํ‘œ๋ณธ(`energy.measured`)์€ ์ฝ์ง€ ์•Š๋Š”๋‹ค โ€”
50
52
  * ์ €๋„์— ์—†๊ณ (ยง4.1 ์ธ์ œ์ŠคํŠธ ๊ณ„์•ฝ) ์—ฌ๊ธฐ์„œ ํ•„์š”ํ•œ ๊ฒƒ์€ ๋งˆ๊ฐ๋œ ๊ตฌ๊ฐ„๋ฟ์ด๋‹ค.
51
53
  */
52
- const BUSINESS_EVENTS = ['task.status', 'order.status', 'energy.demand.window']
54
+ const BUSINESS_EVENTS = ['task.status', 'order.status', 'energy.demand.window', 'energy.generation.period', 'energy.usage.period', 'energy.bill']
53
55
 
54
- /** ํ•œ ๋ฒˆ์— ์ ‘์„ ์ด๋ฒคํŠธ ์ƒํ•œ โ€” ํฐ ๊ตฌ๊ฐ„์ด ์„œ๋ฒ„๋ฅผ ๋ถ™์žก์ง€ ์•Š๊ฒŒ. */
56
+ /** ํ•œ ๋ฒˆ์— ๊ณ„์‚ฐํ•  ์ด๋ฒคํŠธ ์ƒํ•œ โ€” ํฐ ๊ตฌ๊ฐ„์ด ์„œ๋ฒ„๋ฅผ ๋ถ™์žก์ง€ ์•Š๊ฒŒ. */
55
57
  const MAX_EVENTS = 20000
56
58
 
59
+ /**
60
+ * ์ข…๋ฅ˜๋ฅผ ๋‚˜๋ˆ  ๋ฌผ์„ ๋•Œ ๋งŒ๋“ค ๊ฐˆ๋ž˜ ์ˆ˜์˜ ์ƒํ•œ โ€” ๋Œ€์ƒ ร— ์ข…๋ฅ˜.
61
+ *
62
+ * ๊ฐˆ๋ž˜๋งˆ๋‹ค ์ƒํ•œ(`MAX_EVENTS`)๊นŒ์ง€ ๋ฐ›์•„์•ผ ํ•ฉ์นœ ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํ•˜๋‹ค(ยง`mergeStreams`). ๊ทธ๋ž˜์„œ ๊ฐˆ๋ž˜๊ฐ€ ๋Š˜๋ฉด
63
+ * ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ทธ๋งŒํผ ๋“ ๋‹ค. ์…‹(์ข…๋ฅ˜) ร— ๋‘˜(ํŠธ์œˆ)๊นŒ์ง€๋Š” ์ตœ์•…์— 12๋งŒ ํ–‰์ด๊ณ , ๊ทธ ์ด์ƒ์€ ์˜ˆ์ „ ํ•œ ์งˆ์˜๋กœ ๊ฐ„๋‹ค โ€”
64
+ * ๋А๋ฆฌ์ง€๋งŒ ๋‹ต์€ ๊ฐ™๋‹ค.
65
+ */
66
+ const KPI_MAX_SPLIT = 6
67
+
57
68
  export interface TwinKpiInput {
58
69
  domainId: string
59
70
  /** ํ•œ ํŠธ์œˆ. `spaceId` ์™€ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋Š” ์žˆ์–ด์•ผ ํ•œ๋‹ค. */
60
71
  instanceId?: string
61
72
  /**
62
- * ํ•œ **๊ณต๊ฐ„** โ€” ๊ทธ ๊ณต๊ฐ„์—์„œ ๊ฐ€๋™ ์ค‘์ธ ํŠธ์œˆ ์ „๋ถ€๋ฅผ **ํ•œ ๋ฒˆ์— ์ ‘๋Š”๋‹ค**.
73
+ * ํ•œ **๊ณต๊ฐ„** โ€” ๊ทธ ๊ณต๊ฐ„์—์„œ ๊ฐ€๋™ ์ค‘์ธ ํŠธ์œˆ ์ „๋ถ€๋ฅผ **ํ•œ ๋ฒˆ์— ๊ณ„์‚ฐํ•œ๋‹ค**.
63
74
  *
64
- * ํŠธ์œˆ๋ณ„๋กœ ์ ‘์–ด ํ‰๊ท ์„ ๋‹ค์‹œ ํ‰๊ท ํ•˜๋ฉด ๋ถ„์œ„์ˆ˜๊ฐ€ ๊ฑฐ์ง“์ด ๋œ๋‹ค(p90 ์˜ ํ‰๊ท ์€ p90 ์ด ์•„๋‹ˆ๋‹ค).
65
- * ๊ทธ๋ž˜์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ชจ์•„ **ํ•œ ๋ฒˆ** ์ ‘๋Š”๋‹ค. ์‚ฌ์šฉ์ž๋Š” "๋ถ€์‚ฐ ์ฐฝ๊ณ " ๋ฅผ ๋ณด๊ณ , ๊ทธ ์•ˆ์— ํŠธ์œˆ์ด ๋ช‡ ๊ฐœ๋“ 
75
+ * ํŠธ์œˆ๋ณ„๋กœ ๊ณ„์‚ฐํ•ด ํ‰๊ท ์„ ๋‹ค์‹œ ํ‰๊ท ํ•˜๋ฉด ๋ถ„์œ„์ˆ˜๊ฐ€ ๊ฑฐ์ง“์ด ๋œ๋‹ค(p90 ์˜ ํ‰๊ท ์€ p90 ์ด ์•„๋‹ˆ๋‹ค).
76
+ * ๊ทธ๋ž˜์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ชจ์•„ **ํ•œ ๋ฒˆ** ๊ณ„์‚ฐํ•œ๋‹ค. ์‚ฌ์šฉ์ž๋Š” "๋ถ€์‚ฐ ์ฐฝ๊ณ " ๋ฅผ ๋ณด๊ณ , ๊ทธ ์•ˆ์— ํŠธ์œˆ์ด ๋ช‡ ๊ฐœ๋“ 
66
77
  * ํ•ฉ์ณ์„œ ๋ณธ๋‹ค(ํ—ค๋” ํŠธ์œˆ ์…€๋ ‰ํ„ฐ๋ฅผ ์—†์•ค ๊ฒฐ์ •๊ณผ ๊ฐ™์€ ๊ทœ์œจ).
67
78
  */
68
79
  spaceId?: string
@@ -76,14 +87,14 @@ export interface TwinKpiInput {
76
87
  lookbackMinutes?: number
77
88
  /**
78
89
  * ์ฐฝ์„ N ๋“ฑ๋ถ„ํ•ด **๊ตฌ๊ฐ„๋ณ„ ๊ฐ’๋„** ํ•จ๊ป˜ ๋‚ธ๋‹ค(์ถ”์„ธ์šฉ). 1~48, ๊ธฐ๋ณธ 0(์ชผ๊ฐœ์ง€ ์•Š์Œ).
79
- * ์กฐํšŒ๋Š” ํ•œ ๋ฒˆ์ด๊ณ  ์ ‘๊ธฐ๋งŒ N ๋ฒˆ ๋ฐ˜๋ณตํ•œ๋‹ค โ€” ํด๋“œ๊ฐ€ ์ˆœ์ˆ˜ํ•˜๋ฏ€๋กœ ๊ฐ’์‹ธ๋‹ค.
90
+ * ์กฐํšŒ๋Š” ํ•œ ๋ฒˆ์ด๊ณ  ๊ณ„์‚ฐ๋งŒ N ๋ฒˆ ๋ฐ˜๋ณตํ•œ๋‹ค โ€” ํด๋“œ๊ฐ€ ์ˆœ์ˆ˜ํ•˜๋ฏ€๋กœ ๊ฐ’์‹ธ๋‹ค.
80
91
  */
81
92
  buckets?: number
82
93
  /**
83
94
  * ๊ฐ™์€ ์‚ฌ์‹ค์„ **๋‹ค๋ฅธ ๊ด€์ ์œผ๋กœ** ์ชผ๊ฐ ๋‹ค: ์ž์›๋ณ„ยท์ž‘์—…์ข…๋ฅ˜๋ณ„ยท๋„์ฐฉ์ง€์ ๋ณ„ยท์˜ค๋”๋ณ„ยท๊ตฌ์—ญ๋ณ„.
84
95
  *
85
96
  * ๊ณ ์ •๋œ ํ•œ ๋ Œ์ฆˆ๋งŒ ๋‚ผ ์ˆ˜ ์žˆ์œผ๋ฉด "๊ตฌ์—ญ๋ณ„๋กœ๋Š” ์–ด๋–ค๊ฐ€" ๋ผ๋Š” ์งˆ๋ฌธ์— ํ™”๋ฉด๋„ AI ๋„ ๋‹ตํ•  ์ˆ˜ ์—†๋‹ค.
86
- * ์ถ•์€ ์กฐํšŒ ์ธ์ž ํ•˜๋‚˜์ด๊ณ , ์ ‘๊ธฐ๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ผ ์ถ•์„ ํ•˜๋‚˜ ๋” ๋ณด๋Š” ๊ฐ’์ด ๊ฑฐ์˜ ์—†๋‹ค.
97
+ * ์ถ•์€ ์กฐํšŒ ์ธ์ž ํ•˜๋‚˜์ด๊ณ , ๊ณ„์‚ฐ๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ผ ์ถ•์„ ํ•˜๋‚˜ ๋” ๋ณด๋Š” ๊ฐ’์ด ๊ฑฐ์˜ ์—†๋‹ค.
87
98
  */
88
99
  groupBy?: KpiGroupBy
89
100
  /** ์ถ•์˜ ์ƒํ•œ(๊ธฐ๋ณธ 20, ์ตœ๋Œ€ 100). ๋„˜์น˜๋ฉด ์ž˜๋ผ๋‚ด๊ณ  ์ž˜๋ฆฐ ์ˆ˜๋ฅผ ํ•จ๊ป˜ ์•Œ๋ฆฐ๋‹ค. */
@@ -92,12 +103,12 @@ export interface TwinKpiInput {
92
103
  * **๋ฌด์—‡๊ณผ ๋น„๊ตํ•  ๊ฒƒ์ธ๊ฐ€** โ€” ๊ฐ™์€ ๊ธธ์ด์˜ ์•ž ๊ตฌ๊ฐ„(previous)์ด๋‚˜ ํ•˜๋ฃจ ์ „ ๊ฐ™์€ ์‹œ๊ฐ„(yesterday).
93
104
  *
94
105
  * ์ˆซ์ž ํ•˜๋‚˜๋กœ๋Š” ์ข‹์•„์กŒ๋Š”์ง€ ๋‚˜๋น ์กŒ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค("29์ดˆ" ๊ฐ€ ๊ฐœ์„ ์ธ์ง€ ์•…ํ™”์ธ์ง€). ์ฐฝ๋งŒ ์˜ฎ๊ฒจ ํ•œ ๋ฒˆ ๋”
95
- * ์ ‘์œผ๋ฉด ๋˜๋ฏ€๋กœ(ํด๋“œ๋Š” ์ˆœ์ˆ˜) ๊ฐ’์‹ธ๋‹ค. ๋น„๊ต ๊ตฌ๊ฐ„์— ๊ธฐ๋ก์ด ์—†์œผ๋ฉด **delta ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค** โ€”
106
+ * ๊ณ„์‚ฐํ•˜๋ฉด ๋˜๋ฏ€๋กœ(ํด๋“œ๋Š” ์ˆœ์ˆ˜) ๊ฐ’์‹ธ๋‹ค. ๋น„๊ต ๊ตฌ๊ฐ„์— ๊ธฐ๋ก์ด ์—†์œผ๋ฉด **delta ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค** โ€”
96
107
  * ์—†๋Š” ๊ฒƒ์„ 0% ๋กœ ๊ทธ๋ฆฌ๋ฉด "๋ณ€ํ™” ์—†์Œ" ์ด๋ผ๋Š” ๊ฑฐ์ง“์ด ๋œ๋‹ค.
97
108
  */
98
109
  compareTo?: 'previous' | 'yesterday'
99
110
  /**
100
- * **์—ฌ๋Ÿฌ ๊ตฌ๊ฐ„ ๋™์‹œ ๋น„๊ต** โ€” ์ตœ๊ทผ N ๊ตฌ๊ฐ„์˜ ๊ฐ™์€ ์‹œ๊ฐ„๋Œ€๋ฅผ ํ•จ๊ป˜ ์ ‘์–ด **๋ถ„ํฌ**์™€ ๊ทธ ์•ˆ์˜ ์ž๋ฆฌ๋ฅผ ๋‚ธ๋‹ค.
111
+ * **์—ฌ๋Ÿฌ ๊ตฌ๊ฐ„ ๋™์‹œ ๋น„๊ต** โ€” ์ตœ๊ทผ N ๊ตฌ๊ฐ„์˜ ๊ฐ™์€ ์‹œ๊ฐ„๋Œ€๋ฅผ ํ•จ๊ป˜ ๊ณ„์‚ฐํ•ด **๋ถ„ํฌ**์™€ ๊ทธ ์•ˆ์˜ ์ž๋ฆฌ๋ฅผ ๋‚ธ๋‹ค.
101
112
  *
102
113
  * `compareTo` ๋Š” ํ•œ ๊ตฌ๊ฐ„๋งŒ ๋ด์„œ ํŒ๋‹จ์ด "์–ด์ œ๋ณด๋‹ค ๋‚˜์•„์กŒ๋‹ค" ์—์„œ ๋ฉˆ์ถ˜๋‹ค โ€” ์–ด์ œ๋„ ๋‚˜๋นด์œผ๋ฉด ์“ธ๋ชจ๊ฐ€ ์—†๋‹ค.
103
114
  * ์ตœ๊ทผ ๋ฉฐ์น ์˜ ๊ฐ™์€ ์‹œ๊ฐ„๋Œ€๋ฅผ ๋ณด๋ฉด ๋ชฉํ‘œ ์—†์ด๋„ "์ง€๋‚œ 7์ผ ์ค‘ ๊ฐ€์žฅ ๋‚ฎ๋‹ค" ๋ฅผ ๋งํ•  ์ˆ˜ ์žˆ๋‹ค.
@@ -122,7 +133,7 @@ export interface TwinKpiOutput extends Omit<Partial<KpiResult>, 'window'> {
122
133
  /**
123
134
  * ์กฐํšŒ๊ฐ€ **์ƒํ•œ์— ๊ฑธ๋ ธ๋Š”๊ฐ€**(๊ตฌ๊ฐ„์ด ๋„ˆ๋ฌด ์ปค์„œ ์ผ๋ถ€๋งŒ ์ฝ์—ˆ์„ ์ˆ˜ ์žˆ๋‹ค).
124
135
  *
125
- * true ๋ฉด ์ด ๊ตฌ๊ฐ„์˜ ์ˆซ์ž๋Š” **ํ•˜ํ•œ**์ด๋‹ค โ€” ์‹ค์ œ๋กœ๋Š” ๋” ์žˆ์—ˆ์„ ์ˆ˜ ์žˆ๋‹ค. ์กฐ์šฉํžˆ ์ž˜๋ผ๋‚ด๋ฉด ์ˆซ์ž๊ฐ€ ์กฐ์šฉํžˆ
136
+ * true ๋ฉด ์ด ๊ตฌ๊ฐ„์˜ ์ˆซ์ž๋Š” **ํ•˜ํ•œ**์ด๋‹ค โ€” ์‹ค์ œ๋กœ๋Š” ๋” ์žˆ์—ˆ์„ ์ˆ˜ ์žˆ๋‹ค. ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ž˜๋ผ๋‚ด๋ฉด ์ˆซ์ž๊ฐ€ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ 
126
137
  * ์ž‘์•„์ง€๊ณ  ์‚ฌ์šฉ์ž๋Š” ๊ทธ๊ฒƒ์„ ์„ฑ๊ณผ ํ•˜๋ฝ์œผ๋กœ ์ฝ๋Š”๋‹ค. ๊ตฌ๊ฐ„์„ ์ขํ˜€ ๋‹ค์‹œ ๋ฌผ์–ด์•ผ ํ•œ๋‹ค.
127
138
  */
128
139
  eventsCapped?: boolean
@@ -132,9 +143,9 @@ export interface TwinKpiOutput extends Omit<Partial<KpiResult>, 'window'> {
132
143
  */
133
144
  measured: boolean
134
145
  note?: string
135
- /** ๋ฌด์—‡์„ ์ง‘๊ณ„ํ–ˆ๋‚˜ โ€” ๊ณต๊ฐ„์ด๋ฉด ํ•ฉ์ณ ์ ‘์€ ํŠธ์œˆ ๋ชฉ๋ก์„ ๋ฐํžŒ๋‹ค(์ˆซ์ž์˜ ์ถœ์ฒ˜๋ฅผ ์ˆจ๊ธฐ์ง€ ์•Š๋Š”๋‹ค). */
146
+ /** ๋ฌด์—‡์„ ์ง‘๊ณ„ํ–ˆ๋‚˜ โ€” ๊ณต๊ฐ„์ด๋ฉด ํ•ฉ์ณ ๊ณ„์‚ฐํ•œ ํŠธ์œˆ ๋ชฉ๋ก์„ ๋ฐํžŒ๋‹ค(์ˆซ์ž์˜ ์ถœ์ฒ˜๋ฅผ ์ˆจ๊ธฐ์ง€ ์•Š๋Š”๋‹ค). */
136
147
  /**
137
- * ๋ฌด์—‡์„ ์ง‘๊ณ„ํ–ˆ๋‚˜ โ€” ๊ณต๊ฐ„์ด๋ฉด ํ•ฉ์ณ ์ ‘์€ ํŠธ์œˆ ๋ชฉ๋ก.
148
+ * ๋ฌด์—‡์„ ์ง‘๊ณ„ํ–ˆ๋‚˜ โ€” ๊ณต๊ฐ„์ด๋ฉด ํ•ฉ์ณ ๊ณ„์‚ฐํ•œ ํŠธ์œˆ ๋ชฉ๋ก.
138
149
  *
139
150
  * `restartPolicies` ๋Š” **์ด ์ˆซ์ž๊ฐ€ ๋ฌด์—‡์˜ ์„ฑ๊ณผ์ธ์ง€**๋ฅผ ๋งํ•œ๋‹ค: mirror(์‹ค์ œ ์‹œ์Šคํ…œ์„ ๋ฐ˜์˜) ยท
140
151
  * `resume`(์ด์–ด์ง€๋Š” ๊ฐ€์ƒ ์„ธ๊ณ„) ยท `reset`(์žฌํ˜„ ์‹คํ—˜ ์‚ฌ๋ณธ). ๊ฐ™์€ "345๊ฑด" ์ด ํ˜„์‹ค์˜ ์‹ค์ ์ผ ์ˆ˜๋„, ์‹œ๋ฎฌ๋ ˆ์ด์…˜์˜
@@ -152,7 +163,7 @@ export interface TwinKpiOutput extends Omit<Partial<KpiResult>, 'window'> {
152
163
  */
153
164
  targets?: TargetVerdict[]
154
165
  /**
155
- * ๋น„๊ต ๊ตฌ๊ฐ„(์š”์ฒญํ–ˆ์„ ๋•Œ๋งŒ) โ€” ๊ฐ™์€ ๊ทœ์น™ยท๊ฐ™์€ ๊ธธ์ด๋กœ ์ ‘์€ ์•ž ๊ตฌ๊ฐ„๊ณผ์˜ ์ฐจ์ด.
166
+ * ๋น„๊ต ๊ตฌ๊ฐ„(์š”์ฒญํ–ˆ์„ ๋•Œ๋งŒ) โ€” ๊ฐ™์€ ๊ทœ์น™ยท๊ฐ™์€ ๊ธธ์ด๋กœ ๊ณ„์‚ฐํ•œ ์•ž ๊ตฌ๊ฐ„๊ณผ์˜ ์ฐจ์ด.
156
167
  *
157
168
  * `measured:false` ๋ฉด ๊ทธ ๊ตฌ๊ฐ„์— ๊ธฐ๋ก์ด ์—†๋‹ค๋Š” ๋œป์ด๊ณ  `delta` ๋Š” ์—†๋‹ค(0 ์ด ์•„๋‹ˆ๋‹ค).
158
169
  * delta ๋Š” **ํ˜„์žฌ โˆ’ ๋น„๊ต๊ตฌ๊ฐ„** ์ด๋‹ค. ์ข‹๊ณ  ๋‚˜์จ์€ ํŒ๋‹จํ•˜์ง€ ์•Š๋Š”๋‹ค โ€” ์ฒ˜๋ฆฌ๋Ÿ‰์€ ํด์ˆ˜๋ก ์ข‹๊ณ  ์†Œ์š”๋Š”
@@ -212,7 +223,7 @@ type KpiGroupOut = NonNullable<KpiResult['groups']>['items'][number] &
212
223
  *
213
224
  * **ํ•œ ํ™‰๋งŒ ๋ณด๋ฉด ์•ˆ ๋œ๋‹ค.** `parentId` ๋Š” ๊ตฌ์—ญ์ผ ์ˆ˜๋„ ์žˆ๊ณ  ์ค‘๊ฐ„ ๋‹จ(๋ผ์ธยท์…€)์ผ ์ˆ˜๋„ ์žˆ๋‹ค. ๋ผ์ธ์ด ๋ผ๋ฉด
214
225
  * ํ•œ ํ™‰์งœ๋ฆฌ ์ฝ”๋“œ๋Š” "๊ตฌ์—ญ์ด LINE-1" ์ด๋ผ๊ณ  ๋‹ตํ•˜๊ณ , ๊ทธ id ๋Š” ๊ตฌ์—ญ ๋ชฉ๋ก์— ์—†์œผ๋ฏ€๋กœ ๊ทธ ์•„๋ž˜ ์Šคํ…Œ์ด์…˜์ด
215
- * **์ง‘๊ณ„์—์„œ ์กฐ์šฉํžˆ ๋น ์ง„๋‹ค**(์—๋Ÿฌ ์—†์ด ํ‹€๋ฆฐ ์ˆซ์ž). ๊ทธ๋ž˜์„œ ์ปค๋„ `hierarchyOf` ๋กœ ์‚ฌ์Šฌ์„ ๋๊นŒ์ง€ ๊ฑท๋Š”๋‹ค โ€”
226
+ * **์ง‘๊ณ„์—์„œ ์˜ค๋ฅ˜ ์—†์ด ๋น ์ง„๋‹ค**(์—๋Ÿฌ ์—†์ด ํ‹€๋ฆฐ ์ˆซ์ž). ๊ทธ๋ž˜์„œ ์ปค๋„ `hierarchyOf` ๋กœ ์‚ฌ์Šฌ์„ ๋๊นŒ์ง€ ๊ฑท๋Š”๋‹ค โ€”
216
227
  * ๊ฑท๋Š” ๊ทœ์น™์€ ์ปค๋„์— ํ•œ ๋ฒŒ๋ฟ์ด๊ณ  ์†Œ๋น„์ฒ˜๋Š” ๊ทธ๊ฒƒ๋งŒ ๋ถ€๋ฅธ๋‹ค.
217
228
  */
218
229
  /**
@@ -338,7 +349,7 @@ async function locationAreaMap(
338
349
  *
339
350
  * ์—ฌ๋Ÿฌ ์—๋„ˆ์ง€ ํŠธ์œˆ์ด ํ•œ ํ˜„์žฅ์— ์žˆ์œผ๋ฉด **์ฒ˜์Œ ์„ ์–ธ๋œ ๊ฒƒ ํ•˜๋‚˜**๋ฅผ ์“ด๋‹ค. ํ‰๊ท ์„ ๋‚ด๊ฑฐ๋‚˜ ํ•ฉ์น˜์ง€ ์•Š๋Š”๋‹ค:
340
351
  * ์š”๊ธˆ์€ ๊ณ„์•ฝ์ด๊ณ , ๋‘ ๊ณ„์•ฝ์„ ์„ž์€ ๋‹จ๊ฐ€๋Š” ์–ด๋А ๊ณ„์•ฝ๋„ ์•„๋‹ˆ๋‹ค. ์„œ๋กœ ๋‹ค๋ฅธ ๋‹จ๊ฐ€๊ฐ€ ์„ ์–ธ๋ผ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„
341
- * ๋ฐํžŒ๋‹ค(์กฐ์šฉํžˆ ํ•˜๋‚˜๋ฅผ ๊ณ ๋ฅด๋ฉด ์™œ ๊ทธ ๊ธˆ์•ก์ธ์ง€ ์•„๋ฌด๋„ ์„ค๋ช…ํ•  ์ˆ˜ ์—†๋‹ค).
352
+ * ๋ฐํžŒ๋‹ค(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ํ•˜๋‚˜๋ฅผ ๊ณ ๋ฅด๋ฉด ์™œ ๊ทธ ๊ธˆ์•ก์ธ์ง€ ์•„๋ฌด๋„ ์„ค๋ช…ํ•  ์ˆ˜ ์—†๋‹ค).
342
353
  */
343
354
  async function tariffOf(
344
355
  domainId: string,
@@ -357,11 +368,14 @@ async function tariffOf(
357
368
  }
358
369
  const d = num(EMS_PROPERTY.demandChargePerKW)
359
370
  const e = num(EMS_PROPERTY.energyChargePerKWh)
371
+ /* ์š”๊ธˆ์ ์šฉ์ „๋ ฅ โ€” ์žˆ์œผ๋ฉด ๊ธฐ๋ณธ์š”๊ธˆ์ด ์กฐ๊ฑด๋ถ€ ํŒŒ์ƒ์ด ์•„๋‹ˆ๋ผ ์‚ฌ์‹ค์ด ๋œ๋‹ค(ยง`electricityCost`). */
372
+ const b = num(EMS_PROPERTY.billingDemandKW)
360
373
  const cur = props.get(EMS_PROPERTY.currency)
361
374
  if (d === undefined && e === undefined) continue
362
375
  found.push({
363
376
  ...(d !== undefined ? { demandChargePerKW: d } : {}),
364
377
  ...(e !== undefined ? { energyChargePerKWh: e } : {}),
378
+ ...(b !== undefined ? { billingDemandKW: b } : {}),
365
379
  ...(cur ? { currency: String(cur) } : {})
366
380
  })
367
381
  }
@@ -369,7 +383,11 @@ async function tariffOf(
369
383
  if (!found.length) return {}
370
384
  const first = found[0]
371
385
  const same = found.every(
372
- t => t.demandChargePerKW === first.demandChargePerKW && t.energyChargePerKWh === first.energyChargePerKWh && t.currency === first.currency
386
+ t =>
387
+ t.demandChargePerKW === first.demandChargePerKW &&
388
+ t.energyChargePerKWh === first.energyChargePerKWh &&
389
+ t.billingDemandKW === first.billingDemandKW &&
390
+ t.currency === first.currency
373
391
  )
374
392
  return { tariff: first, ...(same ? {} : { conflict: true }) }
375
393
  }
@@ -435,11 +453,33 @@ async function resourceFeederMap(
435
453
  }
436
454
 
437
455
  /**
438
- * ์ด ๊ณต๊ฐ„์˜ ์—๋„ˆ์ง€ ํŠธ์œˆ๋“ค์˜ ๊ณ„ํ†ต๋„ โ€” ๋ชจ๋ธ์„ ์ฝ์–ด ํŠธ๋ฆฌ๋กœ ์ ‘๋Š”๋‹ค.
456
+ * ์ด ๊ณต๊ฐ„์˜ ์—๋„ˆ์ง€ ํŠธ์œˆ๋“ค์˜ ๊ณ„ํ†ต๋„ โ€” ๋ชจ๋ธ์„ ์ฝ์–ด ํŠธ๋ฆฌ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
439
457
  *
440
458
  * ์—ฌ๋Ÿฌ ํŠธ์œˆ์ด ํ•œ ๊ณต๊ฐ„์— ์žˆ์œผ๋ฉด **๊ฐ ํŠธ์œˆ์˜ ๋ฟŒ๋ฆฌ๋ฅผ ๋‚˜๋ž€ํžˆ** ๋‘”๋‹ค(ํ•˜๋‚˜๋กœ ํ•ฉ์น˜๋ฉด ์—†๋Š” ๊ฒฐ์„ ์„ ๊ทธ๋ฆฌ๊ฒŒ ๋œ๋‹ค).
441
459
  * ์—๋„ˆ์ง€ ํŠธ์œˆ์ด ์—†์œผ๋ฉด ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” ๋นˆ ๊ทธ๋ฆผ์€ ๊ณ ์žฅ์œผ๋กœ ์ฝํžŒ๋‹ค.
442
460
  */
461
+ /**
462
+ * ์ด ํŠธ์œˆ๋“ค์ด ์ด์–ด๋ฐ›๊ณ  ์žˆ๋Š” **๋งˆ์ง€๋ง‰ ์ฒญ๊ตฌ์„œ** โ€” ์š”๊ธˆ์ ์šฉ์ „๋ ฅ์˜ ์šฐ์„  ๊ทผ๊ฑฐ.
463
+ *
464
+ * ์ €๋„์ด ์•„๋‹ˆ๋ผ ์ƒํƒœ์—์„œ ์ฝ๋Š” ์ด์œ : ์ฒญ๊ตฌ๋Š” ํ•œ ๋‹ฌ์— ํ•œ ๋ฒˆ์ด๋ผ ํ•˜๋ฃจ ์ฐฝ์˜ ์กฐํšŒ์—๋Š” ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
465
+ * ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ๊ฐ’์€ **๊ทธ ์ฐฝ์—๋„ ์ ์šฉ๋œ๋‹ค**(์š”๊ธˆ ์ œ๋„๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๊ตฐ๋‹ค). ์ปค๋„์ด ์žฌ๊ธฐ๋™์—์„œ๋„ ์ด์–ด๋ฐ›์œผ๋ฏ€๋กœ
466
+ * ์ƒํƒœ๊ฐ€ ๊ทธ ์ž๋ฆฌ๋‹ค.
467
+ *
468
+ * ํŠธ์œˆ์ด ์—ฌ๋Ÿฟ์ด๋ฉด ๊ฐ€์žฅ ๋Šฆ๊ฒŒ ๋๋‚œ ๊ฒƒ์„ ์“ด๋‹ค โ€” ํ˜„์žฅ ํ•˜๋‚˜์— ์ฒญ๊ตฌ๊ฐ€ ์—ฌ๋Ÿฟ์ด๋ฉด ๊ทธ๊ฒƒ์€ ๋‹ค๋ฅธ ๋ฌธ์ œ์ด๊ณ ,
469
+ * ์—ฌ๊ธฐ์„œ ๊ณจ๋ผ ๊ฐ์ถ”์ง€ ์•Š๋Š”๋‹ค.
470
+ */
471
+ function latestBillOf(domainId: string, instanceIds: string[]): { to: string; billingDemandKW?: number } | undefined {
472
+ let best: { to: string; billingDemandKW?: number } | undefined
473
+ for (const instanceId of instanceIds) {
474
+ const bill = (TwinEngine.snapshot(domainId, instanceId) as any)?.energy?.lastBill
475
+ if (!bill?.to) continue
476
+ if (!best || Date.parse(bill.to) > Date.parse(best.to)) {
477
+ best = { to: String(bill.to), ...(Number.isFinite(Number(bill.billingDemandKW)) ? { billingDemandKW: Number(bill.billingDemandKW) } : {}) }
478
+ }
479
+ }
480
+ return best
481
+ }
482
+
443
483
  async function energyTopologyOf(
444
484
  domainId: string,
445
485
  instanceIds: string[],
@@ -465,15 +505,50 @@ async function energyTopologyOf(
465
505
  return merged.roots.length ? merged : undefined
466
506
  }
467
507
 
468
- /** ์ด ๋Œ€์ƒ๋“ค์˜ ๋งˆ์ง€๋ง‰ ์—…๋ฌด ๊ธฐ๋ก ์‹œ๊ฐ(ISO). ๊ธฐ๋ก์ด ์—†์œผ๋ฉด null. */
508
+ /**
509
+ * ์ด ๋Œ€์ƒ๋“ค์˜ **๋งˆ์ง€๋ง‰ ์—…๋ฌด ๊ธฐ๋ก ์‹œ๊ฐ**(ISO). ๊ธฐ๋ก์ด ์—†์œผ๋ฉด null.
510
+ *
511
+ * โ”€โ”€ ๋‘ ๊ฐ€์ง€๋ฅผ ํ•จ๊ป˜ ๊ณ ์ณค๋‹ค (2026-08-28 ์‹ค์ธก) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
512
+ * ์˜ˆ์ „ ํŒ์€ ์ด๋žฌ๋‹ค.
513
+ *
514
+ * findOne({ where: { domain: { id }, instanceId: In(โ€ฆ), eventType: In(์…‹) }, order: { revision: 'DESC' } })
515
+ *
516
+ * **โ‘  ๊ด€๊ณ„๋กœ ๋ฌผ์–ด ๋‘ ๋‹จ๊ณ„ ์งˆ์˜๊ฐ€ ๋œ๋‹ค.** ์•ˆ์ชฝ์— ์ƒํ•œ์ด ์—†์–ด ์กฐ๊ฑด์— ๋งž๋Š” ํ–‰์„ **์ „๋ถ€ ๋งŒ๋“  ๋’ค** ํ•˜๋‚˜๋ฅผ
517
+ * ๊ณ ๋ฅธ๋‹ค โ€” ๊ทธ ํŠธ์œˆ์˜ ์—…๋ฌด ์‚ฌ์‹ค์ด 97๋งŒ ํ–‰์ด๊ณ  `payload` ๊นŒ์ง€ ์‹ค๋ ค ์˜จ๋‹ค. ์ด ํŒŒ์ผ์˜ ๋‹ค๋ฅธ ์ž๋ฆฌ๊ฐ€ ๊ฐ™์€ ๊ฒƒ์„
518
+ * ์‹ค์ธกํ•ด ์ ์–ด ๋‘์—ˆ๋‹ค(ยง`twinEvents`: 0.024์ดˆ ๋Œ€ 12์ดˆ). ์„œ๋ฒ„ ๋กœ๊ทธ์— ใ€Œ๋А๋ฆฐ ์งˆ์˜ใ€๋กœ ์ฐํ˜€ ์žˆ๋˜ ๊ฒƒ์ด
519
+ * ์ด๊ฒƒ์ด๊ณ , ํ˜„์žฅ ์„ฑ๊ณผ ํ™”๋ฉด์˜ 7.2์ดˆ๊ฐ€ ์—ฌ๊ธฐ์„œ ๋‚˜์™”๋‹ค.
520
+ *
521
+ * **โ‘ก ใ€Œ๋งˆ์ง€๋ง‰ ์‹œ๊ฐใ€์„ ์›ํ•˜๋ฉด์„œ `revision` ์œผ๋กœ ์ •๋ ฌํ–ˆ๋‹ค.** ๊ทธ ๋‘˜์€ ์–ด๊ธ‹๋‚œ๋‹ค โ€” ์—ฐ๊ฒฐ๋œ ์‹œ์Šคํ…œ์„ ๋˜์ฝ๋Š”
522
+ * ํŠธ์œˆ์€ ์›๋ณธ์˜ ์‹œ๊ฐ์„ ์‹ฃ๊ณ  ๋„์ฐฉ ์ˆœ์„œ๋กœ ๋ฆฌ๋น„์ „์„ ๋ฐ›์œผ๋ฏ€๋กœ, ๋ฆฌ๋น„์ „์ด ๊ฐ€์žฅ ํฐ ํ–‰์ด 4์›” ์‚ฌ์‹ค์ผ ์ˆ˜ ์žˆ๋‹ค
523
+ * (๊ฐ™์€ ์–ด๊ธ‹๋‚จ์„ ์˜ค๋Š˜ ์ €๋„ ๋ชฉ๋ก์—์„œ๋„ ๊ณ ์ณค๋‹ค). ์ฐฝ์˜ ๋์„ ๊ทธ ๊ฐ’์œผ๋กœ ์žก์œผ๋ฉด **์ฐฝ ์ž์ฒด๊ฐ€ ํ‹€๋ฆฐ๋‹ค.**
524
+ *
525
+ * ์ง€๊ธˆ์€ ์ข…๋ฅ˜๋งˆ๋‹ค `MAX(eventTime)` ์„ ๋ฌป๋Š”๋‹ค. `(domain, instance, event_type, event_time)` ์ƒ‰์ธ์˜
526
+ * ๋์„ ์ง‘์œผ๋ฏ€๋กœ ์ข…๋ฅ˜๋‹น ํ•œ ๋ฒˆ์ด๊ณ , ์…‹ ์ค‘ ๊ฐ€์žฅ ๋Šฆ์€ ๊ฒƒ์ด ๋‹ต์ด๋‹ค.
527
+ */
469
528
  async function latestEventTime(domainId: string, instanceIds: string[]): Promise<string | null> {
470
529
  if (instanceIds.length === 0) return null
471
- const row = await getRepository(TwinEvent).findOne({
472
- where: { domain: { id: domainId }, instanceId: In(instanceIds), eventType: In(BUSINESS_EVENTS) },
473
- order: { revision: 'DESC' }
474
- })
475
- /* ์†Œ๋น„์ฒ˜๋Š” ISO ๋ฌธ์ž์—ด์„ ๊ธฐ๋Œ€ํ•œ๋‹ค(์‹œ๊ฐ ์ถ•์˜ ๊ณต์šฉ ํ‘œ๊ธฐ) โ€” ์ปฌ๋Ÿผ์ด ๋‚ ์งœ๊ฐ€ ๋์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์˜ฎ๊ธด๋‹ค. */
476
- return row?.eventTime ? row.eventTime.toISOString() : null
530
+ const rows = await Promise.all(
531
+ instanceIds.flatMap(instanceId =>
532
+ BUSINESS_EVENTS.map(eventType =>
533
+ getReadRepository(TwinEvent)
534
+ .createQueryBuilder('e')
535
+ .select('MAX(e.eventTime)', 'at')
536
+ .where('e.domain = :domainId', { domainId })
537
+ .andWhere('e.instanceId = :instanceId', { instanceId })
538
+ .andWhere('e.eventType = :eventType', { eventType })
539
+ .getRawOne<{ at: unknown }>()
540
+ .catch(() => null)
541
+ )
542
+ )
543
+ )
544
+ /* ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์ค€ ๊ฐ’์€ ์‹œ๊ฐ„๋Œ€๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ๋‹ค โ€” ์ปฌ๋Ÿผ ๋ณ€ํ™˜์„ ์ง€๋‚˜๊ฒŒ ํ•œ๋‹ค(ยง`hydratedDate`). */
545
+ const repo = getReadRepository(TwinEvent)
546
+ let latest: Date | null = null
547
+ for (const r of rows) {
548
+ const at = hydratedDate(repo, 'eventTime', r?.at)
549
+ if (at && (!latest || at.getTime() > latest.getTime())) latest = at
550
+ }
551
+ return latest ? latest.toISOString() : null
477
552
  }
478
553
 
479
554
  /**
@@ -539,24 +614,82 @@ async function fetchEvents(domainId: string, instanceIds: string[], fromMs: numb
539
614
  * ๋‚จ๋Š” ๋น„์šฉ์„ ๊ฐ์ถ”์ง€ ์•Š๋Š”๋‹ค: `payload` ํŒŒ์‹ฑ์€ ์—ฌ์ „ํžˆ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ์ผ์ด๊ณ  ์—ฐ๊ฒฐ์„ ๋‚˜๋ˆ ๋„ ๋‚˜๋‰˜์ง€ ์•Š๋Š”๋‹ค
540
615
  * (์‹ค์ธก ํ‰๊ท  1.5์ดˆ). ๊ทธ๊ฒƒ์„ ๋” ์ค„์ด๋ ค๋ฉด ํด๋“œ๊ฐ€ payload ์—์„œ ๋ฌด์—‡์„ ์“ฐ๋Š”์ง€ ์ขํ˜€์•ผ ํ•œ๋‹ค โ€” ๋ณ„ ๊ฑด์ด๋‹ค.
541
616
  */
542
- const raw = await getReadRepository(TwinEvent)
543
- .createQueryBuilder('e')
544
- .select(['e.eventType AS "eventType"', 'e.eventTime AS "eventTime"', 'e.payload AS "payload"'])
545
- .where('e.domain = :domainId', { domainId })
546
- .andWhere('e.instanceId IN (:...instanceIds)', { instanceIds })
547
- .andWhere('e.eventType IN (:...types)', { types: BUSINESS_EVENTS })
548
- .andWhere('e.eventTime BETWEEN :from AND :to', { from: new Date(fromMs - lookbackMs), to: new Date(toMs) })
549
- .orderBy('e.eventTime', 'ASC')
550
- .addOrderBy('e.revision', 'ASC')
551
- .limit(MAX_EVENTS)
552
- .getRawMany<{ eventType: string; eventTime: unknown; payload: unknown }>()
553
-
554
- /* ์ƒํ•œ์— ์ •ํ™•ํžˆ ๋‹ฟ์œผ๋ฉด **๋” ์žˆ์—ˆ์„ ์ˆ˜ ์žˆ๋‹ค** โ€” ๊ทธ ์‚ฌ์‹ค์„ ๋Œ๋ ค์ค€๋‹ค. ์กฐ์šฉํžˆ ์ž˜๋ผ๋‚ด๋ฉด ์ˆซ์ž๊ฐ€ ์กฐ์šฉํžˆ
617
+ const from = new Date(fromMs - lookbackMs)
618
+ const to = new Date(toMs)
619
+
620
+ /**
621
+ * ํ•œ ๊ฐˆ๋ž˜๋ฅผ ๋ฌป๋Š”๋‹ค โ€” **์ข…๋ฅ˜๋ฅผ ์ •ํ™•์ผ์น˜๋กœ** ์ค€๋‹ค. ๊ทธ๊ฒƒ์ด ์ด ๊ณ ์นจ์˜ ์š”์ ์ด๋‹ค.
622
+ */
623
+ const readOne = (instanceId: string, eventType: string) =>
624
+ getReadRepository(TwinEvent)
625
+ .createQueryBuilder('e')
626
+ .select(['e.eventType AS "eventType"', 'e.eventTime AS "eventTime"', 'e.revision AS "revision"', 'e.payload AS "payload"'])
627
+ .where('e.domain = :domainId', { domainId })
628
+ .andWhere('e.instanceId = :instanceId', { instanceId })
629
+ .andWhere('e.eventType = :eventType', { eventType })
630
+ .andWhere('e.eventTime BETWEEN :from AND :to', { from, to })
631
+ .orderBy('e.eventTime', 'ASC')
632
+ .addOrderBy('e.revision', 'ASC')
633
+ .limit(MAX_EVENTS)
634
+ .getRawMany<{ eventType: string; eventTime: unknown; revision: unknown; payload: unknown }>()
635
+
636
+ /*
637
+ * โ”€โ”€ **์ข…๋ฅ˜๋ฅผ ๋‚˜๋ˆ  ๋ฌป๋Š”๋‹ค** (2026-08-28 ์‹ค์ธก) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
638
+ *
639
+ * ์˜ˆ์ „์—๋Š” `eventType IN (์…‹)` + ์‹œ๊ฐ ๋ฒ”์œ„ + ์‹œ๊ฐ์ˆœ์ด์—ˆ๋‹ค. ๊ทธ ๋ชจ์–‘์—์„œ sqlite ๋Š” ์ข…๋ฅ˜ ์ƒ‰์ธ์„ ๋ฒ„๋ฆฌ๊ณ 
640
+ * ์‹œ๊ฐ ์ƒ‰์ธ์œผ๋กœ **๊ตฌ๊ฐ„ ์ „์ฒด๋ฅผ ๊ฑธ์œผ๋ฉฐ ํ–‰๋งˆ๋‹ค ์ข…๋ฅ˜๋ฅผ ํ™•์ธํ•œ๋‹ค.** ์Šนํ™”ํ‘ธ๋“œ(์ €๋„ 3,140๋งŒํ–‰) ์‹ค์ธก:
641
+ *
642
+ * ์ฐฝ 1์‹œ๊ฐ„ ๊ตฌ๊ฐ„ ์•ˆ 0ํ–‰ ยท ์“ฐ๋Š” ํ–‰ 0 40๋ฐ€๋ฆฌ์ดˆ
643
+ * ์ฐฝ 24์‹œ๊ฐ„ ๊ตฌ๊ฐ„ ์•ˆ 254,185ํ–‰ ยท ์“ฐ๋Š” ํ–‰ 0 3,776๋ฐ€๋ฆฌ์ดˆ โ† 25๋งŒ ํ–‰์„ ๊ฑธ์–ด 0๊ฑด์„ ์“ด๋‹ค
644
+ * ์ฐฝ 7์ผ ๊ตฌ๊ฐ„ ์•ˆ 2,070,625ํ–‰ ยท 34,143 173๋ฐ€๋ฆฌ์ดˆ โ† ์ƒํ•œ์— ์ผ์ฐ ๋‹ฟ์•„ ์˜คํžˆ๋ ค ๋น ๋ฅด๋‹ค
645
+ *
646
+ * ์ฐฝ์„ ๋„“ํžˆ๋ฉด ๋นจ๋ผ์ง€๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ์‹ ํ˜ธ์˜€๋‹ค. ์ข…๋ฅ˜๋ฅผ ์ •ํ™•์ผ์น˜๋กœ ๋ฌผ์œผ๋ฉด
647
+ * `ix_twin_event_7 (domain, instance, event_type, event_time)` ์ด ๋ฒ”์œ„๊นŒ์ง€ ์ƒ‰์ธ์—์„œ ์ค€๋‹ค.
648
+ *
649
+ * ๋‚˜๋ˆŒ ์ˆ˜๋ฅผ ์ œํ•œํ•œ๋‹ค โ€” ๊ฐˆ๋ž˜๋งˆ๋‹ค ์ƒํ•œ๊นŒ์ง€ ๋ฐ›์•„์•ผ ์ •ํ™•ํ•˜๋ฏ€๋กœ(ยง`mergeStreams`) ๊ฐˆ๋ž˜๊ฐ€ ๋งŽ์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€
650
+ * ๊ทธ๋งŒํผ ๋“ ๋‹ค. ๋„˜์œผ๋ฉด ์˜ˆ์ „ ํ•œ ์งˆ์˜๋กœ ๊ฐ„๋‹ค(๋А๋ฆฌ์ง€๋งŒ ๋‹ต์€ ๊ฐ™๋‹ค).
651
+ */
652
+ const pairs = instanceIds.length * BUSINESS_EVENTS.length
653
+ let raw: { eventType: string; eventTime: unknown; revision: unknown; payload: unknown }[]
654
+ let mergedCapped = false
655
+ if (pairs && pairs <= KPI_MAX_SPLIT) {
656
+ const streams = await Promise.all(
657
+ instanceIds.flatMap(instanceId => BUSINESS_EVENTS.map(eventType => readOne(instanceId, eventType)))
658
+ )
659
+ const merged = mergeStreams(streams, MAX_EVENTS, (a, b) => {
660
+ const at = String(a.eventTime ?? '').localeCompare(String(b.eventTime ?? ''))
661
+ if (at !== 0) return at
662
+ return Number(a.revision ?? 0) - Number(b.revision ?? 0)
663
+ })
664
+ if (merged.underfilled) {
665
+ /* ์žˆ์„ ์ˆ˜ ์—†๋Š” ์ผ์ด๋‹ค โ€” ๊ฐˆ๋ž˜๊ฐ€ ์ƒํ•œ๋ณด๋‹ค ๋งŽ์ด ๋ฐ›์•„ ์™”๋‹ค. ์กฐ์šฉํžˆ ์“ฐ์ง€ ์•Š๊ณ  ๋งํ•œ๋‹ค. */
666
+ twinWarn('[twin-kpi] a per-type stream returned more than the cap โ€” the merge cannot be trusted; falling back')
667
+ raw = []
668
+ } else {
669
+ raw = merged.items
670
+ mergedCapped = merged.capped
671
+ }
672
+ } else {
673
+ raw = await getReadRepository(TwinEvent)
674
+ .createQueryBuilder('e')
675
+ .select(['e.eventType AS "eventType"', 'e.eventTime AS "eventTime"', 'e.revision AS "revision"', 'e.payload AS "payload"'])
676
+ .where('e.domain = :domainId', { domainId })
677
+ .andWhere('e.instanceId IN (:...instanceIds)', { instanceIds })
678
+ .andWhere('e.eventType IN (:...types)', { types: BUSINESS_EVENTS })
679
+ .andWhere('e.eventTime BETWEEN :from AND :to', { from, to })
680
+ .orderBy('e.eventTime', 'ASC')
681
+ .addOrderBy('e.revision', 'ASC')
682
+ .limit(MAX_EVENTS)
683
+ .getRawMany<{ eventType: string; eventTime: unknown; revision: unknown; payload: unknown }>()
684
+ mergedCapped = raw.length >= MAX_EVENTS
685
+ }
686
+
687
+ /* ์ƒํ•œ์— ์ •ํ™•ํžˆ ๋‹ฟ์œผ๋ฉด **๋” ์žˆ์—ˆ์„ ์ˆ˜ ์žˆ๋‹ค** โ€” ๊ทธ ์‚ฌ์‹ค์„ ๋Œ๋ ค์ค€๋‹ค. ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ž˜๋ผ๋‚ด๋ฉด ์ˆซ์ž๊ฐ€ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ 
555
688
  * ์ž‘์•„์ง€๊ณ , ์‚ฌ์šฉ์ž๋Š” ๊ทธ๊ฒƒ์„ ์„ฑ๊ณผ ํ•˜๋ฝ์œผ๋กœ ์ฝ๋Š”๋‹ค(๊ฐ€์žฅ ๋‚˜์œ ์ข…๋ฅ˜์˜ ๊ฑฐ์ง“์ด๋‹ค). */
556
689
  return {
557
690
  /* ํด๋“œ๋Š” ISO ๋ฌธ์ž์—ด ์ถ•์œผ๋กœ ์ˆ˜ํ–‰๋œ๋‹ค(kpi-fold) โ€” ๊ฒฝ๊ณ„์—์„œ ํ•œ ๋ฒˆ๋งŒ ์˜ฎ๊ธด๋‹ค. */
558
691
  events: raw.map(r => ({ eventType: r.eventType, eventTime: isoOf(r.eventTime), payload: jsonOf(r.payload) })),
559
- capped: raw.length >= MAX_EVENTS
692
+ capped: mergedCapped
560
693
  }
561
694
  }
562
695
 
@@ -594,15 +727,37 @@ function jsonOf(v: unknown): any {
594
727
  /**
595
728
  * ์ง‘๊ณ„ ๋Œ€์ƒ ํŠธ์œˆ ๋ชฉ๋ก.
596
729
  *
597
- * ๊ณต๊ฐ„์ด ์ฃผ์–ด์ง€๋ฉด **๊ฐ€๋™ ์ค‘์ด๊ณ  ์šด์˜ ๋ชฉ์ ์ธ** ๊ฒƒ๋งŒ ๋ชจ์€๋‹ค โ€” ๋ฒค์น˜ ์‚ฌ๋ณธ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ํ˜„์žฅ์ด ์•„๋‹ˆ๋‹ค.
730
+ * ๊ณต๊ฐ„์ด ์ฃผ์–ด์ง€๋ฉด ๊ทธ ๊ณต๊ฐ„์˜ **์šด์˜ ๋ชฉ์ ** ํŠธ์œˆ์„ ๋ชจ์€๋‹ค โ€” ๋ฒค์น˜ ์‚ฌ๋ณธ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ํ˜„์žฅ์ด ์•„๋‹ˆ๋‹ค.
598
731
  * ํŠธ์œˆ์ด ์ง€์ •๋˜๋ฉด ๊ทธ๊ฒƒ ํ•˜๋‚˜. ๋‘˜ ๋‹ค ์—†์œผ๋ฉด ๋ฌด์—‡์„ ์žฌ์•ผ ํ• ์ง€ ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๋ช…์‹œ ์‹คํŒจํ•œ๋‹ค.
732
+ *
733
+ * โ”€โ”€ **ใ€Œ์ง€๊ธˆ ๋„๋Š”๊ฐ€ใ€๋กœ ๊ฑธ๋Ÿฌ๋‚ด์ง€ ์•Š๋Š”๋‹ค** (2026-08-28 ์‹ค์ธก) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
734
+ * ์—ฌ๊ธฐ์— `running` ์กฐ๊ฑด์ด ์žˆ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฉˆ์ถ˜ ํŠธ์œˆ์€ ํ˜„์žฅ์œผ๋กœ ๋ฌผ์—ˆ์„ ๋•Œ ๋Œ€์ƒ์—์„œ ๋น ์กŒ๊ณ , ํ™”๋ฉด์€
735
+ * ใ€Œ๊ธฐ๋ก์ด ์—†์Šต๋‹ˆ๋‹คใ€๋ผ๊ณ  ๋งํ–ˆ๋‹ค โ€” **์ €๋„์—๋Š” ์žˆ๋Š”๋ฐ.**
736
+ *
737
+ * twinEventList(instanceId: "ems-plant-1") 1,792๊ฑด
738
+ * twinEventList(spaceId: "ems-plant-1-site") 0๊ฑด โ† ๊ฐ™์€ ํŠธ์œˆ, ๊ฐ™์€ ๋„๋ฉ”์ธ
739
+ *
740
+ * ๊ทœ๋ชจ๋กœ ๋ณด๋ฉด ์ด๊ฒƒ์€ ์˜ˆ์™ธ๊ฐ€ ์•„๋‹ˆ๋ผ ๊ธฐ๋ณธ๊ฐ’์ด์—ˆ๋‹ค: ๋“ฑ๋ก๋œ ํŠธ์œˆ 30๊ฐœ ์ค‘ **๋„๋Š” ๊ฒƒ์ด 2๊ฐœ, ๋ฉˆ์ถ˜ ๊ฒƒ์ด 28๊ฐœ**๋‹ค.
741
+ * ์ฆ‰ ํ˜„์žฅ์œผ๋กœ ๋ฌป๋Š” ์ €๋„ ์กฐํšŒ๊ฐ€ ๋Œ€๋ถ€๋ถ„์˜ ํ˜„์žฅ์—์„œ ๊ฑฐ์ง“ ใ€Œ์—†๋‹คใ€๋ฅผ ๋ƒˆ๋‹ค.
742
+ *
743
+ * ์ง€์šด ์ด์œ ๋Š” ๋‘ ๊ฐ€์ง€๋‹ค.
744
+ *
745
+ * โ‘  **์ €๋„์€ ๊ธฐ๋ก์ด๊ณ , ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ง€๊ธˆ ๋„๋Š”์ง€๋Š” ๋‹ค๋ฅธ ์ถ•์ด๋‹ค.** ์ฐฝ ์•ˆ์— ์žˆ๋Š” ์‚ฌ์‹ค์€ ์‚ฌ์‹ค์ด๋‹ค.
746
+ * ์•„๋ž˜ ์ค„์ด ํŠธ์œˆ์„ ์ง€์ •ํ•œ ๊ฒฝ์šฐ์— ๋Œ€ํ•ด ์ด๋ฏธ ๊ทธ ์›์น™์„ ์ ์–ด ๋‘์—ˆ๋‹ค(ใ€Œ์ •์ง€ยท๋น„๋“ฑ๋ก์ด์–ด๋„ ์ €๋„์€ ๋‚จ์•„
747
+ * ์žˆ์„ ์ˆ˜ ์žˆ๋‹คใ€). ๊ฐ™์€ ์›์น™์ด ํ˜„์žฅ ๊ฒฝ๋กœ์—๋งŒ ์ ์šฉ๋˜์ง€ ์•Š๊ณ  ์žˆ์—ˆ๋‹ค.
748
+ * โ‘ก ์˜› ์ฃผ์„์ด ๋Œ€๋Š” ์ด์œ (ใ€Œ๋ฒค์น˜ ์‚ฌ๋ณธ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ํ˜„์žฅ์ด ์•„๋‹ˆ๋‹คใ€)๋Š” **๋ฒค์น˜ ์ œ์™ธ๋งŒ** ์„ค๋ช…ํ•œ๋‹ค.
749
+ * `running` ์—๋Š” ๊ทผ๊ฑฐ๊ฐ€ ์ ํ˜€ ์žˆ์ง€ ์•Š์•˜๋‹ค.
750
+ *
751
+ * **๋ฐ”๋€Œ๋Š” ๊ฒƒ**: ํ˜„์žฅ KPI ๊ฐ€ ์ด์ œ ๋ฉˆ์ถ˜ ํŠธ์œˆ์˜ ์ง€๋‚œ ๊ธฐ๋ก๋„ ์„ผ๋‹ค. ์ฐฝ ์•ˆ์˜ ์‚ฌ์‹ค์„ ์„ธ๋Š” ๊ฒƒ์ด ๊ทธ ์ง€ํ‘œ์˜
752
+ * ์ •์˜์ด๋ฏ€๋กœ ๊ทธ๊ฒƒ์ด ๋งž๋‹ค. ใ€Œ์ง€๊ธˆ ๋„๋Š” ๊ฒƒ๋งŒใ€์ด ํ•„์š”ํ•œ ํ™”๋ฉด์ด ์ƒ๊ธฐ๋ฉด ๊ทธ๋•Œ **์ด๋ฆ„ ์žˆ๋Š” ์ถ•**์œผ๋กœ ๋ฐ›๋Š”๋‹ค โ€”
753
+ * ์—ฌ๊ธฐ์— ์ˆจ๊ฒจ ๋‘๋ฉด ๋‘ ๋ฌผ์Œ์ด ํ•œ ์นธ์„ ์“ด๋‹ค.
599
754
  */
600
755
  async function resolveTargets(input: TwinKpiInput): Promise<{ instanceIds: string[]; restartPolicies: string[] }> {
601
756
  if (!input.instanceId && !input.spaceId) throw new Error('either instanceId or spaceId is required')
602
757
  const list = await TwinEngine.list(input.domainId)
603
758
  const rows = input.instanceId
604
759
  ? list.filter((x: any) => x?.instanceId === input.instanceId)
605
- : list.filter((x: any) => x?.running && x.spaceId === input.spaceId && x.purpose !== 'bench' && x.instanceId)
760
+ : list.filter((x: any) => x?.spaceId === input.spaceId && x.purpose !== 'bench' && x.instanceId)
606
761
  /* ๋ชฉ๋ก์—์„œ ๋ชป ์ฐพ์•„๋„(์ •์ง€ยท๋น„๋“ฑ๋ก) ์ง€์ •๋œ ํŠธ์œˆ์€ ๊ทธ๋Œ€๋กœ ์กฐํšŒํ•œ๋‹ค โ€” ์ €๋„์€ ๋‚จ์•„ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. */
607
762
  const instanceIds = rows.length ? rows.map((x: any) => x.instanceId as string) : input.instanceId ? [input.instanceId] : []
608
763
  /* ์ปฌ๋Ÿผ์ด NOT NULL ์ด๋ผ 'unknown' ์œผ๋กœ ๋ฉ”์šธ ์ผ์ด ์—†๋‹ค โ€” ๋น„๋ฉด ๊ณ„์•ฝ์ด ๊นจ์ง„ ๊ฒƒ์ด์ง€ ๋ชจ๋ฅด๋Š” ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋‹ค. */
@@ -677,12 +832,23 @@ export async function computeTwinKpi(input: TwinKpiInput): Promise<TwinKpiOutput
677
832
  const feederMap = await resourceFeederMap(input.domainId, instanceIds)
678
833
  /* ์š”๊ธˆ ๋‹จ๊ฐ€๋„ ์ถ•๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ์ฝ๋Š”๋‹ค โ€” ๊ธˆ์•ก์€ ๊ฐ™์€ ์ฐฝ์˜ ์ „๋ ฅ๋Ÿ‰ยท์ตœ๋Œ€์ˆ˜์š”์—์„œ ๋‚˜์˜ค๋Š” ์‚ฌ์‹ค์ด๋‹ค. */
679
834
  const tariffFound = await tariffOf(input.domainId, instanceIds)
835
+ /*
836
+ * โ”€โ”€ ์š”๊ธˆ์ ์šฉ์ „๋ ฅ์€ **์ฒญ๊ตฌ์„œ๊ฐ€ ๋จผ์ €๋‹ค** (2026-08-29) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
837
+ *
838
+ * ๊ทธ ๊ฐ’์€ ์ตœ๊ทผ ๋ช‡ ๋‹ฌ์˜ ์ตœ๊ณ  ์ˆ˜์š”๋กœ **๋งค๋‹ฌ ๋‹ค์‹œ ์ •ํ•ด์ง„๋‹ค.** ์„ ์–ธ์— ๋ฐ•์•„ ๋‘๋ฉด ๋งค๋‹ฌ ์‚ฌ๋žŒ์ด ๊ฐฑ์‹ ํ•ด์•ผ
839
+ * ํ•˜๊ณ , ์•ˆ ํ•˜๋ฉด ๋‚ก์€ ๊ฐ’์œผ๋กœ ๊ธฐ๋ณธ์š”๊ธˆ์ด ์กฐ์šฉํžˆ ํ‹€๋ฆฐ๋‹ค.
840
+ *
841
+ * ์›๋ณธ์ด ์ฒญ๊ตฌ์„œ์— ๊ทธ ๊ฐ’์„ ์‹ค์–ด ๋งค์ผ ๋ณด๋‚ธ๋‹ค. ์ฐฝ ๋ฐ–์˜ ์ฒญ๊ตฌ์„œ๋Š” ์ €๋„ ์กฐํšŒ์— ๊ฑธ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ
842
+ * **์ปค๋„ ์ƒํƒœ์— ์ด์–ด์ ธ ์žˆ๋Š” ๋งˆ์ง€๋ง‰ ์ฒญ๊ตฌ์„œ**๋ฅผ ํ•จ๊ป˜ ๋„˜๊ธด๋‹ค(ยง`EnergyState.lastBill`).
843
+ */
844
+ const lastBill = latestBillOf(input.domainId, instanceIds)
680
845
  const kpi = foldKpi(events, { fromMs, toMs }, {
681
846
  groupBy: input.groupBy,
682
847
  locationArea: areaMap.locationArea,
683
848
  resourceFeeder: feederMap.resourceFeeder,
684
849
  meterFeeder: feederMap.meterFeeder,
685
850
  ...(tariffFound.tariff ? { tariff: tariffFound.tariff } : {}),
851
+ ...(lastBill ? { lastBill } : {}),
686
852
  ...shiftCtx,
687
853
  groupLimit: input.groupLimit
688
854
  })
@@ -691,14 +857,14 @@ export async function computeTwinKpi(input: TwinKpiInput): Promise<TwinKpiOutput
691
857
  *
692
858
  * ํด๋“œ๋Š” ์ˆœ์ˆ˜ํ•˜๋‹ค(๋กœ๊ทธ๋ฅผ ๋ชจ๋ฅธ๋‹ค) โ€” ๊ทธ๋ž˜์„œ ๋ถ€๋ฅด๋Š” ์ชฝ์ด ๋งํ•œ๋‹ค. ์™œ ๋งํ•ด์•ผ ํ•˜๋‚˜: ์ด ํด๋“œ๋Š” ์˜ค๋”์˜
693
859
  * ์™„๋ฃŒ๋ฅผ **๋™์˜์–ด ๋ชฉ๋ก**์œผ๋กœ ํŒ์ •ํ•˜๊ณ , ๊ทธ๊ฒƒ์€ ์›๋ณธ์˜ ๋‚ฑ๋ง์—์„œ ๋œป์„ ์ถ”๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ปค๋„ฅํ„ฐ๊ฐ€
694
- * ์ž๊ธฐ ํ‘œ๋กœ ์˜ฎ๊ธฐ์ง€ ์•Š์œผ๋ฉด ๊ทธ ๋‚ฑ๋ง์ด ๋ชฉ๋ก์— ์—†์–ด **์™„๋ฃŒ๊ฐ€ ์กฐ์šฉํžˆ 0 ์œผ๋กœ ์„ธ์–ด์ง„๋‹ค.**
860
+ * ์ž๊ธฐ ํ‘œ๋กœ ์˜ฎ๊ธฐ์ง€ ์•Š์œผ๋ฉด ๊ทธ ๋‚ฑ๋ง์ด ๋ชฉ๋ก์— ์—†์–ด **์™„๋ฃŒ๊ฐ€ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  0 ์œผ๋กœ ์„ธ์–ด์ง„๋‹ค.**
695
861
  *
696
862
  * ์‹ค์ œ๋กœ ๊ทธ๋žฌ๋‹ค: ์ฒซ ์‹ค ์›๋ณธ์˜ `FINISHED` ๊ฐ€ ๋ชฉ๋ก์— ์—†์–ด ์™„๋ฃŒ ์˜ค๋”๊ฐ€ ์ „๋ถ€ 0 ์ด์—ˆ๊ณ , ์‚ฌ๋žŒ์ด ํ™”๋ฉด์„
697
863
  * ๋ณด๊ณ  ์˜์‹ฌํ•ด์„œ์•ผ ๋“œ๋Ÿฌ๋‚ฌ๋‹ค(๊ทธ ์ปค๋„ฅํ„ฐ์—๋Š” ์ƒํƒœ ํ‘œ๊ฐ€ ์ •์˜๋ผ **์žˆ์œผ๋ฉด์„œ** ์˜ค๋” ๊ฒฝ๋กœ์—์„œ ์“ฐ์ด์ง€ ์•Š์•˜๋‹ค).
698
864
  * ๊ทธ๋•Œ ๋ชฉ๋ก์— ๋‚ฑ๋ง์„ ๋”ํ•œ ๊ฒƒ์€ ๊ทธ ์‚ฌ์‹ค์„ ๊ฐ€๋ฆฐ ๋•œ๋นต์ด์—ˆ๋‹ค.
699
865
  *
700
866
  * ๊ทธ๋Ÿฌ๋‹ˆ ์ด ๋กœ๊ทธ๋Š” ใ€Œ๋ชฉ๋ก์„ ๋Š˜๋ ค๋ผใ€๊ฐ€ ์•„๋‹ˆ๋ผ **ใ€Œ๊ทธ ์ปค๋„ฅํ„ฐ๊ฐ€ ์˜ฎ๊ธฐ์ง€ ์•Š์•˜๋‹คใ€**๋ฅผ ๋œปํ•œ๋‹ค. ํ™”๋ฉด์„ ๋ณด์ง€
701
- * ์•Š๋Š” ์‚ฌ๋žŒ๋„ ์•Œ์•„์•ผ ํ•˜๋ฏ€๋กœ ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค โ€” ์กฐ์šฉํ•œ 0 ์ด ์ด ์‹œ์Šคํ…œ์—์„œ ๊ฐ€์žฅ ๋น„์‹ผ ์นจ๋ฌต์ด๋‹ค.
867
+ * ์•Š๋Š” ์‚ฌ๋žŒ๋„ ์•Œ์•„์•ผ ํ•˜๋ฏ€๋กœ ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค โ€” ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋Š” 0 ์ด ์ด ์‹œ์Šคํ…œ์—์„œ ๊ฐ€์žฅ ๋น„์‹ผ ์นจ๋ฌต์ด๋‹ค.
702
868
  *
703
869
  * ์™„๋ฃŒ๊ฐ€ ํ•˜๋‚˜๋„ ์—†์„ ๋•Œ๋งŒ ๋งํ•œ๋‹ค: `picking` ์ฒ˜๋Ÿผ ์™„๋ฃŒ๊ฐ€ ์•„๋‹Œ ๋‚ฑ๋ง์ด ์„ž์—ฌ ์žˆ๋Š” ๊ฒƒ์€ ์ •์ƒ์ด๊ณ ,
704
870
  * ๊ทธ๊ฒƒ๊นŒ์ง€ ๋งค๋ฒˆ ๊ฒฝ๊ณ ํ•˜๋ฉด ์ง„์งœ ์‹ ํ˜ธ๊ฐ€ ๋ฌปํžŒ๋‹ค.
@@ -741,7 +907,7 @@ export async function computeTwinKpi(input: TwinKpiInput): Promise<TwinKpiOutput
741
907
  for (const w of windows) {
742
908
  const { events: pastEvents, capped: pastCapped } = await fetchEvents(input.domainId, instanceIds, w.fromMs, w.toMs, lookbackMs)
743
909
  baseCapped = baseCapped || pastCapped
744
- /* ์ถ• ์—†์ด, **๊ฐ™์€ ๊ต๋Œ€ ์ •์˜**๋กœ ์ ‘๋Š”๋‹ค(์ •์˜๊ฐ€ ๋‹ค๋ฅด๋ฉด ๊ฐ™์€ ์‹œ๊ฐ„๋Œ€๊ฐ€ ์•„๋‹ˆ๊ฒŒ ๋œ๋‹ค). */
910
+ /* ์ถ• ์—†์ด, **๊ฐ™์€ ๊ต๋Œ€ ์ •์˜**๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค(์ •์˜๊ฐ€ ๋‹ค๋ฅด๋ฉด ๊ฐ™์€ ์‹œ๊ฐ„๋Œ€๊ฐ€ ์•„๋‹ˆ๊ฒŒ ๋œ๋‹ค). */
745
911
  const past = foldKpi(pastEvents, w, { locationArea: areaMap.locationArea, ...shiftCtx })
746
912
  const values: Partial<Record<TwinMetricId, number | undefined>> = {}
747
913
  for (const metric of Object.keys(TWIN_METRIC) as TwinMetricId[]) values[metric] = TWIN_METRIC[metric].of(past)
@@ -783,7 +949,7 @@ export async function computeTwinKpi(input: TwinKpiInput): Promise<TwinKpiOutput
783
949
  }))
784
950
  : undefined
785
951
 
786
- /* ๋น„๊ต ๊ตฌ๊ฐ„ โ€” **๊ฐ™์€ ๊ธธ์ดยท๊ฐ™์€ ๊ทœ์น™**์œผ๋กœ ํ•œ ๋ฒˆ ๋” ์ ‘๋Š”๋‹ค. ๊ธฐ๋ก์ด ์—†์œผ๋ฉด delta ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค
952
+ /* ๋น„๊ต ๊ตฌ๊ฐ„ โ€” **๊ฐ™์€ ๊ธธ์ดยท๊ฐ™์€ ๊ทœ์น™**์œผ๋กœ ํ•œ ๋ฒˆ ๋” ๊ณ„์‚ฐํ•œ๋‹ค. ๊ธฐ๋ก์ด ์—†์œผ๋ฉด delta ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค
787
953
  * (์—†๋Š” ๊ฒƒ์„ 0 ์œผ๋กœ ๊ทธ๋ฆฌ๋ฉด "๋ณ€ํ™” ์—†์Œ" ์ด๋ผ๋Š” ๊ฑฐ์ง“์ด ๋œ๋‹ค). */
788
954
  let comparison: TwinKpiOutput['comparison']
789
955
  /* ๊ด€์  ร— ๋น„๊ต ๊ต์ฐจ โ€” ์ถ•๋ณ„ delta ์™€ **์‚ฌ๋ผ์ง„ ์ถ•**(์ „์— ์žˆ์—ˆ๊ณ  ์ง€๊ธˆ ์—†๋Š” ๊ฒƒ). */
@@ -799,13 +965,13 @@ export async function computeTwinKpi(input: TwinKpiInput): Promise<TwinKpiOutput
799
965
  w.toMs,
800
966
  lookbackMs
801
967
  )
802
- /* ์ถ•์ด ์š”์ฒญ๋์œผ๋ฉด ๋น„๊ต ๊ตฌ๊ฐ„๋„ **๊ฐ™์€ ์ถ•**์œผ๋กœ ์ ‘๋Š”๋‹ค โ€” ๊ต์ฐจ("๊ตฌ์—ญ๋ณ„๋กœ ์–ด์ œ ๋Œ€๋น„")๋ฅผ ๋‚ด๊ธฐ ์œ„ํ•ด.
968
+ /* ์ถ•์ด ์š”์ฒญ๋์œผ๋ฉด ๋น„๊ต ๊ตฌ๊ฐ„๋„ **๊ฐ™์€ ์ถ•**์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค โ€” ๊ต์ฐจ("๊ตฌ์—ญ๋ณ„๋กœ ์–ด์ œ ๋Œ€๋น„")๋ฅผ ๋‚ด๊ธฐ ์œ„ํ•ด.
803
969
  * ์ƒํ•œ์€ ์ถ•์ด ์„œ๋กœ ์–ด๊ธ‹๋‚˜์ง€ ์•Š๊ฒŒ ๋„‰๋„‰ํžˆ ๋‘”๋‹ค(ํ˜„์žฌ ์ถ•์ด ์ƒํ•œ ์•ˆ์— ์žˆ๋Š”๋ฐ ๋น„๊ต ์ถ•์—์„œ ๋ฐ€๋ ค๋‚˜๋ฉด
804
970
  * ์žˆ๋Š”๋ฐ ์—†๋‹ค๊ณ  ๋งํ•˜๊ฒŒ ๋œ๋‹ค). */
805
971
  const prev = foldKpi(prevEvents, w, {
806
972
  groupBy: input.groupBy,
807
973
  locationArea: areaMap.locationArea,
808
- ...shiftCtx, // ๋น„๊ต ๊ตฌ๊ฐ„๋„ **๊ฐ™์€ ๊ต๋Œ€ ์ •์˜**๋กœ ์ ‘๋Š”๋‹ค(์ •์˜๊ฐ€ ๋‹ค๋ฅด๋ฉด ์ถ•์ด ์–ด๊ธ‹๋‚œ๋‹ค)
974
+ ...shiftCtx, // ๋น„๊ต ๊ตฌ๊ฐ„๋„ **๊ฐ™์€ ๊ต๋Œ€ ์ •์˜**๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค(์ •์˜๊ฐ€ ๋‹ค๋ฅด๋ฉด ์ถ•์ด ์–ด๊ธ‹๋‚œ๋‹ค)
809
975
  groupLimit: input.groupBy ? 100 : undefined
810
976
  })
811
977
  prevCappedFlag = prevCapped
@@ -29,7 +29,7 @@ export function registerLiveFeedProbe(probe: LiveFeedProbe): void {
29
29
  * ํ”ผ๋“œ๊ฐ€ ๋ถ™์–ด ์žˆ๋‚˜ โ€” ์–ด๋А ์žฅ๋ถ€๋“  ๊ทธ๋ ‡๋‹ค๊ณ  ํ•˜๋ฉด ๊ทธ๋ ‡๋‹ค.
30
30
  *
31
31
  * ์žฅ๋ถ€๊ฐ€ ํ„ฐ์ง€๋Š” ๊ฒƒ์„ ์ด ํŒ์ • ์ „์ฒด์˜ ์‹คํŒจ๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค: ํ•œ ๊ณ„์ธต์ด ๋‹ตํ•˜์ง€ ๋ชปํ•ด๋„ ๋‹ค๋ฅธ ๊ณ„์ธต์˜ ์‚ฌ์‹ค์€
32
- * ๊ทธ๋Œ€๋กœ ์ฐธ์ด๋‹ค. ๋‹ค๋งŒ **์กฐ์šฉํžˆ ๋„˜๊ธฐ์ง€ ์•Š๋Š”๋‹ค** โ€” ๋‹ตํ•˜์ง€ ๋ชปํ•œ ์žฅ๋ถ€๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค.
32
+ * ๊ทธ๋Œ€๋กœ ์ฐธ์ด๋‹ค. ๋‹ค๋งŒ **์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋„˜๊ธฐ์ง€ ์•Š๋Š”๋‹ค** โ€” ๋‹ตํ•˜์ง€ ๋ชปํ•œ ์žฅ๋ถ€๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค.
33
33
  */
34
34
  export function liveFeedAttached(instanceId: string): boolean {
35
35
  for (const probe of probes) {
@@ -63,7 +63,7 @@ export interface LoadMeter {
63
63
  overBudget: number
64
64
  /**
65
65
  * ๋งˆ์ง€๋ง‰ ๊ฒฝ๊ณ  ์‹œ๊ฐ(ms). **ํ•œ ๋ฒˆ๋„ ์•ˆ ํ–ˆ์œผ๋ฉด `-Infinity`** โ€” 0 ์œผ๋กœ ๋‘๋ฉด "0 ์‹œ์ ์— ๊ฒฝ๊ณ ํ–ˆ๋‹ค" ๊ฐ€ ๋˜์–ด
66
- * ์ฒซ ๋А๋ฆฐ ํ‹ฑ์ด ์กฐ์šฉํžˆ ์ง€๋‚˜๊ฐ„๋‹ค(๊ฒ€์‚ฌ๊ฐ€ ๊ทธ๊ฒƒ์„ ์žก์•˜๋‹ค).
66
+ * ์ฒซ ๋А๋ฆฐ ํ‹ฑ์ด ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ง€๋‚˜๊ฐ„๋‹ค(๊ฒ€์‚ฌ๊ฐ€ ๊ทธ๊ฒƒ์„ ์žก์•˜๋‹ค).
67
67
  */
68
68
  lastWarnMs: number
69
69
  }
@@ -25,7 +25,7 @@
25
25
  *
26
26
  * โ”€โ”€ ์™œ ๋ชจ๋ธ์— ๋ฐ”๋กœ ์“ฐ์ง€ ์•Š๊ณ  ๊ฒน์œผ๋กœ ๋‘๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
27
27
  * ๋ชจ๋ธ์€ **์›์ฒœ์˜ ์‚ฌ๋ณธ**์ด๋‹ค. ๋‹ค์‹œ ์ฝ๊ธฐ(resync)๋Š” ๊ทธ ์‚ฌ๋ณธ์„ ์ƒˆ๋กœ ๊ทธ๋ฆฌ๋ฏ€๋กœ, ๋ชจ๋ธ์— ์ง์ ‘ ์“ด ๊ฐ’์€ ๋‹ค์Œ
28
- * ๋‹ค์‹œ ์ฝ๊ธฐ์—์„œ **์กฐ์šฉํžˆ ์‚ฌ๋ผ์ง„๋‹ค** โ€” ์š”๊ธˆ์ด ์‚ฌ๋ผ์ง€๋ฉด ๊ธˆ์•ก๋„ ์‚ฌ๋ผ์ง€๊ณ , ์‚ฌ์šฉ์ž๋Š” ์ด์œ ๋ฅผ ์•Œ ์ˆ˜ ์—†๋‹ค.
28
+ * ๋‹ค์‹œ ์ฝ๊ธฐ์—์„œ **์˜ค๋ฅ˜ ์—†์ด ์‚ฌ๋ผ์ง„๋‹ค** โ€” ์š”๊ธˆ์ด ์‚ฌ๋ผ์ง€๋ฉด ๊ธˆ์•ก๋„ ์‚ฌ๋ผ์ง€๊ณ , ์‚ฌ์šฉ์ž๋Š” ์ด์œ ๋ฅผ ์•Œ ์ˆ˜ ์—†๋‹ค.
29
29
  * ๊ทธ๋ž˜์„œ ๋กœ์ปฌ ์„ ์–ธ์€ ๋”ฐ๋กœ ๋ณด๊ด€ํ•˜๊ณ , ๋ชจ๋ธ์„ ๊ทธ๋ฆด ๋•Œ๋งˆ๋‹ค ๊ทธ ์œ„์— ์–น๋Š”๋‹ค. ๋ค์œผ๋กœ ใ€Œ๋ˆ„๊ฐ€ ๋งํ•œ ๊ฐ’์ธ๊ฐ€ใ€์—
30
30
  * ๋‹ตํ•  ์ˆ˜ ์žˆ๋‹ค โ€” ์ด ์‹œ์Šคํ…œ์ด ๊ทผ๊ฑฐ๋ฅผ ๊ฐ’๊ณผ ํ•จ๊ป˜ ๋‹ค๋‹ˆ๊ฒŒ ํ•˜๋Š” ๊ทœ์œจ๊ณผ ๊ฐ™๋‹ค(`basis`ยท`derived` ์™€ ํ•œ ๊ฒฐ).
31
31
  *
@@ -56,7 +56,7 @@ export interface LocalDeclaration {
56
56
  /**
57
57
  * ๋‹จ์œ„(UN/CEFACT ์ฝ”๋“œ) โ€” **์–ด๋–ค ๊ฐ’์€ ๋‹จ์œ„ ์—†์ด๋Š” ๋œป์ด ์—†๋‹ค.**
58
58
  *
59
- * ์†๋„๊ฐ€ ๊ทธ๋ ‡๋‹ค: ๋‹จ์œ„๊ฐ€ ์—†์œผ๋ฉด ์ด๋™ ์‹œ๊ฐ„ ์ถ”์ •๊ธฐ๊ฐ€ ๊ทธ ์„ค๋น„๋ฅผ **๊ฑฐ์ ˆํ•œ๋‹ค**(๊ทธ๋Ÿฌ๋ฉด ๊ณต์ • ์‹œ๊ฐ„์ด ์กฐ์šฉํžˆ
59
+ * ์†๋„๊ฐ€ ๊ทธ๋ ‡๋‹ค: ๋‹จ์œ„๊ฐ€ ์—†์œผ๋ฉด ์ด๋™ ์‹œ๊ฐ„ ์ถ”์ •๊ธฐ๊ฐ€ ๊ทธ ์„ค๋น„๋ฅผ **๊ฑฐ์ ˆํ•œ๋‹ค**(๊ทธ๋Ÿฌ๋ฉด ๊ณต์ • ์‹œ๊ฐ„์ด ์•Œ๋ฆฌ์ง€ ์•Š๊ณ 
60
60
  * ์‚ฌ๋ผ์ง„๋‹ค). ๋ช…์„ธ๊ฐ€ ๋‹จ์œ„๋ฅผ ์š”๊ตฌํ•˜๋ฉด(`uomChoices`) ๋ฐ›๊ณ , ๊ทธ ๋ชฉ๋ก์— ์—†๋Š” ์ฝ”๋“œ๋Š” ๊ฑฐ์ ˆํ•œ๋‹ค โ€” ์ฝ๋Š” ์ชฝ์ด
61
61
  * ๋ชจ๋ฅด๋Š” ๋‹จ์œ„๋ฅผ ๋ฐ›์•„ ๋‘๋ฉด ํ™”๋ฉด์€ ๋„ฃ์—ˆ๋‹ค๊ณ  ๋งํ•˜๊ณ  ๊ณ„์‚ฐ์€ ๊ทธ ๊ฐ’์„ ๋ฒ„๋ฆฐ๋‹ค.
62
62
  */
@@ -120,7 +120,7 @@ export interface RejectedDeclaration {
120
120
  * ๊ทธ ์ด๋ฆ„์˜ ๊ณต์ •์„ ์ด ํŠธ์œˆ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์—†๋‹ค โ€” ์ปค๋„์ด ๋Œ๋ฆฐ ์ ๋„ ์—†๊ณ  ๋ช…์„ธ ํ–‰์—๋„ ์—†๋‹ค.
121
121
  *
122
122
  * ๊ณต์ • ์„ ์–ธ์€ ์ž๋ฆฌยท์„ค๋น„์™€ ๋‹ฌ๋ฆฌ **๋ชจ๋ธ์— ํ–‰์ด ์—†์–ด๋„ ์„ฑ๋ฆฝํ•œ๋‹ค**(์ฐฝ๊ณ ยท์•ผ๋“œ์—๋Š” ํ–‰์ด ์•„์˜ˆ ์—†๋‹ค).
123
- * ๊ทธ๋ž˜์„œ ์˜คํƒ€๋ฅผ ์žก์•„ ์ค„ ๊ฒƒ์ด ์ด๋ฆ„ ๋Œ€์กฐ๋ฟ์ด๋‹ค โ€” ๊ทธ๋งˆ์ € ์—†์œผ๋ฉด ๊ฐ’์€ ์กฐ์šฉํžˆ ์•„๋ฌด ์ผ๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
123
+ * ๊ทธ๋ž˜์„œ ์˜คํƒ€๋ฅผ ์žก์•„ ์ค„ ๊ฒƒ์ด ์ด๋ฆ„ ๋Œ€์กฐ๋ฟ์ด๋‹ค โ€” ๊ทธ๋งˆ์ € ์—†์œผ๋ฉด ๊ฐ’์€ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์•„๋ฌด ์ผ๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
124
124
  */
125
125
  | 'unknown-operation'
126
126
  /** ๋ฌด์—‡์„ ๊ธฐ๋Œ€ํ–ˆ๋‚˜ โ€” ์‚ฌ๋žŒ์ด ๊ณ ์น  ์ˆ˜ ์žˆ๊ฒŒ(๋ช…์„ธ์˜ ๋ฌธ์žฅ). */
@@ -199,7 +199,7 @@ const COMMON_SPECS: DeclarablePropertySpec[] = [
199
199
  *
200
200
  * ํ˜ผ์žก 90%ยท๋ถˆ๋Ÿ‰ 15%๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ •ํ•œ ์ˆ˜์˜€๋‹ค. ํ†ต๋กœ๋ฅผ 95%๋กœ ์ฑ„์›Œ ์“ฐ๋Š” ์ฐฝ๊ณ ๋Š” ๊ทธ ๊ธฐ์ค€์—์„œ ์˜๊ตฌํžˆ ใ€Œ๋ณ‘๋ชฉใ€
201
201
  * ์ด๊ณ , ์ดˆ๊ธฐ ๋ผ์ธ์€ ๊ณ„์† ใ€Œ๋ถˆ๋Ÿ‰ ๊ณผ๋‹คใ€๋กœ ์šด๋‹ค โ€” ๊ทธ๋•Œ ์ง„์งœ ๊ฒฝ๋ณด๋„ ํ•จ๊ป˜ ๋ฌปํžŒ๋‹ค. ์„ ์–ธ์ด ์—†์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’์„
202
- * ์“ฐ๋˜ ์ฃผ์˜๊ฐ€ `basis: 'default'` ๋กœ ๊ทธ ์‚ฌ์‹ค์„ ๋ฐํžŒ๋‹ค(์กฐ์šฉํžˆ ๊ธฐ๋ณธ๊ฐ’์„ ์ง„์‹ค๋กœ ๋‘์ง€ ์•Š๋Š”๋‹ค).
202
+ * ์“ฐ๋˜ ์ฃผ์˜๊ฐ€ `basis: 'default'` ๋กœ ๊ทธ ์‚ฌ์‹ค์„ ๋ฐํžŒ๋‹ค(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๊ธฐ๋ณธ๊ฐ’์„ ์ง„์‹ค๋กœ ๋‘์ง€ ์•Š๋Š”๋‹ค).
203
203
  */
204
204
  {
205
205
  id: ATTENTION_PROPERTY.congestionRatio,
@@ -276,7 +276,7 @@ const COMMON_SPECS: DeclarablePropertySpec[] = [
276
276
  * ๊ณต์ • ๋ชจ์ˆ˜ ํ‘œ์— ํ•œ ์นธ ๋‹ด๋Š”๋‹ค โ€” **์ปค๋„์ด ์ฝ๋Š” ํ‘œ๊ธฐ ๊ทธ๋Œ€๋กœ**(์ˆ˜์œจ 0..1, ์…‹์—… ISO 8601).
277
277
  *
278
278
  * ํ‘œ๋ฅผ ํ†ต์งธ๋กœ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด ์–น๋Š”๋‹ค: ๊ฐ™์€ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ๊ฒน์ด ์ง€๋‚˜๋ฏ€๋กœ(ํ˜„์žฅ โ†’ ํŠธ์œˆ) ๊ทธ๋•Œ๋งˆ๋‹ค ์ƒˆ ๊ฐ์ฒด์—ฌ์•ผ
279
- * ์•ž ๊ฒน์˜ ๊ฒƒ์„ ์กฐ์šฉํžˆ ๋ฎ์–ด์“ฐ์ง€ ์•Š๋Š”๋‹ค.
279
+ * ์•ž ๊ฒน์˜ ๊ฒƒ์„ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฎ์–ด์“ฐ์ง€ ์•Š๋Š”๋‹ค.
280
280
  */
281
281
  function putParam(next: any, kind: string, paramId: string, value: string): void {
282
282
  const table = { ...(next.localParams ?? {}) }
@@ -321,7 +321,7 @@ export function declarablePropertySpecs(kind: string | undefined): DeclarablePro
321
321
  /**
322
322
  * ๋กœ์ปฌ ์„ ์–ธ์„ ๊ฒ€์‚ฌํ•œ๋‹ค โ€” **๋ฐ›์€ ๊ฒƒ๊ณผ ๋ฐ›์ง€ ์•Š์€ ๊ฒƒ์„ ํ•จ๊ป˜** ๋Œ๋ ค์ค€๋‹ค.
323
323
  *
324
- * ์กฐ์šฉํžˆ ๊ฑธ๋Ÿฌ๋‚ด์ง€ ์•Š๋Š” ์ด์œ : ์‚ฌ์šฉ์ž๋Š” ๊ฐ’์„ ๋„ฃ์—ˆ๋‹ค๊ณ  ๋ฏฟ๋Š”๋‹ค. ๋ฌด์—‡์ด ์™œ ๋น ์กŒ๋Š”์ง€ ๋งํ•ด์•ผ ๊ณ ์น  ์ˆ˜ ์žˆ๋‹ค.
324
+ * ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๊ฑธ๋Ÿฌ๋‚ด์ง€ ์•Š๋Š” ์ด์œ : ์‚ฌ์šฉ์ž๋Š” ๊ฐ’์„ ๋„ฃ์—ˆ๋‹ค๊ณ  ๋ฏฟ๋Š”๋‹ค. ๋ฌด์—‡์ด ์™œ ๋น ์กŒ๋Š”์ง€ ๋งํ•ด์•ผ ๊ณ ์น  ์ˆ˜ ์žˆ๋‹ค.
325
325
  */
326
326
  export function validateLocalDeclarations(
327
327
  kind: string | undefined,
@@ -342,7 +342,7 @@ export function validateLocalDeclarations(
342
342
  const target: DeclarationTarget = d?.target === 'equipment' ? 'equipment' : d?.target === 'operation' ? 'operation' : 'location'
343
343
  const id = String(d?.id ?? '').trim()
344
344
  if (!id) {
345
- /* ์–ด๋А ๋Œ€์ƒ์ธ์ง€ ๋ชจ๋ฅด๋ฉด ์–น์„ ์ž๋ฆฌ๊ฐ€ ์—†๋‹ค โ€” ๊ทธ ์‚ฌ์‹ค์„ ๋งํ•œ๋‹ค(์กฐ์šฉํžˆ ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค). */
345
+ /* ์–ด๋А ๋Œ€์ƒ์ธ์ง€ ๋ชจ๋ฅด๋ฉด ์–น์„ ์ž๋ฆฌ๊ฐ€ ์—†๋‹ค โ€” ๊ทธ ์‚ฌ์‹ค์„ ๋งํ•œ๋‹ค(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค). */
346
346
  rejected.push({ target, id: '', propertyId: '', reason: 'no-target-id' })
347
347
  continue
348
348
  }
@@ -449,7 +449,7 @@ export function mergeLocalDeclarations(
449
449
  * ๊ณ ์ณ ๋„ฃ์—ˆ๋‹ค๋Š” ๊ฒƒ์€ ใ€Œ์›์ฒœ์˜ ๊ฐ’์ด ๋‚ก์•˜๋‹คใ€๋Š” ๋œป์ด๋‹ค. ๋‹ค๋งŒ ๊ทธ ์‚ฌ์‹ค์ด ๋ณด์—ฌ์•ผ ํ•˜๋ฏ€๋กœ `source: 'local'` ๋ฅผ
450
450
  * ๊ฐ’์— ๋ถ™์ธ๋‹ค โ€” ํ™”๋ฉดยท๊ฐ์‚ฌ๊ฐ€ ใ€Œ๋ˆ„๊ฐ€ ๋งํ•œ ๊ฐ’์ธ๊ฐ€ใ€๋ฅผ ๋˜์งš์„ ์ˆ˜ ์žˆ๋‹ค.
451
451
  *
452
- * ๋ชจ๋ธ์— ์—†๋Š” ๋Œ€์ƒ์— ์–น์œผ๋ ค ํ•˜๋ฉด **์กฐ์šฉํžˆ ๋ฒ„๋ฆฌ์ง€ ์•Š๊ณ ** ๊ทธ ์‚ฌ์‹ค์„ ๋Œ๋ ค์ค€๋‹ค(๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ์–ด ๊ทธ ์„ค๋น„๊ฐ€
452
+ * ๋ชจ๋ธ์— ์—†๋Š” ๋Œ€์ƒ์— ์–น์œผ๋ ค ํ•˜๋ฉด **์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฌ์ง€ ์•Š๊ณ ** ๊ทธ ์‚ฌ์‹ค์„ ๋Œ๋ ค์ค€๋‹ค(๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ์–ด ๊ทธ ์„ค๋น„๊ฐ€
453
453
  * ์‚ฌ๋ผ์กŒ์„ ์ˆ˜ ์žˆ๊ณ , ๊ทธ๋•Œ ์‚ฌ์šฉ์ž์˜ ์„ ์–ธ์€ ๊ฐˆ ๊ณณ์ด ์—†๋‹ค).
454
454
  */
455
455
  export function applyLocalDeclarations<
@@ -461,7 +461,7 @@ export function applyLocalDeclarations<
461
461
  model: T
462
462
  missingTargets: { target: string; id: string }[]
463
463
  /**
464
- * ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์—ˆ๋˜ ์„ ์–ธ โ€” **์กฐ์šฉํžˆ ๋ฒ„๋ฆฌ์ง€ ์•Š๊ธฐ ์œ„ํ•œ ์ž๋ฆฌ.**
464
+ * ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์—ˆ๋˜ ์„ ์–ธ โ€” **์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฌ์ง€ ์•Š๊ธฐ ์œ„ํ•œ ์ž๋ฆฌ.**
465
465
  *
466
466
  * ์ง€๊ธˆ์€ ๊ณต์ • ์‹œ๊ฐ„์—์„œ๋งŒ ๋‚œ๋‹ค(๋‹จ์œ„๊ฐ€ ์—†๊ฑฐ๋‚˜ ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ฉด ISO 8601 ๊ธฐ๊ฐ„์„ ๋งŒ๋“ค ์ˆ˜ ์—†๋‹ค). ๋ฌธ์—์„œ ์ด๋ฏธ
467
467
  * ๊ฒ€์‚ฌํ•˜๋ฏ€๋กœ ์ •์ƒ ๊ฒฝ๋กœ์—์„œ๋Š” ๋น„์–ด ์žˆ๊ณ , ์˜› ๊ฒน์ด๋‚˜ ์†์œผ๋กœ ๋„ฃ์€ ๋ฐ์ดํ„ฐ์—์„œ ๋“œ๋Ÿฌ๋‚œ๋‹ค.
@@ -504,7 +504,7 @@ export function applyLocalDeclarations<
504
504
  *
505
505
  * ๊ฒน์—๋Š” ๋‹จ์œ„๋ฅผ ๋‹ด๊ณ ๋„ ๋ชจ๋ธ์— ์–น์„ ๋•Œ ๋–จ์–ด๋œจ๋ฆฌ๊ณ  ์žˆ์—ˆ๋‹ค โ€” ๊ทธ๋Ÿฌ๋ฉด ์†๋„๋ฅผ ๋„ฃ์–ด๋„ ์ด๋™ ์‹œ๊ฐ„ ์ถ”์ •๊ธฐ๊ฐ€
506
506
  * ๊ทธ ์„ค๋น„๋ฅผ **๋ฒ„๋ฆฐ๋‹ค**(ใ€Œspeed has no unit of measureใ€). ํ™”๋ฉด์€ ใ€Œ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹คใ€๋ผ๊ณ  ๋งํ•˜๊ณ  ๊ณ„์‚ฐ์€
507
- * ๊ทธ ๊ฐ’์„ ๋ชป ์“ฐ๋Š”, ๊ฐ€์žฅ ์กฐ์šฉํ•œ ์ข…๋ฅ˜์˜ ๊ฒฐํ•จ์ด๋‹ค.
507
+ * ๊ทธ ๊ฐ’์„ ๋ชป ์“ฐ๋Š”, ๊ฐ€์žฅ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋Š” ์ข…๋ฅ˜์˜ ๊ฒฐํ•จ์ด๋‹ค.
508
508
  */
509
509
  const next = { id: p.id, value: String(p.value), ...((p as any).uom ? { uom: String((p as any).uom) } : {}), source: 'local' as const }
510
510
  if (at >= 0) props[at] = { ...props[at], ...next }
@@ -531,7 +531,7 @@ export function applyLocalDeclarations<
531
531
  const uom = String((p as any).uom ?? '')
532
532
  /*
533
533
  * ๊ฐ’์„ **์ปค๋„์ด ์ฝ๋Š” ํ‘œ๊ธฐ๋กœ** ๋ฐ”๊ฟ” ๋‹ด๋Š”๋‹ค โ€” ์‹œ๊ฐ„์€ ISO 8601, ์ˆ˜์œจ์€ ๋น„์œจ(0..1).
534
- * ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์œผ๋ฉด ๋‹ด์ง€ ์•Š๊ณ  ๊ทธ ์‚ฌ์‹ค์„ ๋‚ธ๋‹ค(์กฐ์šฉํžˆ 0 ์ด๋‚˜ ๋นˆ ๊ฐ’์œผ๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค).
534
+ * ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์œผ๋ฉด ๋‹ด์ง€ ์•Š๊ณ  ๊ทธ ์‚ฌ์‹ค์„ ๋‚ธ๋‹ค(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  0 ์ด๋‚˜ ๋นˆ ๊ฐ’์œผ๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค).
535
535
  */
536
536
  if (p.id === OPERATION_PROPERTY.duration) {
537
537
  const iso = isoDurationOf(p.value, uom)
@@ -596,7 +596,7 @@ export interface RevokedDeclaration extends DeclarationRevocation {
596
596
  /**
597
597
  * ๊ฒน์—์„œ ์„ ์–ธ์„ **๋บ€๋‹ค** โ€” ๊ฐ’์„ ๋„ฃ๋Š” ๊ฒƒ๊ณผ ๋‹ค๋ฅธ ๊ฒฐ์ •์ด๋ฏ€๋กœ ๋‹ค๋ฅธ ๋ฌธ์ด๋‹ค.
598
598
  *
599
- * ์—†๋Š” ๊ฒƒ์„ ๋บ๋‹ค๊ณ  ๋งํ•˜์ง€ ์•Š๋Š”๋‹ค: ๊ฒน์— ์—†๋˜ ๋Œ€์ƒยท์†์„ฑ์€ `notFound` ๋กœ ๋Œ๋ ค์ค€๋‹ค(์กฐ์šฉํžˆ ์„ฑ๊ณตํ•˜๋ฉด
599
+ * ์—†๋Š” ๊ฒƒ์„ ๋บ๋‹ค๊ณ  ๋งํ•˜์ง€ ์•Š๋Š”๋‹ค: ๊ฒน์— ์—†๋˜ ๋Œ€์ƒยท์†์„ฑ์€ `notFound` ๋กœ ๋Œ๋ ค์ค€๋‹ค(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์„ฑ๊ณตํ•˜๋ฉด
600
600
  * ์‚ฌ์šฉ์ž๋Š” ์ง€์› ๋‹ค๊ณ  ๋ฏฟ๊ณ  ๊ฐ’์€ ๊ทธ๋Œ€๋กœ ๋‚จ๋Š”๋‹ค โ€” ๊ฐ€์žฅ ๋น„์‹ผ ์ข…๋ฅ˜์˜ ์นจ๋ฌต์ด๋‹ค).
601
601
  *
602
602
  * ์†์„ฑ์ด ํ•˜๋‚˜๋„ ๋‚จ์ง€ ์•Š์€ ๋Œ€์ƒ์€ ๊ฒน์—์„œ ์‚ฌ๋ผ์ง„๋‹ค(๋นˆ ๊ป๋ฐ๊ธฐ๋ฅผ ๋‚จ๊ธฐ์ง€ ์•Š๋Š”๋‹ค).
@@ -750,7 +750,7 @@ export function applyDeclarationLayers<
750
750
  instanceLayerApplied: LocalDeclaration[]
751
751
  /** ๊ฐˆ ๊ณณ์ด ์—†๋˜ ๋Œ€์ƒ โ€” ์–ด๋А ๋ฒ”์œ„์—์„œ ๋‚ฌ๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค(ํ˜„์žฅ ๊ฒน์€ ๊ทธ ํŠธ์œˆ์— ์—†์„ ์ˆ˜ ์žˆ๋‹ค). */
752
752
  missingTargets: { scope: DeclarationScope; target: string; id: string }[]
753
- /** ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋˜ ์„ ์–ธ โ€” ๋ฒ”์œ„์™€ ํ•จ๊ป˜(์กฐ์šฉํžˆ ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค). */
753
+ /** ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋˜ ์„ ์–ธ โ€” ๋ฒ”์œ„์™€ ํ•จ๊ป˜(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค). */
754
754
  unreadable: { scope: DeclarationScope; target: string; id: string; propertyId: string }[]
755
755
  } {
756
756
  const first = applyLocalDeclarations(model, spaceLayer)
@@ -46,7 +46,7 @@ export function stamp(at: Date = new Date()): string {
46
46
  * ์ธ์ž ์—ฌ๋Ÿฟ์„ ํ•œ ์ค„๋กœ โ€” **์–ด๋–ค ์ธ์ž๋„ ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.**
47
47
  *
48
48
  * `console.error(msg, err)` ์ฒ˜๋Ÿผ ์“ฐ๋˜ ์ž๋ฆฌ๊ฐ€ ๋งŽ๋‹ค. ํ”„๋ ˆ์ž„์›Œํฌ ๋กœ๊ฑฐ์— ๊ทธ๋Œ€๋กœ ๋„˜๊ธฐ๋ฉด ๋‘ ๋ฒˆ์งธ ์ธ์ž๋Š”
49
- * meta ๋กœ ๋“ค์–ด๊ฐ€ ์šฐ๋ฆฌ ํ˜•์‹(`message` ๋งŒ ์ฐ๋Š”๋‹ค)์—์„œ **์กฐ์šฉํžˆ ์‚ฌ๋ผ์ง„๋‹ค.** ๊ทธ๋ž˜์„œ ์—ฌ๊ธฐ์„œ ๋ฌธ์ž์—ด๋กœ ํŽธ๋‹ค.
49
+ * meta ๋กœ ๋“ค์–ด๊ฐ€ ์šฐ๋ฆฌ ํ˜•์‹(`message` ๋งŒ ์ฐ๋Š”๋‹ค)์—์„œ **์˜ค๋ฅ˜ ์—†์ด ์‚ฌ๋ผ์ง„๋‹ค.** ๊ทธ๋ž˜์„œ ์—ฌ๊ธฐ์„œ ๋ฌธ์ž์—ด๋กœ ํŽธ๋‹ค.
50
50
  * ์˜ค๋ฅ˜๋Š” ์Šคํƒ๊นŒ์ง€ โ€” ์Šคํƒ์ด ์—†์œผ๋ฉด ใ€Œ์–ด๋””์„œ ๋‚ฌ๋‚˜ใ€๋ฅผ ์žƒ๋Š”๋‹ค.
51
51
  */
52
52
  export function line(args: unknown[]): string {
@@ -18,7 +18,7 @@
18
18
  * - ํ‘œ๋ณธ์ด ์ ์œผ๋ฉด ์“ฐ์ง€ ์•Š๋Š”๋‹ค(`minSamples`). ํ•œ๋‘ ๊ฑด์˜ p50 ์€ ๊ทธ ํ˜„์žฅ์˜ ๊ฐ’์ด ์•„๋‹ˆ๋‹ค.
19
19
  * - 'unknown' ์ถ•์€ ์“ฐ์ง€ ์•Š๋Š”๋‹ค โ€” ์ข…๋ฅ˜๋ฅผ ๋ชจ๋ฅด๋Š” ๊ธฐ๋ก์„ ํŠน์ • ์ข…๋ฅ˜์˜ ๊ทผ๊ฑฐ๋กœ ์‚ผ์„ ์ˆ˜ ์—†๋‹ค.
20
20
  * - ํผ์ง์ด ์„ฑ๋ฆฝํ•˜์ง€ ์•Š์œผ๋ฉด(p10 โ‰ฅ p90, ๋˜๋Š” 0) **ํผ์ง ์—†์ด ํ‰๊ท ๋งŒ** ๋„˜๊ธด๋‹ค. ์—†๋Š” ํ”๋“ค๋ฆผ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.
21
- * - ๋ฌด์—‡์„ ๋ฐฐ์› ๊ณ  ๋ฌด์—‡์„ ํ‘œ๋ณธ ๋ถ€์กฑ์œผ๋กœ ๋ฒ„๋ ธ๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค(์กฐ์šฉํžˆ ์ผ๋ถ€๋งŒ ๋ฐ˜์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค).
21
+ * - ๋ฌด์—‡์„ ๋ฐฐ์› ๊ณ  ๋ฌด์—‡์„ ํ‘œ๋ณธ ๋ถ€์กฑ์œผ๋กœ ๋ฒ„๋ ธ๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ผ๋ถ€๋งŒ ๋ฐ˜์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค).
22
22
  */
23
23
 
24
24
  /** ์ปค๋„ `DurationEstimate` ์™€ ๊ฐ™์€ ๋ชจ์–‘(์ปค๋„ ํƒ€์ž…์— ์˜์กดํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๊ตฌ์กฐ๋กœ ๋งž์ถ˜๋‹ค). */
@@ -18,7 +18,7 @@
18
18
  * ์—ฌ๊ธฐ์„œ๋„ `good + scrap` ๋งŒ ์“ด๋‹ค โ€” `tasks` ๋ฅผ ๋ถ„๋ชจ๋กœ ์“ฐ๋ฉด ์ด๋™ยท์ฒด๋ฅ˜๊ฐ€ ์„ž์—ฌ ์–‘ํ’ˆ๋ฅ ์ด ๋‚ฎ๊ฒŒ ๋‚˜์˜จ๋‹ค.
19
19
  * - **0 ๋„ ์‚ฌ์‹ค์ด๋‹ค.** ์ „๋ถ€ ๋ถˆ๋Ÿ‰์ด๋ฉด 0 ์„ ๋ฐฐ์šด๋‹ค(๊ทธ ํ˜„์žฅ์ด ๊ทธ๋žฌ๋‹ค). ใ€Œ๊ฐ’์ด ์—†๋‹คใ€์™€ ๊ตฌ๋ณ„ํ•ด ๋‹ค๋ฃฌ๋‹ค โ€”
20
20
  * ์ปค๋„์ด `0` ์„ ๋ฐ›์œผ๋ฉด ์ „๋Ÿ‰ ๋ถˆ๋Ÿ‰์œผ๋กœ ๊ตด๋Ÿฌ๊ฐ€๊ณ , ๊ทธ๊ฒƒ์ด ๊ด€์ธก๊ณผ ๊ฐ™์€ ๊ฒฐ๋ก ์ด๋‹ค.
21
- * - ๋ฌด์—‡์„ ๋ฐฐ์› ๊ณ  ๋ฌด์—‡์„ ํ‘œ๋ณธ ๋ถ€์กฑ์œผ๋กœ ๋ฒ„๋ ธ๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค(์กฐ์šฉํžˆ ์ผ๋ถ€๋งŒ ๋ฐ˜์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค).
21
+ * - ๋ฌด์—‡์„ ๋ฐฐ์› ๊ณ  ๋ฌด์—‡์„ ํ‘œ๋ณธ ๋ถ€์กฑ์œผ๋กœ ๋ฒ„๋ ธ๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ผ๋ถ€๋งŒ ๋ฐ˜์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค).
22
22
  */
23
23
 
24
24
  /** ์ปค๋„ `YieldEstimator` ์™€ ๊ฐ™์€ ๋ชจ์–‘(์ปค๋„ ํƒ€์ž…์— ์˜์กดํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๊ตฌ์กฐ๋กœ ๋งž์ถ˜๋‹ค). */
@@ -7,7 +7,7 @@
7
7
  * ์ฃผ๋ฉด **์ด์ค‘ ๋ณด์ •**์ด ๋˜์–ด ์ •ํ™•๋„๊ฐ€ ๋‚˜๋น ์ง„๋‹ค. ํ•™์Šต ์‹œ์ ์˜ accAfter ๋„ ๋”๋Š” ์œ ํšจํ•˜์ง€ ์•Š๋‹ค.
8
8
  *
9
9
  * ๊ทธ๋ž˜์„œ ๋ณด์ • ๊ธฐ๋ก์— **๊ทธ๋•Œ ๋ชจ๋ธ์˜ ๊ทผ๊ฑฐ**๋ฅผ ํ•จ๊ป˜ ๋‚จ๊ธฐ๊ณ , ์ ์šฉ ์ „์— ์ง€๊ธˆ ๊ทผ๊ฑฐ์™€ ๋น„๊ตํ•œ๋‹ค. ๋‹ฌ๋ผ์กŒ์œผ๋ฉด
10
- * ์ ์šฉํ•˜์ง€ ์•Š๊ณ  **๊ทธ ์‚ฌ์‹ค์„ ์‘๋‹ต์— ์‹ฃ๋Š”๋‹ค**(์กฐ์šฉํžˆ ์‚ฌ๋ผ์ง€๋ฉด ์‚ฌ์šฉ์ž๋Š” ํ•™์Šต์ด ์™œ ์•ˆ ๋“ฃ๋Š”์ง€ ๋ชจ๋ฅธ๋‹ค).
10
+ * ์ ์šฉํ•˜์ง€ ์•Š๊ณ  **๊ทธ ์‚ฌ์‹ค์„ ์‘๋‹ต์— ์‹ฃ๋Š”๋‹ค**(์˜ค๋ฅ˜ ์—†์ด ์‚ฌ๋ผ์ง€๋ฉด ์‚ฌ์šฉ์ž๋Š” ํ•™์Šต์ด ์™œ ์•ˆ ๋“ฃ๋Š”์ง€ ๋ชจ๋ฅธ๋‹ค).
11
11
  *
12
12
  * ๋น„๊ต๋Š” **์ƒ๋Œ€ ํ—ˆ์šฉ์˜ค์ฐจ**๋กœ ํ•œ๋‹ค(์–‘์žํ™” ์ง€๋ฌธ์ด ์•„๋‹ˆ๋ผ). ์‹ค์ธก p50 ์€ ์ƒˆ ๊ธฐ๋ก์ด ์Œ“์ผ ๋•Œ๋งˆ๋‹ค 1~2์ดˆ์”ฉ
13
13
  * ํ”๋“ค๋ฆฌ๋Š”๋ฐ, ๊ฐ’์„ ๋ฒ„ํ‚ท์œผ๋กœ ํ•ฉ์ณ ๋น„๊ตํ•˜๋ฉด ๋ฒ„ํ‚ท ๊ฒฝ๊ณ„์— ๊ฑธ๋ฆฐ ๊ฐ’์ด 1์ดˆ ๋“œ๋ฆฌํ”„ํŠธ๋กœ๋„ ๋‹ฌ๋ผ์ ธ ๋ฒ„๋ฆฐ๋‹ค โ€”
@@ -40,7 +40,7 @@ export interface ModelBasis {
40
40
  * ์„ ์–ธ๋œ ๊ณต์ • โ€” key + ISO 8601 ์›๋ฌธ(๋ฌธ์ž์—ด์€ ๊ทธ๋Œ€๋กœ ๋น„๊ตํ•œ๋‹ค: ์„ ์–ธ์ด ๋ฐ”๋€Œ๋ฉด ๋‹ค๋ฅธ ๋ชจ๋ธ).
41
41
  *
42
42
  * **๋ณ€๋™ํญ๋„ ์ง€๋ฌธ์— ๋“ ๋‹ค.** ๋ณ€๋™ํญ์ด ํ—ˆ์šฉ์˜ค์ฐจ๋ฅผ ์ •ํ•˜๋ฏ€๋กœ(ยงtoleranceOf), ๊ทธ๊ฒƒ์ด ๋ฐ”๋€Œ์—ˆ๋Š”๋ฐ "๊ฐ™์€ ๋ชจ๋ธ"
43
- * ์ด๋ผ๊ณ  ํ•˜๋ฉด ํŒ์ •์˜ ์žฃ๋Œ€๊ฐ€ ์กฐ์šฉํžˆ ๋ฐ”๋€ ์ฑ„๋กœ ํ•™์Šต์ด ์ด์–ด์ง„๋‹ค.
43
+ * ์ด๋ผ๊ณ  ํ•˜๋ฉด ํŒ์ •์˜ ์žฃ๋Œ€๊ฐ€ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฐ”๋€ ์ฑ„๋กœ ํ•™์Šต์ด ์ด์–ด์ง„๋‹ค.
44
44
  */
45
45
  declared?: DeclaredOperation[]
46
46
  }