diff --git a/package.json b/package.json index 8a90807d6..b50ae32f1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "open-plc-editor", "description": "OpenPLC Editor - IDE capable of creating programs for the OpenPLC Runtime", - "version": "4.2.10", + "version": "4.2.9", "license": "GPL-3.0", "author": { "name": "Autonomy Logic" diff --git a/src/frontend/data/constants/app-version.ts b/src/frontend/data/constants/app-version.ts index 3b99e5528..661f491e9 100644 --- a/src/frontend/data/constants/app-version.ts +++ b/src/frontend/data/constants/app-version.ts @@ -18,4 +18,4 @@ * compares a per-deploy `BUILD_ID` (git commit SHA), not this version, so a * stale tab is detected on every deploy even without a version bump. */ -export const APP_VERSION = '4.2.10' +export const APP_VERSION = '4.2.9' diff --git a/src/frontend/store/__tests__/project-validation-variables.test.ts b/src/frontend/store/__tests__/project-validation-variables.test.ts index 5c1611677..9fbb4ffb8 100644 --- a/src/frontend/store/__tests__/project-validation-variables.test.ts +++ b/src/frontend/store/__tests__/project-validation-variables.test.ts @@ -516,6 +516,28 @@ describe('updateVariableValidation', () => { }, ) + it.each([ + ['BYTE', '%IB0'], + ['BYTE', '%QB1'], + ['BYTE', '%MB2'], + ['SINT', '%IB3'], + ['USINT', '%MB4'], + ])('accepts byte locations (I/Q/M) for %s type (%s)', (type, location) => { + const v = makeVariable('Test', type, '') + const result = updateVariableValidation([], { location }, v) + expect(result.ok).toBe(true) + }) + + it.each([ + ['BYTE', '%IX0.0'], // bit location, wrong width for a byte type + ['SINT', '%IW0'], // word location, wrong width + ['BOOL', '%IB0'], // byte location, wrong width for a bit type + ])('rejects a cross-width location for %s (%s)', (type, location) => { + const v = makeVariable('Test', type, '') + const result = updateVariableValidation([], { location }, v) + expect(result.ok).toBe(false) + }) + it('returns ok: true when location is valid for WORD type', () => { const wordVar = makeVariable('Test', 'WORD', '') const result = updateVariableValidation([], { location: '%QW0' }, wordVar) diff --git a/src/frontend/store/slices/project/validation/variables.ts b/src/frontend/store/slices/project/validation/variables.ts index 24a570ac9..63186db6f 100644 --- a/src/frontend/store/slices/project/validation/variables.ts +++ b/src/frontend/store/slices/project/validation/variables.ts @@ -2,6 +2,7 @@ import type { PLCVariable } from '../../../../../middleware/shared/ports/types' import { DISALLOWED_LOCATION_CLASSES } from '../../../../utils/generate-iec-string-to-variables' import { BOOL_LOCATION_REGEX, + BYTE_LOCATION_REGEX, DWORD_LOCATION_REGEX, LWORD_LOCATION_REGEX, PLC_ADDRESS_PREFIX, @@ -131,6 +132,10 @@ const variableLocationValidation = (variableLocation: string, variableType: stri const boolMatch = BOOL_LOCATION_REGEX.test(variableLocation) && variableLocation.split('.')[1] <= '7' return boolMatch } + case 'BYTE': + case 'SINT': + case 'USINT': + return BYTE_LOCATION_REGEX.test(variableLocation) case 'INT': case 'UINT': case 'WORD': @@ -154,6 +159,10 @@ const variableLocationValidationErrorMessage = (variableType: string) => { switch (variableType.toUpperCase()) { case 'BOOL': return 'Valid locations: %QX0.0..7, %IX0.0..7, %MX0.0..7 (change the number to the desired location)' + case 'BYTE': + case 'SINT': + case 'USINT': + return 'Valid locations: %QB0, %IB0, %MB0 (change the number to the desired location)' case 'INT': case 'UINT': case 'WORD': diff --git a/src/frontend/utils/PLC/address-constants/types.ts b/src/frontend/utils/PLC/address-constants/types.ts index 5ab477d40..d856252de 100644 --- a/src/frontend/utils/PLC/address-constants/types.ts +++ b/src/frontend/utils/PLC/address-constants/types.ts @@ -2,6 +2,9 @@ export const PLC_ADDRESS_PREFIX = { BOOL_OUTPUT: '%QX', BOOL_INPUT: '%IX', BOOL_MEMORY: '%MX', + BYTE_OUTPUT: '%QB', + BYTE_INPUT: '%IB', + BYTE_MEMORY: '%MB', WORD_OUTPUT: '%QW', WORD_INPUT: '%IW', WORD_MEMORY: '%MW', @@ -13,10 +16,14 @@ export const PLC_ADDRESS_PREFIX = { LWORD_MEMORY: '%ML', } as const -// Accept every valid IEC area prefix — input (I), output (Q) and memory (M). -// `%MX` memory bits are legitimate (e.g. Modbus coils) and were previously -// rejected here, unlike the word-width regexes below which already allow M. +// Each IEC size class accepts all three area prefixes — input (I), output (Q) +// and memory (M) — matching what strucpp and the runtime image tables support: +// X = bit (BOOL), B = byte (BYTE/SINT/USINT), W = word (INT/UINT/WORD), +// D = dword (DINT/UDINT/REAL/DWORD), L = lword (LINT/ULINT/LREAL/LWORD). +// NOTE: %MB (memory byte) is accepted here even though the runtime does not +// yet back it (no byte_memory buffer) — a runtime fix is planned separately. export const BOOL_LOCATION_REGEX = /^%[QIM]X\d+\.\d$/ +export const BYTE_LOCATION_REGEX = /^%[QIM]B\d+$/ export const WORD_LOCATION_REGEX = /^%[QIM]W\d+$/ export const DWORD_LOCATION_REGEX = /^%[QIM]D\d+$/ export const LWORD_LOCATION_REGEX = /^%[QIM]L\d+$/