diff --git a/src/main/modules/ipc/main.ts b/src/main/modules/ipc/main.ts index 38804d401..ae73dde85 100644 --- a/src/main/modules/ipc/main.ts +++ b/src/main/modules/ipc/main.ts @@ -1187,6 +1187,13 @@ class MainProcessBridge implements MainIpcModule { serialPort: virtualPort, }) await this.debuggerModbusClient.connect() + + // Trigger endianness detection on the emulated runtime. + // getMd5Hash sends 0xDEAD which the runtime uses to detect byte order + // and call set_endianness(). Without this, the default SAME_ENDIANNESS + // causes multi-byte values to be stored with swapped bytes on the + // little-endian AVR emulator. + await this.debuggerModbusClient.getMd5Hash() } else if (connectionType === 'websocket') { if (this.debuggerModbusClient) { this.debuggerModbusClient.disconnect() diff --git a/src/renderer/components/_molecules/graphical-editor/fbd/index.tsx b/src/renderer/components/_molecules/graphical-editor/fbd/index.tsx index 62998a0de..70fe2ac1c 100644 --- a/src/renderer/components/_molecules/graphical-editor/fbd/index.tsx +++ b/src/renderer/components/_molecules/graphical-editor/fbd/index.tsx @@ -222,6 +222,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } debugForcedVariables, editor.meta.name, project, + getCompositeKey, ]) const styledNodes = useMemo(() => { diff --git a/src/renderer/screens/workspace-screen.tsx b/src/renderer/screens/workspace-screen.tsx index 2ad35c224..34cd1d8d4 100644 --- a/src/renderer/screens/workspace-screen.tsx +++ b/src/renderer/screens/workspace-screen.tsx @@ -447,7 +447,7 @@ const WorkspaceScreen = () => { }) }) - const { ladderFlows } = useOpenPLCStore.getState() + const { ladderFlows, fbdFlows } = useOpenPLCStore.getState() project.data.pous.forEach((pou) => { if (pou.type !== 'program') return @@ -473,6 +473,18 @@ const WorkspaceScreen = () => { }) }) } + } else if (pou.data.body.language === 'fbd') { + const currentFbdFlow = fbdFlows.find((flow) => flow.name === pou.data.name) + if (currentFbdFlow) { + currentFbdFlow.rung.nodes.forEach((node) => { + if (node.type === 'block') { + const blockData = node.data as { variable?: { name: string }; executionControl?: boolean } + if (blockData.variable?.name && blockData.executionControl) { + blockExecutionControlMap.set(blockData.variable.name, true) + } + } + }) + } } functionBlockInstances.forEach((fbInstance) => { @@ -628,74 +640,83 @@ const WorkspaceScreen = () => { } }) - if (pou.data.body.language === 'ld') { - const currentLadderFlow = ladderFlows.find((flow) => flow.name === pou.data.name) - if (currentLadderFlow) { - currentLadderFlow.rungs.forEach((rung) => { - rung.nodes.forEach((node) => { - if (node.type !== 'block') return + // Register _TMP_ variables for function BOOL outputs so they get polled + const registerFunctionTempOutputs = (nodes: Array<{ type?: string; data: object }>) => { + nodes.forEach((node) => { + if (node.type !== 'block') return - const blockData = node.data as { - variable?: { name: string } - variant?: { - name: string - type: string - variables: Array<{ name: string; class: string; type: { definition: string; value: string } }> - } - numericId?: string - executionControl?: boolean - } + const blockData = node.data as { + variable?: { name: string } + variant?: { + name: string + type: string + variables: Array<{ name: string; class: string; type: { definition: string; value: string } }> + } + numericId?: string + executionControl?: boolean + } - if (!blockData.variant || blockData.variant.type !== 'function') return + if (!blockData.variant || blockData.variant.type !== 'function') return - const blockName = blockData.variant.name.toUpperCase() - const numericId = blockData.numericId - if (!numericId) return + const blockName = blockData.variant.name.toUpperCase() + const numericId = blockData.numericId + if (!numericId) return - let boolOutputs = blockData.variant.variables.filter( - (v) => - (v.class === 'output' || v.class === 'inOut') && - v.type.definition === 'base-type' && - v.type.value.toUpperCase() === 'BOOL', - ) + let boolOutputs = blockData.variant.variables.filter( + (v) => + (v.class === 'output' || v.class === 'inOut') && + v.type.definition === 'base-type' && + v.type.value.toUpperCase() === 'BOOL', + ) - const hasExecutionControl = blockData.executionControl || false - if (hasExecutionControl) { - const hasENO = boolOutputs.some((v) => v.name.toUpperCase() === 'ENO') - if (!hasENO) { - boolOutputs = [ - ...boolOutputs, - { name: 'ENO', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, - ] - } - } + const hasExecutionControl = blockData.executionControl || false + if (hasExecutionControl) { + const hasENO = boolOutputs.some((v) => v.name.toUpperCase() === 'ENO') + if (!hasENO) { + boolOutputs = [ + ...boolOutputs, + { name: 'ENO', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + ] + } + } - boolOutputs.forEach((outputVar) => { - // Use fallback to try both FB-style and struct-style paths - const index = getIndexFromMapWithFallback( - debugVariableIndexes, - programInstance.name, - `_TMP_${blockName}${numericId}_${outputVar.name}`, - ) + boolOutputs.forEach((outputVar) => { + const index = getIndexFromMapWithFallback( + debugVariableIndexes, + programInstance.name, + `_TMP_${blockName}${numericId}_${outputVar.name}`, + ) - if (index !== undefined) { - const tempVarName = `_TMP_${blockName}${numericId}_${outputVar.name}` - addVariableInfo(index, { - pouName: pou.data.name, - variable: { - name: tempVarName, - type: { definition: 'base-type', value: 'bool' }, - class: 'local', - location: '', - documentation: '', - debug: false, - }, - }) - } + if (index !== undefined) { + const tempVarName = `_TMP_${blockName}${numericId}_${outputVar.name}` + addVariableInfo(index, { + pouName: pou.data.name, + variable: { + name: tempVarName, + type: { definition: 'base-type', value: 'bool' }, + class: 'local', + location: '', + documentation: '', + debug: false, + }, }) - }) + } + }) + }) + } + + if (pou.data.body.language === 'ld') { + const currentLadderFlow = ladderFlows.find((flow) => flow.name === pou.data.name) + if (currentLadderFlow) { + currentLadderFlow.rungs.forEach((rung) => { + registerFunctionTempOutputs(rung.nodes) }) } + } else if (pou.data.body.language === 'fbd') { + const currentFbdFlow = fbdFlows.find((flow) => flow.name === pou.data.name) + if (currentFbdFlow) { + registerFunctionTempOutputs(currentFbdFlow.rung.nodes) + } } } }) @@ -1365,6 +1386,14 @@ const WorkspaceScreen = () => { } } + // Forced variables must also be polled so their current value appears in the debugger panel + const { + workspace: { debugForcedVariables: currentForcedVars }, + } = useOpenPLCStore.getState() + currentForcedVars.forEach((_value, compositeKey) => { + debugVariableKeys.add(compositeKey) + }) + const allIndexes = Array.from(variableInfoMapRef.current.entries()) .filter(([_, varInfos]) => varInfos.some((varInfo) => {