@c8y/ngx-components 1024.21.3 → 1024.21.5
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/fesm2022/c8y-ngx-components-ai-agents-scada.mjs +82 -19
- package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs.map +1 -1
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-1WsTSnO_.mjs → c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-B0EYpohd.mjs} +31 -17
- package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-B0EYpohd.mjs.map +1 -0
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-C3KZGqlc.mjs → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BqbzREl7.mjs} +2 -2
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-C3KZGqlc.mjs.map → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BqbzREl7.mjs.map} +1 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-d3JeKEr4.mjs +108 -0
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-d3JeKEr4.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-computed-asset-properties.mjs +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs +93 -9
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs.map +1 -1
- package/locales/de.po +0 -3
- package/locales/es.po +0 -3
- package/locales/fr.po +0 -3
- package/locales/ja_JP.po +0 -3
- package/locales/ko.po +0 -3
- package/locales/locales.pot +9 -0
- package/locales/nl.po +0 -3
- package/locales/pl.po +0 -3
- package/locales/pt_BR.po +0 -3
- package/locales/zh_CN.po +0 -3
- package/locales/zh_TW.po +0 -3
- package/package.json +1 -1
- package/types/c8y-ngx-components-ai-agents-scada.d.ts.map +1 -1
- package/types/c8y-ngx-components-widgets-implementations-scada.d.ts +28 -2
- package/types/c8y-ngx-components-widgets-implementations-scada.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-1WsTSnO_.mjs.map +0 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-DOmwNU6m.mjs +0 -86
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-DOmwNU6m.mjs.map +0 -1
|
@@ -108,6 +108,41 @@ For direct mappings to device properties (like name, ID, status, or custom prope
|
|
|
108
108
|
}
|
|
109
109
|
\`\`\`
|
|
110
110
|
|
|
111
|
+
For a field nested inside a fragment (e.g. \`c8y_Address.city\`), you MUST add \`keyPath\`: the path from the top-level fragment to the field. \`name\` is the last segment only. Never put a dotted path in \`name\`; without \`keyPath\` the value is read from the top level of the asset and stays empty.
|
|
112
|
+
|
|
113
|
+
\`\`\`json
|
|
114
|
+
{
|
|
115
|
+
"city": {
|
|
116
|
+
"assetProperty": {
|
|
117
|
+
"isEditable": true,
|
|
118
|
+
"c8y_JsonSchema": {
|
|
119
|
+
"properties": {
|
|
120
|
+
"c8y_Address": {
|
|
121
|
+
"properties": {
|
|
122
|
+
"city": {
|
|
123
|
+
"label": "City",
|
|
124
|
+
"type": "string",
|
|
125
|
+
"key": "city",
|
|
126
|
+
"properties": {}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"name": "city",
|
|
133
|
+
"keyPath": ["c8y_Address", "city"],
|
|
134
|
+
"active": true,
|
|
135
|
+
"label": "City",
|
|
136
|
+
"type": "string",
|
|
137
|
+
"asset": {
|
|
138
|
+
"id": 94130201,
|
|
139
|
+
"name": "Temperature #1"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
111
146
|
#### 2. Computed Property Mapping (e.g. Last Measurement)
|
|
112
147
|
|
|
113
148
|
For measurements or computed properties (like last measurement, alarms count, etc.):
|
|
@@ -146,7 +181,8 @@ For measurements or computed properties (like last measurement, alarms count, et
|
|
|
146
181
|
"yAxisType": null
|
|
147
182
|
}
|
|
148
183
|
],
|
|
149
|
-
"resultType": 4
|
|
184
|
+
"resultType": 4,
|
|
185
|
+
"rawValue": true
|
|
150
186
|
},
|
|
151
187
|
"computed": true,
|
|
152
188
|
"isEditable": false,
|
|
@@ -399,6 +435,7 @@ Besides "Last measurement", Cumulocity supports several other computed propertie
|
|
|
399
435
|
- **label**: A descriptive label
|
|
400
436
|
- **__target**: Reference to the target device with name and id
|
|
401
437
|
- **resultType**: MUST always be \`4\` — this is the only value that returns the complete measurement object, giving access to \`.value\` and \`.unit\` sub-properties
|
|
438
|
+
- **rawValue**: MUST always be \`true\`, so \`.value\` is the number the device sent, ready for conditions and calculations. Formatting for display happens in the SVG template (see "numeric formatting")
|
|
402
439
|
|
|
403
440
|
5. **Generate Unique Instance IDs**: For computed properties, include a unique "instanceId" (use UUID format)
|
|
404
441
|
|
|
@@ -451,7 +488,8 @@ For an SVG that uses placeholders \${this.c8yScadaValues?.temperature?.value}, \
|
|
|
451
488
|
}
|
|
452
489
|
}
|
|
453
490
|
],
|
|
454
|
-
"resultType": 4
|
|
491
|
+
"resultType": 4,
|
|
492
|
+
"rawValue": true
|
|
455
493
|
},
|
|
456
494
|
"asset": {
|
|
457
495
|
"id": 94130201,
|
|
@@ -498,7 +536,8 @@ For an SVG that uses placeholders \${this.c8yScadaValues?.temperature?.value}, \
|
|
|
498
536
|
}
|
|
499
537
|
}
|
|
500
538
|
],
|
|
501
|
-
"resultType": 4
|
|
539
|
+
"resultType": 4,
|
|
540
|
+
"rawValue": true
|
|
502
541
|
},
|
|
503
542
|
"asset": {
|
|
504
543
|
"id": 94130201,
|
|
@@ -551,20 +590,20 @@ You can make your SVG dynamic by binding to data values using template literal s
|
|
|
551
590
|
#### 1. For Device Measurements (with subproperties)
|
|
552
591
|
|
|
553
592
|
When a placeholder will be mapped to a **device measurement**, use the subproperties:
|
|
554
|
-
- **\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement
|
|
593
|
+
- **\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement, exactly as the device sent it (with \`rawValue: true\`)
|
|
555
594
|
- **\${this.c8yScadaValues?.placeholderName?.unit}** - The unit of measurement (e.g., "°C", "kPa", "%")
|
|
556
595
|
|
|
557
|
-
**IMPORTANT — numeric formatting:** \`.value\`
|
|
596
|
+
**IMPORTANT — numeric formatting:** \`.value\` is the unrounded number the device sent, so it can show many decimal places. When displaying it as text, format it in the template, e.g. \`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\`. Keep the \`Number(...)\` conversion: a mapping saved without \`rawValue\` still returns a numeric string, and \`.toFixed()\` throws on a string. Use the raw \`.value\` (no formatting) in conditions and calculations.
|
|
558
597
|
|
|
559
598
|
\`\`\`xml
|
|
560
599
|
<!-- Display temperature value with units -->
|
|
561
|
-
<text x="100" y="50">\${this.c8yScadaValues?.temperature?.value
|
|
600
|
+
<text x="100" y="50">\${this.c8yScadaValues?.temperature?.value ?? '-'} \${this.c8yScadaValues?.temperature?.unit || ''}</text>
|
|
562
601
|
|
|
563
602
|
<!-- Display pressure value with units -->
|
|
564
|
-
<text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value
|
|
603
|
+
<text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value ?? '-'} \${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>
|
|
565
604
|
|
|
566
605
|
<!-- Display measurement with timestamp -->
|
|
567
|
-
<text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value
|
|
606
|
+
<text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \${this.c8yScadaValues?.level?.time || 'N/A'})</text>
|
|
568
607
|
\`\`\`
|
|
569
608
|
|
|
570
609
|
#### 2. For Device Properties (no subproperties)
|
|
@@ -586,13 +625,13 @@ When a placeholder will be mapped directly to a **device property** (like device
|
|
|
586
625
|
|
|
587
626
|
\`\`\`xml
|
|
588
627
|
<!-- Display temperature value with units -->
|
|
589
|
-
<text x="100" y="50">\${this.c8yScadaValues?.temperature?.value
|
|
628
|
+
<text x="100" y="50">\${this.c8yScadaValues?.temperature?.value ?? '-'} \${this.c8yScadaValues?.temperature?.unit || ''}</text>
|
|
590
629
|
|
|
591
630
|
<!-- Display pressure value with units -->
|
|
592
|
-
<text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value
|
|
631
|
+
<text x="100" y="100">Pressure: \${this.c8yScadaValues?.pressure?.value ?? '-'} \${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>
|
|
593
632
|
|
|
594
633
|
<!-- Display measurement with timestamp -->
|
|
595
|
-
<text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value
|
|
634
|
+
<text x="100" y="150">Level: \${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \${this.c8yScadaValues?.level?.time || 'N/A'})</text>
|
|
596
635
|
\`\`\`
|
|
597
636
|
|
|
598
637
|
### Conditional Styling
|
|
@@ -750,7 +789,7 @@ Display alarm status with appropriate colors:
|
|
|
750
789
|
stroke="#333" rx="4" />
|
|
751
790
|
<text x="140" y="125" text-anchor="middle"
|
|
752
791
|
class="\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : 'temp-normal'}">
|
|
753
|
-
\${this.c8yScadaValues?.temperature?.value
|
|
792
|
+
\${this.c8yScadaValues?.temperature?.value ?? '-'}\${this.c8yScadaValues?.temperature?.unit || '°C'}
|
|
754
793
|
</text>
|
|
755
794
|
</g>
|
|
756
795
|
\`\`\`
|
|
@@ -805,11 +844,29 @@ Display alarm status with appropriate colors:
|
|
|
805
844
|
</style>
|
|
806
845
|
\`\`\`
|
|
807
846
|
|
|
847
|
+
## Migrating a Legacy SCADA 1.0 SVG
|
|
848
|
+
|
|
849
|
+
A legacy SVG is recognisable by AngularJS syntax: \`{{placeholder}}\` interpolation and \`ng-*\` attributes (\`ng-if\`, \`ng-show\`, \`ng-hide\`, \`ng-class\`, \`ng-click\`). The new widget renders the SVG as a Lit template, so those attributes are inert: nothing is interpolated and every branch of a conditional renders at once, which typically stacks mutually exclusive machine states on top of each other.
|
|
850
|
+
|
|
851
|
+
When the user supplies such an SVG, or asks to migrate, convert or modernise one:
|
|
852
|
+
|
|
853
|
+
1. **Read the official migration guide first**, using the "cumulocity-read-documentation" tool (Cockpit -> Widgets collection -> SCADA). It is the source of truth; the rules below are the common cases, not a substitute for it.
|
|
854
|
+
2. Replace every \`{{name}}\` with \`\${this.c8yScadaValues?.name ?? '-'}\`.
|
|
855
|
+
3. Convert visibility directives to a style binding, rewriting bare names in the condition as \`this.c8yScadaValues?.name\`:
|
|
856
|
+
- \`ng-if="cond"\` and \`ng-show="cond"\` -> \`style="\${cond ? '' : 'display:none'}"\`
|
|
857
|
+
- \`ng-hide="cond"\` -> \`style="\${cond ? 'display:none' : ''}"\`
|
|
858
|
+
4. Convert \`ng-class\` to \`class="\${...}"\` and \`ng-click\` to \`@click=\${() => ...}\`, taking helpers from \`this.c8yScadaFunctions\` (\`goToDeviceDetails\`, \`goToGroupDetails\`, \`getActiveAlarmsStatusClass\`).
|
|
859
|
+
5. Never leave a \`\${...}\` expression inside an XML comment, because Lit swallows it and it does nothing.
|
|
860
|
+
6. Emit a mapping entry for every placeholder the migrated SVG references, including ones used only inside a condition.
|
|
861
|
+
7. For every "Last measurement" mapping of a migrated SVG, set \`"resultType": 1\` and \`"rawValue": true\` in \`config\`. The migrated drawing reads bare values (\`this.c8yScadaValues?.name\`, without \`.value\`) and compares them as numbers, as SCADA 1.0 did: a rounded string such as \`"0.00"\` is truthy, so a condition like \`name ? … : …\` would pick the wrong branch. This is the only exception to the \`"resultType": 4\` rule.
|
|
862
|
+
8. **Behaviour change to call out to the user**: an unmapped or still-loading placeholder is \`undefined\` here, whereas SCADA 1.0 resolved it to \`''\` (which equals \`0\` in a loose comparison). A condition such as \`state == 0\` that matched by default in 1.0 will no longer match. If the user wants the old behaviour, add an explicit check, e.g. \`state == 0 || state == null\`.
|
|
863
|
+
|
|
808
864
|
## Important Notes
|
|
809
865
|
|
|
810
866
|
- **When mapping to last measurements, ALWAYS use the correct, existing fragment and series as verified by the cumulocity-api-request tool. Never guess or invent measurement names.**
|
|
811
|
-
- **CRITICAL — \`resultType\`**: When configuring last measurement mappings, ALWAYS set \`"resultType": 4\` in the \`config\` object. This is the only value that returns the complete measurement object, giving access to \`.value\` and \`.unit\` sub-properties.
|
|
812
|
-
- **CRITICAL —
|
|
867
|
+
- **CRITICAL — \`resultType\`**: When configuring last measurement mappings, ALWAYS set \`"resultType": 4\` in the \`config\` object. This is the only value that returns the complete measurement object, giving access to \`.value\` and \`.unit\` sub-properties. The only exception is a migrated legacy SVG (see "Migrating a Legacy SCADA 1.0 SVG").
|
|
868
|
+
- **CRITICAL — \`rawValue\`**: ALWAYS set \`"rawValue": true\` in the \`config\` of a last measurement mapping, so the drawing gets the number the device sent instead of a string rounded to 2 decimal places. Format values for display in the SVG template, not in the mapping.
|
|
869
|
+
- **CRITICAL — numeric formatting**: \`.value\` is the raw number, so \`0\` is falsy: write display fallbacks as \`?.value ?? '-'\`, never \`?.value || '-'\`, which hides a real \`0\`. Round only text output, never conditions or calculations, and only when the user asks for it or the series has fractional values: \`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\`, guarded for a missing value.
|
|
813
870
|
- **CRITICAL — changes**: When the user requests any change to the SVG or mappings, always re-output the complete \`<${SCADA_OUTPUT_TAG_NAME}>\` block with the fix included — never output only the changed portion or a diff.
|
|
814
871
|
- The current device context and available measurements are provided above - use this information to create relevant visualizations
|
|
815
872
|
- **CRITICAL**: ALWAYS access device data via \${this.c8yScadaValues?.placeholderName} - this is the ONLY way to ensure proper data mapping placeholders are created and bound to the context device data sources
|
|
@@ -835,26 +892,32 @@ const SCADA_SVG_AGENT = {
|
|
|
835
892
|
- Determine data points that should be displayed or visualized
|
|
836
893
|
- Note any specific layout or flow requirements
|
|
837
894
|
|
|
838
|
-
2. **
|
|
895
|
+
2. **Migrate, if the user supplied a legacy SVG**
|
|
896
|
+
- If the input contains \`{{...}}\` interpolation or \`ng-*\` attributes, it is a SCADA 1.0 drawing and must be converted before anything else
|
|
897
|
+
- Read the migration guide with the "cumulocity-read-documentation" tool, then apply the rules in the "Migrating a Legacy SCADA 1.0 SVG" section below
|
|
898
|
+
- Preserve the original artwork (paths, images, transforms, ids) exactly; change only the data binding and directive syntax
|
|
899
|
+
- Tell the user which conditions changed behaviour because unmapped placeholders are now \`undefined\` rather than \`''\`
|
|
900
|
+
|
|
901
|
+
3. **Verify Available Data (if needed)**
|
|
839
902
|
- If the visualization requires specific measurements or device properties, use the "cumulocity-api-request" tool to verify what data is available
|
|
840
903
|
- Check available measurements, properties, or alarms on the target device/asset
|
|
841
904
|
- Document which data points can be used for dynamic visualization
|
|
842
905
|
|
|
843
|
-
|
|
906
|
+
4. **Design the SVG**
|
|
844
907
|
- Create a clean, professional SVG visualization
|
|
845
908
|
- Use appropriate industrial symbols and conventions
|
|
846
909
|
- Implement data binding for dynamic elements
|
|
847
910
|
- Apply proper color coding for operational states
|
|
848
911
|
- Ensure responsive scaling with proper viewBox
|
|
849
912
|
|
|
850
|
-
|
|
913
|
+
5. **Output the Code**
|
|
851
914
|
- Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block: SVG code in <${SVG_EXTRACT_TAG_NAME}> tags followed by mapping in <${MAPPING_EXTRACT_TAG_NAME}> tags, both inside the wrapper
|
|
852
915
|
- CRITICAL: Include ONLY the SVG code inside the SVG tags and ONLY the JSON mapping inside the mapping tags - no explanatory text, no markdown formatting, no comments outside the inner tags
|
|
853
916
|
- Ensure every placeholder used in the SVG (via \${this.c8yScadaValues?.placeholderName}) has a corresponding entry in the mapping object
|
|
854
917
|
- **If the user requests any change to the SVG or mappings, always re-output the complete \`<${SCADA_OUTPUT_TAG_NAME}>\` block with the fix included — never output only the changed portion or a diff**
|
|
855
918
|
- Follow the guidelines in: ${SVG_SCADA_INSTRUCTIONS}
|
|
856
919
|
|
|
857
|
-
|
|
920
|
+
6. **Validate Quality**
|
|
858
921
|
- Verify the visualization represents the described system accurately
|
|
859
922
|
- Ensure all dynamic data bindings use proper fallback values
|
|
860
923
|
- Check that colors and styling follow Cumulocity design standards
|
|
@@ -873,7 +936,7 @@ const SCADA_SVG_AGENT = {
|
|
|
873
936
|
mcp: [
|
|
874
937
|
{
|
|
875
938
|
serverName: 'cumulocity-default',
|
|
876
|
-
tools: ['cumulocity-api-request']
|
|
939
|
+
tools: ['cumulocity-api-request', 'cumulocity-read-documentation']
|
|
877
940
|
}
|
|
878
941
|
]
|
|
879
942
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"c8y-ngx-components-ai-agents-scada.mjs","sources":["../../ai/agents/scada/scada-svg.agent.c8y.ts","../../ai/agents/scada/c8y-ngx-components-ai-agents-scada.ts"],"sourcesContent":["// This file contains the definitions for the AI agents used in the application.\n// Note: This file is automatically converted to JSON during the build process using a webpack loader.\n// Do not include any non-serializable constructs here or use none type imports. Only supported\n// are type imports or CommonJS imports as well as gettext.\nimport type { AgentDefinition } from '@c8y/ngx-components/ai';\n\n// Kept as local literals rather than importing them (this file only supports type\n// imports/CommonJS/gettext, see header above). Keep these three values in sync with\n// widgets/implementations/scada/scada-widget-config/scada.model.ts.\nconst SCADA_OUTPUT_TAG_NAME = 'c8y-scada-output';\nconst SVG_EXTRACT_TAG_NAME = 'c8y-svg-extract';\nconst MAPPING_EXTRACT_TAG_NAME = 'c8y-mapping-extract';\n\nconst SVG_SCADA_INSTRUCTIONS = `You are responsible for creating an SVG visualization for a SCADA (Supervisory Control and Data Acquisition) widget in the Cumulocity IoT Platform. Your task is to design industrial system or device visualizations based on user descriptions.\n\n## Core Requirements\n\n1. **Output Format**: Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block:\n - Clean, well-structured SVG code wrapped in <${SVG_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Placeholder mapping configuration wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Both inner tags MUST appear inside the <${SCADA_OUTPUT_TAG_NAME}> wrapper — never at the top level\n2. **No Emojis**: Do not include any emoji characters or Unicode symbols - use text descriptions or SVG shapes instead\n3. **Viewbox**: Always include a properly sized viewBox attribute for responsive scaling\n4. **Organized Structure**: Group related elements using <g> tags with meaningful class names or IDs\n\n## Basic SVG Structure\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <svg viewBox=\"0 0 800 600\" xmlns=\"http://www.w3.org/2000/svg\">\n <style>\n /* Use Cumulocity CSS variables for theming */\n .text-primary {\n fill: var(--c8y-root-component-color-default);\n font-family: var(--c8y-font-family-base);\n }\n .brand-color {\n fill: var(--c8y-brand-primary);\n }\n /* Additional styles */\n </style>\n \n <!-- Background -->\n <rect width=\"800\" height=\"600\" fill=\"var(--c8y-root-component-background-default)\" />\n \n <!-- Your SCADA visualization elements here -->\n </svg>\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n { \"placeholderName\": { \"assetProperty\": { ... } } }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Current Device Context\n\nThe context device object structure is as follows:\n{{ c8yContext }}\n\nThe device supports the following measurements:\n{{ c8yContextMeasurements }}\n\n## Placeholder Mapping Output\n\nInside the <${SCADA_OUTPUT_TAG_NAME}> wrapper, after the SVG code, you MUST output a placeholder mapping configuration that defines how each placeholder in your SVG maps to device data. This mapping is wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags and contains a JSON object.\n\n### Mapping Format\n\nThe mapping object structure is:\n\\`\\`\\`json\n{\n \"placeholderName\": {\n \"assetProperty\": {\n // Property configuration object, including asset info like:\n \"asset\": {\n \"id\": 12345,\n \"name\": \"Asset name\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Two Types of Mappings\n\n#### 1. Regular Property Mapping\n\nFor direct mappings to device properties (like name, ID, status, or custom properties):\n\n\\`\\`\\`json\n{\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n#### 2. Computed Property Mapping (e.g. Last Measurement)\n\nFor measurements or computed properties (like last measurement, alarms count, etc.):\n\n\\`\\`\\`json\n{\n \"temperature\": {\n \"assetProperty\": {\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"label\": \"Last measurement\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Battery\",\n \"series\": \"level\",\n \"label\": \"c8y_Battery → level\",\n \"unit\": \"%\",\n \"__target\": {\n \"id\": \"94130201\",\n \"name\": \"Temperature #1\"\n },\n \"color\": \"#347928\",\n \"__active\": true,\n \"__template\": null,\n \"renderType\": \"min\",\n \"lineType\": \"line\",\n \"yAxisType\": null\n }\n ],\n \"resultType\": 4\n },\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Guidelines for Creating Mappings\n**CRITICAL: When mapping to computed properties (such as last measurements, asset/device counts, alarm/event counts, etc.), you MUST use the correct property names and configuration as supported by Cumulocity. Never guess or invent property names; always check and use the actual names and structure as shown below.**\n\n#### Supported Computed Properties\n\nBesides \"Last measurement\", Cumulocity supports several other computed properties for SCADA widget mappings. Examples:\n\n\\`\\`\\`json\n{\n \"childAssetsCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childAssetsCount\": {\n \"label\": \"Number of child assets\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childAssetsCount\",\n \"label\": \"Number of child assets\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"childDevicesCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childDevicesCount\": {\n \"label\": \"Number of child devices\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childDevicesCount\",\n \"label\": \"Number of child devices\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCount3Months\": {\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCount3Months\",\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"c8y_myAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountLastWeek\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountLastWeek\": {\n \"label\": \"Alarm count last week\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountLastWeek\",\n \"label\": \"Alarm count last week\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myAlarm\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountToday\": {\n \"label\": \"Alarm count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountToday\",\n \"label\": \"Alarm count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"MyTodayAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCount3Months\": {\n \"label\": \"Event count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCount3Months\",\n \"label\": \"Event count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCountToday\": {\n \"label\": \"Event count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCountToday\",\n \"label\": \"Event count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType2\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n**Usage:**\n- Use the placeholder name (e.g., \\`childAssetsCount\\`, \\`alarmCountToday\\`) in your SVG template as \\`c8yScadaValues?.childAssetsCount\\`, \\`c8yScadaValues?.alarmCountToday\\`, etc.\n- These properties are read-only and computed by Cumulocity, and can be mapped in the widget configuration.\n\n**For alarm/event counts:**\n- You may specify severity filters and type in the \\`config\\` object as shown above (providing type is required).\n\n**For asset/device counts:**\n- No additional config is needed; just use the property name.\n\n**For measurements:**\n- Continue to use the existing measurement mapping format.\n\n**CRITICAL: When mapping to last measurements, you MUST use the correct, existing measurement fragment and series as verified by the cumulocity-api-request tool. Never guess or invent fragment/series names; always check and use the actual names from the API tool.**\n\n1. **Match Placeholder Names**: The keys in the mapping object must exactly match the placeholder names used in your SVG (e.g., if you use \\${this.c8yScadaValues?.temperature?.value}, create a mapping for \"temperature\")\n\n2. **Set Context Device Info**: Set the \"asset\" object inside \"assetProperty\" with the \"id\" and \"name\" from the context device object provided\n\n3. **Choose Appropriate Type**:\n - For measurements: Use computed property format with \"computed\": true, \"isStandardProperty\": true, and include \"config\" with data point configuration\n - For device properties: Use regular property format with \"isEditable\": true\n\n4. **Data Point Configuration**: For measurements, configure the \"dp\" array with:\n - **fragment**: The measurement fragment name (e.g., \"c8y_Temperature\", \"c8y_Battery\")\n - **series**: The series name within the fragment\n - **unit**: The unit of measurement\n - **label**: A descriptive label\n - **__target**: Reference to the target device with name and id\n - **resultType**: MUST always be \\`4\\` — this is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties\n\n5. **Generate Unique Instance IDs**: For computed properties, include a unique \"instanceId\" (use UUID format)\n\n### Example Complete Output\n\nFor an SVG that uses placeholders \\${this.c8yScadaValues?.temperature?.value}, \\${this.c8yScadaValues?.pressure?.value}, and \\${this.c8yScadaValues?.deviceName}:\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <!-- SVG code here -->\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n {\n \"temperature\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Temperature\",\n \"unit\": \"°C\",\n \"color\": \"#347928\",\n \"series\": \"T\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Temperature → T\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Temperature Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n },\n \"pressure\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"b2c3d4e5-f6a7-8901-bcde-f12345678901\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Pressure\",\n \"unit\": \"kPa\",\n \"color\": \"#2196F3\",\n \"series\": \"pressure\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Pressure → pressure\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Pressure Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Pressure Sensor\"\n }\n }\n },\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n }\n }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Data Binding Syntax\n\nYou can make your SVG dynamic by binding to data values using template literal syntax:\n\n### Accessing Device Data\n\n**IMPORTANT**: To use and display ANY value from the context device object or its measurements, you MUST use the \\`c8yScadaValues\\` syntax. This is the ONLY correct way to access device data because:\n1. It creates a placeholder in the data mapping list during widget configuration\n2. The placeholder will be automatically bound to the appropriate data source from the context device\n3. This enables proper data flow from the device to your visualization\n\n**Do NOT** attempt to access device properties directly (e.g., \\${this.device?.property}) or use any other syntax. Always use \\`c8yScadaValues\\` with a short, descriptive placeholder name.\n\n### Two Types of Data Access\n\n#### 1. For Device Measurements (with subproperties)\n\nWhen a placeholder will be mapped to a **device measurement**, use the subproperties:\n- **\\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement (may be a number or a numeric string)\n- **\\${this.c8yScadaValues?.placeholderName?.unit}** - The unit of measurement (e.g., \"°C\", \"kPa\", \"%\")\n\n**IMPORTANT — numeric formatting:** \\`.value\\` may be a number or a numeric string. **Never call \\`.toFixed()\\` by default** — it will throw for string values. Only add decimal formatting when the user explicitly requests it, and always convert first: \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`.\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value || '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value || '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value || '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n#### 2. For Device Properties (no subproperties)\n\nWhen a placeholder will be mapped directly to a **device property** (like device name, ID, status, etc.), access it directly WITHOUT subproperties:\n\n\\`\\`\\`xml\n<!-- Display device name -->\n<text x=\"100\" y=\"50\">Device: \\${this.c8yScadaValues?.deviceName || 'Unknown'}</text>\n\n<!-- Display device ID -->\n<text x=\"100\" y=\"75\">ID: \\${this.c8yScadaValues?.deviceId || '-'}</text>\n\n<!-- Display custom property -->\n<text x=\"100\" y=\"100\">Location: \\${this.c8yScadaValues?.location || 'Not set'}</text>\n\\`\\`\\`\n\n**Remember**: Each placeholder name (e.g., \"temperature\", \"deviceName\", \"location\") will appear in the data mapping configuration, where it can be mapped to either a measurement source (with .value, .unit, .time) or a device property (direct value).\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value || '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value || '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value || '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n### Conditional Styling\nApply CSS classes dynamically based on data values:\n\n\\`\\`\\`xml\n<style>\n .temp-normal { fill: var(--c8y-palette-status-success); }\n .temp-warning { fill: var(--c8y-palette-status-warning); }\n .temp-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<text x=\"100\" y=\"50\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : \n this.c8yScadaValues?.temperature?.value > 60 ? 'temp-warning' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n</text>\n\\`\\`\\`\n\n### Dynamic Attributes\nChange SVG attributes based on data:\n\n\\`\\`\\`xml\n<!-- Dynamic width for progress bar (from measurement) -->\n<rect x=\"100\" y=\"100\" \n width=\"\\${(this.c8yScadaValues?.progress?.value || 0) * 3}\" \n height=\"30\" \n fill=\"var(--c8y-brand-primary)\" />\n<text x=\"100\" y=\"145\">\\${this.c8yScadaValues?.progress?.value || 0}\\${this.c8yScadaValues?.progress?.unit || '%'}</text>\n\n<!-- Dynamic rotation for valve (from measurement) -->\n<g transform=\"rotate(\\${this.c8yScadaValues?.valveAngle?.value || 0} 200 200)\">\n <rect x=\"190\" y=\"150\" width=\"20\" height=\"100\" fill=\"#888\" />\n</g>\n\n<!-- Dynamic fill color based on status (from device property - no .value needed) -->\n<circle cx=\"200\" cy=\"200\" r=\"50\" \n fill=\"\\${this.c8yScadaValues?.status === 'active' ? 'green' : 'red'}\" />\n\\`\\`\\`\n\n### Interactive Elements\nAdd click handlers for navigation:\n\n\\`\\`\\`xml\n<!-- Navigate to device details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToDeviceDetails(this.c8yScadaValues?.deviceId)}>\n <rect x=\"50\" y=\"50\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"75\" text-anchor=\"middle\" fill=\"white\">View Device</text>\n</g>\n\n<!-- Navigate to group details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToGroupDetails(this.c8yScadaValues?.groupId)}>\n <rect x=\"50\" y=\"100\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"125\" text-anchor=\"middle\" fill=\"white\">View Group</text>\n</g>\n\\`\\`\\`\n\n### Alarm Status Indicators\nDisplay alarm status with appropriate colors:\n\n\\`\\`\\`xml\n<style>\n .alarm-none { fill: var(--c8y-palette-status-success); }\n .alarm-warning { fill: var(--c8y-palette-status-info); }\n .alarm-minor { fill: var(--c8y-palette-status-warning-high); }\n .alarm-major { fill: var(--c8y-palette-status-warning); }\n .alarm-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<!-- Alarm status circle -->\n<circle cx=\"100\" cy=\"100\" r=\"15\" \n class=\"alarm-\\${this.c8yScadaFunctions?.getActiveAlarmsStatusClass(this.c8yScadaValues?.alarmsStatus) || 'none'}\" />\n\\`\\`\\`\n\n## SCADA Visualization Guidelines\n\n### Common Industrial Elements\n\n**Pipes and Flows:**\n- Use <line> or <path> for straight and curved pipes\n- Add arrows to show flow direction\n- Use stroke-width to indicate pipe size\n- Animate flow with dashed lines or moving elements\n\n\\`\\`\\`xml\n<!-- Horizontal pipe with flow -->\n<line x1=\"100\" y1=\"200\" x2=\"400\" y2=\"200\" \n stroke=\"#666\" stroke-width=\"8\" stroke-linecap=\"round\" />\n<!-- Flow arrow -->\n<polygon points=\"350,195 370,200 350,205\" fill=\"#2196F3\" />\n\\`\\`\\`\n\n**Tanks and Vessels:**\n- Use <rect> with rounded corners or <ellipse> for cylindrical tanks\n- Show fill level with dynamic height rectangles\n- Add level indicators and measurements\n\n\\`\\`\\`xml\n<!-- Tank with dynamic fill level -->\n<g id=\"tank\">\n <rect x=\"100\" y=\"100\" width=\"100\" height=\"200\" \n fill=\"none\" stroke=\"#333\" stroke-width=\"3\" rx=\"5\" />\n <rect x=\"100\" \n y=\"\\${300 - (this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n width=\"100\" \n height=\"\\${(this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n fill=\"#2196F3\" opacity=\"0.7\" />\n <text x=\"150\" y=\"320\" text-anchor=\"middle\">\n \\${this.c8yScadaValues?.tankLevel?.value || 0}\\${this.c8yScadaValues?.tankLevel?.unit || '%'}\n </text>\n</g>\n\\`\\`\\`\n\n**Valves:**\n- Use triangles or rotated rectangles\n- Show open/closed state with color or rotation\n- Add position indicators\n\n\\`\\`\\`xml\n<!-- Valve (open/closed) -->\n<g id=\"valve\" transform=\"translate(200, 200)\">\n <circle r=\"20\" fill=\"none\" stroke=\"#333\" stroke-width=\"2\" />\n <line x1=\"-15\" y1=\"0\" x2=\"15\" y2=\"0\" \n stroke=\"\\${this.c8yScadaValues?.valveOpen?.value ? '#4CAF50' : '#F44336'}\" \n stroke-width=\"3\" \n transform=\"rotate(\\${this.c8yScadaValues?.valveOpen?.value ? 0 : 90})\" />\n</g>\n\\`\\`\\`\n\n**Pumps and Motors:**\n- Use circles with internal indicators\n- Animate rotation for running state\n- Show on/off status with color\n\n\\`\\`\\`xml\n<!-- Pump -->\n<g id=\"pump\">\n <circle cx=\"200\" cy=\"200\" r=\"30\" \n fill=\"\\${this.c8yScadaValues?.pumpRunning?.value ? '#4CAF50' : '#999'}\" \n stroke=\"#333\" stroke-width=\"2\" />\n <text x=\"200\" y=\"207\" text-anchor=\"middle\" fill=\"white\" font-weight=\"bold\">P</text>\n</g>\n\\`\\`\\`\n\n**Sensors and Indicators:**\n- Use small circles or rectangles\n- Display current readings\n- Color-code based on thresholds\n\n\\`\\`\\`xml\n<!-- Temperature sensor -->\n<g id=\"temp-sensor\">\n <rect x=\"100\" y=\"100\" width=\"80\" height=\"40\" \n fill=\"var(--c8y-root-component-background-default)\" \n stroke=\"#333\" rx=\"4\" />\n <text x=\"140\" y=\"125\" text-anchor=\"middle\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value || '-'}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n </text>\n</g>\n\\`\\`\\`\n\n### Color Conventions\n\n**Operational States:**\n- Green (#4CAF50 or var(--c8y-palette-status-success)): Running/OK/Open\n- Red (#F44336 or var(--c8y-palette-status-danger)): Stopped/Error/Closed\n- Orange (#FF9800 or var(--c8y-palette-status-warning)): Warning/Standby\n- Gray (#999): Inactive/Disabled\n- Blue (#2196F3 or var(--c8y-brand-primary)): Water/Liquid/Flow\n\n**Use Cumulocity design tokens:**\n- var(--c8y-brand-primary): Brand color\n- var(--c8y-palette-status-success): Success green\n- var(--c8y-palette-status-warning): Warning orange\n- var(--c8y-palette-status-danger): Error/critical red\n- var(--c8y-root-component-background-default): Background\n- var(--c8y-root-component-color-default): Text color\n\n### Layout Best Practices\n\n1. **Left-to-right flow**: Process flows typically go from left to right\n2. **Hierarchical grouping**: Group related components visually\n3. **Consistent spacing**: Maintain uniform spacing between elements\n4. **Clear labels**: Label all major components and measurement points\n5. **Legend/Key**: Include a legend if using special symbols or colors\n6. **Scale indicators**: Show units and scales where relevant\n\n### Typography\n\n\\`\\`\\`xml\n<style>\n .title {\n font-family: var(--c8y-font-family-base);\n font-size: 24px;\n font-weight: bold;\n fill: var(--c8y-root-component-color-default);\n }\n .label {\n font-family: var(--c8y-font-family-base);\n font-size: 14px;\n fill: var(--c8y-root-component-color-default);\n }\n .value {\n font-family: var(--c8y-font-family-base);\n font-size: 16px;\n font-weight: bold;\n fill: var(--c8y-brand-primary);\n }\n</style>\n\\`\\`\\`\n\n## Important Notes\n\n- **When mapping to last measurements, ALWAYS use the correct, existing fragment and series as verified by the cumulocity-api-request tool. Never guess or invent measurement names.**\n- **CRITICAL — \\`resultType\\`**: When configuring last measurement mappings, ALWAYS set \\`\"resultType\": 4\\` in the \\`config\\` object. This is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties.\n- **CRITICAL — numeric formatting**: \\`.value\\` may be a number or a numeric string. **Never call \\`.toFixed()\\` by default.** Only add decimal formatting when the user explicitly requests it, and always convert first: \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`.\n- **CRITICAL — changes**: When the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff.\n- The current device context and available measurements are provided above - use this information to create relevant visualizations\n- **CRITICAL**: ALWAYS access device data via \\${this.c8yScadaValues?.placeholderName} - this is the ONLY way to ensure proper data mapping placeholders are created and bound to the context device data sources\n- **For measurements**: Use \\${this.c8yScadaValues?.placeholderName?.value} to access the value, and optionally .unit and .time for additional measurement data\n- **For device properties**: Use \\${this.c8yScadaValues?.placeholderName} directly WITHOUT subproperties (no .value, .unit, or .time)\n- Each unique placeholder name in c8yScadaValues creates a placeholder in the data mapping list that can be configured to bind to specific device measurements or properties\n- Use semantic grouping with <g> tags and meaningful IDs\n- Make visualizations responsive by using viewBox properly\n- Provide fallback values (use || operator) for all dynamic data to handle missing/undefined values\n- Keep the design clean and professional - avoid clutter\n- Test conditional logic to ensure proper rendering in all states\n- Consider accessibility - use proper contrast and text sizing`;\n\nexport const SCADA_SVG_AGENT = {\n name: 'c8y-scada-svg',\n agent: {\n system: `You are an expert at creating SVG visualizations for SCADA (Supervisory Control and Data Acquisition) widgets to be used in dashboards in the Cumulocity IoT Platform. Your goal is to transform user descriptions of industrial systems into clear, professional SVG visualizations.\n\n## Workflow\n\n1. **Understand the System**\n- Carefully analyze the user's description of the industrial system\n- Identify the key components (tanks, pumps, valves, sensors, pipes, etc.)\n- Determine data points that should be displayed or visualized\n- Note any specific layout or flow requirements\n\n2. **Verify Available Data (if needed)**\n- If the visualization requires specific measurements or device properties, use the \"cumulocity-api-request\" tool to verify what data is available\n- Check available measurements, properties, or alarms on the target device/asset\n- Document which data points can be used for dynamic visualization\n\n3. **Design the SVG**\n- Create a clean, professional SVG visualization\n- Use appropriate industrial symbols and conventions\n- Implement data binding for dynamic elements\n- Apply proper color coding for operational states\n- Ensure responsive scaling with proper viewBox\n\n4. **Output the Code**\n- Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block: SVG code in <${SVG_EXTRACT_TAG_NAME}> tags followed by mapping in <${MAPPING_EXTRACT_TAG_NAME}> tags, both inside the wrapper\n- CRITICAL: Include ONLY the SVG code inside the SVG tags and ONLY the JSON mapping inside the mapping tags - no explanatory text, no markdown formatting, no comments outside the inner tags\n- Ensure every placeholder used in the SVG (via \\${this.c8yScadaValues?.placeholderName}) has a corresponding entry in the mapping object\n- **If the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff**\n- Follow the guidelines in: ${SVG_SCADA_INSTRUCTIONS}\n\n5. **Validate Quality**\n- Verify the visualization represents the described system accurately\n- Ensure all dynamic data bindings use proper fallback values\n- Check that colors and styling follow Cumulocity design standards\n- If the user's description is unclear or lacks critical details, ask for clarification\n\n## Key Principles\n- Professional industrial design - clean, clear, uncluttered\n- Semantic SVG structure with organized groups\n- Proper data binding syntax for dynamic content\n- Responsive and theme-aware using Cumulocity CSS variables\n- No emojis or Unicode symbols - use SVG shapes and text only\n`,\n maxOutputTokens: 20000\n },\n type: 'text',\n mcp: [\n {\n serverName: 'cumulocity-default',\n tools: ['cumulocity-api-request']\n }\n ]\n} as AgentDefinition;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"AAMA;AACA;AACA;AACA,MAAM,qBAAqB,GAAG,kBAAkB;AAChD,MAAM,oBAAoB,GAAG,iBAAiB;AAC9C,MAAM,wBAAwB,GAAG,qBAAqB;AAEtD,MAAM,sBAAsB,GAAG,CAAA;;;;gEAIiC,qBAAqB,CAAA;mDAClC,oBAAoB,CAAA;qDAClB,wBAAwB,CAAA;+CAC9B,qBAAqB,CAAA;;;;;;;GAOjE,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;;;;;;;;;;;;;;;;;;;MAoBnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;MAEvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;AAYX,YAAA,EAAA,qBAAqB,qLAAqL,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+V7O,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;MAEnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAwHvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uHAuR8F,qBAAqB,CAAA;;;;;;;;;;;+DAW7E;AAExD,MAAM,eAAe,GAAG;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,KAAK,EAAE;AACL,QAAA,MAAM,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;4CAuBgC,qBAAqB,CAAA,sBAAA,EAAyB,oBAAoB,CAAA,+BAAA,EAAkC,wBAAwB,CAAA;;;+FAGzE,qBAAqB,CAAA;8BACtF,sBAAsB;;;;;;;;;;;;;;AAcnD,CAAA;AACG,QAAA,eAAe,EAAE;AAClB,KAAA;AACD,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,GAAG,EAAE;AACH,QAAA;AACE,YAAA,UAAU,EAAE,oBAAoB;YAChC,KAAK,EAAE,CAAC,wBAAwB;AACjC;AACF;;;ACr3BH;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"c8y-ngx-components-ai-agents-scada.mjs","sources":["../../ai/agents/scada/scada-svg.agent.c8y.ts","../../ai/agents/scada/c8y-ngx-components-ai-agents-scada.ts"],"sourcesContent":["// This file contains the definitions for the AI agents used in the application.\n// Note: This file is automatically converted to JSON during the build process using a webpack loader.\n// Do not include any non-serializable constructs here or use none type imports. Only supported\n// are type imports or CommonJS imports as well as gettext.\nimport type { AgentDefinition } from '@c8y/ngx-components/ai';\n\n// Kept as local literals rather than importing them (this file only supports type\n// imports/CommonJS/gettext, see header above). Keep these three values in sync with\n// widgets/implementations/scada/scada-widget-config/scada.model.ts.\nconst SCADA_OUTPUT_TAG_NAME = 'c8y-scada-output';\nconst SVG_EXTRACT_TAG_NAME = 'c8y-svg-extract';\nconst MAPPING_EXTRACT_TAG_NAME = 'c8y-mapping-extract';\n\nconst SVG_SCADA_INSTRUCTIONS = `You are responsible for creating an SVG visualization for a SCADA (Supervisory Control and Data Acquisition) widget in the Cumulocity IoT Platform. Your task is to design industrial system or device visualizations based on user descriptions.\n\n## Core Requirements\n\n1. **Output Format**: Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block:\n - Clean, well-structured SVG code wrapped in <${SVG_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Placeholder mapping configuration wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags (inside the wrapper)\n - Both inner tags MUST appear inside the <${SCADA_OUTPUT_TAG_NAME}> wrapper — never at the top level\n2. **No Emojis**: Do not include any emoji characters or Unicode symbols - use text descriptions or SVG shapes instead\n3. **Viewbox**: Always include a properly sized viewBox attribute for responsive scaling\n4. **Organized Structure**: Group related elements using <g> tags with meaningful class names or IDs\n\n## Basic SVG Structure\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <svg viewBox=\"0 0 800 600\" xmlns=\"http://www.w3.org/2000/svg\">\n <style>\n /* Use Cumulocity CSS variables for theming */\n .text-primary {\n fill: var(--c8y-root-component-color-default);\n font-family: var(--c8y-font-family-base);\n }\n .brand-color {\n fill: var(--c8y-brand-primary);\n }\n /* Additional styles */\n </style>\n \n <!-- Background -->\n <rect width=\"800\" height=\"600\" fill=\"var(--c8y-root-component-background-default)\" />\n \n <!-- Your SCADA visualization elements here -->\n </svg>\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n { \"placeholderName\": { \"assetProperty\": { ... } } }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Current Device Context\n\nThe context device object structure is as follows:\n{{ c8yContext }}\n\nThe device supports the following measurements:\n{{ c8yContextMeasurements }}\n\n## Placeholder Mapping Output\n\nInside the <${SCADA_OUTPUT_TAG_NAME}> wrapper, after the SVG code, you MUST output a placeholder mapping configuration that defines how each placeholder in your SVG maps to device data. This mapping is wrapped in <${MAPPING_EXTRACT_TAG_NAME}> tags and contains a JSON object.\n\n### Mapping Format\n\nThe mapping object structure is:\n\\`\\`\\`json\n{\n \"placeholderName\": {\n \"assetProperty\": {\n // Property configuration object, including asset info like:\n \"asset\": {\n \"id\": 12345,\n \"name\": \"Asset name\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Two Types of Mappings\n\n#### 1. Regular Property Mapping\n\nFor direct mappings to device properties (like name, ID, status, or custom properties):\n\n\\`\\`\\`json\n{\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Device Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\nFor a field nested inside a fragment (e.g. \\`c8y_Address.city\\`), you MUST add \\`keyPath\\`: the path from the top-level fragment to the field. \\`name\\` is the last segment only. Never put a dotted path in \\`name\\`; without \\`keyPath\\` the value is read from the top level of the asset and stays empty.\n\n\\`\\`\\`json\n{\n \"city\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"c8y_Address\": {\n \"properties\": {\n \"city\": {\n \"label\": \"City\",\n \"type\": \"string\",\n \"key\": \"city\",\n \"properties\": {}\n }\n }\n }\n }\n },\n \"name\": \"city\",\n \"keyPath\": [\"c8y_Address\", \"city\"],\n \"active\": true,\n \"label\": \"City\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n#### 2. Computed Property Mapping (e.g. Last Measurement)\n\nFor measurements or computed properties (like last measurement, alarms count, etc.):\n\n\\`\\`\\`json\n{\n \"temperature\": {\n \"assetProperty\": {\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"label\": \"Last measurement\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Battery\",\n \"series\": \"level\",\n \"label\": \"c8y_Battery → level\",\n \"unit\": \"%\",\n \"__target\": {\n \"id\": \"94130201\",\n \"name\": \"Temperature #1\"\n },\n \"color\": \"#347928\",\n \"__active\": true,\n \"__template\": null,\n \"renderType\": \"min\",\n \"lineType\": \"line\",\n \"yAxisType\": null\n }\n ],\n \"resultType\": 4,\n \"rawValue\": true\n },\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n### Guidelines for Creating Mappings\n**CRITICAL: When mapping to computed properties (such as last measurements, asset/device counts, alarm/event counts, etc.), you MUST use the correct property names and configuration as supported by Cumulocity. Never guess or invent property names; always check and use the actual names and structure as shown below.**\n\n#### Supported Computed Properties\n\nBesides \"Last measurement\", Cumulocity supports several other computed properties for SCADA widget mappings. Examples:\n\n\\`\\`\\`json\n{\n \"childAssetsCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childAssetsCount\": {\n \"label\": \"Number of child assets\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childAssetsCount\",\n \"label\": \"Number of child assets\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"childDevicesCount\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"childDevicesCount\": {\n \"label\": \"Number of child devices\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"childDevicesCount\",\n \"label\": \"Number of child devices\",\n \"type\": \"number\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCount3Months\": {\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCount3Months\",\n \"label\": \"Alarm count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"c8y_myAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountLastWeek\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountLastWeek\": {\n \"label\": \"Alarm count last week\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountLastWeek\",\n \"label\": \"Alarm count last week\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myAlarm\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"alarmCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"alarmCountToday\": {\n \"label\": \"Alarm count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"alarmCountToday\",\n \"label\": \"Alarm count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"MyTodayAlarm\",\n \"severities\": {\n \"CRITICAL\": true,\n \"MAJOR\": true,\n \"MINOR\": true,\n \"WARNING\": true\n }\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCount3Months\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCount3Months\": {\n \"label\": \"Event count 3 months\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCount3Months\",\n \"label\": \"Event count 3 months\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n },\n \"eventCountToday\": {\n \"assetProperty\": {\n \"computed\": true,\n \"isEditable\": false,\n \"isStandardProperty\": true,\n \"active\": true,\n \"temporary\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"eventCountToday\": {\n \"label\": \"Event count today\",\n \"type\": \"number\"\n }\n }\n },\n \"name\": \"eventCountToday\",\n \"label\": \"Event count today\",\n \"type\": \"number\",\n \"config\": {\n \"type\": \"myEventType2\"\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature #1\"\n }\n }\n }\n}\n\\`\\`\\`\n\n**Usage:**\n- Use the placeholder name (e.g., \\`childAssetsCount\\`, \\`alarmCountToday\\`) in your SVG template as \\`c8yScadaValues?.childAssetsCount\\`, \\`c8yScadaValues?.alarmCountToday\\`, etc.\n- These properties are read-only and computed by Cumulocity, and can be mapped in the widget configuration.\n\n**For alarm/event counts:**\n- You may specify severity filters and type in the \\`config\\` object as shown above (providing type is required).\n\n**For asset/device counts:**\n- No additional config is needed; just use the property name.\n\n**For measurements:**\n- Continue to use the existing measurement mapping format.\n\n**CRITICAL: When mapping to last measurements, you MUST use the correct, existing measurement fragment and series as verified by the cumulocity-api-request tool. Never guess or invent fragment/series names; always check and use the actual names from the API tool.**\n\n1. **Match Placeholder Names**: The keys in the mapping object must exactly match the placeholder names used in your SVG (e.g., if you use \\${this.c8yScadaValues?.temperature?.value}, create a mapping for \"temperature\")\n\n2. **Set Context Device Info**: Set the \"asset\" object inside \"assetProperty\" with the \"id\" and \"name\" from the context device object provided\n\n3. **Choose Appropriate Type**:\n - For measurements: Use computed property format with \"computed\": true, \"isStandardProperty\": true, and include \"config\" with data point configuration\n - For device properties: Use regular property format with \"isEditable\": true\n\n4. **Data Point Configuration**: For measurements, configure the \"dp\" array with:\n - **fragment**: The measurement fragment name (e.g., \"c8y_Temperature\", \"c8y_Battery\")\n - **series**: The series name within the fragment\n - **unit**: The unit of measurement\n - **label**: A descriptive label\n - **__target**: Reference to the target device with name and id\n - **resultType**: MUST always be \\`4\\` — this is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties\n - **rawValue**: MUST always be \\`true\\`, so \\`.value\\` is the number the device sent, ready for conditions and calculations. Formatting for display happens in the SVG template (see \"numeric formatting\")\n\n5. **Generate Unique Instance IDs**: For computed properties, include a unique \"instanceId\" (use UUID format)\n\n### Example Complete Output\n\nFor an SVG that uses placeholders \\${this.c8yScadaValues?.temperature?.value}, \\${this.c8yScadaValues?.pressure?.value}, and \\${this.c8yScadaValues?.deviceName}:\n\n<${SCADA_OUTPUT_TAG_NAME}>\n <${SVG_EXTRACT_TAG_NAME}>\n <!-- SVG code here -->\n </${SVG_EXTRACT_TAG_NAME}>\n <${MAPPING_EXTRACT_TAG_NAME}>\n {\n \"temperature\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Temperature\",\n \"unit\": \"°C\",\n \"color\": \"#347928\",\n \"series\": \"T\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Temperature → T\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Temperature Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4,\n \"rawValue\": true\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n },\n \"pressure\": {\n \"assetProperty\": {\n \"temporary\": true,\n \"instanceId\": \"b2c3d4e5-f6a7-8901-bcde-f12345678901\",\n \"computed\": true,\n \"isEditable\": false,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"lastMeasurement\": {\n \"label\": \"Last measurement\",\n \"type\": \"string\"\n }\n }\n },\n \"name\": \"lastMeasurement\",\n \"active\": true,\n \"isStandardProperty\": true,\n \"label\": \"Last measurement\",\n \"contextAssetId\": \"94130201\",\n \"type\": \"string\",\n \"config\": {\n \"dp\": [\n {\n \"fragment\": \"c8y_Pressure\",\n \"unit\": \"kPa\",\n \"color\": \"#2196F3\",\n \"series\": \"pressure\",\n \"lineType\": \"line\",\n \"__active\": true,\n \"__template\": null,\n \"label\": \"c8y_Pressure → pressure\",\n \"renderType\": \"min\",\n \"yAxisType\": null,\n \"__target\": {\n \"name\": \"Pressure Sensor\",\n \"id\": \"94130201\"\n }\n }\n ],\n \"resultType\": 4,\n \"rawValue\": true\n },\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Pressure Sensor\"\n }\n }\n },\n \"deviceName\": {\n \"assetProperty\": {\n \"isEditable\": true,\n \"c8y_JsonSchema\": {\n \"properties\": {\n \"name\": {\n \"label\": \"Name\",\n \"type\": \"string\",\n \"key\": \"name\",\n \"properties\": {}\n }\n }\n },\n \"name\": \"name\",\n \"active\": true,\n \"label\": \"Name\",\n \"type\": \"string\",\n \"asset\": {\n \"id\": 94130201,\n \"name\": \"Temperature Sensor\"\n }\n }\n }\n }\n </${MAPPING_EXTRACT_TAG_NAME}>\n</${SCADA_OUTPUT_TAG_NAME}>\n\n## Data Binding Syntax\n\nYou can make your SVG dynamic by binding to data values using template literal syntax:\n\n### Accessing Device Data\n\n**IMPORTANT**: To use and display ANY value from the context device object or its measurements, you MUST use the \\`c8yScadaValues\\` syntax. This is the ONLY correct way to access device data because:\n1. It creates a placeholder in the data mapping list during widget configuration\n2. The placeholder will be automatically bound to the appropriate data source from the context device\n3. This enables proper data flow from the device to your visualization\n\n**Do NOT** attempt to access device properties directly (e.g., \\${this.device?.property}) or use any other syntax. Always use \\`c8yScadaValues\\` with a short, descriptive placeholder name.\n\n### Two Types of Data Access\n\n#### 1. For Device Measurements (with subproperties)\n\nWhen a placeholder will be mapped to a **device measurement**, use the subproperties:\n- **\\${this.c8yScadaValues?.placeholderName?.value}** - The numeric value of the measurement, exactly as the device sent it (with \\`rawValue: true\\`)\n- **\\${this.c8yScadaValues?.placeholderName?.unit}** - The unit of measurement (e.g., \"°C\", \"kPa\", \"%\")\n\n**IMPORTANT — numeric formatting:** \\`.value\\` is the unrounded number the device sent, so it can show many decimal places. When displaying it as text, format it in the template, e.g. \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`. Keep the \\`Number(...)\\` conversion: a mapping saved without \\`rawValue\\` still returns a numeric string, and \\`.toFixed()\\` throws on a string. Use the raw \\`.value\\` (no formatting) in conditions and calculations.\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value ?? '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value ?? '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n#### 2. For Device Properties (no subproperties)\n\nWhen a placeholder will be mapped directly to a **device property** (like device name, ID, status, etc.), access it directly WITHOUT subproperties:\n\n\\`\\`\\`xml\n<!-- Display device name -->\n<text x=\"100\" y=\"50\">Device: \\${this.c8yScadaValues?.deviceName || 'Unknown'}</text>\n\n<!-- Display device ID -->\n<text x=\"100\" y=\"75\">ID: \\${this.c8yScadaValues?.deviceId || '-'}</text>\n\n<!-- Display custom property -->\n<text x=\"100\" y=\"100\">Location: \\${this.c8yScadaValues?.location || 'Not set'}</text>\n\\`\\`\\`\n\n**Remember**: Each placeholder name (e.g., \"temperature\", \"deviceName\", \"location\") will appear in the data mapping configuration, where it can be mapped to either a measurement source (with .value, .unit, .time) or a device property (direct value).\n\n\\`\\`\\`xml\n<!-- Display temperature value with units -->\n<text x=\"100\" y=\"50\">\\${this.c8yScadaValues?.temperature?.value ?? '-'} \\${this.c8yScadaValues?.temperature?.unit || ''}</text>\n\n<!-- Display pressure value with units -->\n<text x=\"100\" y=\"100\">Pressure: \\${this.c8yScadaValues?.pressure?.value ?? '-'} \\${this.c8yScadaValues?.pressure?.unit || 'kPa'}</text>\n\n<!-- Display measurement with timestamp -->\n<text x=\"100\" y=\"150\">Level: \\${this.c8yScadaValues?.level?.value ?? '-'}% (Updated: \\${this.c8yScadaValues?.level?.time || 'N/A'})</text>\n\\`\\`\\`\n\n### Conditional Styling\nApply CSS classes dynamically based on data values:\n\n\\`\\`\\`xml\n<style>\n .temp-normal { fill: var(--c8y-palette-status-success); }\n .temp-warning { fill: var(--c8y-palette-status-warning); }\n .temp-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<text x=\"100\" y=\"50\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : \n this.c8yScadaValues?.temperature?.value > 60 ? 'temp-warning' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n</text>\n\\`\\`\\`\n\n### Dynamic Attributes\nChange SVG attributes based on data:\n\n\\`\\`\\`xml\n<!-- Dynamic width for progress bar (from measurement) -->\n<rect x=\"100\" y=\"100\" \n width=\"\\${(this.c8yScadaValues?.progress?.value || 0) * 3}\" \n height=\"30\" \n fill=\"var(--c8y-brand-primary)\" />\n<text x=\"100\" y=\"145\">\\${this.c8yScadaValues?.progress?.value || 0}\\${this.c8yScadaValues?.progress?.unit || '%'}</text>\n\n<!-- Dynamic rotation for valve (from measurement) -->\n<g transform=\"rotate(\\${this.c8yScadaValues?.valveAngle?.value || 0} 200 200)\">\n <rect x=\"190\" y=\"150\" width=\"20\" height=\"100\" fill=\"#888\" />\n</g>\n\n<!-- Dynamic fill color based on status (from device property - no .value needed) -->\n<circle cx=\"200\" cy=\"200\" r=\"50\" \n fill=\"\\${this.c8yScadaValues?.status === 'active' ? 'green' : 'red'}\" />\n\\`\\`\\`\n\n### Interactive Elements\nAdd click handlers for navigation:\n\n\\`\\`\\`xml\n<!-- Navigate to device details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToDeviceDetails(this.c8yScadaValues?.deviceId)}>\n <rect x=\"50\" y=\"50\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"75\" text-anchor=\"middle\" fill=\"white\">View Device</text>\n</g>\n\n<!-- Navigate to group details -->\n<g class=\"button\" @click=\\${() => this.c8yScadaFunctions?.goToGroupDetails(this.c8yScadaValues?.groupId)}>\n <rect x=\"50\" y=\"100\" width=\"150\" height=\"40\" fill=\"var(--c8y-brand-primary)\" rx=\"4\" />\n <text x=\"125\" y=\"125\" text-anchor=\"middle\" fill=\"white\">View Group</text>\n</g>\n\\`\\`\\`\n\n### Alarm Status Indicators\nDisplay alarm status with appropriate colors:\n\n\\`\\`\\`xml\n<style>\n .alarm-none { fill: var(--c8y-palette-status-success); }\n .alarm-warning { fill: var(--c8y-palette-status-info); }\n .alarm-minor { fill: var(--c8y-palette-status-warning-high); }\n .alarm-major { fill: var(--c8y-palette-status-warning); }\n .alarm-critical { fill: var(--c8y-palette-status-danger); }\n</style>\n\n<!-- Alarm status circle -->\n<circle cx=\"100\" cy=\"100\" r=\"15\" \n class=\"alarm-\\${this.c8yScadaFunctions?.getActiveAlarmsStatusClass(this.c8yScadaValues?.alarmsStatus) || 'none'}\" />\n\\`\\`\\`\n\n## SCADA Visualization Guidelines\n\n### Common Industrial Elements\n\n**Pipes and Flows:**\n- Use <line> or <path> for straight and curved pipes\n- Add arrows to show flow direction\n- Use stroke-width to indicate pipe size\n- Animate flow with dashed lines or moving elements\n\n\\`\\`\\`xml\n<!-- Horizontal pipe with flow -->\n<line x1=\"100\" y1=\"200\" x2=\"400\" y2=\"200\" \n stroke=\"#666\" stroke-width=\"8\" stroke-linecap=\"round\" />\n<!-- Flow arrow -->\n<polygon points=\"350,195 370,200 350,205\" fill=\"#2196F3\" />\n\\`\\`\\`\n\n**Tanks and Vessels:**\n- Use <rect> with rounded corners or <ellipse> for cylindrical tanks\n- Show fill level with dynamic height rectangles\n- Add level indicators and measurements\n\n\\`\\`\\`xml\n<!-- Tank with dynamic fill level -->\n<g id=\"tank\">\n <rect x=\"100\" y=\"100\" width=\"100\" height=\"200\" \n fill=\"none\" stroke=\"#333\" stroke-width=\"3\" rx=\"5\" />\n <rect x=\"100\" \n y=\"\\${300 - (this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n width=\"100\" \n height=\"\\${(this.c8yScadaValues?.tankLevel?.value || 0) * 2}\" \n fill=\"#2196F3\" opacity=\"0.7\" />\n <text x=\"150\" y=\"320\" text-anchor=\"middle\">\n \\${this.c8yScadaValues?.tankLevel?.value || 0}\\${this.c8yScadaValues?.tankLevel?.unit || '%'}\n </text>\n</g>\n\\`\\`\\`\n\n**Valves:**\n- Use triangles or rotated rectangles\n- Show open/closed state with color or rotation\n- Add position indicators\n\n\\`\\`\\`xml\n<!-- Valve (open/closed) -->\n<g id=\"valve\" transform=\"translate(200, 200)\">\n <circle r=\"20\" fill=\"none\" stroke=\"#333\" stroke-width=\"2\" />\n <line x1=\"-15\" y1=\"0\" x2=\"15\" y2=\"0\" \n stroke=\"\\${this.c8yScadaValues?.valveOpen?.value ? '#4CAF50' : '#F44336'}\" \n stroke-width=\"3\" \n transform=\"rotate(\\${this.c8yScadaValues?.valveOpen?.value ? 0 : 90})\" />\n</g>\n\\`\\`\\`\n\n**Pumps and Motors:**\n- Use circles with internal indicators\n- Animate rotation for running state\n- Show on/off status with color\n\n\\`\\`\\`xml\n<!-- Pump -->\n<g id=\"pump\">\n <circle cx=\"200\" cy=\"200\" r=\"30\" \n fill=\"\\${this.c8yScadaValues?.pumpRunning?.value ? '#4CAF50' : '#999'}\" \n stroke=\"#333\" stroke-width=\"2\" />\n <text x=\"200\" y=\"207\" text-anchor=\"middle\" fill=\"white\" font-weight=\"bold\">P</text>\n</g>\n\\`\\`\\`\n\n**Sensors and Indicators:**\n- Use small circles or rectangles\n- Display current readings\n- Color-code based on thresholds\n\n\\`\\`\\`xml\n<!-- Temperature sensor -->\n<g id=\"temp-sensor\">\n <rect x=\"100\" y=\"100\" width=\"80\" height=\"40\" \n fill=\"var(--c8y-root-component-background-default)\" \n stroke=\"#333\" rx=\"4\" />\n <text x=\"140\" y=\"125\" text-anchor=\"middle\" \n class=\"\\${this.c8yScadaValues?.temperature?.value > 80 ? 'temp-critical' : 'temp-normal'}\">\n \\${this.c8yScadaValues?.temperature?.value ?? '-'}\\${this.c8yScadaValues?.temperature?.unit || '°C'}\n </text>\n</g>\n\\`\\`\\`\n\n### Color Conventions\n\n**Operational States:**\n- Green (#4CAF50 or var(--c8y-palette-status-success)): Running/OK/Open\n- Red (#F44336 or var(--c8y-palette-status-danger)): Stopped/Error/Closed\n- Orange (#FF9800 or var(--c8y-palette-status-warning)): Warning/Standby\n- Gray (#999): Inactive/Disabled\n- Blue (#2196F3 or var(--c8y-brand-primary)): Water/Liquid/Flow\n\n**Use Cumulocity design tokens:**\n- var(--c8y-brand-primary): Brand color\n- var(--c8y-palette-status-success): Success green\n- var(--c8y-palette-status-warning): Warning orange\n- var(--c8y-palette-status-danger): Error/critical red\n- var(--c8y-root-component-background-default): Background\n- var(--c8y-root-component-color-default): Text color\n\n### Layout Best Practices\n\n1. **Left-to-right flow**: Process flows typically go from left to right\n2. **Hierarchical grouping**: Group related components visually\n3. **Consistent spacing**: Maintain uniform spacing between elements\n4. **Clear labels**: Label all major components and measurement points\n5. **Legend/Key**: Include a legend if using special symbols or colors\n6. **Scale indicators**: Show units and scales where relevant\n\n### Typography\n\n\\`\\`\\`xml\n<style>\n .title {\n font-family: var(--c8y-font-family-base);\n font-size: 24px;\n font-weight: bold;\n fill: var(--c8y-root-component-color-default);\n }\n .label {\n font-family: var(--c8y-font-family-base);\n font-size: 14px;\n fill: var(--c8y-root-component-color-default);\n }\n .value {\n font-family: var(--c8y-font-family-base);\n font-size: 16px;\n font-weight: bold;\n fill: var(--c8y-brand-primary);\n }\n</style>\n\\`\\`\\`\n\n## Migrating a Legacy SCADA 1.0 SVG\n\nA legacy SVG is recognisable by AngularJS syntax: \\`{{placeholder}}\\` interpolation and \\`ng-*\\` attributes (\\`ng-if\\`, \\`ng-show\\`, \\`ng-hide\\`, \\`ng-class\\`, \\`ng-click\\`). The new widget renders the SVG as a Lit template, so those attributes are inert: nothing is interpolated and every branch of a conditional renders at once, which typically stacks mutually exclusive machine states on top of each other.\n\nWhen the user supplies such an SVG, or asks to migrate, convert or modernise one:\n\n1. **Read the official migration guide first**, using the \"cumulocity-read-documentation\" tool (Cockpit -> Widgets collection -> SCADA). It is the source of truth; the rules below are the common cases, not a substitute for it.\n2. Replace every \\`{{name}}\\` with \\`\\${this.c8yScadaValues?.name ?? '-'}\\`.\n3. Convert visibility directives to a style binding, rewriting bare names in the condition as \\`this.c8yScadaValues?.name\\`:\n - \\`ng-if=\"cond\"\\` and \\`ng-show=\"cond\"\\` -> \\`style=\"\\${cond ? '' : 'display:none'}\"\\`\n - \\`ng-hide=\"cond\"\\` -> \\`style=\"\\${cond ? 'display:none' : ''}\"\\`\n4. Convert \\`ng-class\\` to \\`class=\"\\${...}\"\\` and \\`ng-click\\` to \\`@click=\\${() => ...}\\`, taking helpers from \\`this.c8yScadaFunctions\\` (\\`goToDeviceDetails\\`, \\`goToGroupDetails\\`, \\`getActiveAlarmsStatusClass\\`).\n5. Never leave a \\`\\${...}\\` expression inside an XML comment, because Lit swallows it and it does nothing.\n6. Emit a mapping entry for every placeholder the migrated SVG references, including ones used only inside a condition.\n7. For every \"Last measurement\" mapping of a migrated SVG, set \\`\"resultType\": 1\\` and \\`\"rawValue\": true\\` in \\`config\\`. The migrated drawing reads bare values (\\`this.c8yScadaValues?.name\\`, without \\`.value\\`) and compares them as numbers, as SCADA 1.0 did: a rounded string such as \\`\"0.00\"\\` is truthy, so a condition like \\`name ? … : …\\` would pick the wrong branch. This is the only exception to the \\`\"resultType\": 4\\` rule.\n8. **Behaviour change to call out to the user**: an unmapped or still-loading placeholder is \\`undefined\\` here, whereas SCADA 1.0 resolved it to \\`''\\` (which equals \\`0\\` in a loose comparison). A condition such as \\`state == 0\\` that matched by default in 1.0 will no longer match. If the user wants the old behaviour, add an explicit check, e.g. \\`state == 0 || state == null\\`.\n\n## Important Notes\n\n- **When mapping to last measurements, ALWAYS use the correct, existing fragment and series as verified by the cumulocity-api-request tool. Never guess or invent measurement names.**\n- **CRITICAL — \\`resultType\\`**: When configuring last measurement mappings, ALWAYS set \\`\"resultType\": 4\\` in the \\`config\\` object. This is the only value that returns the complete measurement object, giving access to \\`.value\\` and \\`.unit\\` sub-properties. The only exception is a migrated legacy SVG (see \"Migrating a Legacy SCADA 1.0 SVG\").\n- **CRITICAL — \\`rawValue\\`**: ALWAYS set \\`\"rawValue\": true\\` in the \\`config\\` of a last measurement mapping, so the drawing gets the number the device sent instead of a string rounded to 2 decimal places. Format values for display in the SVG template, not in the mapping.\n- **CRITICAL — numeric formatting**: \\`.value\\` is the raw number, so \\`0\\` is falsy: write display fallbacks as \\`?.value ?? '-'\\`, never \\`?.value || '-'\\`, which hides a real \\`0\\`. Round only text output, never conditions or calculations, and only when the user asks for it or the series has fractional values: \\`Number(this.c8yScadaValues?.pressure?.value).toFixed(2)\\`, guarded for a missing value.\n- **CRITICAL — changes**: When the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff.\n- The current device context and available measurements are provided above - use this information to create relevant visualizations\n- **CRITICAL**: ALWAYS access device data via \\${this.c8yScadaValues?.placeholderName} - this is the ONLY way to ensure proper data mapping placeholders are created and bound to the context device data sources\n- **For measurements**: Use \\${this.c8yScadaValues?.placeholderName?.value} to access the value, and optionally .unit and .time for additional measurement data\n- **For device properties**: Use \\${this.c8yScadaValues?.placeholderName} directly WITHOUT subproperties (no .value, .unit, or .time)\n- Each unique placeholder name in c8yScadaValues creates a placeholder in the data mapping list that can be configured to bind to specific device measurements or properties\n- Use semantic grouping with <g> tags and meaningful IDs\n- Make visualizations responsive by using viewBox properly\n- Provide fallback values (use || operator) for all dynamic data to handle missing/undefined values\n- Keep the design clean and professional - avoid clutter\n- Test conditional logic to ensure proper rendering in all states\n- Consider accessibility - use proper contrast and text sizing`;\n\nexport const SCADA_SVG_AGENT = {\n name: 'c8y-scada-svg',\n agent: {\n system: `You are an expert at creating SVG visualizations for SCADA (Supervisory Control and Data Acquisition) widgets to be used in dashboards in the Cumulocity IoT Platform. Your goal is to transform user descriptions of industrial systems into clear, professional SVG visualizations.\n\n## Workflow\n\n1. **Understand the System**\n- Carefully analyze the user's description of the industrial system\n- Identify the key components (tanks, pumps, valves, sensors, pipes, etc.)\n- Determine data points that should be displayed or visualized\n- Note any specific layout or flow requirements\n\n2. **Migrate, if the user supplied a legacy SVG**\n- If the input contains \\`{{...}}\\` interpolation or \\`ng-*\\` attributes, it is a SCADA 1.0 drawing and must be converted before anything else\n- Read the migration guide with the \"cumulocity-read-documentation\" tool, then apply the rules in the \"Migrating a Legacy SCADA 1.0 SVG\" section below\n- Preserve the original artwork (paths, images, transforms, ids) exactly; change only the data binding and directive syntax\n- Tell the user which conditions changed behaviour because unmapped placeholders are now \\`undefined\\` rather than \\`''\\`\n\n3. **Verify Available Data (if needed)**\n- If the visualization requires specific measurements or device properties, use the \"cumulocity-api-request\" tool to verify what data is available\n- Check available measurements, properties, or alarms on the target device/asset\n- Document which data points can be used for dynamic visualization\n\n4. **Design the SVG**\n- Create a clean, professional SVG visualization\n- Use appropriate industrial symbols and conventions\n- Implement data binding for dynamic elements\n- Apply proper color coding for operational states\n- Ensure responsive scaling with proper viewBox\n\n5. **Output the Code**\n- Wrap BOTH outputs together in a single <${SCADA_OUTPUT_TAG_NAME}> block: SVG code in <${SVG_EXTRACT_TAG_NAME}> tags followed by mapping in <${MAPPING_EXTRACT_TAG_NAME}> tags, both inside the wrapper\n- CRITICAL: Include ONLY the SVG code inside the SVG tags and ONLY the JSON mapping inside the mapping tags - no explanatory text, no markdown formatting, no comments outside the inner tags\n- Ensure every placeholder used in the SVG (via \\${this.c8yScadaValues?.placeholderName}) has a corresponding entry in the mapping object\n- **If the user requests any change to the SVG or mappings, always re-output the complete \\`<${SCADA_OUTPUT_TAG_NAME}>\\` block with the fix included — never output only the changed portion or a diff**\n- Follow the guidelines in: ${SVG_SCADA_INSTRUCTIONS}\n\n6. **Validate Quality**\n- Verify the visualization represents the described system accurately\n- Ensure all dynamic data bindings use proper fallback values\n- Check that colors and styling follow Cumulocity design standards\n- If the user's description is unclear or lacks critical details, ask for clarification\n\n## Key Principles\n- Professional industrial design - clean, clear, uncluttered\n- Semantic SVG structure with organized groups\n- Proper data binding syntax for dynamic content\n- Responsive and theme-aware using Cumulocity CSS variables\n- No emojis or Unicode symbols - use SVG shapes and text only\n`,\n maxOutputTokens: 20000\n },\n type: 'text',\n mcp: [\n {\n serverName: 'cumulocity-default',\n tools: ['cumulocity-api-request', 'cumulocity-read-documentation']\n }\n ]\n} as AgentDefinition;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"AAMA;AACA;AACA;AACA,MAAM,qBAAqB,GAAG,kBAAkB;AAChD,MAAM,oBAAoB,GAAG,iBAAiB;AAC9C,MAAM,wBAAwB,GAAG,qBAAqB;AAEtD,MAAM,sBAAsB,GAAG,CAAA;;;;gEAIiC,qBAAqB,CAAA;mDAClC,oBAAoB,CAAA;qDAClB,wBAAwB,CAAA;+CAC9B,qBAAqB,CAAA;;;;;;;GAOjE,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;;;;;;;;;;;;;;;;;;;MAoBnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;MAEvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;AAYX,YAAA,EAAA,qBAAqB,qLAAqL,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoY7O,qBAAqB,CAAA;KACnB,oBAAoB,CAAA;;MAEnB,oBAAoB,CAAA;KACrB,wBAAwB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA0HvB,wBAAwB,CAAA;IAC1B,qBAAqB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uHAyS8F,qBAAqB,CAAA;;;;;;;;;;;+DAW7E;AAExD,MAAM,eAAe,GAAG;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,KAAK,EAAE;AACL,QAAA,MAAM,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CA6BgC,qBAAqB,CAAA,sBAAA,EAAyB,oBAAoB,CAAA,+BAAA,EAAkC,wBAAwB,CAAA;;;+FAGzE,qBAAqB,CAAA;8BACtF,sBAAsB;;;;;;;;;;;;;;AAcnD,CAAA;AACG,QAAA,eAAe,EAAE;AAClB,KAAA;AACD,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,GAAG,EAAE;AACH,QAAA;AACE,YAAA,UAAU,EAAE,oBAAoB;AAChC,YAAA,KAAK,EAAE,CAAC,wBAAwB,EAAE,+BAA+B;AAClE;AACF;;;ACp7BH;;AAEG;;;;"}
|
|
@@ -4,7 +4,7 @@ import { inject, Injector, Injectable } from '@angular/core';
|
|
|
4
4
|
import { TranslateService } from '@ngx-translate/core';
|
|
5
5
|
import { MeasurementService, InventoryService, AlarmService, EventService, OperationService } from '@c8y/client';
|
|
6
6
|
import { MeasurementRealtimeService, DatePipe, translateDatapointUnit, ManagedObjectRealtimeService, AlarmRealtimeService, EventRealtimeService, OperationRealtimeService } from '@c8y/ngx-components';
|
|
7
|
-
import { from, map, filter, startWith, take, switchMap, distinctUntilChanged, pairwise, share, NEVER, scan, tap, of, catchError, combineLatest, merge } from 'rxjs';
|
|
7
|
+
import { from, map, filter, startWith, take, defaultIfEmpty, switchMap, distinctUntilChanged, pairwise, share, NEVER, scan, tap, of, catchError, combineLatest, merge } from 'rxjs';
|
|
8
8
|
import { gettext } from '@c8y/ngx-components/gettext';
|
|
9
9
|
import { get } from 'lodash-es';
|
|
10
10
|
|
|
@@ -37,19 +37,28 @@ class LastMeasurementStrategy {
|
|
|
37
37
|
return from(this.measurementService.list(measurementFilter)).pipe(map(({ data }) => data[0]), filter(measurement => !!measurement), map(measurement => this.formatMeasurement(measurement)));
|
|
38
38
|
}
|
|
39
39
|
createRealtimeStream(initialValue) {
|
|
40
|
-
|
|
40
|
+
const measurementUpdates$ = this.measurementRealtime
|
|
41
41
|
.onCreateOfSpecificMeasurement$(this.datapoint.fragment, this.datapoint.series, this.datapoint.__target)
|
|
42
|
-
.pipe(map(measurement => this.formatMeasurement(measurement))
|
|
42
|
+
.pipe(map(measurement => this.formatMeasurement(measurement)));
|
|
43
|
+
// Without a measurement yet, consumers keep their own empty state until the first one arrives.
|
|
44
|
+
const hasInitialValue = initialValue != null;
|
|
45
|
+
return hasInitialValue
|
|
46
|
+
? measurementUpdates$.pipe(startWith(initialValue))
|
|
47
|
+
: measurementUpdates$;
|
|
43
48
|
}
|
|
44
49
|
formatMeasurement(measurement) {
|
|
45
50
|
const resultType = this.config?.resultType;
|
|
46
51
|
const fragment = this.datapoint.fragment;
|
|
47
52
|
const series = this.datapoint.series;
|
|
53
|
+
const seriesValue = measurement[fragment][series];
|
|
54
|
+
const keepRawValue = !!this.config?.rawValue;
|
|
48
55
|
const decimalPlaces = this.config?.numberOfDecimalPlaces ?? DEFAULT_DECIMAL_PLACES;
|
|
49
|
-
const value =
|
|
56
|
+
const value = keepRawValue
|
|
57
|
+
? seriesValue.value
|
|
58
|
+
: this.formatValue(seriesValue.value, decimalPlaces);
|
|
50
59
|
// The data point carries the unit configured for it in the library, which is the one a tenant
|
|
51
60
|
// registers a translation for. The measurement only reports whatever the device sent.
|
|
52
|
-
const unit = translateDatapointUnit(this.datapoint.unit ||
|
|
61
|
+
const unit = translateDatapointUnit(this.datapoint.unit || seriesValue.unit, this.translate);
|
|
53
62
|
if (resultType === RESULT_TYPES$1.VALUE.value) {
|
|
54
63
|
return value;
|
|
55
64
|
}
|
|
@@ -61,7 +70,9 @@ class LastMeasurementStrategy {
|
|
|
61
70
|
return `${date} | ${value} ${unit}`;
|
|
62
71
|
}
|
|
63
72
|
else if (resultType === RESULT_TYPES$1.OBJECT.value) {
|
|
64
|
-
return
|
|
73
|
+
return keepRawValue
|
|
74
|
+
? { ...seriesValue }
|
|
75
|
+
: { ...seriesValue, value: parseFloat(value).toFixed(decimalPlaces) };
|
|
65
76
|
}
|
|
66
77
|
return '';
|
|
67
78
|
}
|
|
@@ -104,9 +115,14 @@ class RealtimeValueHandler {
|
|
|
104
115
|
}
|
|
105
116
|
}
|
|
106
117
|
handleUncontrolledRealtime() {
|
|
107
|
-
return this.
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
return this.fetchCurrentValueThenStream();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A fetch that completes without a value, e.g. a series with no measurement yet, still starts
|
|
122
|
+
* the realtime stream, with `null` as the initial value.
|
|
123
|
+
*/
|
|
124
|
+
fetchCurrentValueThenStream() {
|
|
125
|
+
return this.strategy.fetchCurrentValue().pipe(defaultIfEmpty(null), switchMap(currentValue => this.strategy.createRealtimeStream(currentValue)));
|
|
110
126
|
}
|
|
111
127
|
handleControlledRealtime(control$) {
|
|
112
128
|
const controlWithPrevious$ = control$.pipe(distinctUntilChanged(), startWith(null), pairwise(), share());
|
|
@@ -130,9 +146,7 @@ class RealtimeValueHandler {
|
|
|
130
146
|
// Realtime is enabled
|
|
131
147
|
if (this.config.refetchOnResume || previous === null) {
|
|
132
148
|
// Re-fetch current value and start streaming
|
|
133
|
-
return this.
|
|
134
|
-
.fetchCurrentValue()
|
|
135
|
-
.pipe(switchMap(currentValue => this.strategy.createRealtimeStream(currentValue)));
|
|
149
|
+
return this.fetchCurrentValueThenStream();
|
|
136
150
|
}
|
|
137
151
|
else {
|
|
138
152
|
// Continue with realtime stream without re-fetching
|
|
@@ -149,7 +163,7 @@ class RealtimeValueHandler {
|
|
|
149
163
|
*
|
|
150
164
|
* @param config - Measurement configuration (datapoint, result type, etc.)
|
|
151
165
|
* @param metadata - Configuration controlling the behavior of the function
|
|
152
|
-
* @returns
|
|
166
|
+
* @returns Stream of measurement values, shaped by `config`
|
|
153
167
|
*/
|
|
154
168
|
function lastMeasurementValue(config, metadata = { mode: 'realtime' }) {
|
|
155
169
|
const injector = inject(Injector);
|
|
@@ -173,12 +187,12 @@ const lastMeasurement = {
|
|
|
173
187
|
name: 'lastMeasurement',
|
|
174
188
|
label: gettext('Last measurement'),
|
|
175
189
|
type: 'string',
|
|
176
|
-
config: { dp: [], resultType: 1 },
|
|
190
|
+
config: { dp: [], resultType: 1, rawValue: true },
|
|
177
191
|
computed: true,
|
|
178
192
|
isEditable: false,
|
|
179
193
|
isStandardProperty: true
|
|
180
194
|
},
|
|
181
|
-
loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-last-measurement-config.component-
|
|
195
|
+
loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-last-measurement-config.component-d3JeKEr4.mjs').then(m => m.ComputedPropertyLastMeasurementConfigComponent),
|
|
182
196
|
value: ({ config }) => {
|
|
183
197
|
return lastMeasurementValue(config);
|
|
184
198
|
}
|
|
@@ -774,7 +788,7 @@ const fieldbusItemStatus = {
|
|
|
774
788
|
isEditable: false,
|
|
775
789
|
isStandardProperty: true
|
|
776
790
|
},
|
|
777
|
-
loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-
|
|
791
|
+
loadConfigComponent: () => import('./c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BqbzREl7.mjs').then(m => m.FieldbusItemStatusConfigComponent),
|
|
778
792
|
value: ({ config, context }) => fieldbusItemStatusValue(context, config)
|
|
779
793
|
};
|
|
780
794
|
function fieldbusItemStatusValue(asset, config, metadata = { mode: 'realtime' }) {
|
|
@@ -809,4 +823,4 @@ const computedAssetPropertiesProviders = [
|
|
|
809
823
|
*/
|
|
810
824
|
|
|
811
825
|
export { DEFAULT_DECIMAL_PLACES as D, FieldbusService as F, RESULT_TYPES$1 as R, RESULT_TYPES as a, computedAssetPropertiesProviders as c };
|
|
812
|
-
//# sourceMappingURL=c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-
|
|
826
|
+
//# sourceMappingURL=c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-B0EYpohd.mjs.map
|