Add full Ignition Perspective view.json generator with bindings, scripts, and controls

Extracts all CDW5_SCADA boilerplate (zoom controls, markdown tooltip,
pan/wheel events, element color/state/priority tag bindings, display
filters, Start/Stop special-case bindings) into a new ignition-view.ts
module.  deployToIgnition() now produces the complete view.json instead
of a minimal skeleton.  Also adds ignitionViewPath to the store/toolbar
and routes it through the deploy endpoint so views land under the correct
sub-folder (e.g. DetailedView/MCM09).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
igurielidze 2026-04-01 01:02:39 +04:00
parent 3f122d9177
commit 0f50ab27ae
5 changed files with 677 additions and 24 deletions

View File

@ -207,7 +207,11 @@
<div class="section-body"> <div class="section-body">
<div class="inline-row"> <div class="inline-row">
<label>Project</label> <label>Project</label>
<input type="text" bind:value={layout.ignitionProject} placeholder="Testing_Project"> <input type="text" bind:value={layout.ignitionProject} placeholder="CDW5_SCADA">
</div>
<div class="inline-row">
<label>Path</label>
<input type="text" bind:value={layout.ignitionViewPath} placeholder="DetailedView">
</div> </div>
<div class="inline-row"> <div class="inline-row">
<label>View</label> <label>View</label>

View File

@ -2,6 +2,7 @@ import { layout } from './stores/layout.svelte.js';
import { isEpcType, isInductionType, isSpurType, isCurvedType, isRectConveyanceType, isExtendoType, isPhotoeyeType, getSymbolGroup, EPC_CONFIG, INDUCTION_CONFIG, PHOTOEYE_CONFIG, EXTENDO_CONFIG, LABEL_CONFIG, CONVEYANCE_STYLE, getCurveGeometry } from './symbols.js'; import { isEpcType, isInductionType, isSpurType, isCurvedType, isRectConveyanceType, isExtendoType, isPhotoeyeType, getSymbolGroup, EPC_CONFIG, INDUCTION_CONFIG, PHOTOEYE_CONFIG, EXTENDO_CONFIG, LABEL_CONFIG, CONVEYANCE_STYLE, getCurveGeometry } from './symbols.js';
import { serializeSymbol, deserializeSymbol } from './serialization.js'; import { serializeSymbol, deserializeSymbol } from './serialization.js';
import { parseConveyanceLabel } from './label-utils.js'; import { parseConveyanceLabel } from './label-utils.js';
import { buildIgnitionViewJson } from './ignition-view.js';
import type { PlacedSymbol } from './types.js'; import type { PlacedSymbol } from './types.js';
/** Emit conveyance label text inside a <g> — inherits outer transform from group */ /** Emit conveyance label text inside a <g> — inherits outer transform from group */
@ -556,27 +557,12 @@ export async function exportIgnitionJSON() {
/** Deploy directly to Ignition project directory */ /** Deploy directly to Ignition project directory */
export async function deployToIgnition() { export async function deployToIgnition() {
const component = await buildIgnitionComponent(); const component = await buildIgnitionComponent();
const projectName = layout.ignitionProject || 'Testing_Project'; const projectName = layout.ignitionProject || 'CDW5_SCADA';
const viewName = layout.ignitionViewName || layout.currentMcm || 'MCM01'; const viewName = layout.ignitionViewName || layout.currentMcm || 'MCM01';
const viewPath = layout.ignitionViewPath || 'DetailedView';
const viewJson = JSON.stringify({ const viewData = buildIgnitionViewJson(viewName, component);
custom: {}, const viewJson = JSON.stringify(viewData, null, 2);
params: {},
props: {
defaultSize: { height: layout.canvasH, width: layout.canvasW },
},
root: {
children: [{
meta: { name: viewName },
position: { width: 1, height: 1 },
props: component.props,
type: 'ia.shapes.svg',
}],
meta: { name: 'root' },
props: { mode: 'percent' },
type: 'ia.container.coord',
},
}, null, 2);
// Compute SHA-256 signature of view.json for Ignition resource integrity // Compute SHA-256 signature of view.json for Ignition resource integrity
const viewBytes = new TextEncoder().encode(viewJson); const viewBytes = new TextEncoder().encode(viewJson);
@ -603,7 +589,7 @@ export async function deployToIgnition() {
const resp = await fetch('/api/deploy-ignition', { const resp = await fetch('/api/deploy-ignition', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectName, viewName, viewJson, resourceJson }), body: JSON.stringify({ projectName, viewName, viewPath, viewJson, resourceJson }),
}); });
const result = await resp.json(); const result = await resp.json();
if (result.ok) { if (result.ok) {

File diff suppressed because one or more lines are too long

View File

@ -26,8 +26,9 @@ class LayoutStore {
currentMcm = $state<string>(''); currentMcm = $state<string>('');
// Ignition export settings // Ignition export settings
ignitionProject = $state<string>('Testing_Project'); ignitionProject = $state<string>('CDW5_SCADA');
ignitionViewName = $state<string>(''); ignitionViewName = $state<string>('');
ignitionViewPath = $state<string>('DetailedView');
// PDF state // PDF state
pdfScale = $state(1.0); pdfScale = $state(1.0);

View File

@ -263,9 +263,10 @@ export default defineConfig({
req.on('data', (chunk: Buffer) => { body += chunk.toString(); }); req.on('data', (chunk: Buffer) => { body += chunk.toString(); });
req.on('end', () => { req.on('end', () => {
try { try {
const { projectName, viewName, viewJson, resourceJson } = JSON.parse(body); const { projectName, viewName, viewPath, viewJson, resourceJson } = JSON.parse(body);
const ignitionBase = 'C:/Program Files/Inductive Automation/Ignition/data/projects'; const ignitionBase = 'C:/Program Files/Inductive Automation/Ignition/data/projects';
const viewDir = path.join(ignitionBase, projectName, 'com.inductiveautomation.perspective/views', viewName); const viewSubPath = viewPath ? `${viewPath}/${viewName}` : viewName;
const viewDir = path.join(ignitionBase, projectName, 'com.inductiveautomation.perspective/views', viewSubPath);
fs.mkdirSync(viewDir, { recursive: true }); fs.mkdirSync(viewDir, { recursive: true });
fs.writeFileSync(path.join(viewDir, 'view.json'), viewJson); fs.writeFileSync(path.join(viewDir, 'view.json'), viewJson);
fs.writeFileSync(path.join(viewDir, 'resource.json'), resourceJson); fs.writeFileSync(path.join(viewDir, 'resource.json'), resourceJson);