@fightlightdiamond/scada-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @your-scope/scada-core
2
+
3
+ Engine-agnostic SCADA core utilities extracted from the studio codebase.
4
+
5
+ ## Includes
6
+
7
+ - Simulation helpers:
8
+ - `computeSimulationPatches()`
9
+ - `applyPatchesToNodeDataMap()`
10
+ - `documentToSimulationInputs()`
11
+ - `resolveMimicTagBinding()`
12
+ - Canonical mimic payload normalization:
13
+ - `normalizeDiagramNodePayload()`
14
+ - `transformMimicNodePayload()`
15
+ - Shared core types (`MimicNodeDataContract`, `LiveTagData`, `GraphDocument`, ...)
16
+
17
+ ## Local development
18
+
19
+ ```bash
20
+ npm run core:build
21
+ npm run core:typecheck
22
+ ```
23
+
24
+ ## Publish flow
25
+
26
+ ```bash
27
+ cd packages/scada-core
28
+ npm publish --access public
29
+ ```
30
+
@@ -0,0 +1,19 @@
1
+ export declare const MIMIC_GLYPH_TYPES: readonly ["pump", "valve", "tank", "meter", "motor", "compressor", "fan", "heatExchanger", "conveyor", "sensor", "agitator", "controller"];
2
+ export type MimicGlyphType = (typeof MIMIC_GLYPH_TYPES)[number];
3
+ export declare const GLYPH_SCADA_TYPE: {
4
+ readonly pump: "pump";
5
+ readonly valve: "valve";
6
+ readonly tank: "tank";
7
+ readonly meter: "sensor";
8
+ readonly motor: "motor";
9
+ readonly compressor: "compressor";
10
+ readonly fan: "fan";
11
+ readonly heatExchanger: "heatExchanger";
12
+ readonly conveyor: "conveyor";
13
+ readonly sensor: "sensor";
14
+ readonly agitator: "agitator";
15
+ readonly controller: "controller";
16
+ };
17
+ export declare function isMimicGlyphType(value: string): value is MimicGlyphType;
18
+ export declare function coalesceMimicGlyphType(value: string): MimicGlyphType | undefined;
19
+ //# sourceMappingURL=glyphTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyphTypes.d.ts","sourceRoot":"","sources":["../src/glyphTypes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,4IAapB,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;CAasB,CAAA;AAEnD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,cAAc,CAEvE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAKhF"}
@@ -0,0 +1,39 @@
1
+ export const MIMIC_GLYPH_TYPES = [
2
+ 'pump',
3
+ 'valve',
4
+ 'tank',
5
+ 'meter',
6
+ 'motor',
7
+ 'compressor',
8
+ 'fan',
9
+ 'heatExchanger',
10
+ 'conveyor',
11
+ 'sensor',
12
+ 'agitator',
13
+ 'controller',
14
+ ];
15
+ export const GLYPH_SCADA_TYPE = {
16
+ pump: 'pump',
17
+ valve: 'valve',
18
+ tank: 'tank',
19
+ meter: 'sensor',
20
+ motor: 'motor',
21
+ compressor: 'compressor',
22
+ fan: 'fan',
23
+ heatExchanger: 'heatExchanger',
24
+ conveyor: 'conveyor',
25
+ sensor: 'sensor',
26
+ agitator: 'agitator',
27
+ controller: 'controller',
28
+ };
29
+ export function isMimicGlyphType(value) {
30
+ return MIMIC_GLYPH_TYPES.includes(value);
31
+ }
32
+ export function coalesceMimicGlyphType(value) {
33
+ if (!value)
34
+ return undefined;
35
+ if (isMimicGlyphType(value))
36
+ return value;
37
+ const lower = value.toLowerCase();
38
+ return MIMIC_GLYPH_TYPES.find((kind) => kind.toLowerCase() === lower);
39
+ }
@@ -0,0 +1,7 @@
1
+ export * from './types.js';
2
+ export * from './glyphTypes.js';
3
+ export * from './nodeVocabulary.js';
4
+ export * from './normalizeDiagramNodePayload.js';
5
+ export * from './simulation.js';
6
+ export * from './useScadaSimulation.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,qBAAqB,CAAA;AACnC,cAAc,kCAAkC,CAAA;AAChD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './types.js';
2
+ export * from './glyphTypes.js';
3
+ export * from './nodeVocabulary.js';
4
+ export * from './normalizeDiagramNodePayload.js';
5
+ export * from './simulation.js';
6
+ export * from './useScadaSimulation.js';
@@ -0,0 +1,7 @@
1
+ import type { MimicGlyphType } from './glyphTypes.js';
2
+ export type EquipmentKind = MimicGlyphType;
3
+ export declare const NODE_VIEW_MODES: readonly ["static", "display", "numeric", "equipment"];
4
+ export type NodeViewMode = (typeof NODE_VIEW_MODES)[number];
5
+ export declare const HMI_LEGACY_SCADA_TYPES: Set<string>;
6
+ export declare function isLegacyEquipmentScadaType(value: string): boolean;
7
+ //# sourceMappingURL=nodeVocabulary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeVocabulary.d.ts","sourceRoot":"","sources":["../src/nodeVocabulary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,MAAM,aAAa,GAAG,cAAc,CAAA;AAE1C,eAAO,MAAM,eAAe,wDAAyD,CAAA;AACrF,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3D,eAAO,MAAM,sBAAsB,aAAyD,CAAA;AAE5F,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIjE"}
@@ -0,0 +1,8 @@
1
+ export const NODE_VIEW_MODES = ['static', 'display', 'numeric', 'equipment'];
2
+ export const HMI_LEGACY_SCADA_TYPES = new Set(['static', 'display', 'indicator', 'numeric']);
3
+ export function isLegacyEquipmentScadaType(value) {
4
+ const lower = value.toLowerCase();
5
+ if (HMI_LEGACY_SCADA_TYPES.has(lower))
6
+ return false;
7
+ return lower.length > 0;
8
+ }
@@ -0,0 +1,24 @@
1
+ import { type EquipmentKind, type NodeViewMode } from './nodeVocabulary.js';
2
+ import type { MimicNodeDataContract } from './types.js';
3
+ export interface DiagramNodePayloadInput {
4
+ equipmentKind?: string;
5
+ viewMode?: string;
6
+ scadaType?: string;
7
+ mimicType?: string;
8
+ [key: string]: unknown;
9
+ }
10
+ export interface NormalizeDiagramNodeOptions {
11
+ writeLegacyKeys?: boolean;
12
+ legacyInference?: boolean;
13
+ }
14
+ export interface NormalizedDiagramNodePayload extends MimicNodeDataContract {
15
+ equipmentKind?: EquipmentKind;
16
+ viewMode: NodeViewMode;
17
+ scadaType?: string;
18
+ }
19
+ export declare function legacyBindingRoleFromNormalized(viewMode: NodeViewMode, equipmentKind?: EquipmentKind): string;
20
+ export declare function normalizeDiagramNodePayload(input?: DiagramNodePayloadInput, titleFallback?: string, options?: NormalizeDiagramNodeOptions): NormalizedDiagramNodePayload;
21
+ export declare const transformMimicNodePayload: typeof normalizeDiagramNodePayload;
22
+ export declare function resolveEquipmentKindFromPayload(data: DiagramNodePayloadInput | undefined): EquipmentKind | undefined;
23
+ export declare function resolveViewModeFromPayload(data: DiagramNodePayloadInput | undefined): NodeViewMode;
24
+ //# sourceMappingURL=normalizeDiagramNodePayload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeDiagramNodePayload.d.ts","sourceRoot":"","sources":["../src/normalizeDiagramNodePayload.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,4BAA6B,SAAQ,qBAAqB;IACzE,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,QAAQ,EAAE,YAAY,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAkFD,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,YAAY,EACtB,aAAa,CAAC,EAAE,aAAa,GAC5B,MAAM,CAKR;AAyCD,wBAAgB,2BAA2B,CACzC,KAAK,GAAE,uBAA4B,EACnC,aAAa,SAAoB,EACjC,OAAO,GAAE,2BAAgC,GACxC,4BAA4B,CAW9B;AAED,eAAO,MAAM,yBAAyB,oCAA8B,CAAA;AAEpE,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,uBAAuB,GAAG,SAAS,GACxC,aAAa,GAAG,SAAS,CAG3B;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,GAAG,YAAY,CAGlG"}
@@ -0,0 +1,112 @@
1
+ import { GLYPH_SCADA_TYPE, coalesceMimicGlyphType } from './glyphTypes.js';
2
+ import { isLegacyEquipmentScadaType } from './nodeVocabulary.js';
3
+ const TITLE_EQUIPMENT_KEYWORDS = [
4
+ { keywords: ['valve'], value: 'valve' },
5
+ { keywords: ['pump'], value: 'pump' },
6
+ { keywords: ['compressor'], value: 'compressor' },
7
+ { keywords: ['motor', 'drive'], value: 'motor' },
8
+ { keywords: ['fan', 'blower'], value: 'fan' },
9
+ { keywords: ['tank', 'boiler', 'vessel', 'reservoir'], value: 'tank' },
10
+ { keywords: ['sensor', 'temp'], value: 'sensor' },
11
+ ];
12
+ const HMI_ONLY_MODES = new Set(['display', 'static', 'indicator']);
13
+ const VALID_VIEW_MODES = new Set(['static', 'display', 'numeric', 'equipment']);
14
+ const LEGACY_VIEW_MODE_MAP = {
15
+ static: 'static',
16
+ display: 'display',
17
+ indicator: 'display',
18
+ numeric: 'numeric',
19
+ dynamic: 'equipment',
20
+ };
21
+ const VIEW_MODE_BINDING_ROLE_MAP = {
22
+ static: 'static',
23
+ display: 'display',
24
+ numeric: 'numeric',
25
+ };
26
+ function inferEquipmentKindFromTitle(raw) {
27
+ const title = String(raw.title ?? '').toLowerCase();
28
+ const match = TITLE_EQUIPMENT_KEYWORDS.find(({ keywords }) => keywords.some((keyword) => title.includes(keyword)));
29
+ return match ? match.value : 'tank';
30
+ }
31
+ function resolveEquipmentKindFromLegacyRole(raw, legacyRole) {
32
+ const fromLegacyRole = coalesceMimicGlyphType(legacyRole);
33
+ if (fromLegacyRole)
34
+ return fromLegacyRole;
35
+ if (legacyRole === 'dynamic')
36
+ return inferEquipmentKindFromTitle(raw);
37
+ return undefined;
38
+ }
39
+ function resolveEquipmentKind(raw, options = {}) {
40
+ const canonical = coalesceMimicGlyphType(String(raw.equipmentKind ?? ''));
41
+ if (canonical)
42
+ return canonical;
43
+ if (options.legacyInference === false)
44
+ return undefined;
45
+ const legacyRole = String(raw.scadaType ?? raw.mimicType ?? '').toLowerCase();
46
+ const explicitView = String(raw.viewMode ?? '').toLowerCase();
47
+ if (HMI_ONLY_MODES.has(explicitView) || HMI_ONLY_MODES.has(legacyRole))
48
+ return undefined;
49
+ return resolveEquipmentKindFromLegacyRole(raw, legacyRole);
50
+ }
51
+ function resolveViewMode(raw, equipmentKind, options = {}) {
52
+ const explicit = String(raw.viewMode ?? '').toLowerCase();
53
+ if (VALID_VIEW_MODES.has(explicit)) {
54
+ return explicit;
55
+ }
56
+ if (options.legacyInference === false)
57
+ return 'numeric';
58
+ const legacy = String(raw.scadaType ?? raw.mimicType ?? '').toLowerCase();
59
+ const fromLegacy = LEGACY_VIEW_MODE_MAP[legacy];
60
+ if (fromLegacy !== undefined)
61
+ return fromLegacy;
62
+ if (equipmentKind || isLegacyEquipmentScadaType(legacy))
63
+ return 'equipment';
64
+ return 'numeric';
65
+ }
66
+ export function legacyBindingRoleFromNormalized(viewMode, equipmentKind) {
67
+ if (viewMode === 'equipment') {
68
+ return equipmentKind ? GLYPH_SCADA_TYPE[equipmentKind] : 'numeric';
69
+ }
70
+ return VIEW_MODE_BINDING_ROLE_MAP[viewMode] ?? 'numeric';
71
+ }
72
+ function stripLegacyInputKeys(input) {
73
+ const { equipmentKind: _ek, viewMode: _vm, scadaType: _st, mimicType: _mt, glyphType: _legacyGlyph, scadaGlyph: _legacyScadaGlyph, ...passthrough } = input;
74
+ return passthrough;
75
+ }
76
+ function assembleNormalizedPayload(input, titleFallback, resolved) {
77
+ const passthrough = stripLegacyInputKeys(input);
78
+ const payload = {
79
+ ...passthrough,
80
+ title: String(passthrough.title ?? titleFallback),
81
+ viewMode: resolved.viewMode,
82
+ };
83
+ if (resolved.equipmentKind)
84
+ payload.equipmentKind = resolved.equipmentKind;
85
+ if (resolved.writeLegacyKeys)
86
+ payload.scadaType = resolved.bindingRole;
87
+ else
88
+ delete payload.scadaType;
89
+ return payload;
90
+ }
91
+ export function normalizeDiagramNodePayload(input = {}, titleFallback = 'SCADA Component', options = {}) {
92
+ const equipmentKind = resolveEquipmentKind(input, options);
93
+ const viewMode = resolveViewMode(input, equipmentKind, options);
94
+ const bindingRole = legacyBindingRoleFromNormalized(viewMode, equipmentKind);
95
+ return assembleNormalizedPayload(input, titleFallback, {
96
+ equipmentKind,
97
+ viewMode,
98
+ bindingRole,
99
+ writeLegacyKeys: options.writeLegacyKeys === true,
100
+ });
101
+ }
102
+ export const transformMimicNodePayload = normalizeDiagramNodePayload;
103
+ export function resolveEquipmentKindFromPayload(data) {
104
+ if (!data)
105
+ return undefined;
106
+ return resolveEquipmentKind(data);
107
+ }
108
+ export function resolveViewModeFromPayload(data) {
109
+ if (!data)
110
+ return 'numeric';
111
+ return resolveViewMode(data, resolveEquipmentKind(data));
112
+ }
@@ -0,0 +1,5 @@
1
+ import type { GraphDocument, MimicNodeDataContract, SimulationNodeInput } from './types.js';
2
+ export declare function resolveMimicTagBinding(data: Record<string, unknown> | MimicNodeDataContract | undefined): string;
3
+ export declare function mimicDataForSimulation(data: Record<string, unknown> | MimicNodeDataContract | undefined): MimicNodeDataContract | null;
4
+ export declare function documentToSimulationInputs(doc: GraphDocument): SimulationNodeInput[];
5
+ //# sourceMappingURL=simulation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulation.d.ts","sourceRoot":"","sources":["../src/simulation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAE3F,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,qBAAqB,GAAG,SAAS,GAChE,MAAM,CAIR;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,qBAAqB,GAAG,SAAS,GAChE,qBAAqB,GAAG,IAAI,CAc9B;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,aAAa,GAAG,mBAAmB,EAAE,CASpF"}
@@ -0,0 +1,34 @@
1
+ export function resolveMimicTagBinding(data) {
2
+ if (!data)
3
+ return '';
4
+ const telemetry = data.telemetry;
5
+ return String(data.tagBinding ?? telemetry?.tag ?? data.tag ?? '').trim();
6
+ }
7
+ export function mimicDataForSimulation(data) {
8
+ if (!data)
9
+ return null;
10
+ const tagBinding = resolveMimicTagBinding(data);
11
+ if (!tagBinding)
12
+ return null;
13
+ const telemetry = data.telemetry;
14
+ return {
15
+ ...data,
16
+ tagBinding,
17
+ value: Number(data.value ?? telemetry?.value ?? 50),
18
+ min: Number(data.min ?? telemetry?.min ?? 0),
19
+ max: Number(data.max ?? telemetry?.max ?? 100),
20
+ status: String(data.status ?? telemetry?.status ?? 'RUN'),
21
+ };
22
+ }
23
+ export function documentToSimulationInputs(doc) {
24
+ const inputs = [];
25
+ for (const node of doc.nodes) {
26
+ if (!node.id)
27
+ continue;
28
+ const contract = mimicDataForSimulation(node.data);
29
+ if (!contract)
30
+ continue;
31
+ inputs.push({ id: node.id, data: contract });
32
+ }
33
+ return inputs;
34
+ }
@@ -0,0 +1,49 @@
1
+ import type { MimicGlyphType } from './glyphTypes.js';
2
+ export interface LiveTagData {
3
+ name: string;
4
+ value?: number;
5
+ status?: string;
6
+ [key: string]: unknown;
7
+ }
8
+ export interface TagApiResponse {
9
+ tags: LiveTagData[];
10
+ }
11
+ export interface MimicNodeDataContract {
12
+ title?: string;
13
+ equipmentKind?: MimicGlyphType;
14
+ viewMode?: 'static' | 'display' | 'numeric' | 'equipment';
15
+ scadaType?: string;
16
+ mimicType?: string;
17
+ value?: number | string;
18
+ min?: number | string;
19
+ max?: number | string;
20
+ unit?: string;
21
+ precision?: number | string;
22
+ status?: 'RUN' | 'STOP' | 'ALARM' | string;
23
+ tagBinding?: string;
24
+ [key: string]: unknown;
25
+ }
26
+ export interface SimulationNodeInput {
27
+ id: string;
28
+ data: MimicNodeDataContract;
29
+ }
30
+ export interface SimulationDataPatch {
31
+ value: number;
32
+ status: string;
33
+ }
34
+ export interface GraphNodeData {
35
+ id?: string;
36
+ shape: string;
37
+ data?: Record<string, unknown>;
38
+ }
39
+ export interface GraphEdgeData {
40
+ id?: string;
41
+ source: string;
42
+ target: string;
43
+ data?: Record<string, unknown>;
44
+ }
45
+ export interface GraphDocument {
46
+ nodes: GraphNodeData[];
47
+ edges: GraphEdgeData[];
48
+ }
49
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,cAAc,CAAA;IAC9B,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAA;IACzD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,qBAAqB,CAAA;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,aAAa,EAAE,CAAA;IACtB,KAAK,EAAE,aAAa,EAAE,CAAA;CACvB"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { LiveTagData, MimicNodeDataContract, SimulationDataPatch, SimulationNodeInput, TagApiResponse } from './types.js';
2
+ export declare function computeSimulationPatches(nodes: SimulationNodeInput[], tags: LiveTagData[] | TagApiResponse): Record<string, SimulationDataPatch>;
3
+ export declare function applyPatchesToNodeDataMap<T extends MimicNodeDataContract>(nodeDataById: Record<string, T>, patches: Record<string, SimulationDataPatch>): string[];
4
+ //# sourceMappingURL=useScadaSimulation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScadaSimulation.d.ts","sourceRoot":"","sources":["../src/useScadaSimulation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAA;AAGnB,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,mBAAmB,EAAE,EAC5B,IAAI,EAAE,WAAW,EAAE,GAAG,cAAc,GACnC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CA0BrC;AAED,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,qBAAqB,EACvE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC3C,MAAM,EAAE,CAaV"}
@@ -0,0 +1,38 @@
1
+ import { resolveMimicTagBinding } from './simulation.js';
2
+ export function computeSimulationPatches(nodes, tags) {
3
+ const tagList = Array.isArray(tags) ? tags : tags.tags;
4
+ if (!Array.isArray(tagList)) {
5
+ console.warn('[SCADA Simulation] Tags is not a valid array:', tags);
6
+ return {};
7
+ }
8
+ const tagMap = new Map(tagList.map((t) => [t.name, t]));
9
+ const patches = {};
10
+ for (const { id, data } of nodes) {
11
+ const binding = resolveMimicTagBinding(data);
12
+ if (!binding)
13
+ continue;
14
+ const liveTag = tagMap.get(binding);
15
+ const mn = Number(data.min ?? 0);
16
+ const mx = Number(data.max ?? 100);
17
+ const span = Math.max(1, mx - mn);
18
+ const newVal = mn + Math.random() * span;
19
+ const newStatus = liveTag?.status ?? data.status ?? 'RUN';
20
+ patches[id] = { value: newVal, status: newStatus };
21
+ }
22
+ return patches;
23
+ }
24
+ export function applyPatchesToNodeDataMap(nodeDataById, patches) {
25
+ const updated = [];
26
+ for (const [nodeId, patch] of Object.entries(patches)) {
27
+ const current = nodeDataById[nodeId];
28
+ if (!current)
29
+ continue;
30
+ nodeDataById[nodeId] = {
31
+ ...current,
32
+ value: patch.value,
33
+ status: patch.status,
34
+ };
35
+ updated.push(nodeId);
36
+ }
37
+ return updated;
38
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@fightlightdiamond/scada-core",
3
+ "version": "0.1.0",
4
+ "description": "Engine-agnostic SCADA core utilities (simulation + payload normalization).",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "clean": "rm -rf dist",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit"
25
+ },
26
+ "keywords": [
27
+ "scada",
28
+ "simulation",
29
+ "mimic",
30
+ "vue"
31
+ ],
32
+ "license": "UNLICENSED",
33
+ "engines": {
34
+ "node": ">=20"
35
+ }
36
+ }