From eefc68dfb17d1f97e1400dc73ea1b05e1b89e3bd Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Fri, 13 Mar 2026 18:15:27 -0300 Subject: [PATCH 01/40] fix(step-31): wire components to ProjectPort and update store action names Replace direct window.bridge casts and sharedWorkspaceActions with ProjectPort methods for create/save/watch operations. Update snapshot actions (addSnapshot -> pushToHistory) and tab close (closeFile -> forceCloseFile) to match unified store API. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../[start]/new-project/steps/third-step.tsx | 44 ++++++++++---- .../editor/graphical/ladder/index.tsx | 24 ++++++-- .../[workspace]/editor/monaco/index.tsx | 60 ++++++++----------- .../display-recent-projects/index.tsx | 6 +- src2/frontend/hooks/use-remove-tab.tsx | 4 +- .../adapters/editor/project-adapter.ts | 4 +- src2/middleware/shared/ports/project-port.ts | 2 + 7 files changed, 89 insertions(+), 55 deletions(-) diff --git a/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx b/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx index b79417703..9266cc586 100644 --- a/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx +++ b/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx @@ -5,20 +5,13 @@ import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../_a import { useOpenPLCStore } from '../../../../../store' import { cn } from '../../../../../utils/cn' import { ConvertToLangShortenedFormat } from '../../../../../utils/formatters/POU' +import { useProject } from '../../../../../../middleware/shared/providers' import { useState } from 'react' import { Controller, SubmitHandler, useForm } from 'react-hook-form' import { useToast } from '../../../[app]/toast/use-toast' import { IntervalModal } from '../interval-model' import { NewProjectStore } from '../store' - -type CreateProjectFileProps = { - language: 'il' | 'st' | 'ld' | 'sfc' | 'fbd' - time: string - type: 'plc-project' | 'plc-library' - name: string - path: string -} { /** TODO: Need to be implemented - Sequential Functional Chart and Functional Block Diagram */ } @@ -47,8 +40,11 @@ const Step3 = ({ onPrev, onFinish, onClose }: { onPrev: () => void; onFinish: () const projectData = NewProjectStore((state) => state.formData) const [isModalOpen, setModalOpen] = useState(false) const [intervalValue, setIntervalValue] = useState('T#20ms') + const projectPort = useProject() const { - sharedWorkspaceActions: { createProject }, + projectActions: { setProject }, + deviceActions: { setDeviceDefinitions }, + workspaceActions: { setEditingState }, } = useOpenPLCStore() const handleFormSubmit: SubmitHandler = async (data) => { @@ -60,7 +56,35 @@ const Step3 = ({ onPrev, onFinish, onClose }: { onPrev: () => void; onFinish: () handleUpdateForm(allData) try { - await createProject(allData as CreateProjectFileProps) + const result = await projectPort.createProject({ + name: allData.name, + type: allData.type as 'plc-project' | 'plc-library', + path: allData.path, + language: allData.language, + time: allData.time, + }) + + if (!result.success || !result.data) { + toast({ + title: 'Cannot create a project!', + description: result.error?.description ?? 'Failed to create the project.', + variant: 'fail', + }) + return + } + + // Hydrate store with returned project data + setProject({ + meta: result.data.meta, + data: result.data.projectData, + }) + if (result.data.deviceConfiguration || result.data.devicePinMapping) { + setDeviceDefinitions({ + configuration: result.data.deviceConfiguration, + pinMapping: result.data.devicePinMapping, + }) + } + setEditingState('saved') // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (_error) { toast({ diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx index a7cd10ac4..5f1323b40 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx @@ -24,8 +24,9 @@ import { Rung } from '../../../../../_organisms/graphical-editor/ladder/rung' import { ladderSelectors } from '../../../../../../hooks/use-store-selectors' import { openPLCStoreBase, useOpenPLCStore } from '../../../../../../store' import { RungLadderState, zodLadderFlowSchema } from '../../../../../../store/slices/ladder' +import type { PouHistorySnapshot } from '../../../../../../store/slices/shared/types' import { cn } from '../../../../../../utils/cn' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { v4 as uuidv4 } from 'uuid' @@ -47,11 +48,24 @@ export default function LadderEditor() { editorActions: { saveEditorViewState }, modalActions: { closeModal }, sharedWorkspaceActions: { handleFileAndWorkspaceSavedState }, - snapshotActions: { addSnapshot }, + snapshotActions: { pushToHistory }, libraries: { user: userLibraries }, workspace: { isDebuggerVisible }, } = useOpenPLCStore() + const captureSnapshot = useCallback( + (pouName: string): PouHistorySnapshot | null => { + const pou = pous.find((p) => p.name === pouName) + if (!pou) return null + return { + variables: pou.interface?.variables ?? [], + body: pou.body.value, + globalVariables: openPLCStoreBase.getState().project.data.configurations.resource.globalVariables, + } + }, + [pous], + ) + const updateModelLadder = ladderSelectors.useUpdateModelLadder() const flow = ladderFlows.find((flow) => flow.name === editor.meta.name) @@ -137,7 +151,8 @@ export default function LadderEditor() { const handleAddNewRung = () => { if (isDebuggerVisible) return - addSnapshot(editor.meta.name) + const snapshot = captureSnapshot(editor.meta.name) + if (snapshot) pushToHistory(editor.meta.name, snapshot) const defaultViewport: [number, number] = [300, 100] @@ -196,7 +211,8 @@ export default function LadderEditor() { auxRungs.splice(destinationIndex, 0, removed) try { - addSnapshot(editor.meta.name) + const snapshot = captureSnapshot(editor.meta.name) + if (snapshot) pushToHistory(editor.meta.name, snapshot) ladderFlowActions.setRungs({ editorName: editor.meta.name, rungs: auxRungs }) } catch (error) { console.error('Failed to update rungs:', error) diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx b/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx index 925614fa8..f158cd05b 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx @@ -5,7 +5,7 @@ import { Modal, ModalContent, ModalTitle } from '../../../../_molecules/modal' import { openPLCStoreBase, useOpenPLCStore } from '../../../../../store' import type { PLCVariable, PLCPou } from '../../../../../../middleware/shared/ports/types' import { baseTypeSchema } from '../../../../../../middleware/shared/ports/plc-schemas' -import { useCapabilities } from '../../../../../../middleware/shared/providers/platform-context' +import { useCapabilities, useProject } from '../../../../../../middleware/shared/providers' import * as monaco from 'monaco-editor' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -59,19 +59,6 @@ type SnippetController = { insert: (snippet: string, options?: unknown) => void } -// --------------------------------------------------------------------------- -// File watcher bridge type (editor-only, accessed via window.bridge) -// --------------------------------------------------------------------------- - -type FileWatcherBridge = { - fileWatchStart: (path: string) => Promise<{ success: boolean; error?: string }> - fileWatchStop: (path: string) => Promise<{ success: boolean }> - fileReadContent: (path: string) => Promise<{ success: boolean; content?: string; error?: string }> - onFileExternalChange: ( - handler: (event: unknown, data: { filePath: string }) => void, - ) => () => void -} - // --------------------------------------------------------------------------- // Comment stripping (for debug variable position scanning) // --------------------------------------------------------------------------- @@ -140,6 +127,7 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType string) | null = null @@ -293,10 +280,10 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType { - if (data.filePath !== watchedFilePathRef.current) return + const handleExternalChange = (filePath: string) => { + if (filePath !== watchedFilePathRef.current) return const isSaved = openPLCStoreBase.getState().fileActions.getSavedState({ name }) if (isSaved) { @@ -308,7 +295,7 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType { cleanup() if (watchedFilePathRef.current) { - void bridge.fileWatchStop(watchedFilePathRef.current) + void projectPort.unwatchFile?.(watchedFilePathRef.current) watchedFilePathRef.current = null } } @@ -851,8 +838,7 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType { - if (editingState !== 'save-request' && name) { - const sharedActions = openPLCStoreBase.getState().sharedWorkspaceActions - if ('saveFile' in sharedActions) { - void (sharedActions as { saveFile: (name: string) => Promise }).saveFile(name) - } + if (editingState !== 'save-request') { + const state = openPLCStoreBase.getState() + void projectPort.saveProject({ + projectPath: state.project.meta.path, + projectData: state.project.data, + deviceConfiguration: state.deviceDefinitions.configuration, + devicePinMapping: state.deviceDefinitions.pinMapping.pins, + }) } }) @@ -916,10 +905,13 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType { if (editingState !== 'save-request') { - const sharedActions = openPLCStoreBase.getState().sharedWorkspaceActions - if ('saveProject' in sharedActions) { - void (sharedActions as { saveProject: () => Promise }).saveProject() - } + const state = openPLCStoreBase.getState() + void projectPort.saveProject({ + projectPath: state.project.meta.path, + projectData: state.project.data, + deviceConfiguration: state.deviceDefinitions.configuration, + devicePinMapping: state.deviceDefinitions.pinMapping.pins, + }) } }, ) diff --git a/src2/frontend/components/_organisms/display-recent-projects/index.tsx b/src2/frontend/components/_organisms/display-recent-projects/index.tsx index 5651ba3af..1a89c6302 100644 --- a/src2/frontend/components/_organisms/display-recent-projects/index.tsx +++ b/src2/frontend/components/_organisms/display-recent-projects/index.tsx @@ -91,12 +91,12 @@ const DisplayRecentProjects = ({ searchNameFilterValue, ...props }: IDisplayRece } const handleOpenProjectByPath = async (projectPath: string) => { - const result = await project.openProject(projectPath) - if (!result.ok) { + const result = await project.openProjectByPath(projectPath) + if (!result.success) { void updateUserRecentProjects() toast({ title: 'Cannot open the project.', - description: result.message ?? `The path ${projectPath} does not exist on this computer.`, + description: result.error?.description ?? `The path ${projectPath} does not exist on this computer.`, variant: 'fail', }) } diff --git a/src2/frontend/hooks/use-remove-tab.tsx b/src2/frontend/hooks/use-remove-tab.tsx index 1805067ac..0da96bccd 100644 --- a/src2/frontend/hooks/use-remove-tab.tsx +++ b/src2/frontend/hooks/use-remove-tab.tsx @@ -3,12 +3,12 @@ import { useState } from 'react' const useHandleRemoveTab = () => { const { - sharedWorkspaceActions: { closeFile }, + sharedWorkspaceActions: { forceCloseFile }, } = useOpenPLCStore() const [selectedTab, setSelectedTab] = useState('') const handleRemoveTab = (tabToRemove: string) => { - const result = closeFile(tabToRemove) + const result = forceCloseFile(tabToRemove) if (result.success) { // Tab was closed successfully, update selected tab from current state setSelectedTab('') diff --git a/src2/middleware/adapters/editor/project-adapter.ts b/src2/middleware/adapters/editor/project-adapter.ts index 07144d98c..b485ab257 100644 --- a/src2/middleware/adapters/editor/project-adapter.ts +++ b/src2/middleware/adapters/editor/project-adapter.ts @@ -156,8 +156,8 @@ export function createEditorProjectAdapter(): ProjectPort { name: params.name, type: params.type, path: params.path ?? '', - language: 'il', - time: new Date().toISOString(), + language: params.language ?? 'il', + time: params.time ?? new Date().toISOString(), })) as unknown as IpcProjectResponse return mapIpcResponse(response, { name: params.name, type: params.type }) diff --git a/src2/middleware/shared/ports/project-port.ts b/src2/middleware/shared/ports/project-port.ts index 64585857c..e17889943 100644 --- a/src2/middleware/shared/ports/project-port.ts +++ b/src2/middleware/shared/ports/project-port.ts @@ -42,6 +42,8 @@ export interface CreateProjectParams { name: string type: 'plc-project' | 'plc-library' path?: string + language?: 'il' | 'st' | 'ld' | 'sfc' | 'fbd' | 'python' | 'cpp' + time?: string } export interface ProjectResponse { From bf897fc0632c128179f24744eca9964e21e504e0 Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Fri, 13 Mar 2026 18:55:24 -0300 Subject: [PATCH 02/40] fix(step-31): update POU references from DTO format to flat PLCPou Components copied from src/ used the old DTO shape (pou.data.name, pou.type) but src2/ stores POUs as flat PLCPou (pou.name, pou.pouType, pou.interface?.variables). Updated 33 files across frontend surface. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../_atoms/graphical-editor/fbd/block.tsx | 14 +-- .../graphical-editor/fbd/utils/utils.ts | 4 +- .../_atoms/graphical-editor/fbd/variable.tsx | 6 +- .../ladder/autocomplete/index.tsx | 4 +- .../_atoms/graphical-editor/ladder/block.tsx | 32 +++---- .../graphical-editor/ladder/utils/utils.ts | 4 +- .../graphical-editor/ladder/variable.tsx | 4 +- .../[workspace]/ai-chat/ai-chat-panel.tsx | 8 +- .../editor/device/configuration/board.tsx | 2 +- .../editor/graphical/FBD/index.tsx | 10 +-- .../graphical/elements/fbd/block/index.tsx | 19 ++-- .../graphical/elements/fbd/block/library.tsx | 4 +- .../graphical/elements/ladder/block/index.tsx | 19 ++-- .../elements/ladder/block/library.tsx | 4 +- .../editor/graphical/ladder/index.tsx | 10 +-- .../monaco/ai-completion/context-builder.ts | 6 +- .../editor/monaco/completion/fb.completion.ts | 33 +++---- .../editor/monaco/completion/index.ts | 17 ++-- .../editor/search-in-project/index.tsx | 30 ++++--- .../hooks/use-project-variables.ts | 14 +-- .../[workspace]/search/display/tree-view.tsx | 4 +- .../_molecules/graphical-editor/fbd/index.tsx | 26 +++--- .../graphical-editor/ladder/rung/body.tsx | 28 +++--- .../_molecules/project-tree/index.tsx | 5 +- .../variables-table/elements/array-modal.tsx | 4 +- .../display-recent-projects/index.tsx | 35 ++++---- .../components/_organisms/explorer/index.tsx | 2 +- .../_organisms/explorer/project.tsx | 60 +++++++------ .../_organisms/variables-editor/index.tsx | 26 +++--- src2/frontend/hooks/useDebugSession.ts | 28 +++--- src2/frontend/screens/start-screen.tsx | 8 +- .../frontend/services/ai/context-collector.ts | 23 ++--- .../slices/project/validation/type-change.ts | 11 +-- src2/frontend/store/slices/shared/slice.ts | 89 +++++++++++++++++++ src2/frontend/store/slices/shared/types.ts | 22 ++++- src2/frontend/utils/debug-tree-traversal.ts | 9 +- .../utils/generate-iec-string-to-variables.ts | 2 +- src2/frontend/utils/pou-helpers.ts | 21 +++-- src2/frontend/utils/variable-references.ts | 34 +++---- 39 files changed, 404 insertions(+), 277 deletions(-) diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx index 7e8e2f0fe..a4d071317 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx @@ -124,7 +124,7 @@ export const BlockNodeElement = ({ if (!pou || !rung || !node) return - if (libraryBlock && pou.type === 'function' && (libraryBlock as BlockVariant).type !== 'function') { + if (libraryBlock && pou.pouType === 'function' && (libraryBlock as BlockVariant).type !== 'function') { setWrongName(true) toast({ title: 'Can not add block', @@ -543,11 +543,11 @@ export const Block = (block: BlockProps) => { const libMatch = userLibraries.find((lib) => lib.name === variant.name && lib.type === variant.type) if (!libMatch) return - const libPou = pous.find((pou) => pou.data.name === libMatch.name) + const libPou = pous.find((pou) => pou.name === libMatch.name) if (!libPou) return const blockVariant = node.data.variant as BlockVariant - const newNodeVariables = (libPou.data.variables || []).map((variable) => { + const newNodeVariables = (libPou.interface?.variables ?? []).map((variable) => { let newType switch (variable.type.definition) { case 'array': @@ -584,8 +584,8 @@ export const Block = (block: BlockProps) => { } }) - if (libPou.type === 'function') { - const variable = getVariableRestrictionType(libPou.data.returnType) + if (libPou.pouType === 'function') { + const variable = getVariableRestrictionType(libPou.interface?.returnType ?? '') const hasOut = newNodeVariables.some((v) => v.name === 'OUT') if (!hasOut) newNodeVariables.push({ @@ -594,7 +594,7 @@ export const Block = (block: BlockProps) => { class: 'output', type: { definition: variable.definition ?? 'derived', - value: libPou.data.returnType.toUpperCase(), + value: (libPou.interface?.returnType ?? '').toUpperCase(), }, location: '', documentation: '', @@ -608,7 +608,7 @@ export const Block = (block: BlockProps) => { x: node.position.x, y: node.position.y, }, - variant: { ...libPou.data, type: blockVariant.type, variables: newNodeVariables }, + variant: { name: libPou.name, type: blockVariant.type, variables: newNodeVariables }, executionControl: (node.data as BlockNodeData).executionControl, }) updatedNewNode.data = { diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts b/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts index dbc1e43c1..621c89280 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts @@ -27,11 +27,11 @@ export const getFBDPouVariablesRungNodeAndEdges = ( } node: LadderFlowType['rungs'][0]['nodes'][0] | undefined } => { - const pou = pous.find((pou) => pou.data.name === editor.meta.name) + const pou = pous.find((pou) => pou.name === editor.meta.name) const rung = fbdFlows.find((flow) => flow.name === editor.meta.name)?.rung const node = rung?.nodes.find((node) => node.id === data.nodeId) - const variables: PLCVariable[] = pou?.data.variables as PLCVariable[] + const variables: PLCVariable[] = (pou?.interface?.variables ?? []) as PLCVariable[] let variable = variables.find((variable) => { if (!node) return undefined switch (node.type as keyof typeof customNodeTypes) { diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx index 92a287485..0bb4cfce7 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx @@ -281,7 +281,7 @@ const VariableElement = (block: VariableProps) => { if (!data.variable || !data.variable.name) return undefined const { pou } = getFBDPouVariablesRungNodeAndEdges(editor, pous, fbdFlows, { nodeId: id }) if (!pou) return undefined - const variable = pou.data.variables.find((v) => v.name.toLowerCase() === data.variable.name.toLowerCase()) + const variable = (pou.interface?.variables ?? []).find((v: PLCVariable) => v.name.toLowerCase() === data.variable.name.toLowerCase()) return variable?.type.value } @@ -500,8 +500,8 @@ const VariableElement = (block: VariableProps) => { // For variable nodes, allow all types including derived (user-defined types) // Don't use getVariableByName here as it filters out derived types let variable: PLCVariable | { name: string } | undefined = - (pou.data.variables as PLCVariable[]).find((v) => v.name.toLowerCase() === variableNameToSubmit.toLowerCase()) || - resolveArrayVariableByName(pou.data.variables as PLCVariable[], variableNameToSubmit) + ((pou.interface?.variables ?? []) as PLCVariable[]).find((v) => v.name.toLowerCase() === variableNameToSubmit.toLowerCase()) || + resolveArrayVariableByName((pou.interface?.variables ?? []) as PLCVariable[], variableNameToSubmit) if (!variable) { setIsAVariable(false) variable = { name: variableNameToSubmit } diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx index 780266b94..a6e2370c8 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx @@ -68,8 +68,8 @@ const VariablesBlockAutoComplete = forwardRef pou.data.name === editor.meta.name) - const variables = pou?.data.variables || [] + const pou = pous.find((pou) => pou.name === editor.meta.name) + const variables = pou?.interface?.variables ?? [] const variableRestrictions = blockTypeRestrictions(block, blockType) const expandedVariables = expandArrayVariables(variables as PLCVariable[]) diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx index 0f9400239..465dff5e0 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx @@ -104,7 +104,7 @@ export const BlockNodeElement = ({ const resolveLibraryBlock = (blockNameValue: string, libraries: LibraryState['libraries'], pous: PLCPou[]) => { const userLibrary = libraries.user.find((lib) => lib.name.toLowerCase() === blockNameValue.toLowerCase()) - const userPou = pous.find((pou) => pou.data.name.toLowerCase() === userLibrary?.name.toLowerCase()) + const userPou = pous.find((pou) => pou.name.toLowerCase() === userLibrary?.name.toLowerCase()) if (!userPou) { return ( @@ -116,7 +116,7 @@ export const BlockNodeElement = ({ ) } - const variables = userPou.data.variables.map((variable) => ({ + const variables = (userPou.interface?.variables ?? []).map((variable) => ({ name: variable.name, class: variable.class, type: { @@ -125,23 +125,24 @@ export const BlockNodeElement = ({ }, })) - if (userPou.type === 'function') { - const variable = getVariableRestrictionType(userPou.data.returnType) + if (userPou.pouType === 'function') { + const returnType = userPou.interface?.returnType ?? '' + const variable = getVariableRestrictionType(returnType) variables.push({ name: 'OUT', class: 'output', type: { definition: (variable.definition as 'array' | 'base-type' | 'user-data-type' | 'derived') ?? 'derived', - value: userPou.data.returnType.toUpperCase(), + value: returnType.toUpperCase(), }, }) } return { - name: userPou.data.name, - type: userPou.type, + name: userPou.name, + type: userPou.pouType, variables, - documentation: userPou.data.documentation, + documentation: userPou.documentation, extensible: false, } } @@ -166,7 +167,7 @@ export const BlockNodeElement = ({ }) if (!pou || !rung || !node) return - if (libraryBlock && pou.type === 'function' && (libraryBlock as BlockVariant).type !== 'function') { + if (libraryBlock && pou.pouType === 'function' && (libraryBlock as BlockVariant).type !== 'function') { setWrongName(true) toast({ title: 'Can not add block', @@ -578,11 +579,11 @@ export const Block = (block: BlockProps) => { const libMatch = userLibraries.find((lib) => lib.name === variant.name && lib.type === variant.type) if (!libMatch) return - const libPou = pous.find((pou) => pou.data.name === libMatch.name) + const libPou = pous.find((pou) => pou.name === libMatch.name) if (!libPou) return const blockVariant = node.data.variant as BlockVariant - const newNodeVariables = (libPou.data.variables || []).map((variable) => { + const newNodeVariables = (libPou.interface?.variables ?? []).map((variable) => { let newType switch (variable.type.definition) { case 'array': @@ -619,14 +620,15 @@ export const Block = (block: BlockProps) => { } }) - if (libPou.type === 'function') { - const variable = getVariableRestrictionType(libPou.data.returnType) + if (libPou.pouType === 'function') { + const returnType = libPou.interface?.returnType ?? '' + const variable = getVariableRestrictionType(returnType) newNodeVariables.push({ name: 'OUT', class: 'output', type: { definition: variable.definition ?? 'derived', - value: libPou.data.returnType.toUpperCase(), + value: returnType.toUpperCase(), }, location: '', documentation: '', @@ -640,7 +642,7 @@ export const Block = (block: BlockProps) => { posY: node.position.y, handleX: (node.data as BasicNodeData).handles[0].glbPosition.x, handleY: (node.data as BasicNodeData).handles[0].glbPosition.y, - variant: { ...libPou.data, type: blockVariant.type, variables: [...newNodeVariables] }, + variant: { name: libPou.name, documentation: libPou.documentation, type: blockVariant.type, variables: [...newNodeVariables] }, executionControl: (node.data as BlockNodeData).executionControl, }) diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts index 49602109d..b52cec0f9 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts @@ -26,7 +26,7 @@ export const getLadderPouVariablesRungNodeAndEdges = ( } node: LadderFlowType['rungs'][0]['nodes'][0] | undefined } => { - const pou = pous.find((pou) => pou.data.name === editor.meta.name) + const pou = pous.find((pou) => pou.name === editor.meta.name) const rung = ladderFlows .find((flow) => flow.name === editor.meta.name) @@ -34,7 +34,7 @@ export const getLadderPouVariablesRungNodeAndEdges = ( const node = rung?.nodes.find((node) => node.id === data.nodeId) - const variables: PLCVariable[] = pou?.data.variables as PLCVariable[] + const variables: PLCVariable[] = (pou?.interface?.variables ?? []) as PLCVariable[] let variable = variables.find((variable) => { if (!node) return undefined const nodeVariable = (node.data as BasicNodeData).variable diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx index a1111ad42..a17655240 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx @@ -194,7 +194,7 @@ const VariableElement = (block: VariableProps) => { // For variable nodes (block pins), allow all types including derived (user-defined types) // Don't use getVariableByName here as it filters out derived types - let variable: PLCVariable | { name: string } | undefined = (pou.data.variables as PLCVariable[]).find( + let variable: PLCVariable | { name: string } | undefined = ((pou.interface?.variables ?? []) as PLCVariable[]).find( (v) => v.name.toLowerCase() === variableNameToSubmit.toLowerCase(), ) if (!variable) { @@ -231,7 +231,7 @@ const VariableElement = (block: VariableProps) => { if (!data.variable || !data.variable.name) return undefined const { pou } = getLadderPouVariablesRungNodeAndEdges(editor, pous, ladderFlows, { nodeId: id }) if (!pou) return undefined - const variable = pou.data.variables.find((v) => v.name.toLowerCase() === data.variable.name.toLowerCase()) + const variable = (pou.interface?.variables ?? []).find((v) => v.name.toLowerCase() === data.variable.name.toLowerCase()) return variable?.type.value } diff --git a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx index a46f6e463..cb47f2f07 100644 --- a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx +++ b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx @@ -90,14 +90,14 @@ export const AIChatPanel = () => { .map((m) => ({ role: m.role, content: m.content })) // Resolve language from POU data (covers graphical editors like FBD/LD) - const pou = pouName ? storeState.project.data.pous.find((p) => p.data.name === pouName) : undefined - const pouLang = pou?.data.body.language ?? language ?? 'st' + const pou = pouName ? storeState.project.data.pous.find((p) => p.name === pouName) : undefined + const pouLang = pou?.body.language ?? language ?? 'st' // Collect project context: POU identity + body + variables/globals/FBs let pouContext: string | undefined if (pouName) { - const pouType = pou?.type ?? 'program' - const bodyValue = typeof pou?.data.body.value === 'string' ? pou.data.body.value : '' + const pouType = pou?.pouType ?? 'program' + const bodyValue = typeof pou?.body.value === 'string' ? pou.body.value : '' const projectCtx = collectProjectContext(storeState, pouName, 4000) const parts: string[] = [] diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx index 7ee639171..54bb1bbed 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx @@ -167,7 +167,7 @@ const Board = memo(function () { const targetBoardInfo = availableBoards.get(normalizedBoard) const isArduino = isArduinoTarget(targetBoardInfo) const hasPythonFunctionBlocks = pous.some( - (pou) => pou.type === 'function-block' && pou.data.language === 'python', + (pou) => pou.pouType === 'function-block' && pou.body.language === 'python', ) if (isArduino && hasPythonFunctionBlocks) { diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/FBD/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/FBD/index.tsx index e699f2bf7..617a129d9 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/FBD/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/FBD/index.tsx @@ -32,11 +32,11 @@ export default function FbdEditor() { const libMatch = userLibraries.find((lib) => lib.name === variant.name && lib.type === variant.type) if (!libMatch) continue - const originalPou = pous.find((pou) => pou.data.name === libMatch.name) + const originalPou = pous.find((pou) => pou.name === libMatch.name) if (!originalPou) continue - const originalVariables = originalPou.data?.variables ?? [] - const originalInOut = originalVariables?.filter((variable) => + const originalVariables = originalPou.interface?.variables ?? [] + const originalInOut = originalVariables.filter((variable) => ['input', 'output', 'inOut'].includes(variable.class || ''), ) @@ -51,10 +51,10 @@ export default function FbdEditor() { type: { definition: string; value: string } }) => `${variable.name}|${variable.class}|${variable.type.definition}|${variable.type.value?.toLowerCase()}` - if (originalPou.type === 'function') { + if (originalPou.pouType === 'function') { const outVariable = variant.variables.find((v) => v.name === 'OUT') const outType = outVariable?.type?.value?.toUpperCase() - const returnType = originalPou.data.returnType?.toUpperCase() + const returnType = originalPou.interface?.returnType?.toUpperCase() if (!outType || !returnType || outType !== returnType) { divergences.push(node.id) continue diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx index ff04c368e..eeeb9d8da 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx @@ -49,7 +49,7 @@ const searchLibraryByPouName = ( let libraryBlock: unknown = undefined const filteredLibraries = libraries.system.filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.pous.some((pou) => pou.type === 'function') : true, ) @@ -134,30 +134,31 @@ const BlockElement = ({ isOpen, onClose, selectedNode }: Block let pouLibrary = undefined if (type === 'user') { - const pou = pous.find((pou) => pou.data.name === selectedFile?.name) + const pou = pous.find((pou) => pou.name === selectedFile?.name) if (!pou) return - const variables = pou.data.variables.map((variable) => ({ + const variables = (pou.interface?.variables ?? []).map((variable) => ({ name: variable.name, class: variable.class, type: { definition: variable.type.definition, value: variable.type.value.toUpperCase() }, })) - if (pou.type === 'function') { - const variable = getVariableRestrictionType(pou.data.returnType) + if (pou.pouType === 'function') { + const returnType = pou.interface?.returnType ?? '' + const variable = getVariableRestrictionType(returnType) variables.push({ name: 'OUT', class: 'output', type: { definition: (variable.definition as 'array' | 'base-type' | 'user-data-type' | 'derived') ?? 'derived', - value: pou.data.returnType.toUpperCase(), + value: returnType.toUpperCase(), }, }) } pouLibrary = { - name: pou.data.name, - type: pou.type as 'function-block' | 'function', + name: pou.name, + type: pou.pouType as 'function-block' | 'function', variables: variables, - documentation: pou.data.documentation, + documentation: pou.documentation, extensible: false, } } diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx index 877c4d842..35d06c5ad 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx @@ -22,12 +22,12 @@ export const ModalBlockLibrary = ({ const [filterText, setFilterText] = useState('') const systemLibraries = system.filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.pous.some((pou) => pou.name.toLowerCase().includes(filterText) && pou.type === 'function') : library.pous.some((pou) => pou.name.toLowerCase().includes(filterText)), ) const userLibraries = user.filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.type === 'function' && library.name.toLowerCase().includes(filterText) : library.name.toLowerCase().includes(filterText), ) diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx index 8fa7626a8..9d68ff8e7 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx @@ -51,7 +51,7 @@ const searchLibraryByPouName = ( let libraryBlock: unknown = undefined const filteredLibraries = libraries.system.filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.pous.some((pou) => pou.type === 'function') : true, ) @@ -139,30 +139,31 @@ const BlockElement = ({ isOpen, onClose, selectedNode }: Block let pouLibrary = undefined if (type === 'user') { - const pou = pous.find((pou) => pou.data.name === selectedFile?.name) + const pou = pous.find((pou) => pou.name === selectedFile?.name) if (!pou) return - const variables = pou.data.variables.map((variable) => ({ + const variables = (pou.interface?.variables ?? []).map((variable) => ({ name: variable.name, class: variable.class, type: { definition: variable.type.definition, value: variable.type.value.toUpperCase() }, })) - if (pou.type === 'function') { - const variable = getVariableRestrictionType(pou.data.returnType) + if (pou.pouType === 'function') { + const returnType = pou.interface?.returnType ?? '' + const variable = getVariableRestrictionType(returnType) variables.push({ name: 'OUT', class: 'output', type: { definition: (variable.definition as 'array' | 'base-type' | 'user-data-type' | 'derived') ?? 'derived', - value: pou.data.returnType.toUpperCase(), + value: returnType.toUpperCase(), }, }) } pouLibrary = { - name: pou.data.name, - type: pou.type as 'function-block' | 'function', + name: pou.name, + type: pou.pouType as 'function-block' | 'function', variables: variables, - documentation: pou.data.documentation, + documentation: pou.documentation, extensible: false, } } diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx index 877c4d842..35d06c5ad 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx @@ -22,12 +22,12 @@ export const ModalBlockLibrary = ({ const [filterText, setFilterText] = useState('') const systemLibraries = system.filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.pous.some((pou) => pou.name.toLowerCase().includes(filterText) && pou.type === 'function') : library.pous.some((pou) => pou.name.toLowerCase().includes(filterText)), ) const userLibraries = user.filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.type === 'function' && library.name.toLowerCase().includes(filterText) : library.name.toLowerCase().includes(filterText), ) diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx index 5f1323b40..ce862911e 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx @@ -273,11 +273,11 @@ export default function LadderEditor() { const libMatch = userLibraries.find((lib) => lib.name === variant.name && lib.type === variant.type) if (!libMatch) continue - const originalPou = pous.find((pou) => pou.data.name === libMatch.name) + const originalPou = pous.find((pou) => pou.name === libMatch.name) if (!originalPou) continue - const originalVariables = originalPou.data?.variables ?? [] - const originalInOut = originalVariables?.filter((variable) => + const originalVariables = originalPou.interface?.variables ?? [] + const originalInOut = originalVariables.filter((variable) => ['input', 'output', 'inOut'].includes(variable.class || ''), ) @@ -293,10 +293,10 @@ export default function LadderEditor() { type: { definition: string; value: string } }) => `${variable.name}|${variable.class}|${variable.type.definition}|${variable.type.value?.toLowerCase()}` - if (originalPou.type === 'function') { + if (originalPou.pouType === 'function') { const outVariable = variant.variables.find((v) => v.name === 'OUT') const outType = outVariable?.type?.value?.toUpperCase() - const returnType = originalPou.data.returnType?.toUpperCase() + const returnType = originalPou.interface?.returnType?.toUpperCase() if (!outType || !returnType || outType !== returnType) { divergences.push(`${rung.id}:${node.id}`) continue diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts index 09abe56bf..ad366dab5 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts @@ -27,9 +27,9 @@ function collectProjectContext(pouName: string, maxTokenBudget: number): string let approxTokens = 0 for (const pou of pous) { - if (pou.data.name === pouName) continue - const header = `${pou.type} ${pou.data.name}` - const vars = pou.data.variables + if (pou.name === pouName) continue + const header = `${pou.pouType} ${pou.name}` + const vars = (pou.interface?.variables ?? []) .slice(0, 10) .map((v) => ` ${v.class ?? 'local'} ${v.name}: ${v.type.value}`) .join('\n') diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts index 4cae311f5..1e8e83a86 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts @@ -82,10 +82,10 @@ function findFinalType( } else { // Check if it's a Custom FB const customFB = customFBs.find( - (fb) => fb.data.name.toUpperCase() === currentTypeName.toUpperCase() && fb.type === 'function-block', + (fb) => fb.name.toUpperCase() === currentTypeName.toUpperCase() && fb.pouType === 'function-block', ) if (customFB) { - const field = customFB.data.variables.find( + const field = (customFB.interface?.variables ?? []).find( (v) => v.name === fieldName && (v.class === 'input' || v.class === 'output' || v.class === 'inOut'), ) if (field && (field.type?.definition === 'derived' || field.type?.definition === 'user-data-type')) { @@ -134,9 +134,9 @@ function findFBType( } // Check if it's a Custom FB - const customFB = customFBs.find((fb) => fb.data.name.toUpperCase() === typeName && fb.type === 'function-block') + const customFB = customFBs.find((fb) => fb.name.toUpperCase() === typeName && fb.pouType === 'function-block') if (customFB) { - return { type: customFB.data.name, isStandard: false } + return { type: customFB.name, isStandard: false } } } @@ -154,9 +154,9 @@ function findFBType( } // Check Custom FBs - const customFB = customFBs.find((fb) => fb.data.name.toUpperCase() === typeName && fb.type === 'function-block') + const customFB = customFBs.find((fb) => fb.name.toUpperCase() === typeName && fb.pouType === 'function-block') if (customFB) { - return { type: customFB.data.name, isStandard: false } + return { type: customFB.name, isStandard: false } } } @@ -201,12 +201,12 @@ function getCustomFBVariableSuggestions( range: monaco.IRange, editorName: string, ): monaco.languages.CompletionItem[] { - const functionBlock = customFBs.find((fb) => fb.data.name === fbType && fb.type === 'function-block') + const functionBlock = customFBs.find((fb) => fb.name === fbType && fb.pouType === 'function-block') if (!functionBlock) return [] // Filter only public variables (Input, Output, InOut) - same as Standard FBs - const publicVariables = functionBlock.data.variables + const publicVariables = (functionBlock.interface?.variables ?? []) .filter((variable) => variable.class === 'input' || variable.class === 'output' || variable.class === 'inOut') .filter((variable) => variable.name !== editorName) @@ -216,7 +216,7 @@ function getCustomFBVariableSuggestions( insertText: variable.name, detail: `${variable.type.value} (${variable.class})`, documentation: { - value: `**${variable.name}** - ${variable.class} variable\n\nType: \`${variable.type.value}\`\n\nCustom Function Block: ${fbType}\n\n${variable.documentation || functionBlock.data.documentation}`, + value: `**${variable.name}** - ${variable.class} variable\n\nType: \`${variable.type.value}\`\n\nCustom Function Block: ${fbType}\n\n${variable.documentation || functionBlock.documentation}`, }, range, sortText: `${variable.class === 'input' ? '1' : variable.class === 'output' ? '2' : '3'}_${variable.name}`, @@ -232,13 +232,14 @@ function getCustomFBInstanceSuggestions( editorName: string, ): monaco.languages.CompletionItem[] { return customFBs - .filter((fb) => fb.type === 'function-block') - .filter((fb) => fb.data.name !== editorName) + .filter((fb) => fb.pouType === 'function-block') + .filter((fb) => fb.name !== editorName) .map((fb) => { - const inputVars = fb.data.variables.filter((v) => v.class === 'input') + const variables = fb.interface?.variables ?? [] + const inputVars = variables.filter((v) => v.class === 'input') // Generate snippet with input parameters - let snippet = `${fb.data.name}(` + let snippet = `${fb.name}(` if (inputVars.length > 0) { snippet += '\n' inputVars.forEach((input, index) => { @@ -251,16 +252,16 @@ function getCustomFBInstanceSuggestions( snippet += ')' return { - label: fb.data.name, + label: fb.name, kind: monaco.languages.CompletionItemKind.Class, insertText: snippet, insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, detail: `Custom Function Block`, documentation: { - value: `**${fb.data.name}** - Custom Function Block\n\n${fb.data.documentation || 'User-defined function block'}\n\n**Variables:**\n${fb.data.variables.map((v) => `- **${v.name}** (${v.class}): ${v.type.value}`).join('\n')}`, + value: `**${fb.name}** - Custom Function Block\n\n${fb.documentation || 'User-defined function block'}\n\n**Variables:**\n${variables.map((v) => `- **${v.name}** (${v.class}): ${v.type.value}`).join('\n')}`, }, range, - sortText: `2_${fb.data.name}`, // Sort after standard FBs + sortText: `2_${fb.name}`, // Sort after standard FBs } }) } diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts index 79343ea56..7bf7d32fb 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts @@ -335,7 +335,7 @@ export const libraryCompletion = ({ }) => { const systemSuggestions = library.system .filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.pous.some((pou) => pou.type === 'function') : library.pous.some((pou) => pou), ) @@ -374,9 +374,10 @@ export const libraryCompletion = ({ return userLibrary.name !== editor.meta.name }) .flatMap((user) => { - const pou = pous.find((pou) => pou.data.name === user.name) + const pou = pous.find((pou) => pou.name === user.name) if (!pou) return [] + const variables = pou.interface?.variables ?? [] const data: { name: string language?: string @@ -390,22 +391,22 @@ export const libraryCompletion = ({ }[] extensible?: boolean } = { - name: pou.data.name, - type: pou.type, - variables: pou.data.variables.map((variable) => ({ + name: pou.name, + type: pou.pouType, + variables: variables.map((variable) => ({ name: variable.name, class: variable.class, type: { definition: variable.type.definition, value: variable.type.value.toUpperCase() }, })), - documentation: pou.data.documentation, + documentation: pou.documentation, extensible: false, } const text = parsePouToStText(data) return { - label: pou.data.name, + label: pou.name, insertText: text, - documentation: pou.data.documentation, + documentation: pou.documentation, kind: monaco.languages.CompletionItemKind.Function, range, } diff --git a/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx b/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx index dce874443..3a569ee5d 100644 --- a/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx @@ -205,15 +205,16 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) const groupedPous = data.pous .filter((pou) => { - const pouTypeMatchesFilter = activeFilters.length === 0 || activeFilters.includes(pou.type) + const pouTypeMatchesFilter = activeFilters.length === 0 || activeFilters.includes(pou.pouType) const pouMatches = regularExpressionOption - ? countOccurrences(pou.data.name, searchQuery, sensitiveCaseOption, regularExpressionOption) > 0 + ? countOccurrences(pou.name, searchQuery, sensitiveCaseOption, regularExpressionOption) > 0 : sensitiveCaseOption - ? pou.data.name.includes(searchQuery) - : pou.data.name.toLowerCase().includes(searchQuery.toLowerCase()) + ? pou.name.includes(searchQuery) + : pou.name.toLowerCase().includes(searchQuery.toLowerCase()) - const variableMatches = pou.data.variables.some((variable) => + const pouVariables = pou.interface?.variables ?? [] + const variableMatches = pouVariables.some((variable) => regularExpressionOption ? countOccurrences(variable.name, searchQuery, sensitiveCaseOption, regularExpressionOption) > 0 : sensitiveCaseOption @@ -225,11 +226,11 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) pouTypeMatchesFilter && (pouMatches || variableMatches || - (['st', 'il'].includes(pou.data.language) && + (['st', 'il'].includes(pou.body.language) && (() => { try { const regex = new RegExp(searchQuery, sensitiveCaseOption ? 'g' : 'gi') - const matches = (pou.data.body.value as string).match(regex) + const matches = (pou.body.value as string).match(regex) return matches ? matches.length > 0 : false } catch (error) { console.error('Invalid regex or error processing body:', error) @@ -240,22 +241,23 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) }) .reduce( (acc, pou) => { - const pouType = pou.type + const pouType = pou.pouType if (!acc[pouType]) { acc[pouType] = [] } + const pouVariables = pou.interface?.variables ?? [] acc[pouType].push({ - name: pou.data.name, - language: pou.data.language, - pouType: pou.type, + name: pou.name, + language: pou.body.language, + pouType: pou.pouType, body: - (['st', 'il'].includes(pou.data.language) && + (['st', 'il'].includes(pou.body.language) && (() => { try { const regex = new RegExp(`\\b(${searchQuery}\\w*)`, sensitiveCaseOption ? 'g' : 'gi') - const matches = (pou.data.body.value as string).match(regex) + const matches = (pou.body.value as string).match(regex) return matches ? matches.join(', ') : '' } catch (error) { console.error('Invalid regex or error processing body:', error) @@ -263,7 +265,7 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) } })()) || '', - variable: pou.data.variables + variable: pouVariables .filter((variable) => regularExpressionOption ? countOccurrences(variable.name, searchQuery, sensitiveCaseOption, regularExpressionOption) > 0 diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts index eeadfeb4d..205042c78 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts @@ -383,19 +383,19 @@ const buildVariableNodeFromPLC = ( * Build a tree node for a program POU. */ const buildProgramNode = (pou: PLCPou, dataTypes: PLCDataType[], pous: PLCPou[]): VariableTreeNode => { - if (pou.type !== 'program') { + if (pou.pouType !== 'program') { throw new Error('Expected program POU') } - const children = pou.data.variables - .map((v) => buildVariableNodeFromPLC(v, pou.data.name, dataTypes, pous)) + const children = (pou.interface?.variables ?? []) + .map((v) => buildVariableNodeFromPLC(v, pou.name, dataTypes, pous)) .filter((node): node is VariableTreeNode => node !== null) return { - id: `pou-${pou.data.name}`, - name: pou.data.name, + id: `pou-${pou.name}`, + name: pou.name, type: 'program', - pouName: pou.data.name, + pouName: pou.name, variablePath: '', isSelectable: false, children, @@ -440,7 +440,7 @@ export const useProjectVariables = (): VariableTreeNode[] => { const nodes: VariableTreeNode[] = [] for (const pou of projectData.pous) { - if (pou.type === 'program') { + if (pou.pouType === 'program') { nodes.push(buildProgramNode(pou, projectData.dataTypes, projectData.pous)) } } diff --git a/src2/frontend/components/_features/[workspace]/search/display/tree-view.tsx b/src2/frontend/components/_features/[workspace]/search/display/tree-view.tsx index 3528fdc19..92ebe03c5 100644 --- a/src2/frontend/components/_features/[workspace]/search/display/tree-view.tsx +++ b/src2/frontend/components/_features/[workspace]/search/display/tree-view.tsx @@ -90,7 +90,7 @@ const ProjectSearchTreeBranch = ({ branchTarget, children, ...res }: IProjectSea const { BranchIcon, label } = BranchSources[branchTarget] const handleBranchVisibility = useCallback(() => setBranchIsOpen(!branchIsOpen), [branchIsOpen]) const hasAssociatedPou: boolean = - pous.some((pou) => pou.type === branchTarget) || + pous.some((pou) => pou.pouType === branchTarget) || (branchTarget === 'data-type' && dataTypes.length > 0) || (branchTarget === 'resource' && configuration !== null) useEffect(() => setBranchIsOpen(hasAssociatedPou), [hasAssociatedPou]) @@ -269,7 +269,7 @@ const ProjectSearchTreeVariableBranch = ({ leafLang, label, children, ...res }: const [branchIsOpen, setBranchIsOpen] = useState(false) const { LeafIcon } = LeafSources[leafLang] const handleBranchVisibility = useCallback(() => setBranchIsOpen(!branchIsOpen), [branchIsOpen]) - const hasVariable: boolean = pous.some((pou) => pou.data.variables.length > 0) || configuration !== null + const hasVariable: boolean = pous.some((pou) => (pou.interface?.variables ?? []).length > 0) || configuration !== null useEffect(() => setBranchIsOpen(hasVariable), [hasVariable]) return ( diff --git a/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx b/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx index c37754221..1b7c73d52 100644 --- a/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx +++ b/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx @@ -54,7 +54,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } const { captureAndPush } = usePouSnapshot() const { pous } = project.data - const pouRef = pous.find((pou) => pou.data.name === editor.meta.name) + const pouRef = pous.find((pou) => pou.name === editor.meta.name) const getCompositeKey = useDebugCompositeKey() const [rungLocal, setRungLocal] = useState(rung) const [dragging, setDragging] = useState(false) @@ -102,7 +102,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } if (!variableName) return undefined if (!pouRef) return undefined - const variable = pouRef.data.variables.find((v) => v.name.toLowerCase() === variableName.toLowerCase()) + const variable = (pouRef.interface?.variables ?? []).find((v) => v.name.toLowerCase() === variableName.toLowerCase()) if (!variable || variable.type.value.toUpperCase() !== 'BOOL') return undefined const compositeKey = getCompositeKey(variableName) @@ -125,7 +125,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } } if (!sourceHandle) return undefined - if (pouRef?.type !== 'function-block') { + if (pouRef?.pouType !== 'function-block') { const instances = project.data.configuration.resource.instances const programInstance = instances.find((inst: { program: string }) => inst.program === editor.meta.name) if (!programInstance) return undefined @@ -236,7 +236,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } debugVariableValues, debugForcedVariables, editor.meta.name, - pouRef?.data.variables, + pouRef?.interface?.variables, project.data.configuration.resource.instances, ]) @@ -401,33 +401,33 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } if (blockLibraryType === 'user') { const library = libraries.user.find((library) => library.name === blockLibrary) - const pou = pous.find((pou) => pou.data.name === library?.name) + const pou = pous.find((pou) => pou.name === library?.name) if (!pou) return - const variables = pou.data.variables.map((variable) => ({ + const variables = (pou.interface?.variables ?? []).map((variable) => ({ id: variable.id, name: variable.name, class: variable.class, type: { definition: variable.type.definition, value: variable.type.value.toUpperCase() }, })) - if (pou.type === 'function') { - const variable = getVariableRestrictionType(pou.data.returnType) + if (pou.pouType === 'function') { + const variable = getVariableRestrictionType(pou.interface?.returnType) variables.push({ id: 'OUT', name: 'OUT', class: 'output', type: { definition: (variable.definition as 'array' | 'base-type' | 'user-data-type' | 'derived') ?? 'derived', - value: pou.data.returnType.toUpperCase(), + value: (pou.interface?.returnType ?? '').toUpperCase(), }, }) } pouLibrary = { - name: pou.data.name, - type: pou.type, + name: pou.name, + type: pou.pouType, variables: variables, - documentation: pou.data.documentation, + documentation: pou.documentation, extensible: false, } } @@ -479,7 +479,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } }) if (pouRef && nodes.length > 0) { - const allVariables = pouRef.data.variables + const allVariables = pouRef.interface?.variables ?? [] const allRungs = [rung] const variablesToDelete = getFunctionBlockVariablesToCleanup(nodes, allRungs, allVariables) diff --git a/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx b/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx index c9f821d47..eacb43b1c 100644 --- a/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx +++ b/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx @@ -85,7 +85,7 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi const { captureAndPush } = usePouSnapshot() const { pous } = project.data - const pouRef = pous.find((pou) => pou.data.name === editor.meta.name) + const pouRef = pous.find((pou) => pou.name === editor.meta.name) const getCompositeKey = useDebugCompositeKey() const nodeTypes = useMemo(() => customNodeTypes, []) @@ -183,7 +183,7 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi } if (!sourceHandle) return undefined - if (pouRef?.type !== 'function-block') { + if (pouRef?.pouType !== 'function-block') { const instances = project.data.configuration.resource.instances const programInstance = instances.find((inst) => inst.program === editor.meta.name) if (!programInstance) return undefined @@ -471,32 +471,32 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi if (blockLibraryType === 'user') { const library = libraries.user.find((library) => library.name === blockLibrary) - const pou = pous.find((pou) => pou.data.name === library?.name) + const pou = pous.find((pou) => pou.name === library?.name) if (!pou) return - const variables = pou.data.variables.map((variable) => ({ + const variables = (pou.interface?.variables ?? []).map((variable) => ({ id: variable.id, name: variable.name, class: variable.class, type: { definition: variable.type.definition, value: variable.type.value.toUpperCase() }, })) - if (pou.type === 'function') { - const variable = getVariableRestrictionType(pou.data.returnType) + if (pou.pouType === 'function') { + const variable = getVariableRestrictionType(pou.interface?.returnType) variables.push({ id: 'OUT', name: 'OUT', class: 'output', type: { definition: (variable.definition as 'array' | 'base-type' | 'user-data-type' | 'derived') ?? 'derived', - value: pou.data.returnType.toUpperCase(), + value: (pou.interface?.returnType ?? '').toUpperCase(), }, }) } pouLibrary = { - name: pou.data.name, - type: pou.type, + name: pou.name, + type: pou.pouType, variables: variables, - documentation: pou.data.documentation, + documentation: pou.documentation, extensible: false, } } @@ -531,7 +531,7 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi }) if (pouRef) { - syncNodesWithVariables(pouRef.data.variables, ladderFlows, ladderFlowActions.updateNode, editor.meta.name) + syncNodesWithVariables(pouRef.interface?.variables ?? [], ladderFlows, ladderFlowActions.updateNode, editor.meta.name) } } @@ -566,7 +566,7 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi const blockNodes = nodes.filter((node) => node.type === 'block') if (blockNodes.length > 0) { let variables: PLCVariable[] = [] - if (pouRef) variables = [...pouRef.data.variables] as PLCVariable[] + if (pouRef) variables = [...(pouRef.interface?.variables ?? [])] as PLCVariable[] const variablesToCleanup = getFunctionBlockVariablesToCleanup(blockNodes, variables, ladderFlows) @@ -601,7 +601,7 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi } if (pouRef) { - syncNodesWithVariables(pouRef.data.variables, ladderFlows, ladderFlowActions.updateNode, editor.meta.name) + syncNodesWithVariables(pouRef.interface?.variables ?? [], ladderFlows, ladderFlowActions.updateNode, editor.meta.name) } } @@ -652,7 +652,7 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi ladderFlowActions.setEdges({ editorName: editor.meta.name, rungId: rungLocal.id, edges: result.edges }) if (pouRef) { - syncNodesWithVariables(pouRef.data.variables, ladderFlows, ladderFlowActions.updateNode, editor.meta.name) + syncNodesWithVariables(pouRef.interface?.variables ?? [], ladderFlows, ladderFlowActions.updateNode, editor.meta.name) } } diff --git a/src2/frontend/components/_molecules/project-tree/index.tsx b/src2/frontend/components/_molecules/project-tree/index.tsx index 20b476f66..aca1fb23b 100644 --- a/src2/frontend/components/_molecules/project-tree/index.tsx +++ b/src2/frontend/components/_molecules/project-tree/index.tsx @@ -24,6 +24,7 @@ import { SFCIcon } from '../../../assets/icons/project/SFC' import { STIcon } from '../../../assets/icons/project/ST' import { ServerIcon } from '../../../assets/icons/project/Server' import { StructureIcon } from '../../../assets/icons/project/Structure' +import { OrchestratorIcon } from '../../../assets/icons/project/Orchestrator' import { DuplicateIcon } from '../../../assets/icons/interface/Duplicate' import { useOpenPLCStore } from '../../../store' import { WorkspaceProjectTreeLeafType } from '../../../store/slices/workspace/types' @@ -114,7 +115,7 @@ const ProjectTreeBranch = ({ branchTarget, children, ...res }: ProjectTreeBranch const { BranchIcon, label } = BranchSources[branchTarget] const handleBranchVisibility = useCallback(() => setBranchIsOpen(!branchIsOpen), [branchIsOpen]) const hasAssociatedPou = - pous.some((pou) => pou.type === branchTarget) || + pous.some((pou) => pou.pouType === branchTarget) || branchTarget === 'device' || (branchTarget === 'data-type' && dataTypes.length > 0) || (branchTarget === 'server' && servers !== undefined && servers.length > 0) || @@ -252,6 +253,7 @@ type IProjectTreeLeafProps = ComponentPropsWithoutRef<'li'> & { | 'res' | 'devConfig' | 'devPin' + | 'devOrchestrators' | 'server' | 'remoteDevice' leafType: WorkspaceProjectTreeLeafType @@ -272,6 +274,7 @@ const LeafSources = { res: { LeafIcon: ResourceIcon }, devConfig: { LeafIcon: ConfigIcon }, devPin: { LeafIcon: DeviceTransferIcon }, + devOrchestrators: { LeafIcon: OrchestratorIcon }, server: { LeafIcon: ServerIcon }, remoteDevice: { LeafIcon: RemoteDeviceIcon }, } diff --git a/src2/frontend/components/_molecules/variables-table/elements/array-modal.tsx b/src2/frontend/components/_molecules/variables-table/elements/array-modal.tsx index fdc6727f6..646d84511 100644 --- a/src2/frontend/components/_molecules/variables-table/elements/array-modal.tsx +++ b/src2/frontend/components/_molecules/variables-table/elements/array-modal.tsx @@ -88,8 +88,8 @@ export const ArrayModal = ({ useEffect(() => { const variable = pous - .find((pou) => pou.data.name === name) - ?.data.variables.find((variable) => variable.name === variableName) + .find((pou) => pou.name === name) + ?.interface?.variables?.find((variable) => variable.name === variableName) if (!variable) return if (variable.type.definition === 'array') { diff --git a/src2/frontend/components/_organisms/display-recent-projects/index.tsx b/src2/frontend/components/_organisms/display-recent-projects/index.tsx index 1a89c6302..233518574 100644 --- a/src2/frontend/components/_organisms/display-recent-projects/index.tsx +++ b/src2/frontend/components/_organisms/display-recent-projects/index.tsx @@ -1,4 +1,4 @@ -import { File as FileElement } from '../../_atoms/file' +import { File } from '../../_atoms/file' import { toast } from '../../_features/[app]/toast/use-toast' import { useOpenPLCStore } from '../../../store' import { useSystem, useProject } from '../../../../middleware/shared/providers' @@ -12,6 +12,7 @@ const DisplayRecentProjects = ({ searchNameFilterValue, ...props }: IDisplayRece const { workspace: { recent }, workspaceActions: { setRecent }, + sharedWorkspaceActions: { handleOpenProjectResponse }, } = useOpenPLCStore() const system = useSystem() @@ -92,14 +93,16 @@ const DisplayRecentProjects = ({ searchNameFilterValue, ...props }: IDisplayRece const handleOpenProjectByPath = async (projectPath: string) => { const result = await project.openProjectByPath(projectPath) - if (!result.success) { - void updateUserRecentProjects() - toast({ - title: 'Cannot open the project.', - description: result.error?.description ?? `The path ${projectPath} does not exist on this computer.`, - variant: 'fail', - }) + if (result.success && result.data) { + handleOpenProjectResponse(result.data) + return } + void updateUserRecentProjects() + toast({ + title: 'Cannot open the project.', + description: result.error?.description ?? `The path ${projectPath} does not exist on this computer.`, + variant: 'fail', + }) } return ( @@ -112,18 +115,14 @@ const DisplayRecentProjects = ({ searchNameFilterValue, ...props }: IDisplayRece
{recentProjects.map((proj) => ( - void handleOpenProjectByPath(proj.path)} - className='overflow-hidden ' + className='overflow-hidden' key={proj.path} - > - - - + projectName={proj.name} + projectPath={proj.path} + lastModified={projectTimes[proj.path]} + /> ))}
diff --git a/src2/frontend/components/_organisms/explorer/index.tsx b/src2/frontend/components/_organisms/explorer/index.tsx index 4ca641a18..47984281c 100644 --- a/src2/frontend/components/_organisms/explorer/index.tsx +++ b/src2/frontend/components/_organisms/explorer/index.tsx @@ -47,7 +47,7 @@ const Explorer = ({ collapse }: ExplorerProps): ReactElement => { // System Libraries filtering with type and text filter const filteredLibraries = system.filter((library) => - pous.find((pou) => pou.data.name === editor.meta.name)?.type === 'function' + pous.find((pou) => pou.name === editor.meta.name)?.pouType === 'function' ? library.pous.some((pou) => pou.name.toLowerCase().includes(filterText) && pou.type === 'function') : library.pous.some((pou) => pou.name.toLowerCase().includes(filterText)), ) diff --git a/src2/frontend/components/_organisms/explorer/project.tsx b/src2/frontend/components/_organisms/explorer/project.tsx index 9faad6849..0de6f5fa5 100644 --- a/src2/frontend/components/_organisms/explorer/project.tsx +++ b/src2/frontend/components/_organisms/explorer/project.tsx @@ -8,10 +8,12 @@ import { useEffect, useState } from 'react' import { CreatePLCElement } from '../../_features/[workspace]/create-element' +type PouLeafLang = 'il' | 'st' | 'ld' | 'sfc' | 'fbd' | 'python' | 'cpp' + const Project = () => { const { project: { - data: { pous, dataTypes, configuration, servers, remoteDevices }, + data: { pous, dataTypes, configurations, servers, remoteDevices }, meta: { name }, }, projectActions: { updateMetaName }, @@ -95,19 +97,19 @@ const Project = () => { {/* Project Functions tree branch */} {pous - ?.filter(({ type }) => type === 'function') - .sort((a, b) => a.data.name.localeCompare(b.data.name)) - .map(({ data }) => ( + ?.filter((pou) => pou.pouType === 'function') + .sort((a, b) => a.name.localeCompare(b.name)) + .map((pou) => ( handleCreateTab({ - name: data.name, - path: `/data/pous/function/${data.name}`, - elementType: { type: 'function', language: data.language }, + name: pou.name, + path: `/data/pous/function/${pou.name}`, + elementType: { type: 'function', language: pou.body.language as PouLeafLang }, }) } /> @@ -117,19 +119,19 @@ const Project = () => { {/* Project Function Blocks tree branch */} {pous - ?.filter(({ type }) => type === 'function-block') - .sort((a, b) => a.data.name.localeCompare(b.data.name)) - .map(({ data }) => ( + ?.filter((pou) => pou.pouType === 'function-block') + .sort((a, b) => a.name.localeCompare(b.name)) + .map((pou) => ( handleCreateTab({ - name: data.name, - path: `/data/pous/function-block/${data.name}`, - elementType: { type: 'function-block', language: data.language }, + name: pou.name, + path: `/data/pous/function-block/${pou.name}`, + elementType: { type: 'function-block', language: pou.body.language as PouLeafLang }, }) } /> @@ -139,19 +141,19 @@ const Project = () => { {/* Project Programs tree branch */} {pous - ?.filter(({ type }) => type === 'program') - .sort((a, b) => a.data.name.localeCompare(b.data.name)) - .map(({ data }) => ( + ?.filter((pou) => pou.pouType === 'program') + .sort((a, b) => a.name.localeCompare(b.name)) + .map((pou) => ( handleCreateTab({ - name: data.name, - path: `/data/pous/program/${data.name}`, - elementType: { type: 'program', language: data.language }, + name: pou.name, + path: `/data/pous/program/${pou.name}`, + elementType: { type: 'program', language: pou.body.language as PouLeafLang }, }) } /> @@ -226,7 +228,7 @@ const Project = () => { branchTarget='resource' onClick={() => { handleCreateTab({ - configuration: configuration, + configuration: configurations, name: 'Resource', path: `/data/configuration/resource`, elementType: { type: 'resource' }, diff --git a/src2/frontend/components/_organisms/variables-editor/index.tsx b/src2/frontend/components/_organisms/variables-editor/index.tsx index 358deb129..57519e50e 100644 --- a/src2/frontend/components/_organisms/variables-editor/index.tsx +++ b/src2/frontend/components/_organisms/variables-editor/index.tsx @@ -115,23 +115,23 @@ const VariablesEditor = () => { } }) - const pou = pous.find((p) => p.data.name === editor.meta.name) + const pou = pous.find((p) => p.name === editor.meta.name) useEffect(() => { - const data = pou?.data.documentation + const data = pou?.documentation if (data) setPouDescription(data) return () => { setPouDescription('') } - }, [editor, pou?.data.documentation]) + }, [editor, pou?.documentation]) /** * Update the table data and the editor's variables when the editor or the pous change */ useEffect(() => { if (pou) { - setTableData(pou.data.variables) - if (pou.type === 'function') { - setReturnType(pou.data.returnType) + setTableData(pou.interface?.variables ?? []) + if (pou.pouType === 'function') { + setReturnType(pou.interface?.returnType ?? 'BOOL') } } else { setTableData([]) @@ -291,7 +291,8 @@ const VariablesEditor = () => { pushToHistory(editor.meta.name) - const variables = pous.filter((pou) => pou.data.name === editor.meta.name)[0].data.variables + const matchedPou = pous.find((p) => p.name === editor.meta.name) + const variables = matchedPou?.interface?.variables ?? [] const selectedRow = parseInt(editorVariables.selectedRow) const language = 'language' in editor.meta ? editor.meta.language : null @@ -361,7 +362,8 @@ const VariablesEditor = () => { pushToHistory(editor.meta.name) const selectedRow = parseInt(editorVariables.selectedRow) - const variables = pous.filter((pou) => pou.data.name === editor.meta.name)[0].data.variables + const deletePou = pous.find((p) => p.name === editor.meta.name) + const variables = deletePou?.interface?.variables ?? [] const variableToDelete = variables[selectedRow] if (variableToDelete) { @@ -792,7 +794,7 @@ const VariablesEditor = () => { }) const response = setPouVariables({ - pouName: pou?.data?.name ?? '', + pouName: pou?.name ?? '', variables: finalVariables, }) @@ -808,8 +810,8 @@ const VariablesEditor = () => { fbdFlows: freshFBDFlows, } = useOpenPLCStore.getState() - const freshPou = freshPous.find((p) => p.data.name === editor.meta.name) - const freshVariables = freshPou?.data.variables ?? [] + const freshPou = freshPous.find((p) => p.name === editor.meta.name) + const freshVariables = freshPou?.interface?.variables ?? [] if (language === 'ld') { syncNodesWithVariablesUtil(freshVariables, freshLadderFlows, updateNode) @@ -850,7 +852,7 @@ const VariablesEditor = () => { setParseError(null) handleFileAndWorkspaceSavedState(editor.meta.name) - if (freshPou && 'variablesText' in freshPou.data) { + if (freshPou && freshPou.interface && 'variablesText' in freshPou.interface) { clearPouVariablesText(editor.meta.name) } diff --git a/src2/frontend/hooks/useDebugSession.ts b/src2/frontend/hooks/useDebugSession.ts index bb638d284..11238a860 100644 --- a/src2/frontend/hooks/useDebugSession.ts +++ b/src2/frontend/hooks/useDebugSession.ts @@ -91,15 +91,15 @@ export function useDebugSession(): UseDebugSessionReturn { const trees: Record = {} for (const pou of projectData.pous) { - if (pou.type !== 'program') continue + if (pou.pouType !== 'program') continue - const instanceName = findInstanceName(pou.data.name, instances) + const instanceName = findInstanceName(pou.name, instances) if (!instanceName) continue const pouTrees: DebugTreeNode[] = [] - for (const variable of pou.data.variables) { - const tree = buildDebugTree(variable, pou.data.name, instanceName, parsed.variables, projectData) + for (const variable of (pou.interface?.variables ?? [])) { + const tree = buildDebugTree(variable, pou.name, instanceName, parsed.variables, projectData) pouTrees.push(tree) } @@ -121,14 +121,14 @@ export function useDebugSession(): UseDebugSessionReturn { pouTrees.push({ name: localName, fullPath: dv.name, - compositeKey: `${pou.data.name}:${localName}`, + compositeKey: `${pou.name}:${localName}`, type: typeName, isComplex: false, debugIndex: dv.index, }) } - trees[pou.data.name] = pouTrees + trees[pou.name] = pouTrees } debugTreesRef.current = trees @@ -163,21 +163,21 @@ export function useDebugSession(): UseDebugSessionReturn { // Build FB instance maps from project data const fbInstances = new Map() for (const pou of projectData.pous) { - if (pou.type !== 'function-block') continue - const fbTypeName = pou.data.name.toUpperCase() + if (pou.pouType !== 'function-block') continue + const fbTypeName = pou.name.toUpperCase() const instanceList: FbInstanceInfo[] = [] for (const programPou of projectData.pous) { - if (programPou.type !== 'program') continue - const progInstanceName = findInstanceName(programPou.data.name, instances) + if (programPou.pouType !== 'program') continue + const progInstanceName = findInstanceName(programPou.name, instances) if (!progInstanceName) continue - for (const variable of programPou.data.variables) { + for (const variable of (programPou.interface?.variables ?? [])) { if (variable.type.definition === 'derived' && variable.type.value.toUpperCase() === fbTypeName) { - const key = `${programPou.data.name}:${variable.name}` + const key = `${programPou.name}:${variable.name}` instanceList.push({ - fbTypeName: pou.data.name, - programName: programPou.data.name, + fbTypeName: pou.name, + programName: programPou.name, programInstanceName: progInstanceName, fbVariableName: variable.name, key, diff --git a/src2/frontend/screens/start-screen.tsx b/src2/frontend/screens/start-screen.tsx index fbecdf2ab..19d7650e0 100644 --- a/src2/frontend/screens/start-screen.tsx +++ b/src2/frontend/screens/start-screen.tsx @@ -24,14 +24,18 @@ const StartScreen = () => { workspaceActions: { setRecent }, modalActions: { openModal }, deviceActions: { setAvailableOptions }, + sharedWorkspaceActions: { handleOpenProjectResponse }, } = useOpenPLCStore() const handleCreateProject = () => { openModal('create-project', null) } - const handleOpenProject = () => { - void projectPort.openProject() + const handleOpenProject = async () => { + const result = await projectPort.openProject() + if (result.success && result.data) { + handleOpenProjectResponse(result.data) + } } const searchFilter = (value: string) => { diff --git a/src2/frontend/services/ai/context-collector.ts b/src2/frontend/services/ai/context-collector.ts index 0da2168e1..9caab2b5d 100644 --- a/src2/frontend/services/ai/context-collector.ts +++ b/src2/frontend/services/ai/context-collector.ts @@ -12,7 +12,7 @@ type StoreState = ReturnType export function collectProjectContext(state: StoreState, currentPouName: string, maxTokenBudget: number): string { const maxChars = maxTokenBudget * 4 const pous = state.project.data.pous - const pou = pous.find((p) => p.data.name === currentPouName) + const pou = pous.find((p) => p.name === currentPouName) if (!pou) return '' const sections: string[] = [] @@ -26,14 +26,15 @@ export function collectProjectContext(state: StoreState, currentPouName: string, } // 1. Current POU variables (always included — most important context) - if (pou.data.variables.length > 0) { - const varLines = pou.data.variables + const pouVariables = pou.interface?.variables ?? [] + if (pouVariables.length > 0) { + const varLines = pouVariables .map((v) => { const cls = v.class ? `${v.class} ` : '' return ` ${cls}${v.name} : ${v.type.value};` }) .join('\n') - addSection(`(* Current POU: ${pou.data.name} [${pou.type}] *)\nVAR\n${varLines}\nEND_VAR`) + addSection(`(* Current POU: ${pou.name} [${pou.pouType}] *)\nVAR\n${varLines}\nEND_VAR`) } // 2. Global variables @@ -44,19 +45,19 @@ export function collectProjectContext(state: StoreState, currentPouName: string, } // 3. Referenced function blocks — signatures only - const derivedTypeNames = pou.data.variables + const derivedTypeNames = pouVariables .filter((v) => v.type.definition === 'user-data-type') .map((v) => v.type.value) if (derivedTypeNames.length > 0) { - const fbPous = pous.filter((p) => p.type === 'function-block' && derivedTypeNames.includes(p.data.name)) + const fbPous = pous.filter((p) => p.pouType === 'function-block' && derivedTypeNames.includes(p.name)) if (fbPous.length > 0) { const fbSigs = fbPous.map((fb) => { - const vars = fb.data.variables + const vars = (fb.interface?.variables ?? []) .filter((v) => v.class === 'input' || v.class === 'output' || v.class === 'inOut') .map((v) => `${v.class?.toUpperCase()} ${v.name} : ${v.type.value}`) .join(', ') - return `FUNCTION_BLOCK ${fb.data.name} (* ${vars} *)` + return `FUNCTION_BLOCK ${fb.name} (* ${vars} *)` }) addSection(`(* Referenced Function Blocks *)\n${fbSigs.join('\n')}`) } @@ -89,14 +90,14 @@ export function collectProjectContext(state: StoreState, currentPouName: string, // 5. Sibling POU signatures (limited to 5) const siblings = pous - .filter((p) => p.data.name !== currentPouName) + .filter((p) => p.name !== currentPouName) .slice(0, 5) .map((p) => { - const params = p.data.variables + const params = (p.interface?.variables ?? []) .filter((v) => v.class === 'input' || v.class === 'output') .map((v) => `${v.class?.toUpperCase()} ${v.name} : ${v.type.value}`) .join(', ') - return `${p.type.toUpperCase()} ${p.data.name}${params ? ` (* ${params} *)` : ''}` + return `${p.pouType.toUpperCase()} ${p.name}${params ? ` (* ${params} *)` : ''}` }) if (siblings.length > 0) { diff --git a/src2/frontend/store/slices/project/validation/type-change.ts b/src2/frontend/store/slices/project/validation/type-change.ts index 463e731e2..6ee3e0b0d 100644 --- a/src2/frontend/store/slices/project/validation/type-change.ts +++ b/src2/frontend/store/slices/project/validation/type-change.ts @@ -59,20 +59,21 @@ export const validateTypeChange = ( ladderFlows: LadderFlowState['ladderFlows'], fbdFlows: FBDFlowState['fbdFlows'], scope?: 'local' | 'global', - pous?: Array<{ data: { name: string; variables: PLCVariable[] } }>, + pous?: Array<{ name: string; interface?: { variables: PLCVariable[] } }>, ): TypeChangeValidationResult => { const affectedNodes: NodeUsage[] = [] const warnings: string[] = [] if (scope === 'global' && pous) { pous.forEach((pou) => { - const externalVars = pou.data.variables.filter( + const variables = pou.interface?.variables ?? [] + const externalVars = variables.filter( (v) => v.class === 'external' && v.name.toLowerCase() === variableName.toLowerCase(), ) if (externalVars.length > 0) { warnings.push( - `POU "${pou.data.name}" has ${externalVars.length} external variable(s) referencing this global variable. Their types will be updated automatically.`, + `POU "${pou.name}" has ${externalVars.length} external variable(s) referencing this global variable. Their types will be updated automatically.`, ) } }) @@ -82,11 +83,11 @@ export const validateTypeChange = ( scope === 'global' && pous ? pous .filter((pou) => - pou.data.variables.some( + (pou.interface?.variables ?? []).some( (v) => v.class === 'external' && v.name.toLowerCase() === variableName.toLowerCase(), ), ) - .map((pou) => pou.data.name) + .map((pou) => pou.name) : [] const shouldCheckFlow = (flowName: string) => { diff --git a/src2/frontend/store/slices/shared/slice.ts b/src2/frontend/store/slices/shared/slice.ts index 7b33ef389..327d5eac6 100644 --- a/src2/frontend/store/slices/shared/slice.ts +++ b/src2/frontend/store/slices/shared/slice.ts @@ -2,7 +2,12 @@ import { produce } from 'immer' import { StateCreator } from 'zustand' import type { SharedRootState, SharedSlice } from './types' +import type { FileSliceDataObject } from '../file' +import type { TabsProps } from '../tabs' +import type { LadderFlowType } from '../ladder' +import type { FBDFlowType } from '../fbd' import { CreateEditorObjectFromTab } from '../tabs/utils' +import { toast } from '../../../components/_features/[app]/toast/use-toast' import { createDatatypeObject, createEditorObjectForDatatype, @@ -330,6 +335,90 @@ const createSharedSlice: StateCreator = (s getState().searchActions.clearSearch() getState().modalActions.closeModal() }, + + handleOpenProjectResponse: (data) => { + getState().sharedWorkspaceActions.clearStatesOnCloseProject() + getState().workspaceActions.setEditingState('saved') + + // Set project data (setting meta.path triggers navigation from start to workspace) + getState().projectActions.setProject({ + meta: data.meta, + data: data.projectData, + }) + + // Add ladder and FBD flows for graphical POUs + const pous = data.projectData.pous + pous.forEach((pou) => { + if (pou.body.language === 'ld') { + getState().ladderFlowActions.addLadderFlow(pou.body.value as LadderFlowType) + } + if (pou.body.language === 'fbd') { + getState().fbdFlowActions.addFBDFlow(pou.body.value as FBDFlowType) + } + }) + + // Register user-defined functions/function-blocks in the library + pous.forEach((pou) => { + if (pou.pouType !== 'program') { + getState().libraryActions.addLibrary(pou.name, pou.pouType) + } + }) + + // Set device definitions + if (data.deviceConfiguration || data.devicePinMapping) { + getState().deviceActions.setDeviceDefinitions({ + configuration: data.deviceConfiguration, + pinMapping: data.devicePinMapping, + }) + } + + // Register files for save-state tracking + const files: FileSliceDataObject = {} + pous.forEach((pou) => { + files[pou.name] = { type: pou.pouType, filePath: pou.name, saved: true } + }) + data.projectData.dataTypes.forEach((dt) => { + files[dt.name] = { type: 'data-type', filePath: dt.name, saved: true } + }) + const servers = data.projectData.servers + if (servers) { + servers.forEach((s) => { + files[s.name] = { type: 'server', filePath: s.name, saved: true } + }) + } + const remoteDevices = data.projectData.remoteDevices + if (remoteDevices) { + remoteDevices.forEach((d) => { + files[d.name] = { type: 'remote-device', filePath: d.name, saved: true } + }) + } + files['Resource'] = { type: 'resource', filePath: 'Resource', saved: true } + files['Configuration'] = { type: 'device', filePath: 'Configuration', saved: true } + getState().fileActions.setFiles({ files }) + + // Open the main POU tab (if present) + const mainPou = pous.find((p) => p.name === 'main' && p.pouType === 'program') + if (mainPou) { + const language = mainPou.body.language as 'il' | 'st' | 'ld' | 'sfc' | 'fbd' | 'python' | 'cpp' + const tabToBeCreated: TabsProps = { + name: mainPou.name, + path: `/data/pous/program/${mainPou.name}`, + elementType: { type: 'program', language }, + } + const model = CreateEditorObjectFromTab(tabToBeCreated) + getState().editorActions.addModel(model) + getState().editorActions.setEditor(model) + getState().tabsActions.updateTabs(tabToBeCreated) + getState().tabsActions.setSelectedTab(mainPou.name) + getState().workspaceActions.setSelectedProjectTreeLeaf({ label: mainPou.name, type: 'program' }) + } + + toast({ + title: 'Project opened!', + description: 'Your project was opened, and loaded.', + variant: 'default', + }) + }, }, snapshotActions: { diff --git a/src2/frontend/store/slices/shared/types.ts b/src2/frontend/store/slices/shared/types.ts index af68741f4..816211bf0 100644 --- a/src2/frontend/store/slices/shared/types.ts +++ b/src2/frontend/store/slices/shared/types.ts @@ -1,4 +1,12 @@ -import type { PLCBody, PLCDataType, PLCVariable } from '../../../../middleware/shared/ports/types' +import type { + DeviceConfiguration, + DevicePin, + PLCBody, + PLCDataType, + PLCProjectData, + PLCVariable, + ProjectMeta, +} from '../../../../middleware/shared/ports/types' import type { ConsoleSlice } from '../console' import type { DeviceSlice } from '../device' import type { EditorSlice } from '../editor' @@ -102,6 +110,13 @@ export type SnapshotActions = { redo: (pouName: string) => void } +export type OpenProjectResponseData = { + meta: ProjectMeta + projectData: PLCProjectData + deviceConfiguration?: DeviceConfiguration + devicePinMapping?: DevicePin[] +} + export type SharedWorkspaceActions = { /** Mark a file as unsaved and set workspace editingState to 'unsaved'. */ handleFileAndWorkspaceSavedState: (name: string) => void @@ -114,6 +129,11 @@ export type SharedWorkspaceActions = { closeProject: () => void /** Reset all slice state for project close. */ clearStatesOnCloseProject: () => void + /** + * Populate store with project data returned from a ProjectPort open call. + * Sets project state, device config, files, libraries, flows, and opens main POU tab. + */ + handleOpenProjectResponse: (data: OpenProjectResponseData) => void } export type SharedSlice = { diff --git a/src2/frontend/utils/debug-tree-traversal.ts b/src2/frontend/utils/debug-tree-traversal.ts index 0aedfee04..1e6b8d571 100644 --- a/src2/frontend/utils/debug-tree-traversal.ts +++ b/src2/frontend/utils/debug-tree-traversal.ts @@ -7,8 +7,7 @@ */ import { StandardFunctionBlocks } from '../data/library/standard-function-blocks' -import type { PLCDataType, PLCVariable } from '../../middleware/shared/ports/types' -import type { PouDTO } from '../store/slices/project/types' +import type { PLCDataType, PLCPou, PLCVariable } from '../../middleware/shared/ports/types' import type { DebugVariableEntry } from './debug-parser' import { @@ -26,7 +25,7 @@ export interface TraversalContext { /** Parsed debug variables from debug.c */ debugVariables: DebugVariableEntry[] /** Project POUs for FB lookup */ - projectPous: PouDTO[] + projectPous: PLCPou[] /** Project data types for struct lookup */ dataTypes: PLCDataType[] /** Instance name from Resources configuration */ @@ -74,7 +73,7 @@ interface ArrayTypeData { /** * Check if a type is a function block (standard library or custom). */ -function isFunctionBlock(typeName: string, projectPous: PouDTO[]): boolean { +function isFunctionBlock(typeName: string, projectPous: PLCPou[]): boolean { const typeNameUpper = typeName.toUpperCase() // Check standard library @@ -85,7 +84,7 @@ function isFunctionBlock(typeName: string, projectPous: PouDTO[]): boolean { // Check custom FBs return projectPous.some( - (pou) => normalizeTypeString(pou.type) === 'functionblock' && pou.data.name.toUpperCase() === typeNameUpper, + (pou) => normalizeTypeString(pou.pouType) === 'functionblock' && pou.name.toUpperCase() === typeNameUpper, ) } diff --git a/src2/frontend/utils/generate-iec-string-to-variables.ts b/src2/frontend/utils/generate-iec-string-to-variables.ts index caeec4550..9ce870533 100644 --- a/src2/frontend/utils/generate-iec-string-to-variables.ts +++ b/src2/frontend/utils/generate-iec-string-to-variables.ts @@ -154,7 +154,7 @@ export const parseIecStringToVariables = ( const baseCheck = baseTypeSchema.safeParse(parsedType.toLowerCase()) const isUserFunctionBlock = pous?.some( - (pou) => pou.type === 'function-block' && pou.data.name.toLowerCase() === parsedType.toLowerCase(), + (pou) => pou.pouType === 'function-block' && pou.name.toLowerCase() === parsedType.toLowerCase(), ) const isSystemFunctionBlock = libraries?.system.some((lib) => { diff --git a/src2/frontend/utils/pou-helpers.ts b/src2/frontend/utils/pou-helpers.ts index eb60b3840..47f32a826 100644 --- a/src2/frontend/utils/pou-helpers.ts +++ b/src2/frontend/utils/pou-helpers.ts @@ -4,8 +4,7 @@ */ import { StandardFunctionBlocks } from '../data/library/standard-function-blocks' -import type { PLCDataType } from '../../middleware/shared/ports/types' -import type { PouDTO } from '../store/slices/project/types' +import type { PLCDataType, PLCPou } from '../../middleware/shared/ports/types' /** * Variable definition from a POU or library FB. @@ -68,7 +67,7 @@ export const isBaseType = (typeName: string): boolean => { * Searches BOTH the built-in library AND project POUs. * Returns the variables array from the FB definition, or null if not found. */ -export const findFunctionBlockVariables = (typeName: string, projectPous: PouDTO[]): PouVariable[] | null => { +export const findFunctionBlockVariables = (typeName: string, projectPous: PLCPou[]): PouVariable[] | null => { const typeNameUpper = typeName.toUpperCase() // Check standard library FBs @@ -81,10 +80,10 @@ export const findFunctionBlockVariables = (typeName: string, projectPous: PouDTO // Check project POUs (user-defined FBs) const customFB = projectPous.find( - (pou) => normalizeTypeString(pou.type) === 'functionblock' && pou.data.name.toUpperCase() === typeNameUpper, + (pou) => normalizeTypeString(pou.pouType) === 'functionblock' && pou.name.toUpperCase() === typeNameUpper, ) - if (customFB && customFB.type === 'function-block') { - return customFB.data.variables as PouVariable[] + if (customFB && customFB.pouType === 'function-block') { + return (customFB.interface?.variables ?? []) as PouVariable[] } return null @@ -93,7 +92,7 @@ export const findFunctionBlockVariables = (typeName: string, projectPous: PouDTO /** * Check if a type name is a function block (library or project). */ -export const isFunctionBlockType = (typeName: string, projectPous: PouDTO[]): boolean => { +export const isFunctionBlockType = (typeName: string, projectPous: PLCPou[]): boolean => { return findFunctionBlockVariables(typeName, projectPous) !== null } @@ -147,7 +146,7 @@ export interface LeafVariable { */ export const findLeafVariables = ( typeName: string, - projectPous: PouDTO[], + projectPous: PLCPou[], dataTypes: PLCDataType[], pathPrefix: string = '', visited: Set = new Set(), @@ -212,9 +211,9 @@ export const findLeafVariables = ( /** * Get all variables from a POU (program or function block). */ -export const getPouVariables = (pou: PouDTO): PouVariable[] => { - if (pou.type === 'program' || pou.type === 'function-block') { - return pou.data.variables as PouVariable[] +export const getPouVariables = (pou: PLCPou): PouVariable[] => { + if (pou.pouType === 'program' || pou.pouType === 'function-block') { + return (pou.interface?.variables ?? []) as PouVariable[] } return [] } diff --git a/src2/frontend/utils/variable-references.ts b/src2/frontend/utils/variable-references.ts index 73f815f45..ce2b3cd95 100644 --- a/src2/frontend/utils/variable-references.ts +++ b/src2/frontend/utils/variable-references.ts @@ -37,7 +37,7 @@ export function findAllReferencesToVariable( const isGlobalScope = scope === 'global' if (!isGlobalScope) { - const pou = pous.find((p) => p.data.name === pouName) + const pou = pous.find((p) => p.name === pouName) if (!pou) { return { totalReferences: 0, @@ -50,12 +50,12 @@ export function findAllReferencesToVariable( searchWithinPou(pou, pouName, normalizedName, variableName, ladderFlows, fbdFlows, references) } else { pous.forEach((pou) => { - const hasExternalVar = pou.data.variables.some( + const hasExternalVar = (pou.interface?.variables ?? []).some( (v) => v.class === 'external' && v.name.toLowerCase() === normalizedName, ) if (hasExternalVar) { - searchWithinPou(pou, pou.data.name, normalizedName, variableName, ladderFlows, fbdFlows, references) + searchWithinPou(pou, pou.name, normalizedName, variableName, ladderFlows, fbdFlows, references) } }) } @@ -217,12 +217,12 @@ function searchWithinPou( } if ( - pou.data.body.language === 'st' || - pou.data.body.language === 'il' || - pou.data.body.language === 'python' || - pou.data.body.language === 'cpp' + pou.body.language === 'st' || + pou.body.language === 'il' || + pou.body.language === 'python' || + pou.body.language === 'cpp' ) { - const bodyValue = pou.data.body.value + const bodyValue = pou.body.value const lines = bodyValue.split('\n') const variablePattern = new RegExp(`\\b${variableName}\\b`, 'gi') @@ -231,7 +231,7 @@ function searchWithinPou( while ((match = variablePattern.exec(line)) !== null) { references.push({ pouName, - editorType: pou.data.body.language as 'st' | 'il' | 'python' | 'cpp', + editorType: pou.body.language as 'st' | 'il' | 'python' | 'cpp', lineNumber: lineIndex + 1, columnStart: match.index, columnEnd: match.index + variableName.length, @@ -255,12 +255,12 @@ export function propagateVariableTypeChange( }, ): void { pous.forEach((pou) => { - pou.data.variables.forEach((variable, varIndex) => { + ;(pou.interface?.variables ?? []).forEach((variable, varIndex) => { if (variable.class === 'external' && variable.name.toLowerCase() === variableName.toLowerCase()) { projectActions.updateVariable({ scope: 'local', rowId: varIndex, - associatedPou: pou.data.name, + associatedPou: pou.name, data: { type: newType }, }) } @@ -282,7 +282,7 @@ export function propagateVariableRename( updateNode: (params: { editorName: string; nodeId: string; node: Node }) => void }, projectActions: { - updatePou: (params: { name: string; content: PLCPou['data']['body'] }) => void + updatePou: (params: { name: string; content: PLCPou['body'] }) => void updateVariable: (params: { scope: 'local' | 'global' rowId: number @@ -294,12 +294,12 @@ export function propagateVariableRename( ): void { if (scope === 'global') { pous.forEach((pou) => { - pou.data.variables.forEach((variable, varIndex) => { + ;(pou.interface?.variables ?? []).forEach((variable, varIndex) => { if (variable.class === 'external' && variable.name.toLowerCase() === oldName.toLowerCase()) { projectActions.updateVariable({ scope: 'local', rowId: varIndex, - associatedPou: pou.data.name, + associatedPou: pou.name, data: { name: newName }, }) } @@ -321,13 +321,13 @@ export function propagateVariableRename( }) textBasedRefsByPou.forEach((_refs, pouName) => { - const pou = pous.find((p) => p.data.name === pouName) + const pou = pous.find((p) => p.name === pouName) if (!pou) return - const bodyValue = pou.data.body.value + const bodyValue = pou.body.value if (typeof bodyValue !== 'string') return - const language = pou.data.body.language + const language = pou.body.language if (language !== 'st' && language !== 'il' && language !== 'python' && language !== 'cpp') return try { From 09341c2b2ca70769004752ccc747b038d8512061 Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:15:02 -0300 Subject: [PATCH 03/40] fix(step-31): adjust panel defaults and add forwardRef to ActivityBarButton ActivityBarButton now uses forwardRef for tooltip compatibility. Explorer panels split 50/50 instead of 40/40, and workspace/editor panels adjusted to 84/69 for better proportions. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/_atoms/buttons/activity-bar/index.tsx | 8 +++++--- src/renderer/components/_organisms/explorer/index.tsx | 4 ++-- src/renderer/screens/workspace-screen.tsx | 4 ++-- .../components/_atoms/buttons/activity-bar/index.tsx | 8 +++++--- src2/frontend/components/_organisms/explorer/index.tsx | 4 ++-- src2/frontend/screens/workspace-screen.tsx | 4 ++-- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/renderer/components/_atoms/buttons/activity-bar/index.tsx b/src/renderer/components/_atoms/buttons/activity-bar/index.tsx index 2e68d934a..95c74e2f4 100644 --- a/src/renderer/components/_atoms/buttons/activity-bar/index.tsx +++ b/src/renderer/components/_atoms/buttons/activity-bar/index.tsx @@ -1,16 +1,17 @@ import { cn } from '@root/utils' -import { ComponentPropsWithoutRef } from 'react' +import { ComponentPropsWithoutRef, forwardRef } from 'react' type IActivityBarButtonProps = ComponentPropsWithoutRef<'button'> & { 'data-active'?: string } -const ActivityBarButton = (props: IActivityBarButtonProps) => { +const ActivityBarButton = forwardRef((props, ref) => { const { children, className, 'data-active': dataActive, ...res } = props const isActive = dataActive === 'true' return ( ) -} +}) +ActivityBarButton.displayName = 'ActivityBarButton' export { ActivityBarButton } diff --git a/src/renderer/components/_organisms/explorer/index.tsx b/src/renderer/components/_organisms/explorer/index.tsx index 2a228b968..454a1a11a 100644 --- a/src/renderer/components/_organisms/explorer/index.tsx +++ b/src/renderer/components/_organisms/explorer/index.tsx @@ -69,14 +69,14 @@ const Explorer = ({ collapse }: ExplorerProps): ReactElement => { className="flex h-full w-[200px] max-w-lg flex-col overflow-auto rounded-lg border-2 border-inherit border-neutral-200 bg-white data-[panel-size='0.0']:hidden dark:border-neutral-850 dark:bg-neutral-950" > - + - + { ref={workspacePanelRef} id='workspacePanel' order={2} - defaultSize={87} + defaultSize={84} className='relative flex h-full w-[400px]' > { id='editorPanel' order={1} minSize={45} - defaultSize={75} + defaultSize={69} className={cn( 'relative flex flex-1 grow flex-col overflow-hidden rounded-lg border-2 border-neutral-200 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-950', { diff --git a/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx b/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx index e9f787eae..d9d5fb5ba 100644 --- a/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx +++ b/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx @@ -1,16 +1,17 @@ import { cn } from '../../../../utils/cn' -import { ComponentPropsWithoutRef } from 'react' +import { ComponentPropsWithoutRef, forwardRef } from 'react' type IActivityBarButtonProps = ComponentPropsWithoutRef<'button'> & { 'data-active'?: string } -const ActivityBarButton = (props: IActivityBarButtonProps) => { +const ActivityBarButton = forwardRef((props, ref) => { const { children, className, 'data-active': dataActive, ...res } = props const isActive = dataActive === 'true' return ( ) -} +}) +ActivityBarButton.displayName = 'ActivityBarButton' export { ActivityBarButton } diff --git a/src2/frontend/components/_organisms/explorer/index.tsx b/src2/frontend/components/_organisms/explorer/index.tsx index 47984281c..e7b51f3f7 100644 --- a/src2/frontend/components/_organisms/explorer/index.tsx +++ b/src2/frontend/components/_organisms/explorer/index.tsx @@ -69,14 +69,14 @@ const Explorer = ({ collapse }: ExplorerProps): ReactElement => { className="flex h-full w-[200px] max-w-lg flex-col overflow-auto rounded-lg border-2 border-inherit border-neutral-200 bg-white data-[panel-size='0.0']:hidden dark:border-neutral-850 dark:bg-neutral-950" > - + - + { { id='editorPanel' order={1} minSize={15} - defaultSize={75} + defaultSize={69} className={cn( 'relative flex flex-1 grow flex-col overflow-hidden rounded-lg border-2 border-neutral-200 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-950', { From df42759e0e09b9e341e35d07314276fa75ec8f75 Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Mon, 16 Mar 2026 13:05:21 -0300 Subject: [PATCH 04/40] fix(step-31): move closeModal/submitAutocompletion before useImperativeHandle These const declarations were referenced in useImperativeHandle before their initialization, causing a ReferenceError (temporal dead zone). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../graphical-editor/autocomplete/index.tsx | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx b/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx index f6e051acb..2c5b4773a 100644 --- a/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx @@ -85,6 +85,17 @@ export const GraphicalEditorAutocomplete = forwardRef { + setAutocompleteFocus(false) + setSelectedVariable({ positionInArray: -1, variable: { id: '', name: '' } }) + if (setIsOpen) setIsOpen(false) + } + + const submitAutocompletion = ({ variable }: { variable: { id: string; name: string } }) => { + closeModal() + submit({ variable }) + } + // @ts-expect-error - not all properties are used useImperativeHandle(ref, () => { return { @@ -93,8 +104,25 @@ export const GraphicalEditorAutocomplete = forwardRef { + if (selectedVariable.positionInArray === -1) { + const addVariableOption = selectableValues.find((item) => item.type === 'add') + if (addVariableOption) { + submitAutocompletion({ variable: addVariableOption.variable }) + } else { + closeModal() + } + } else { + submitAutocompletion({ variable: selectedVariable.variable }) + } + }, } - }, [selectedVariable, popoverRef, autocompleteFocus]) + }, [selectedVariable, selectableValues, popoverRef, autocompleteFocus, submitAutocompletion, closeModal]) useEffect(() => { switch (keyDown) { @@ -162,17 +190,6 @@ export const GraphicalEditorAutocomplete = forwardRef { - setAutocompleteFocus(false) - setSelectedVariable({ positionInArray: -1, variable: { id: '', name: '' } }) - if (setIsOpen) setIsOpen(false) - } - - const submitAutocompletion = ({ variable }: { variable: { id: string; name: string } }) => { - closeModal() - submit({ variable }) - } - return ( From ccbf964dafee4ab86059f2320624c0c53cfa46bf Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Mon, 16 Mar 2026 13:33:20 -0300 Subject: [PATCH 05/40] fix(step-31): fix ladder variable creation and configuration field naming createVariable silently failed when POU.interface was undefined (set by project adapter for POUs with no variables). Now the adapter always initializes interface and createVariable returns the created data. Also fix configuration -> configurations (plural) mismatch across 6 frontend files, and add error toast to ladder autocomplete. Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 1 + .../ladder/autocomplete/index.tsx | 9 +- .../_atoms/graphical-editor/ladder/block.tsx | 6 +- .../_atoms/graphical-editor/ladder/coil.tsx | 6 +- .../graphical-editor/ladder/contact.tsx | 6 +- .../_atoms/graphical-editor/ladder/index.ts | 132 +--------- .../graphical-editor/ladder/node-builders.ts | 137 ++++++++++ .../editor/search-in-project/index.tsx | 6 +- .../hooks/use-project-variables.ts | 4 +- .../_molecules/graphical-editor/fbd/index.tsx | 4 +- .../graphical-editor/ladder/rung/body.tsx | 2 +- .../variables-table/editable-cell.tsx | 4 +- .../frontend/services/ai/context-collector.ts | 2 +- .../store/__tests__/ladder-slice.test.ts | 65 +++-- src2/frontend/store/slices/ladder/slice.ts | 59 ++++- src2/frontend/store/slices/ladder/types.ts | 16 +- .../store/slices/ladder/utils/index.ts | 236 ++++++++++++++++++ src2/frontend/store/slices/project/slice.ts | 2 +- .../adapters/editor/project-adapter.ts | 11 +- 19 files changed, 515 insertions(+), 193 deletions(-) create mode 100644 src2/frontend/components/_atoms/graphical-editor/ladder/node-builders.ts create mode 100644 src2/frontend/store/slices/ladder/utils/index.ts diff --git a/package.json b/package.json index 30ebd57a4..e33953ce4 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./configs/webpack/webpack.config.preload.dev.ts", "start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./configs/webpack/webpack.config.renderer.dev.ts", "start:src2": "ts-node scripts/check-port-in-use.js && npm run prestart && npm run start:renderer:src2", + "start:src2:electron": "ts-node scripts/check-port-in-use.js && npm run prestart && concurrently -k \"npm run start:renderer:src2\" \"electronmon .\"", "start:renderer:src2": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./configs/webpack/webpack.config.renderer.src2.dev.ts", "test:unit": "jest --watch --no-coverage", "test:e2e": "npm run build && npx playwright test", diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx index a6e2370c8..3b28450f6 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx @@ -199,7 +199,14 @@ const VariablesBlockAutoComplete = forwardRef({ if (!userPou) { return ( libraries.system - // @ts-expect-error - type is dynamic - .flatMap((block) => block.pous) - // @ts-expect-error - type is dynamic - .find((pou) => pou.name.toLowerCase() === blockNameValue.toLowerCase()) + .flatMap((block: { pous: Array<{ name: string }> }) => block.pous) + .find((pou: { name: string }) => pou.name.toLowerCase() === blockNameValue.toLowerCase()) ) } diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx index 85df70a4b..0e2d17cfd 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx @@ -44,6 +44,7 @@ export const Coil = (block: CoilProps) => { focus: () => void isFocused: boolean selectedVariable: { positionInArray: number; variableName: string } + triggerSubmit: () => void } >(null) @@ -347,8 +348,11 @@ export const Coil = (block: CoilProps) => { onChange={onChangeHandler} onKeyDown={(e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Tab') e.preventDefault() - if (e.key === 'Enter' && autocompleteRef.current?.selectedVariable.positionInArray !== -1) { + if (e.key === 'Enter' && openAutocomplete) { + e.preventDefault() + autocompleteRef.current?.triggerSubmit?.() inputVariableRef.current?.blur({ submit: false }) + return } setKeyPressedAtTextarea(e.key) }} diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx index de028b809..b44b18464 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx @@ -43,6 +43,7 @@ export const Contact = (block: ContactProps) => { focus: () => void isFocused: boolean selectedVariable: { positionInArray: number; variableName: string } + triggerSubmit: () => void } >(null) @@ -345,8 +346,11 @@ export const Contact = (block: ContactProps) => { onChange={onChangeHandler} onKeyDown={(e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Tab') e.preventDefault() - if (e.key === 'Enter' && autocompleteRef.current?.selectedVariable.positionInArray !== -1) { + if (e.key === 'Enter' && openAutocomplete) { + e.preventDefault() + autocompleteRef.current?.triggerSubmit?.() inputVariableRef.current?.blur({ submit: false }) + return } setKeyPressedAtTextarea(e.key) }} diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/index.ts b/src2/frontend/components/_atoms/graphical-editor/ladder/index.ts index 951423aa3..c6ceb456b 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/index.ts +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/index.ts @@ -8,8 +8,9 @@ import * as parallelNode from './parallel' import * as placeholderNode from './placeholder' import * as powerRailNode from './power-rail' import * as variableNode from './variable' -import * as constants from './utils/constants' -import * as buildNodes from './buildNodes' + +// Re-export from node-builders (safe for store-layer imports, no circular deps) +export { defaultCustomNodesStyles, nodesBuilder } from './node-builders' export const DEFAULT_NODES_GAP = 50 @@ -25,133 +26,6 @@ export const customNodeTypes = { mockNode: mockNode.MockNode, } -type CustomLadderNodeTypes = { - [key: string]: { - width: number - height: number - gap: number - verticalGap: number - handle: { - x: number - y: number - offsetY: number - } - } -} -export const defaultCustomNodesStyles: CustomLadderNodeTypes = { - block: { - width: constants.DEFAULT_BLOCK_WIDTH, - height: constants.DEFAULT_BLOCK_HEIGHT, - gap: 120, - verticalGap: 80, - handle: { - x: constants.DEFAULT_BLOCK_WIDTH, - y: constants.DEFAULT_BLOCK_CONNECTOR_Y, - offsetY: constants.DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, - }, - }, - coil: { - width: constants.DEFAULT_COIL_BLOCK_WIDTH, - height: constants.DEFAULT_COIL_BLOCK_HEIGHT, - gap: 45, - verticalGap: 80, - handle: { - x: constants.DEFAULT_COIL_CONNECTOR_X, - y: constants.DEFAULT_COIL_CONNECTOR_Y, - offsetY: 0, - }, - }, - contact: { - width: constants.DEFAULT_CONTACT_BLOCK_WIDTH, - height: constants.DEFAULT_CONTACT_BLOCK_HEIGHT, - gap: 45, - verticalGap: 80, - handle: { - x: constants.DEFAULT_CONTACT_CONNECTOR_X, - y: constants.DEFAULT_CONTACT_CONNECTOR_Y, - offsetY: 0, - }, - }, - parallel: { - width: constants.DEFAULT_PARALLEL_WIDTH, - height: constants.DEFAULT_PARALLEL_HEIGHT, - gap: constants.GAP, - verticalGap: constants.GAP, - handle: { - x: 0, - y: constants.DEFAULT_PARALLEL_CONNECTOR_Y, - offsetY: 0, - }, - }, - parallelPlaceholder: { - width: constants.DEFAULT_PLACEHOLDER_WIDTH, - height: constants.DEFAULT_PLACEHOLDER_HEIGHT, - gap: constants.DEFAULT_PLACEHOLDER_GAP, - verticalGap: constants.DEFAULT_PLACEHOLDER_GAP, - handle: { - x: 0, - y: constants.DEFAULT_PLACEHOLDER_CONNECTOR_Y, - offsetY: 0, - }, - }, - placeholder: { - width: constants.DEFAULT_PLACEHOLDER_WIDTH, - height: constants.DEFAULT_PLACEHOLDER_HEIGHT, - gap: constants.DEFAULT_PLACEHOLDER_GAP, - verticalGap: constants.DEFAULT_PLACEHOLDER_GAP, - handle: { - x: 0, - y: constants.DEFAULT_PLACEHOLDER_CONNECTOR_Y, - offsetY: 0, - }, - }, - powerRail: { - width: constants.DEFAULT_POWER_RAIL_WIDTH, - height: constants.DEFAULT_POWER_RAIL_HEIGHT, - gap: 20, - verticalGap: 0, - handle: { - x: constants.DEFAULT_POWER_RAIL_CONNECTOR_X, - y: constants.DEFAULT_POWER_RAIL_CONNECTOR_Y, - offsetY: 0, - }, - }, - variable: { - width: constants.DEFAULT_VARIABLE_WIDTH, - height: constants.DEFAULT_VARIABLE_HEIGHT, - gap: 30, - verticalGap: 0, - handle: { - x: constants.DEFAULT_VARIABLE_CONNECTOR_X, - y: constants.DEFAULT_VARIABLE_CONNECTOR_Y, - offsetY: constants.DEFAULT_VARIABLE_CONNECTOR_Y, - }, - }, - mockNode: { - width: 150, - height: 40, - gap: 50, - verticalGap: 50, - handle: { - x: 0, - y: 20, - offsetY: 0, - }, - }, -} - -export const nodesBuilder = { - block: buildNodes.buildBlockNode, - coil: buildNodes.buildCoilNode, - contact: buildNodes.buildContactNode, - parallel: buildNodes.buildParallel, - parallelPlaceholder: buildNodes.builderPlaceholderNode, - placeholder: buildNodes.builderPlaceholderNode, - powerRail: buildNodes.buildPowerRailNode, - variable: buildNodes.buildVariableNode, - mockNode: buildNodes.buildMockNode, -} - export const checkIfElementIsNode = (element: unknown): element is Node => { return (element as Node)?.data !== undefined } diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/node-builders.ts b/src2/frontend/components/_atoms/graphical-editor/ladder/node-builders.ts new file mode 100644 index 000000000..988a6a597 --- /dev/null +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/node-builders.ts @@ -0,0 +1,137 @@ +/** + * Node builder functions and styles extracted from index.ts to avoid circular + * dependencies when imported by the store layer. + * + * index.ts re-exports from here so that existing consumers are unaffected. + */ + +import * as buildNodes from './buildNodes' +import * as constants from './utils/constants' + +type CustomLadderNodeTypes = { + [key: string]: { + width: number + height: number + gap: number + verticalGap: number + handle: { + x: number + y: number + offsetY: number + } + } +} + +export const defaultCustomNodesStyles: CustomLadderNodeTypes = { + block: { + width: constants.DEFAULT_BLOCK_WIDTH, + height: constants.DEFAULT_BLOCK_HEIGHT, + gap: 120, + verticalGap: 80, + handle: { + x: constants.DEFAULT_BLOCK_WIDTH, + y: constants.DEFAULT_BLOCK_CONNECTOR_Y, + offsetY: constants.DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, + }, + }, + coil: { + width: constants.DEFAULT_COIL_BLOCK_WIDTH, + height: constants.DEFAULT_COIL_BLOCK_HEIGHT, + gap: 45, + verticalGap: 80, + handle: { + x: constants.DEFAULT_COIL_CONNECTOR_X, + y: constants.DEFAULT_COIL_CONNECTOR_Y, + offsetY: 0, + }, + }, + contact: { + width: constants.DEFAULT_CONTACT_BLOCK_WIDTH, + height: constants.DEFAULT_CONTACT_BLOCK_HEIGHT, + gap: 45, + verticalGap: 80, + handle: { + x: constants.DEFAULT_CONTACT_CONNECTOR_X, + y: constants.DEFAULT_CONTACT_CONNECTOR_Y, + offsetY: 0, + }, + }, + parallel: { + width: constants.DEFAULT_PARALLEL_WIDTH, + height: constants.DEFAULT_PARALLEL_HEIGHT, + gap: constants.GAP, + verticalGap: constants.GAP, + handle: { + x: 0, + y: constants.DEFAULT_PARALLEL_CONNECTOR_Y, + offsetY: 0, + }, + }, + parallelPlaceholder: { + width: constants.DEFAULT_PLACEHOLDER_WIDTH, + height: constants.DEFAULT_PLACEHOLDER_HEIGHT, + gap: constants.DEFAULT_PLACEHOLDER_GAP, + verticalGap: constants.DEFAULT_PLACEHOLDER_GAP, + handle: { + x: 0, + y: constants.DEFAULT_PLACEHOLDER_CONNECTOR_Y, + offsetY: 0, + }, + }, + placeholder: { + width: constants.DEFAULT_PLACEHOLDER_WIDTH, + height: constants.DEFAULT_PLACEHOLDER_HEIGHT, + gap: constants.DEFAULT_PLACEHOLDER_GAP, + verticalGap: constants.DEFAULT_PLACEHOLDER_GAP, + handle: { + x: 0, + y: constants.DEFAULT_PLACEHOLDER_CONNECTOR_Y, + offsetY: 0, + }, + }, + powerRail: { + width: constants.DEFAULT_POWER_RAIL_WIDTH, + height: constants.DEFAULT_POWER_RAIL_HEIGHT, + gap: 20, + verticalGap: 0, + handle: { + x: constants.DEFAULT_POWER_RAIL_CONNECTOR_X, + y: constants.DEFAULT_POWER_RAIL_CONNECTOR_Y, + offsetY: 0, + }, + }, + variable: { + width: constants.DEFAULT_VARIABLE_WIDTH, + height: constants.DEFAULT_VARIABLE_HEIGHT, + gap: 30, + verticalGap: 0, + handle: { + x: constants.DEFAULT_VARIABLE_CONNECTOR_X, + y: constants.DEFAULT_VARIABLE_CONNECTOR_Y, + offsetY: constants.DEFAULT_VARIABLE_CONNECTOR_Y, + }, + }, + mockNode: { + width: 150, + height: 40, + gap: 50, + verticalGap: 50, + handle: { + x: 0, + y: 20, + offsetY: 0, + }, + }, +} + +export const nodesBuilder = { + block: buildNodes.buildBlockNode, + coil: buildNodes.buildCoilNode, + contact: buildNodes.buildContactNode, + parallel: buildNodes.buildParallel, + parallelPlaceholder: buildNodes.builderPlaceholderNode, + placeholder: buildNodes.builderPlaceholderNode, + powerRail: buildNodes.buildPowerRailNode, + variable: buildNodes.buildVariableNode, + mockNode: buildNodes.buildMockNode, +} diff --git a/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx b/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx index 3a569ee5d..531121084 100644 --- a/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx @@ -309,7 +309,7 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) type: dataType.derivation, })) - const resourceGlobalVar = data.configuration.resource.globalVariables.filter((variable) => { + const resourceGlobalVar = data.configurations.resource.globalVariables.filter((variable) => { const resourceMatchesFilter = activeFilters.length === 0 || activeFilters.includes('configuration') return ( resourceMatchesFilter && @@ -321,7 +321,7 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) ) }) - const resourceTasks = data.configuration.resource.tasks.filter((task) => { + const resourceTasks = data.configurations.resource.tasks.filter((task) => { const resourceMatchesFilter = activeFilters.length === 0 || activeFilters.includes('configuration') return ( resourceMatchesFilter && @@ -333,7 +333,7 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) ) }) - const resourceInstances = data.configuration.resource.instances.filter((instance) => { + const resourceInstances = data.configurations.resource.instances.filter((instance) => { const resourceMatchesFilter = activeFilters.length === 0 || activeFilters.includes('configuration') return ( resourceMatchesFilter && diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts index 205042c78..59aaea8f3 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts @@ -445,7 +445,7 @@ export const useProjectVariables = (): VariableTreeNode[] => { } } - const globalVars = projectData.configuration.resource.globalVariables + const globalVars = projectData.configurations.resource.globalVariables if (globalVars && globalVars.length > 0) { nodes.push( buildGlobalVariablesNode( @@ -457,7 +457,7 @@ export const useProjectVariables = (): VariableTreeNode[] => { } return nodes - }, [projectData.pous, projectData.dataTypes, projectData.configuration.resource.globalVariables]) + }, [projectData.pous, projectData.dataTypes, projectData.configurations.resource.globalVariables]) } // Helper exports for tree manipulation diff --git a/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx b/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx index 1b7c73d52..9d5b70179 100644 --- a/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx +++ b/src2/frontend/components/_molecules/graphical-editor/fbd/index.tsx @@ -126,7 +126,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } if (!sourceHandle) return undefined if (pouRef?.pouType !== 'function-block') { - const instances = project.data.configuration.resource.instances + const instances = project.data.configurations.resource.instances const programInstance = instances.find((inst: { program: string }) => inst.program === editor.meta.name) if (!programInstance) return undefined } @@ -237,7 +237,7 @@ export const FBDBody = ({ rung, nodeDivergences = [], isDebuggerActive = false } debugForcedVariables, editor.meta.name, pouRef?.interface?.variables, - project.data.configuration.resource.instances, + project.data.configurations.resource.instances, ]) const styledNodes = useMemo(() => { diff --git a/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx b/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx index eacb43b1c..55c3b88d5 100644 --- a/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx +++ b/src2/frontend/components/_molecules/graphical-editor/ladder/rung/body.tsx @@ -184,7 +184,7 @@ export const RungBody = ({ rung, className, nodeDivergences = [], isDebuggerActi if (!sourceHandle) return undefined if (pouRef?.pouType !== 'function-block') { - const instances = project.data.configuration.resource.instances + const instances = project.data.configurations.resource.instances const programInstance = instances.find((inst) => inst.program === editor.meta.name) if (!programInstance) return undefined } diff --git a/src2/frontend/components/_molecules/variables-table/editable-cell.tsx b/src2/frontend/components/_molecules/variables-table/editable-cell.tsx index 12ca65c57..ab0315d4a 100644 --- a/src2/frontend/components/_molecules/variables-table/editable-cell.tsx +++ b/src2/frontend/components/_molecules/variables-table/editable-cell.tsx @@ -49,7 +49,7 @@ const EditableNameCell = ({ searchQuery, projectActions: { getVariable, updatePou, updateVariable }, project: { - data: { pous, configuration }, + data: { pous, configurations }, }, workspace: { isDebuggerVisible }, } = useOpenPLCStore() @@ -61,7 +61,7 @@ const EditableNameCell = ({ const [impactAnalysis, setImpactAnalysis] = useState(null) const confirmResolveRef = useRef<(v: boolean) => void>() - const globalVariables = configuration.resource.globalVariables + const globalVariables = configurations.resource.globalVariables const isExternalVariable = variable?.class === 'external' diff --git a/src2/frontend/services/ai/context-collector.ts b/src2/frontend/services/ai/context-collector.ts index 9caab2b5d..8766dc524 100644 --- a/src2/frontend/services/ai/context-collector.ts +++ b/src2/frontend/services/ai/context-collector.ts @@ -38,7 +38,7 @@ export function collectProjectContext(state: StoreState, currentPouName: string, } // 2. Global variables - const globals = state.project.data.configuration.resource['global-variables'] + const globals = state.project.data.configurations.resource.globalVariables if (globals && globals.length > 0) { const globalLines = globals.map((v) => ` ${v.name} : ${v.type.value};`).join('\n') addSection(`(* Global Variables *)\nVAR_GLOBAL\n${globalLines}\nEND_VAR`) diff --git a/src2/frontend/store/__tests__/ladder-slice.test.ts b/src2/frontend/store/__tests__/ladder-slice.test.ts index 3a2d8ca09..0e804d82d 100644 --- a/src2/frontend/store/__tests__/ladder-slice.test.ts +++ b/src2/frontend/store/__tests__/ladder-slice.test.ts @@ -55,7 +55,11 @@ function makeFlow(overrides?: Partial): LadderFlowType { function seedFlowWithRung(store: ReturnType, editorName = 'editor-1', rung?: RungLadderState) { const r = rung ?? makeRung() - store.getState().ladderFlowActions.startLadderRung({ editorName, rung: r }) + store.getState().ladderFlowActions.addLadderFlow({ + name: editorName, + updated: true, + rungs: [r], + }) return r } @@ -144,22 +148,31 @@ describe('createLadderFlowSlice', () => { // startLadderRung // ------------------------------------------------------------------------- it('startLadderRung creates a flow and adds the rung', () => { - const rung = makeRung({ id: 'rung-1' }) - store.getState().ladderFlowActions.startLadderRung({ editorName: 'editor-1', rung }) + store.getState().ladderFlowActions.startLadderRung({ + editorName: 'editor-1', + rungId: 'rung-1', + defaultBounds: [800, 200], + }) const { ladderFlows } = store.getState() expect(ladderFlows).toHaveLength(1) expect(ladderFlows[0].name).toBe('editor-1') expect(ladderFlows[0].rungs).toHaveLength(1) expect(ladderFlows[0].rungs[0].id).toBe('rung-1') + expect(ladderFlows[0].rungs[0].nodes.length).toBeGreaterThanOrEqual(2) }) it('startLadderRung appends rung to existing flow', () => { - const rung1 = makeRung({ id: 'rung-1' }) - const rung2 = makeRung({ id: 'rung-2' }) - - store.getState().ladderFlowActions.startLadderRung({ editorName: 'editor-1', rung: rung1 }) - store.getState().ladderFlowActions.startLadderRung({ editorName: 'editor-1', rung: rung2 }) + store.getState().ladderFlowActions.startLadderRung({ + editorName: 'editor-1', + rungId: 'rung-1', + defaultBounds: [800, 200], + }) + store.getState().ladderFlowActions.startLadderRung({ + editorName: 'editor-1', + rungId: 'rung-2', + defaultBounds: [800, 200], + }) const { ladderFlows } = store.getState() expect(ladderFlows).toHaveLength(1) @@ -211,8 +224,10 @@ describe('createLadderFlowSlice', () => { // removeRung // ------------------------------------------------------------------------- it('removeRung removes a rung by id', () => { - seedFlowWithRung(store, 'editor-1', makeRung({ id: 'rung-1' })) - store.getState().ladderFlowActions.startLadderRung({ editorName: 'editor-1', rung: makeRung({ id: 'rung-2' }) }) + store.getState().ladderFlowActions.addLadderFlow(makeFlow({ + name: 'editor-1', + rungs: [makeRung({ id: 'rung-1' }), makeRung({ id: 'rung-2' })], + })) store.getState().ladderFlowActions.removeRung('editor-1', 'rung-1') @@ -254,16 +269,18 @@ describe('createLadderFlowSlice', () => { // duplicateRung // ------------------------------------------------------------------------- it('duplicateRung inserts new rung after the source rung', () => { - seedFlowWithRung(store, 'editor-1', makeRung({ id: 'rung-1' })) - store.getState().ladderFlowActions.startLadderRung({ editorName: 'editor-1', rung: makeRung({ id: 'rung-2' }) }) + store.getState().ladderFlowActions.addLadderFlow(makeFlow({ + name: 'editor-1', + rungs: [makeRung({ id: 'rung-1' }), makeRung({ id: 'rung-2' })], + })) - const newRung = makeRung({ id: 'rung-dup' }) - store.getState().ladderFlowActions.duplicateRung({ editorName: 'editor-1', rungId: 'rung-1', newRung }) + store.getState().ladderFlowActions.duplicateRung({ editorName: 'editor-1', rungId: 'rung-1' }) const flow = store.getState().ladderFlows[0] expect(flow.rungs).toHaveLength(3) expect(flow.rungs[0].id).toBe('rung-1') - expect(flow.rungs[1].id).toBe('rung-dup') + // The duplicated rung has a generated ID + expect(flow.rungs[1].id).toContain('rung_editor-1_') expect(flow.rungs[2].id).toBe('rung-2') expect(flow.updated).toBe(true) }) @@ -273,13 +290,13 @@ describe('createLadderFlowSlice', () => { store .getState() - .ladderFlowActions.duplicateRung({ editorName: 'editor-1', rungId: 'missing', newRung: makeRung() }) + .ladderFlowActions.duplicateRung({ editorName: 'editor-1', rungId: 'missing' }) expect(store.getState().ladderFlows[0].rungs).toHaveLength(1) }) it('duplicateRung does nothing for nonexistent editor', () => { - store.getState().ladderFlowActions.duplicateRung({ editorName: 'nonexistent', rungId: 'rung-1', newRung: makeRung() }) + store.getState().ladderFlowActions.duplicateRung({ editorName: 'nonexistent', rungId: 'rung-1' }) expect(store.getState().ladderFlows).toEqual([]) }) @@ -485,10 +502,10 @@ describe('createLadderFlowSlice', () => { }) it('setSelectedNodes clears other rungs selected state when nodes are selected', () => { - seedFlowWithRung(store, 'editor-1', makeRung({ id: 'rung-1' })) - store - .getState() - .ladderFlowActions.startLadderRung({ editorName: 'editor-1', rung: makeRung({ id: 'rung-2' }) }) + store.getState().ladderFlowActions.addLadderFlow(makeFlow({ + name: 'editor-1', + rungs: [makeRung({ id: 'rung-1' }), makeRung({ id: 'rung-2' })], + })) const node = makeNode({ id: 'n1', data: { draggable: true } }) store.getState().ladderFlowActions.setNodes({ @@ -740,8 +757,10 @@ describe('createLadderFlowSlice', () => { }) it('setSelectedNodes with empty array does not clear other rungs', () => { - seedFlowWithRung(store, 'editor-1', makeRung({ id: 'rung-1' })) - store.getState().ladderFlowActions.startLadderRung({ editorName: 'editor-1', rung: makeRung({ id: 'rung-2' }) }) + store.getState().ladderFlowActions.addLadderFlow(makeFlow({ + name: 'editor-1', + rungs: [makeRung({ id: 'rung-1' }), makeRung({ id: 'rung-2' })], + })) store.getState().ladderFlowActions.setSelectedNodes({ editorName: 'editor-1', rungId: 'rung-1', nodes: [] }) diff --git a/src2/frontend/store/slices/ladder/slice.ts b/src2/frontend/store/slices/ladder/slice.ts index c91bdb1c5..0cbde5028 100644 --- a/src2/frontend/store/slices/ladder/slice.ts +++ b/src2/frontend/store/slices/ladder/slice.ts @@ -2,7 +2,10 @@ import { addEdge, applyEdgeChanges, applyNodeChanges } from '@xyflow/react' import { produce } from 'immer' import { StateCreator } from 'zustand' +import { defaultCustomNodesStyles, nodesBuilder } from '../../../components/_atoms/graphical-editor/ladder/node-builders' + import { LadderFlowSlice, LadderFlowState } from './types' +import { duplicateLadderRung } from './utils' export const createLadderFlowSlice: StateCreator = (setState) => ({ ladderFlows: [], @@ -43,20 +46,58 @@ export const createLadderFlowSlice: StateCreator { + startLadderRung: ({ editorName, rungId, defaultBounds, reactFlowViewport }) => { setState( produce(({ ladderFlows }: LadderFlowState) => { - let flow = ladderFlows.find((f) => f.name === editorName) - if (!flow) { - flow = { + if (!ladderFlows.find((flow) => flow.name === editorName)) { + ladderFlows.push({ name: editorName, updated: true, rungs: [], - } - ladderFlows.push(flow) + }) } - flow.rungs.push(rung) + const flow = ladderFlows.find((flow) => flow.name === editorName) + if (!flow) return + + const { powerRail } = defaultCustomNodesStyles + const railNodes = [ + nodesBuilder.powerRail({ + id: `left-rail-${rungId}`, + posX: 0, + posY: defaultBounds[1] / 2 - powerRail.height / 2, + connector: 'right', + handleX: powerRail.width, + handleY: defaultBounds[1] / 2, + }), + nodesBuilder.powerRail({ + id: `right-rail-${rungId}`, + posX: defaultBounds[0], + posY: defaultBounds[1] / 2 - powerRail.height / 2, + connector: 'left', + handleX: defaultBounds[0] - powerRail.width, + handleY: defaultBounds[1] / 2, + }), + ] + flow.rungs.push({ + id: rungId, + comment: '', + defaultBounds, + reactFlowViewport: + reactFlowViewport && reactFlowViewport > defaultBounds ? reactFlowViewport : defaultBounds, + nodes: [...railNodes], + edges: [ + { + id: `e_${railNodes[0].id}_${railNodes[1].id}`, + source: railNodes[0].id, + target: railNodes[1].id, + sourceHandle: railNodes[0].data.handles[0].id, + targetHandle: railNodes[1].data.handles[0].id, + type: 'smoothstep', + }, + ], + selectedNodes: [], + }) }), ) }, @@ -111,7 +152,7 @@ export const createLadderFlowSlice: StateCreator { const flow = ladderFlows.find((flow) => flow.name === editorName) @@ -120,6 +161,8 @@ export const createLadderFlowSlice: StateCreator rung.id === rungId) if (rungIndex === -1) return + const rung = flow.rungs[rungIndex] + const newRung = duplicateLadderRung(flow.name, rung) flow.rungs.splice(rungIndex + 1, 0, newRung) flow.updated = true }), diff --git a/src2/frontend/store/slices/ladder/types.ts b/src2/frontend/store/slices/ladder/types.ts index 69d0e3ebf..f3fc3483b 100644 --- a/src2/frontend/store/slices/ladder/types.ts +++ b/src2/frontend/store/slices/ladder/types.ts @@ -62,19 +62,21 @@ type LadderFlowActions = { /** * Control the rungs of the flow */ - startLadderRung: ({ editorName, rung }: { editorName: string; rung: RungLadderState }) => void - setRungs: ({ rungs, editorName }: { rungs: RungLadderState[]; editorName: string }) => void - removeRung: (editorName: string, rungId: string) => void - addComment: ({ editorName, rungId, comment }: { editorName: string; rungId: string; comment: string }) => void - duplicateRung: ({ + startLadderRung: ({ editorName, rungId, - newRung, + defaultBounds, + reactFlowViewport, }: { editorName: string rungId: string - newRung: RungLadderState + defaultBounds: [number, number] + reactFlowViewport?: [number, number] }) => void + setRungs: ({ rungs, editorName }: { rungs: RungLadderState[]; editorName: string }) => void + removeRung: (editorName: string, rungId: string) => void + addComment: ({ editorName, rungId, comment }: { editorName: string; rungId: string; comment: string }) => void + duplicateRung: ({ editorName, rungId }: { editorName: string; rungId: string }) => void /** * Control the rungs transactions diff --git a/src2/frontend/store/slices/ladder/utils/index.ts b/src2/frontend/store/slices/ladder/utils/index.ts new file mode 100644 index 000000000..dd0d9ee53 --- /dev/null +++ b/src2/frontend/store/slices/ladder/utils/index.ts @@ -0,0 +1,236 @@ +import { nodesBuilder } from '../../../../components/_atoms/graphical-editor/ladder/node-builders' +import type { + BlockNode, + BlockVariant, + CoilNode, + ContactNode, + ParallelNode, + PowerRailNode, + VariableNode, +} from '../../../../components/_atoms/graphical-editor/ladder/utils/types' +import { generateNumericUUID } from '../../../../utils/generate-uuid' +import { newGraphicalEditorNodeID } from '../../../../utils/new-graphical-editor-node-id' +import type { PLCVariable } from '../../../../../middleware/shared/ports/types' +import { Edge, Node } from '@xyflow/react' + +import { RungLadderState } from '../types' + +export const duplicateLadderRung = (editorName: string, rung: RungLadderState): RungLadderState => { + const nodeMaps: { [key: string]: Node } = rung.nodes.reduce( + (acc, node) => { + acc[node.id] = { + ...node, + id: node.type === 'powerRail' ? node.id : newGraphicalEditorNodeID(node.type?.toUpperCase()), + } + return acc + }, + {} as { [key: string]: Node }, + ) + + const edgeMaps: { [key: string]: Edge } = rung.edges.reduce( + (acc, edge) => { + acc[edge.id] = { + id: `e_${nodeMaps[edge.source].id}_${nodeMaps[edge.target].id}__${edge.sourceHandle}_${edge.targetHandle}`, + source: nodeMaps[edge.source].id, + target: nodeMaps[edge.target].id, + sourceHandle: edge.sourceHandle, + targetHandle: edge.targetHandle, + } + return acc + }, + {} as { [key: string]: Edge }, + ) + + const newNodes = rung.nodes.map((node) => { + switch (node.type) { + case 'block': { + const newBlock = nodesBuilder.block({ + id: nodeMaps[node.id].id, + posX: node.position.x, + posY: node.position.y, + handleX: (node as BlockNode).data.inputConnector?.glbPosition.x ?? 0, + handleY: (node as BlockNode).data.inputConnector?.glbPosition.y ?? 0, + variant: (node as BlockNode).data.variant, + executionControl: (node as BlockNode).data.executionControl, + }) + return { + ...newBlock, + data: { + ...newBlock.data, + variable: + (node as BlockNode).data.variant.type === 'function-block' + ? { name: '' } + : node.data.variable, + connectedVariables: (node as BlockNode).data.connectedVariables, + }, + } as BlockNode + } + case 'coil': { + const newCoil = nodesBuilder.coil({ + id: nodeMaps[node.id].id, + posX: node.position.x, + posY: node.position.y, + handleX: (node as CoilNode).data.inputConnector?.glbPosition.x ?? 0, + handleY: (node as CoilNode).data.inputConnector?.glbPosition.y ?? 0, + variant: (node as CoilNode).data.variant, + }) + return { + ...newCoil, + data: { + ...newCoil.data, + variable: (node as CoilNode).data.variable, + }, + } as CoilNode + } + case 'contact': { + const newContact = nodesBuilder.contact({ + id: nodeMaps[node.id].id, + posX: node.position.x, + posY: node.position.y, + handleX: (node as ContactNode).data.inputConnector?.glbPosition.x ?? 0, + handleY: (node as ContactNode).data.inputConnector?.glbPosition.y ?? 0, + variant: (node as ContactNode).data.variant, + }) + return { + ...newContact, + data: { + ...newContact.data, + variable: (node as ContactNode).data.variable, + }, + } as ContactNode + } + case 'parallel': { + return { + ...node, + id: nodeMaps[node.id].id, + data: { + ...node.data, + numericId: generateNumericUUID(), + parallelCloseReference: (node as ParallelNode).data.parallelCloseReference + ? nodeMaps[(node as ParallelNode).data.parallelCloseReference ?? ''].id + : undefined, + parallelOpenReference: (node as ParallelNode).data.parallelOpenReference + ? nodeMaps[(node as ParallelNode).data.parallelOpenReference ?? ''].id + : undefined, + }, + } as ParallelNode + } + case 'powerRail': { + return { + ...node, + id: nodeMaps[node.id].id, + data: { + ...node.data, + numericId: generateNumericUUID(), + }, + } as PowerRailNode + } + case 'variable': { + return { + ...node, + id: nodeMaps[node.id].id, + data: { + ...node.data, + numericId: generateNumericUUID(), + block: { + ...(node as VariableNode).data.block, + id: nodeMaps[(node as VariableNode).data.block.id]?.id ?? (node as VariableNode).data.block.id, + }, + }, + } as VariableNode + } + default: { + return node + } + } + }) + + const newEdges = rung.edges.map((edge) => ({ + ...edge, + id: edgeMaps[edge.id].id, + source: edgeMaps[edge.id].source, + target: edgeMaps[edge.id].target, + })) + + const newRung = { + id: `rung_${editorName}_${crypto.randomUUID()}`, + comment: rung.comment, + defaultBounds: rung.defaultBounds, + reactFlowViewport: rung.reactFlowViewport, + selectedNodes: [], + nodes: newNodes, + edges: newEdges, + } + + return newRung +} + +/** + * Checks if a function block variable is still in use by any block in the flows. + * This is used to determine if a variable should be deleted when a function block is removed. + * + * @param variableName - Name of the variable to check (case-insensitive) + * @param allRungs - All rungs/diagrams (must have nodes property) + * @returns true if the variable is still in use, false otherwise + */ +export const isFunctionBlockVariableInUse = (variableName: string, allRungs: Array<{ nodes: Node[] }>): boolean => { + const normalizedName = variableName.toLowerCase() + + for (const rung of allRungs) { + for (const node of rung.nodes) { + if (node.type === 'block') { + const blockNode = node as BlockNode + const blockVariableName = blockNode.data.variable?.name?.toLowerCase() + + if (blockVariableName === normalizedName && blockNode.data.variant.type === 'function-block') { + return true + } + } + } + } + + return false +} + +/** + * Gets all function block variables that should be cleaned up after nodes are removed. + * Returns an array of variable names that are no longer in use. + * + * @param removedNodes - Nodes that were removed + * @param allRungs - All rungs/diagrams (must have nodes property) + * @param allVariables - All variables in the POU + * @returns Array of variable names to delete + */ +export const getFunctionBlockVariablesToCleanup = ( + removedNodes: Node[], + allRungs: Array<{ nodes: Node[] }>, + allVariables: PLCVariable[], +): string[] => { + const variablesToCheck = new Set() + + for (const node of removedNodes) { + if (node.type === 'block') { + const blockNode = node as BlockNode + if (blockNode.data.variant.type === 'function-block') { + const variableName = blockNode.data.variable?.name + if (variableName) { + variablesToCheck.add(variableName) + } + } + } + } + + const variablesToDelete: string[] = [] + + for (const variableName of variablesToCheck) { + const variable = allVariables.find((v) => v.name.toLowerCase() === variableName.toLowerCase()) + + if (variable && variable.type.definition === 'derived') { + if (!isFunctionBlockVariableInUse(variableName, allRungs)) { + variablesToDelete.push(variableName) + } + } + } + + return variablesToDelete +} diff --git a/src2/frontend/store/slices/project/slice.ts b/src2/frontend/store/slices/project/slice.ts index 71876a4bb..a22ce7e82 100644 --- a/src2/frontend/store/slices/project/slice.ts +++ b/src2/frontend/store/slices/project/slice.ts @@ -344,7 +344,7 @@ const createProjectSlice: StateCreator = (se } }), ) - return ok() + return ok(data) }, setPouVariables: ({ pouName, variables }) => { setState( diff --git a/src2/middleware/adapters/editor/project-adapter.ts b/src2/middleware/adapters/editor/project-adapter.ts index b485ab257..413d5b4ca 100644 --- a/src2/middleware/adapters/editor/project-adapter.ts +++ b/src2/middleware/adapters/editor/project-adapter.ts @@ -85,13 +85,10 @@ function mapIpcPouToPortPou(ipcPou: IpcPou): PLCPou { return { name: ipcPou.data.name, pouType: ipcPou.type as PLCPou['pouType'], - interface: - ipcPou.data.variables.length > 0 || ipcPou.data.returnType - ? { - returnType: ipcPou.data.returnType, - variables: ipcPou.data.variables as PLCVariable[], - } - : undefined, + interface: { + returnType: ipcPou.data.returnType, + variables: ipcPou.data.variables as PLCVariable[], + }, body: ipcPou.data.body as PLCPou['body'], documentation: ipcPou.data.documentation || undefined, } From 2250ac4fda3d278dc7c7ffd25c8aef421600fce5 Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:35:16 -0300 Subject: [PATCH 06/40] fix(step-31): add debug badges, fix block variable matching and literal support Add DebugValueBadge and BlockOutputDebugBadges components for showing live debug values on block outputs and variable nodes during debug sessions. Fix case-sensitive variable matching in block instance linking, add type validation in wrongVariable detection, wire literal type support in ladder variable nodes, and fix autocomplete Enter key handling to use triggerSubmit consistently. Also includes variable table validation improvements and editor config updates. Co-Authored-By: Claude Opus 4.6 (1M context) --- .devcontainer/devcontainer.json | 4 +- .../webpack/webpack.config.renderer.dev.ts | 2 +- .../webpack.config.renderer.src2.dev.ts | 2 +- scripts/check-port-in-use.js | 2 +- src/main/utils/resolve-html-path.ts | 2 +- .../block-output-debug-badges.tsx | 79 +++++ .../graphical-editor/debug-value-badge.tsx | 52 +++ .../_atoms/graphical-editor/fbd/block.tsx | 44 ++- .../_atoms/graphical-editor/fbd/variable.tsx | 33 +- .../_atoms/graphical-editor/ladder/block.tsx | 74 +++- .../graphical-editor/ladder/variable.tsx | 43 ++- .../global-variables-table/index.tsx | 4 + .../variables-table/editable-cell.tsx | 6 +- .../_molecules/variables-table/index.tsx | 9 +- .../global-variables-editor/index.tsx | 24 +- .../_organisms/instances-editor/index.tsx | 24 +- .../_organisms/task-editor/index.tsx | 24 +- .../_organisms/variables-editor/index.tsx | 23 +- src2/frontend/store/slices/project/slice.ts | 124 +++++-- .../slices/project/validation/variables.ts | 335 +++++++++++++++++- 20 files changed, 819 insertions(+), 91 deletions(-) create mode 100644 src2/frontend/components/_atoms/graphical-editor/block-output-debug-badges.tsx create mode 100644 src2/frontend/components/_atoms/graphical-editor/debug-value-badge.tsx diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d38bbada3..fedc90c66 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,9 +8,9 @@ "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/github-cli:1": {} }, - "forwardPorts": [1212, 3000], + "forwardPorts": [1313, 3000], "portsAttributes": { - "1212": { "label": "Webpack Dev Server (Renderer)" }, + "1313": { "label": "Webpack Dev Server (Renderer)" }, "3000": { "label": "Electron Debug Port" } }, "mounts": [ diff --git a/configs/webpack/webpack.config.renderer.dev.ts b/configs/webpack/webpack.config.renderer.dev.ts index 4e26b436e..f3f5da75f 100644 --- a/configs/webpack/webpack.config.renderer.dev.ts +++ b/configs/webpack/webpack.config.renderer.dev.ts @@ -24,7 +24,7 @@ if (process.env.NODE_ENV === 'production') { checkNodeEnv('development') } -const port = process.env.PORT || 1212 +const port = process.env.PORT || 1313 const manifest = resolve(webpackPaths.dllPath, 'renderer.json') const skipDLLs = module.parent?.filename.includes('webpack.config.renderer.dev.dll') || diff --git a/configs/webpack/webpack.config.renderer.src2.dev.ts b/configs/webpack/webpack.config.renderer.src2.dev.ts index c44ebbccd..933db83e6 100644 --- a/configs/webpack/webpack.config.renderer.src2.dev.ts +++ b/configs/webpack/webpack.config.renderer.src2.dev.ts @@ -17,7 +17,7 @@ import { mergeWithCustomize, customizeArray } from 'webpack-merge' import rendererDevConfig from './webpack.config.renderer.dev' import webpackPaths from './webpack.paths' -const port = process.env.PORT || 1212 +const port = process.env.PORT || 1313 const src2Path = join(webpackPaths.rootPath, 'src2') // Remove the base HtmlWebpackPlugin and EslintPlugin so we can replace/skip them diff --git a/scripts/check-port-in-use.js b/scripts/check-port-in-use.js index 398cbc179..d418ba3ab 100644 --- a/scripts/check-port-in-use.js +++ b/scripts/check-port-in-use.js @@ -1,7 +1,7 @@ import chalk from 'chalk'; import detectPort from 'detect-port'; -const port = process.env.PORT || '1212'; +const port = process.env.PORT || '1313'; detectPort(port, (_err, availablePort) => { if (port !== String(availablePort)) { diff --git a/src/main/utils/resolve-html-path.ts b/src/main/utils/resolve-html-path.ts index 7f26b02e2..b4716c9ff 100644 --- a/src/main/utils/resolve-html-path.ts +++ b/src/main/utils/resolve-html-path.ts @@ -3,7 +3,7 @@ import { URL } from 'url' export function resolveHtmlPath(htmlFileName: string) { if (process.env.NODE_ENV === 'development') { - const port = process.env.PORT || 1212 + const port = process.env.PORT || 1313 const url = new URL(`http://localhost:${port}`) url.pathname = htmlFileName return url.href diff --git a/src2/frontend/components/_atoms/graphical-editor/block-output-debug-badges.tsx b/src2/frontend/components/_atoms/graphical-editor/block-output-debug-badges.tsx new file mode 100644 index 000000000..e66868098 --- /dev/null +++ b/src2/frontend/components/_atoms/graphical-editor/block-output-debug-badges.tsx @@ -0,0 +1,79 @@ +import { useDebugCompositeKey } from '../../../hooks/use-debug-composite-key' +import { useOpenPLCStore } from '../../../store' +import { DebugValueBadge } from './debug-value-badge' + +type BlockOutputDebugBadgesProps = { + blockType: string + blockName: string + blockVariableName: string + numericId: string + outputVariables: Array<{ name: string; class: string; type: { definition: string; value: string } }> + connectorStartY: number + connectorOffsetY: number + blockWidth: number + /** Output names that already have a variable node connected showing its own badge. */ + connectedOutputNames?: Set +} + +/** + * Renders debug value badges next to each output connector of a block node. + * Works for both function blocks (using instance-qualified names) and + * functions (using _TMP_ variable names). Shared between FBD and LD. + * + * Outputs that are connected to a variable node are skipped since the + * variable node already displays its own badge (avoids double badges). + */ +const BlockOutputDebugBadges = ({ + blockType, + blockName, + blockVariableName, + numericId, + outputVariables, + connectorStartY, + connectorOffsetY, + blockWidth, + connectedOutputNames, +}: BlockOutputDebugBadgesProps) => { + const { + workspace: { isDebuggerVisible }, + } = useOpenPLCStore() + const getCompositeKey = useDebugCompositeKey() + + if (!isDebuggerVisible || blockType === 'generic') { + return null + } + + const outputs = outputVariables.filter((v) => v.class === 'output' || v.class === 'inOut') + + return ( + <> + {outputs.map((outputVar, index) => { + if (connectedOutputNames?.has(outputVar.name)) { + return null + } + + let compositeKey: string + if (blockType === 'function-block') { + compositeKey = getCompositeKey(`${blockVariableName}.${outputVar.name}`) + } else { + compositeKey = getCompositeKey(`_TMP_${blockName.toUpperCase()}${numericId}_${outputVar.name}`) + } + + return ( +
+ +
+ ) + })} + + ) +} + +export { BlockOutputDebugBadges } diff --git a/src2/frontend/components/_atoms/graphical-editor/debug-value-badge.tsx b/src2/frontend/components/_atoms/graphical-editor/debug-value-badge.tsx new file mode 100644 index 000000000..9afae7e22 --- /dev/null +++ b/src2/frontend/components/_atoms/graphical-editor/debug-value-badge.tsx @@ -0,0 +1,52 @@ +import { useOpenPLCStore } from '../../../store' +import { cn } from '../../../utils/cn' + +type DebugValueBadgeProps = { + compositeKey: string + variableType: string | undefined + position?: 'right' | 'left' | 'below' +} + +/** + * Displays a real-time debug value badge next to graphical editor nodes. + * Shows the current polled value for non-BOOL variables when the debugger is active. + * BOOL variables are skipped since they already have dedicated color indicators. + * + * Designed to be used by both FBD and LD variable/block nodes. + */ +const DebugValueBadge = ({ compositeKey, variableType, position = 'right' }: DebugValueBadgeProps) => { + const { + workspace: { debugVariableValues }, + } = useOpenPLCStore() + + if (!variableType || variableType.toUpperCase() === 'BOOL') { + return null + } + + const value = debugVariableValues.get(compositeKey) + if (value === undefined) { + return null + } + + const positionClasses: Record = { + right: 'left-full ml-1 top-1/2 -translate-y-1/2', + left: 'right-full mr-1 top-1/2 -translate-y-1/2', + below: 'top-full mt-0.5 left-1/2 -translate-x-1/2', + } + + return ( +
+ {value} +
+ ) +} + +export { DebugValueBadge } +export type { DebugValueBadgeProps } diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx index a4d071317..bff04ae37 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/block.tsx @@ -1,14 +1,16 @@ -import { toast } from '../../../_features/[app]/toast/use-toast' +import { FocusEvent, useEffect, useMemo, useRef, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + +import type { PLCVariable } from '../../../../../middleware/shared/ports/types' +import { RefreshIcon } from '../../../../assets/icons/interface/Refresh' import { useOpenPLCStore } from '../../../../store' import { checkVariableNameUnit } from '../../../../store/slices/project/validation/variables' -import type { PLCVariable } from '../../../../../middleware/shared/ports/types' import { cn } from '../../../../utils/cn' -import { FocusEvent, useEffect, useRef, useState } from 'react' - -import { v4 as uuidv4 } from 'uuid' +import { toast } from '../../../_features/[app]/toast/use-toast' import { HighlightedTextArea } from '../../highlighted-textarea' import { InputWithRef } from '../../input' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../tooltip' +import { BlockOutputDebugBadges } from '../block-output-debug-badges' import { BlockVariant } from '../types/block' import { getBlockDocumentation, getVariableRestrictionType } from '../utils' import { buildBlockNode } from './buildNodes' @@ -16,7 +18,6 @@ import { CustomHandle } from './handle' import { BasicNodeData, BlockNodeData, BlockProps } from './utils' import { DEFAULT_BLOCK_CONNECTOR_Y, DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, DEFAULT_BLOCK_HEIGHT, DEFAULT_BLOCK_TYPE, DEFAULT_BLOCK_WIDTH, } from './utils/constants' import { getFBDPouVariablesRungNodeAndEdges } from './utils/utils' -import { RefreshIcon } from '../../../../assets/icons/interface/Refresh' export const BlockNodeElement = ({ nodeId, @@ -344,6 +345,20 @@ export const Block = (block: BlockProps) => { nodeId: id ?? '', }) + // Outputs connected to variable nodes already show their own badge — skip those + const connectedOutputNames = useMemo(() => { + const names = new Set() + if (!rung) return names + const outgoingEdges = rung.edges.filter((e) => e.source === id) + for (const edge of outgoingEdges) { + const targetNode = rung.nodes.find((n) => n.id === edge.target) + if (targetNode && typeof targetNode.type === 'string' && targetNode.type.includes('variable')) { + if (edge.sourceHandle) names.add(edge.sourceHandle) + } + } + return names + }, [rung, id]) + const inputVariableRef = useRef< HTMLTextAreaElement & { blur: ({ submit }: { submit?: boolean }) => void @@ -456,9 +471,9 @@ export const Block = (block: BlockProps) => { const findMatchingVariable = () => variables.all.find( (variable) => - variable.name === variableNameToSubmit && + variable.name.toLowerCase() === variableNameToSubmit.toLowerCase() && variable.type.definition === 'derived' && - variable.type.value === blockType, + variable.type.value.toLowerCase() === blockType.toLowerCase(), ) const updateNodeVariable = (variable: Partial | { name: string }) => { @@ -477,7 +492,7 @@ export const Block = (block: BlockProps) => { const matchingVariable = findMatchingVariable() if (variableToLink) { - if (variableToLink.name === variableNameToSubmit) return + if (variableToLink.name.toLowerCase() === variableNameToSubmit.toLowerCase()) return if (matchingVariable && matchingVariable.id !== variableToLink.id) { variableToLink = matchingVariable @@ -787,6 +802,17 @@ export const Block = (block: BlockProps) => { {data.handles.map((handle, index) => ( ))} + ) } diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx index 0bb4cfce7..313b4d4a8 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/variable.tsx @@ -1,7 +1,11 @@ +import * as Popover from '@radix-ui/react-popover' +import { useEffect, useMemo, useRef, useState } from 'react' + +import { resolveArrayVariableByName } from '../../../../../backend/shared/array-variable-utils' +import { PLCVariable } from '../../../../../middleware/shared/ports/types' import { useDebugger } from '../../../../../middleware/shared/providers' import { useDebugCompositeKey } from '../../../../hooks/use-debug-composite-key' import { useOpenPLCStore } from '../../../../store' -import { PLCVariable } from '../../../../../middleware/shared/ports/types' import { cn } from '../../../../utils/cn' import { floatToBuffer, @@ -12,19 +16,15 @@ import { parseStringValue, stringToBuffer, } from '../../../../utils/variable-types' -import { resolveArrayVariableByName } from '../../../../../backend/shared/array-variable-utils' -import { useEffect, useMemo, useRef, useState } from 'react' -import * as Popover from '@radix-ui/react-popover' - +import { Modal, ModalContent, ModalTitle } from '../../../_molecules/modal' import { HighlightedTextArea } from '../../highlighted-textarea' import { Label } from '../../label' -import { Modal, ModalContent, ModalTitle } from '../../../_molecules/modal' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../tooltip' +import { DebugValueBadge } from '../debug-value-badge' import { BlockVariant } from '../types/block' import { validateVariableType } from '../utils' import { FBDBlockAutoComplete } from './autocomplete' import { CustomHandle } from './handle' -import { getFBDPouVariablesRungNodeAndEdges } from './utils/utils' import { BlockNode, VariableNode, VariableProps } from './utils' import { DEFAULT_VARIABLE_HEIGHT, @@ -32,6 +32,7 @@ import { VARIABLE_ELEMENT_HEIGHT, VARIABLE_ELEMENT_SIZE, } from './utils/constants' +import { getFBDPouVariablesRungNodeAndEdges } from './utils/utils' const VariableElement = (block: VariableProps) => { const { id, data, selected } = block @@ -60,6 +61,7 @@ const VariableElement = (block: VariableProps) => { focus: () => void isFocused: boolean selectedVariable: { positionInArray: number; variableName: string } + triggerSubmit?: () => void } >(null) @@ -500,8 +502,8 @@ const VariableElement = (block: VariableProps) => { // For variable nodes, allow all types including derived (user-defined types) // Don't use getVariableByName here as it filters out derived types let variable: PLCVariable | { name: string } | undefined = - ((pou.interface?.variables ?? []) as PLCVariable[]).find((v) => v.name.toLowerCase() === variableNameToSubmit.toLowerCase()) || - resolveArrayVariableByName((pou.interface?.variables ?? []) as PLCVariable[], variableNameToSubmit) + ((pou.interface?.variables ?? [])).find((v) => v.name.toLowerCase() === variableNameToSubmit.toLowerCase()) || + resolveArrayVariableByName((pou.interface?.variables ?? []), variableNameToSubmit) if (!variable) { setIsAVariable(false) variable = { name: variableNameToSubmit } @@ -613,8 +615,11 @@ const VariableElement = (block: VariableProps) => { onFocus={onChangeHandler} onKeyDown={(e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Tab') e.preventDefault() - if (e.key === 'Enter' && (autocompleteRef.current?.selectedVariable.positionInArray ?? -1) !== -1) { + if (e.key === 'Enter' && openAutocomplete) { + e.preventDefault() + autocompleteRef.current?.triggerSubmit?.() inputVariableRef.current?.blur({ submit: false }) + return } setKeyPressedAtTextarea(e.key) }} @@ -634,6 +639,14 @@ const VariableElement = (block: VariableProps) => { )} + {isDebuggerVisible && isAVariable && ( + + )} + {isDebuggerVisible && contextMenuPosition && ( diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx index a2b380db7..ae632526e 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/block.tsx @@ -1,17 +1,19 @@ -import { updateDiagramElementsPosition } from '../../../_molecules/graphical-editor/ladder/rung/ladder-utils/elements/diagram' -import { toast } from '../../../_features/[app]/toast/use-toast' -import { useOpenPLCStore } from '../../../../store' -import { checkVariableNameUnit } from '../../../../store/slices/project/validation/variables' -import type { PLCVariable } from '../../../../../middleware/shared/ports' -import { cn } from '../../../../utils/cn' -import { FocusEvent, useEffect, useRef, useState } from 'react' +import { FocusEvent, useEffect, useMemo, useRef, useState } from 'react' import { v4 as uuidv4 } from 'uuid' -import { LibraryState } from '../../../../store/slices/library' +import type { PLCVariable } from '../../../../../middleware/shared/ports' import { PLCPou } from '../../../../../middleware/shared/ports' +import { RefreshIcon } from '../../../../assets/icons/interface/Refresh' +import { useOpenPLCStore } from '../../../../store' +import { LibraryState } from '../../../../store/slices/library' +import { checkVariableNameUnit } from '../../../../store/slices/project/validation/variables' +import { cn } from '../../../../utils/cn' +import { toast } from '../../../_features/[app]/toast/use-toast' +import { updateDiagramElementsPosition } from '../../../_molecules/graphical-editor/ladder/rung/ladder-utils/elements/diagram' import { HighlightedTextArea } from '../../highlighted-textarea' import { InputWithRef } from '../../input' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../tooltip' +import { BlockOutputDebugBadges } from '../block-output-debug-badges' import { BlockVariant as newBlockVariant } from '../types/block' import { getBlockDocumentation, getVariableRestrictionType } from '../utils' import { buildBlockNode } from './buildNodes' @@ -19,7 +21,6 @@ import { CustomHandle } from './handle' import { getLadderPouVariablesRungNodeAndEdges } from './utils' import { DEFAULT_BLOCK_CONNECTOR_Y, DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, DEFAULT_BLOCK_HEIGHT, DEFAULT_BLOCK_TYPE, DEFAULT_BLOCK_WIDTH, } from './utils/constants' import type { BasicNodeData, BlockNodeData, BlockProps, BlockVariant, LadderBlockConnectedVariables, } from './utils/types' -import { RefreshIcon } from '../../../../assets/icons/interface/Refresh' export const BlockNodeElement = ({ nodeId, @@ -385,6 +386,18 @@ export const Block = (block: BlockProps) => { nodeId: id, }) + const connectedOutputNames = useMemo(() => { + const names = new Set() + if (data.connectedVariables) { + for (const cv of data.connectedVariables) { + if (cv.type === 'output' && cv.variable) { + names.add(cv.handleId) + } + } + } + return names + }, [data.connectedVariables]) + const inputVariableRef = useRef< HTMLTextAreaElement & { blur: ({ submit }: { submit?: boolean }) => void @@ -447,8 +460,22 @@ export const Block = (block: BlockProps) => { return } - if ((node.data as BasicNodeData).variable.id === variable.id) { - if ((node.data as BasicNodeData).variable.name !== variable.name) { + const nodeVariable = (node.data as BasicNodeData).variable + const nodeVariableName = nodeVariable.name.toLowerCase() + const selectedVariableName = variable.name.toLowerCase() + const nodeBlockType = (node.data as BlockNodeData).variant.name + + if (nodeVariableName === selectedVariableName) { + const typeMatches = + variable.type.definition === 'derived' && + variable.type.value.toLowerCase() === nodeBlockType.toLowerCase() + + if (!typeMatches) { + setWrongVariable(true) + return + } + + if (nodeVariable.name !== variable.name) { updateNode({ editorName: editor.meta.name, rungId: rung.id, @@ -461,14 +488,14 @@ export const Block = (block: BlockProps) => { }, }, }) - setWrongVariable(false) - return } + setWrongVariable(false) + return } - setWrongVariable(false) + setWrongVariable(true) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pous]) + }, [pous, data.variable.name]) /** * Handle with the variable input onBlur event @@ -492,9 +519,9 @@ export const Block = (block: BlockProps) => { const findMatchingVariable = () => variables.all.find( (variable) => - variable.name === variableNameToSubmit && + variable.name.toLowerCase() === variableNameToSubmit.toLowerCase() && variable.type.definition === 'derived' && - variable.type.value === blockType, + variable.type.value.toLowerCase() === blockType.toLowerCase(), ) const updateNodeVariable = (variable: Partial | { name: string }) => @@ -513,7 +540,7 @@ export const Block = (block: BlockProps) => { const matchingVariable = findMatchingVariable() if (variableToLink) { - if (variableToLink.name === variableNameToSubmit) return + if (variableToLink.name.toLowerCase() === variableNameToSubmit.toLowerCase()) return if (matchingVariable && matchingVariable.id !== variableToLink.id) { variableToLink = matchingVariable @@ -815,6 +842,17 @@ export const Block = (block: BlockProps) => { {data.handles.map((handle, index) => ( ))} + ) } diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx index a17655240..ccd74f3b0 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/variable.tsx @@ -1,10 +1,13 @@ -import { useDebugCompositeKey } from '../../../../hooks/use-debug-composite-key' import * as Popover from '@radix-ui/react-popover' +import { useEffect, useRef, useState } from 'react' + +import { PLCVariable } from '../../../../../middleware/shared/ports' import { useDebugger } from '../../../../../middleware/shared/providers' +import { useDebugCompositeKey } from '../../../../hooks/use-debug-composite-key' import { useOpenPLCStore } from '../../../../store' import { RungLadderState } from '../../../../store/slices/ladder' -import { PLCVariable } from '../../../../../middleware/shared/ports' import { cn } from '../../../../utils/cn' +import { getLiteralType } from '../../../../utils/keywords' import { floatToBuffer, getVariableTypeInfo, @@ -14,16 +17,15 @@ import { parseStringValue, stringToBuffer, } from '../../../../utils/variable-types' -import { useEffect, useRef, useState } from 'react' - -import { Label } from '../../label' import { Modal, ModalContent, ModalTitle } from '../../../_molecules/modal' import { HighlightedTextArea } from '../../highlighted-textarea' +import { Label } from '../../label' +import { DebugValueBadge } from '../debug-value-badge' +import { VariablesBlockAutoComplete } from './autocomplete' import { CustomHandle } from './handle' import { getLadderPouVariablesRungNodeAndEdges, validateVariableType } from './utils' -import { VariablesBlockAutoComplete } from './autocomplete' -import { BlockNodeData, BlockVariant, LadderBlockConnectedVariables, VariableNode, VariableProps } from './utils/types' import { DEFAULT_VARIABLE_HEIGHT, DEFAULT_VARIABLE_WIDTH } from './utils/constants' +import { BlockNodeData, BlockVariant, LadderBlockConnectedVariables, VariableNode, VariableProps } from './utils/types' const VariableElement = (block: VariableProps) => { const { id, data } = block @@ -51,6 +53,7 @@ const VariableElement = (block: VariableProps) => { focus: () => void isFocused: boolean selectedVariable: { positionInArray: number; variableName: string } + triggerSubmit?: () => void } >(null) @@ -194,14 +197,22 @@ const VariableElement = (block: VariableProps) => { // For variable nodes (block pins), allow all types including derived (user-defined types) // Don't use getVariableByName here as it filters out derived types - let variable: PLCVariable | { name: string } | undefined = ((pou.interface?.variables ?? []) as PLCVariable[]).find( + let variable: PLCVariable | { name: string } | undefined = ((pou.interface?.variables ?? [])).find( (v) => v.name.toLowerCase() === variableNameToSubmit.toLowerCase(), ) - if (!variable) { + const literalTypes = getLiteralType(variableNameToSubmit) + if (variable) { + setIsAVariable(true) + setInputError(false) + } else if (literalTypes) { setIsAVariable(false) + const mismatchType = !literalTypes.includes(data.block.variableType.type.value) + setInputError(mismatchType) variable = { name: variableNameToSubmit } } else { setIsAVariable(true) + setInputError(true) + variable = { name: variableNameToSubmit } } updateNode({ @@ -218,7 +229,6 @@ const VariableElement = (block: VariableProps) => { }) updateRelatedNode(rung, variableNode, variable as PLCVariable) - setInputError(false) } const onChangeHandler = () => { @@ -443,8 +453,11 @@ const VariableElement = (block: VariableProps) => { onChange={onChangeHandler} onKeyDown={(e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Tab') e.preventDefault() - if (e.key === 'Enter' && (autocompleteRef.current?.selectedVariable.positionInArray ?? -1) !== -1) { + if (e.key === 'Enter' && openAutocomplete) { + e.preventDefault() + autocompleteRef.current?.triggerSubmit?.() inputVariableRef.current?.blur({ submit: false }) + return } setKeyPressedAtTextarea(e.key) }} @@ -466,6 +479,14 @@ const VariableElement = (block: VariableProps) => { )} + {isDebuggerVisible && isAVariable && ( + + )} + {isDebuggerVisible && contextMenuPosition && ( diff --git a/src2/frontend/components/_molecules/global-variables-table/index.tsx b/src2/frontend/components/_molecules/global-variables-table/index.tsx index fa7969c6c..aefa19e45 100644 --- a/src2/frontend/components/_molecules/global-variables-table/index.tsx +++ b/src2/frontend/components/_molecules/global-variables-table/index.tsx @@ -78,6 +78,7 @@ const GlobalVariablesTable = ({ tableData, selectedRow, handleRowClick }: PLCVar meta: { name }, }, projectActions: { updateVariable }, + sharedWorkspaceActions: { handleFileAndWorkspaceSavedState }, } = useOpenPLCStore() const { captureAndPush } = usePouSnapshot() @@ -91,6 +92,9 @@ const GlobalVariablesTable = ({ tableData, selectedRow, handleRowClick }: PLCVar updateData={(rowIndex, columnId, value) => { captureAndPush(name) const result = updateVariable({ scope: 'global', rowId: rowIndex, data: { [columnId]: value } }) + if (result.ok) { + handleFileAndWorkspaceSavedState('Resource') + } return result }} tableContext='Variables' diff --git a/src2/frontend/components/_molecules/variables-table/editable-cell.tsx b/src2/frontend/components/_molecules/variables-table/editable-cell.tsx index ab0315d4a..0413f03f2 100644 --- a/src2/frontend/components/_molecules/variables-table/editable-cell.tsx +++ b/src2/frontend/components/_molecules/variables-table/editable-cell.tsx @@ -248,7 +248,7 @@ const EditableNameCell = ({ useEffect(() => { setVariable( getVariable({ - variableId: table.options.data[index].id, + rowId: index, scope, associatedPou: editor.meta.name, }), @@ -389,7 +389,7 @@ const EditableInitialValueCell = ({ useEffect(() => { setVariable( getVariable({ - variableId: table.options.data[index].id, + rowId: index, scope, associatedPou: editor.meta.name, }), @@ -478,7 +478,7 @@ const EditableLocationCell = ({ useEffect(() => { setVariable( getVariable({ - variableId: table.options.data[index].id, + rowId: index, scope, associatedPou: editor.meta.name, }), diff --git a/src2/frontend/components/_molecules/variables-table/index.tsx b/src2/frontend/components/_molecules/variables-table/index.tsx index 1de9647af..4f7f2bfa3 100644 --- a/src2/frontend/components/_molecules/variables-table/index.tsx +++ b/src2/frontend/components/_molecules/variables-table/index.tsx @@ -138,6 +138,7 @@ const VariablesTable = ({ data: { pous }, }, projectActions: { updateVariable }, + sharedWorkspaceActions: { handleFileAndWorkspaceSavedState }, } = useOpenPLCStore() const { captureAndPush } = usePouSnapshot() @@ -159,14 +160,18 @@ const VariablesTable = ({ } } - return updateVariable({ + const result = updateVariable({ scope: 'local', associatedPou: name, - variableId: tableData[rowIndex].id, + rowId: rowIndex, data: { [columnId]: value, }, }) + if (result.ok) { + handleFileAndWorkspaceSavedState(name) + } + return result }} tableContext='Variables' columnFilters={columnFilters} diff --git a/src2/frontend/components/_organisms/global-variables-editor/index.tsx b/src2/frontend/components/_organisms/global-variables-editor/index.tsx index 67d70a410..8bf2aaa1a 100644 --- a/src2/frontend/components/_organisms/global-variables-editor/index.tsx +++ b/src2/frontend/components/_organisms/global-variables-editor/index.tsx @@ -8,7 +8,7 @@ import { useOpenPLCStore } from '../../../store' import type { GlobalVariablesTableType } from '../../../store/slices/editor' import { PLCGlobalVariable } from '../../../../middleware/shared/ports/types' import { cn } from '../../../utils/cn' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import TableActions from '../../_atoms/table-actions' import { toast } from '../../_features/[app]/toast/use-toast' @@ -32,9 +32,29 @@ const GlobalVariablesEditor = () => { }, }, editorActions: { updateModelVariables, updateModelVariablesForName }, - projectActions: { createVariable, deleteVariable, rearrangeVariables, setGlobalVariables, pushToHistory }, + projectActions: { createVariable, deleteVariable, rearrangeVariables, setGlobalVariables }, sharedWorkspaceActions: { handleFileAndWorkspaceSavedState }, } = useOpenPLCStore() + + const { + project: { + data: { pous: snapshotPous, configurations }, + }, + snapshotActions: { pushToHistory: rawPushToHistory }, + } = useOpenPLCStore() + + const pushToHistory = useCallback( + (pouName: string) => { + const pou = snapshotPous.find((p) => p.name === pouName) + if (!pou) return + rawPushToHistory(pouName, { + variables: pou.interface?.variables ?? [], + body: pou.body.value, + globalVariables: configurations.resource.globalVariables, + }) + }, + [snapshotPous, configurations.resource.globalVariables, rawPushToHistory], + ) /** * Table data and column filters states to keep track of the table data and column filters */ diff --git a/src2/frontend/components/_organisms/instances-editor/index.tsx b/src2/frontend/components/_organisms/instances-editor/index.tsx index f0cef44c7..29e804edf 100644 --- a/src2/frontend/components/_organisms/instances-editor/index.tsx +++ b/src2/frontend/components/_organisms/instances-editor/index.tsx @@ -9,7 +9,7 @@ import type { PLCInstance } from '../../../../middleware/shared/ports/types' import { cn } from '../../../utils/cn' import { parseResourceConfigurationToString } from '../../../utils/parse-resource-configuration-to-string' import { parseResourceStringToConfiguration } from '../../../utils/parse-resource-string-to-configuration' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import TableActions from '../../_atoms/table-actions' import { toast } from '../../_features/[app]/toast/use-toast' @@ -32,10 +32,30 @@ const InstancesEditor = () => { }, }, editorActions: { updateModelInstances }, - projectActions: { createInstance, rearrangeInstances, deleteInstance, setInstances, setTasks, pushToHistory }, + projectActions: { createInstance, rearrangeInstances, deleteInstance, setInstances, setTasks }, sharedWorkspaceActions: { handleFileAndWorkspaceSavedState }, } = useOpenPLCStore() + const { + project: { + data: { pous: snapshotPous, configurations }, + }, + snapshotActions: { pushToHistory: rawPushToHistory }, + } = useOpenPLCStore() + + const pushToHistory = useCallback( + (pouName: string) => { + const pou = snapshotPous.find((p) => p.name === pouName) + if (!pou) return + rawPushToHistory(pouName, { + variables: pou.interface?.variables ?? [], + body: pou.body.value, + globalVariables: configurations.resource.globalVariables, + }) + }, + [snapshotPous, configurations.resource.globalVariables, rawPushToHistory], + ) + const [instanceData, setInstanceData] = useState([]) const [editorCode, setEditorCode] = useState(() => parseResourceConfigurationToString(tasks, instanceData)) const [parseError, setParseError] = useState(null) diff --git a/src2/frontend/components/_organisms/task-editor/index.tsx b/src2/frontend/components/_organisms/task-editor/index.tsx index 8856ab26b..bae418594 100644 --- a/src2/frontend/components/_organisms/task-editor/index.tsx +++ b/src2/frontend/components/_organisms/task-editor/index.tsx @@ -9,7 +9,7 @@ import type { PLCTask } from '../../../../middleware/shared/ports/types' import { cn } from '../../../utils/cn' import { parseResourceConfigurationToString } from '../../../utils/parse-resource-configuration-to-string' import { parseResourceStringToConfiguration } from '../../../utils/parse-resource-string-to-configuration' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import TableActions from '../../_atoms/table-actions' import { toast } from '../../_features/[app]/toast/use-toast' @@ -32,10 +32,30 @@ const TaskEditor = () => { }, }, editorActions: { updateModelTasks }, - projectActions: { createTask, rearrangeTasks, deleteTask, setTasks, setInstances, pushToHistory }, + projectActions: { createTask, rearrangeTasks, deleteTask, setTasks, setInstances }, sharedWorkspaceActions: { handleFileAndWorkspaceSavedState }, } = useOpenPLCStore() + const { + project: { + data: { pous: snapshotPous, configurations }, + }, + snapshotActions: { pushToHistory: rawPushToHistory }, + } = useOpenPLCStore() + + const pushToHistory = useCallback( + (pouName: string) => { + const pou = snapshotPous.find((p) => p.name === pouName) + if (!pou) return + rawPushToHistory(pouName, { + variables: pou.interface?.variables ?? [], + body: pou.body.value, + globalVariables: configurations.resource.globalVariables, + }) + }, + [snapshotPous, configurations.resource.globalVariables, rawPushToHistory], + ) + const [taskData, setTaskData] = useState([]) const [editorCode, setEditorCode] = useState(() => parseResourceConfigurationToString(taskData, instances)) const [parseError, setParseError] = useState(null) diff --git a/src2/frontend/components/_organisms/variables-editor/index.tsx b/src2/frontend/components/_organisms/variables-editor/index.tsx index 57519e50e..a652599f0 100644 --- a/src2/frontend/components/_organisms/variables-editor/index.tsx +++ b/src2/frontend/components/_organisms/variables-editor/index.tsx @@ -16,7 +16,7 @@ import { cn } from '../../../utils/cn' import { parseIecStringToVariables } from '../../../utils/generate-iec-string-to-variables' import { generateIecVariablesToString } from '../../../utils/generate-iec-variables-to-string' import { ColumnFiltersState } from '@tanstack/react-table' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { InputWithRef } from '../../_atoms/input' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../_atoms/select' @@ -55,11 +55,30 @@ const VariablesEditor = () => { setPouVariables, updatePou, updateVariable, - pushToHistory, }, sharedWorkspaceActions: { handleFileAndWorkspaceSavedState }, } = useOpenPLCStore() + const { + project: { + data: { pous: snapshotPous, configurations }, + }, + snapshotActions: { pushToHistory: rawPushToHistory }, + } = useOpenPLCStore() + + const pushToHistory = useCallback( + (pouName: string) => { + const pou = snapshotPous.find((p) => p.name === pouName) + if (!pou) return + rawPushToHistory(pouName, { + variables: pou.interface?.variables ?? [], + body: pou.body.value, + globalVariables: configurations.resource.globalVariables, + }) + }, + [snapshotPous, configurations.resource.globalVariables, rawPushToHistory], + ) + /** * Table data and column filters states to keep track of the table data and column filters */ diff --git a/src2/frontend/store/slices/project/slice.ts b/src2/frontend/store/slices/project/slice.ts index a22ce7e82..c6734976c 100644 --- a/src2/frontend/store/slices/project/slice.ts +++ b/src2/frontend/store/slices/project/slice.ts @@ -2,6 +2,7 @@ import type { ModbusIOPoint, OpcUaServerConfig, PLCServer, + PLCVariable, S7CommLogging, S7CommPlcIdentity, S7CommServerSettings, @@ -9,8 +10,16 @@ import type { import { produce } from 'immer' import { StateCreator } from 'zustand' +import { isLegalIdentifier } from '../../../utils/keywords' + import type { ProjectResponse, ProjectSlice } from './types' import { getVariableBasedOnRowIdOrVariableId } from './utils' +import { + createGlobalVariableValidation, + createVariableValidation, + updateGlobalVariableValidation, + updateVariableValidation, +} from './validation/variables' const ok = (data?: unknown): ProjectResponse => ({ ok: true, data }) const fail = (message: string, title?: string): ProjectResponse => ({ ok: false, message, title }) @@ -313,38 +322,45 @@ const createProjectSlice: StateCreator = (se // Variables // ----------------------------------------------------------------------- createVariable: (dto) => { - const { scope, associatedPou, data, rowToInsert } = dto - if (scope === 'local' && associatedPou) { - const pou = getState().project.data.pous.find((p) => p.name === associatedPou) - if (!pou) return fail('POU not found') - const variables = pou.interface?.variables ?? [] - if (variables.some((v) => v.name === data.name)) return fail('Variable already exists') - } else { - const globalVars = getState().project.data.configurations.resource.globalVariables - if (globalVars.some((v) => v.name === data.name)) return fail('Variable already exists') + const { scope, associatedPou, rowToInsert } = dto + let { data } = dto + + const [isNameLegal, reason] = isLegalIdentifier(data.name) + if (!isNameLegal) { + return fail(`'${data.name}' ${reason}`, 'Illegal Variable Name') } + let response: ProjectResponse = { ok: true } setState( produce((slice: ProjectSlice) => { if (scope === 'local' && associatedPou) { const pou = slice.project.data.pous.find((p) => p.name === associatedPou) - if (!pou?.interface) return + if (!pou?.interface) { + response = fail('POU not found') + return + } + data = { ...data, ...createVariableValidation(pou.interface.variables, data) } if (rowToInsert !== undefined) { - pou.interface.variables.splice(rowToInsert, 0, data) + const pouVariables = pou.interface.variables.filter((variable) => variable.name !== 'OUT') + pouVariables.splice(rowToInsert, 0, data) + pou.interface.variables = [...pouVariables] } else { pou.interface.variables.push(data) } + response.data = data } else { const vars = slice.project.data.configurations.resource.globalVariables + data.name = createGlobalVariableValidation(vars, data.name) if (rowToInsert !== undefined) { vars.splice(rowToInsert, 0, data) } else { vars.push(data) } + response.data = data } }), ) - return ok(data) + return response }, setPouVariables: ({ pouName, variables }) => { setState( @@ -364,20 +380,52 @@ const createProjectSlice: StateCreator = (se return ok() }, updateVariable: ({ scope, associatedPou, rowId, variableId, data: updates }) => { + let response: ProjectResponse = { ok: true } setState( produce((slice: ProjectSlice) => { - const variables = - scope === 'local' && associatedPou - ? slice.project.data.pous.find((p) => p.name === associatedPou)?.interface?.variables - : slice.project.data.configurations.resource.globalVariables - if (!variables) return - - const found = getVariableBasedOnRowIdOrVariableId(variables, rowId, variableId) - if (!found) return - Object.assign(variables[found.index], updates) + if (scope === 'local' && associatedPou) { + const pou = slice.project.data.pous.find((p) => p.name === associatedPou) + if (!pou?.interface) { + response = fail('POU not found') + return + } + const found = getVariableBasedOnRowIdOrVariableId(pou.interface.variables, rowId, variableId) + if (!found) { + response = { ok: false, title: 'Variable not found', message: 'Internal error' } + return + } + const validationResponse = updateVariableValidation(pou.interface.variables, updates, found.variable) + if (!validationResponse.ok) { + response = validationResponse + return + } + pou.interface.variables[found.index] = { + ...pou.interface.variables[found.index], + ...updates, + ...(validationResponse.data ? validationResponse.data : {}), + } + response.data = pou.interface.variables[found.index] + } else { + const globalVars = slice.project.data.configurations.resource.globalVariables + const validationResponse = updateGlobalVariableValidation(globalVars, updates) + if (!validationResponse.ok) { + response = validationResponse + return + } + const found = getVariableBasedOnRowIdOrVariableId(globalVars, rowId, variableId) + if (!found) { + response = { ok: false, title: 'Variable not found' } + return + } + globalVars[found.index] = { + ...globalVars[found.index], + ...updates, + } + response.data = globalVars[found.index] + } }), ) - return ok() + return response }, getVariable: ({ scope, associatedPou, rowId, variableId }) => { const variables = @@ -390,6 +438,36 @@ const createProjectSlice: StateCreator = (se return found?.variable }, deleteVariable: ({ scope, associatedPou, rowId, variableId, variableName }) => { + if (scope === 'global') { + const state = getState() + const globalVars = state.project.data.configurations.resource.globalVariables + + let variableToDelete: PLCVariable | undefined + if (variableName) { + variableToDelete = globalVars.find( + (v) => v.name.toLowerCase() === variableName.toLowerCase(), + ) + } else { + variableToDelete = getVariableBasedOnRowIdOrVariableId(globalVars, rowId, variableId)?.variable + } + + if (variableToDelete) { + const externalReferences = state.project.data.pous.filter((pou) => + pou.interface?.variables?.some( + (v) => v.class === 'external' && v.name.toLowerCase() === variableToDelete.name.toLowerCase(), + ), + ) + + if (externalReferences.length > 0) { + const pouNames = externalReferences.map((pou) => pou.name).join(', ') + return fail( + `The global variable "${variableToDelete.name}" is referenced by external variables in the following POUs: ${pouNames}. Please remove these references before deleting the global variable.`, + 'Cannot Delete Global Variable', + ) + } + } + } + setState( produce((slice: ProjectSlice) => { const variables = @@ -399,7 +477,7 @@ const createProjectSlice: StateCreator = (se if (!variables) return if (variableName) { - const idx = variables.findIndex((v) => v.name === variableName) + const idx = variables.findIndex((v) => v.name.toLowerCase() === variableName.toLowerCase()) if (idx !== -1) variables.splice(idx, 1) return } diff --git a/src2/frontend/store/slices/project/validation/variables.ts b/src2/frontend/store/slices/project/validation/variables.ts index a010a131d..c662e94e7 100644 --- a/src2/frontend/store/slices/project/validation/variables.ts +++ b/src2/frontend/store/slices/project/validation/variables.ts @@ -1,5 +1,7 @@ import type { PLCVariable } from '../../../../../middleware/shared/ports/types' +import type { ProjectResponse } from '../types' + /** * This function extracts the number at the end of a string. */ @@ -13,6 +15,34 @@ export const extractNumberAtEnd = (str: string): { number: number; string: strin } } +/** + * This is a validation to check if the variable name already exists. + **/ +const checkIfVariableExists = (variables: PLCVariable[], name: string) => { + const nameAlreadyInUse = variables.some((variable) => variable.name.toLowerCase() === name.toLowerCase()) + return nameAlreadyInUse +} +const checkIfGlobalVariableExists = (variables: PLCVariable[], name: string) => { + return variables.some((variable) => variable.name === name) +} + +/** + * This is a validation to check if the value of the location is unique. + */ +const checkIfLocationExists = (variables: PLCVariable[], location: string) => { + return variables.some((variable) => variable.location === location) +} + +/** + * This is a validation to check if the variable name is correct. + * CamelCase, PascalCase or SnakeCase and can not be empty. + **/ +const variableNameValidation = (variableName: string) => { + const regex = + /^([a-zA-Z0-9]+(?:[A-Z][a-z0-9]*)*)|([A-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)*)|([a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*)$/ + return regex.test(variableName) +} + const checkVariableNameUnit = (variables: PLCVariable[], variableName: string) => { // Check if there is a variable with the same name when removing the number at the end const variableNameWithoutNumber = variableName.substring( @@ -105,4 +135,307 @@ const arrayValidation = ({ value }: { value: string }) => { return { ok: true } } -export { arrayValidation, checkVariableNameUnit, enumeratedValidation } +/** + * This is a validation to check if the value of the location is valid. + */ +const variableLocationValidation = (variableLocation: string, variableType: string) => { + switch (variableType.toUpperCase()) { + case 'BOOL': { + const boolRegex = /^%[QI]X\d+\.\d$/ + const boolMatch = boolRegex.test(variableLocation) && variableLocation.split('.')[1] <= '7' + return boolMatch + } + case 'INT': + case 'UINT': + case 'WORD': + return /^%[QIM]W\d+$/.test(variableLocation) + case 'DINT': + case 'UDINT': + case 'REAL': + case 'DWORD': + return /^%MD\d+$/.test(variableLocation) + case 'LINT': + case 'ULINT': + case 'LREAL': + case 'LWORD': + return /^%ML\d+$/.test(variableLocation) + default: + return false + } +} + +const variableLocationValidationErrorMessage = (variableType: string) => { + switch (variableType.toUpperCase()) { + case 'BOOL': + return 'Valid locations: %QX0.0..7, %IX0.0..7 (change the number to the desired location)' + case 'INT': + case 'UINT': + case 'WORD': + return 'Valid locations: %QW0, %IW0, %MW0 (change the number to the desired location)' + case 'DINT': + case 'UDINT': + case 'REAL': + case 'DWORD': + return 'Valid locations: %MD0 (change the number to the desired location)' + case 'LINT': + case 'ULINT': + case 'LREAL': + case 'LWORD': + return 'Valid locations: %ML0 (change the number to the desired location)' + default: + return '' + } +} + +/** + * Check if the variable name exists and if it is needed to change the name of the variable. + * Returns an object containing: + * - ok: boolean (true if the variable exists, false otherwise) + * - name: string (the new name of the variable) + * - number: number (the biggest number at the end of the variable name) + */ +const checkVariableName = (variables: PLCVariable[], variableName: string) => { + // Check if there is a variable with the same name when removing the number at the end + const variableNameWithoutNumber = variableName.substring( + 0, + variableName.length - extractNumberAtEnd(variableName).length, + ) + const filteredVariables = variables.filter((variable: PLCVariable) => + variable.name.toLowerCase().includes(variableNameWithoutNumber.toLowerCase()), + ) + + // If there is a variable with the same name, sort the variables by the number at the end and get the biggest number + const sortedVariables = filteredVariables.sort((a, b) => { + const numberA = extractNumberAtEnd(a.name).number + const numberB = extractNumberAtEnd(b.name).number + + // Treat variables without numbers as having number -1 for sorting purposes + // This ensures they come before numbered variables + const sortNumberA = numberA === -1 ? -1 : numberA + const sortNumberB = numberB === -1 ? -1 : numberB + + return sortNumberA - sortNumberB + }) + + // Get the biggest number at the end of the variable name + // If there is no number at the end of the variable name, return -1 (because the number at the end of the variable name is 0) + const biggestVariable = + sortedVariables.length > 0 ? extractNumberAtEnd(sortedVariables[sortedVariables.length - 1].name) : { number: -1 } + + return { + ok: filteredVariables.length > 0, + name: variableNameWithoutNumber, + number: biggestVariable.number + 1, + } +} + +/** + * This is a validation to check if it is needed changing the name of a variable at creation. + * If the variable exists change the variable name. + **/ +const createVariableValidation = ( + variables: PLCVariable[], + variable: PLCVariable, +): { name: string; location: string } => { + const { name: variableName, location: variableLocation } = variable + const response = { name: variableName, location: variableLocation } + + if (checkIfVariableExists(variables, variableName)) { + const { name: variableNameWithoutNumber, number } = checkVariableName(variables, variableName) + response.name = `${variableNameWithoutNumber}${number}` + } + + if (checkIfLocationExists(variables, variableLocation)) { + if (variableLocation === '') return response + + const variableFound = variables.find((variable) => variable.location === variableLocation) + if (!variableFound) return response + + switch (variable.type.value.toUpperCase()) { + case 'BOOL': { + const stringWithNoPrefix = variableFound.location.replace('%QX', '').replace('%IX', '') + const position = parseInt(stringWithNoPrefix.split('.')[0]) + const dotPosition = parseInt(stringWithNoPrefix.split('.')[1]) + + if (variableFound?.location.startsWith('%QX')) { + response.location = `%QX${dotPosition === 7 ? position + 1 : position}.${dotPosition === 7 ? 0 : dotPosition + 1}` + } else { + response.location = `%IX${dotPosition === 7 ? position + 1 : position}.${dotPosition === 7 ? 0 : dotPosition + 1}` + } + break + } + + case 'INT': + case 'UINT': + case 'WORD': { + const stringWithNoPrefix = variableFound.location.replace('%QW', '').replace('%IW', '').replace('%MW', '') + const position = parseInt(stringWithNoPrefix) + if (variableFound?.location.startsWith('%QW')) { + response.location = `%QW${position + 1}` + } else if (variableFound?.location.startsWith('%IW')) { + response.location = `%IW${position + 1}` + } else { + response.location = `%MW${position + 1}` + } + break + } + + case 'DINT': + case 'UDINT': + case 'REAL': + case 'DWORD': { + const stringWithNoPrefix = variableFound.location.replace('%MD', '') + const position = parseInt(stringWithNoPrefix) + response.location = `%MD${position + 1}` + break + } + + case 'LINT': + case 'ULINT': + case 'LREAL': + case 'LWORD': { + const stringWithNoPrefix = variableFound.location.replace('%ML', '') + const position = parseInt(stringWithNoPrefix) + response.location = `%ML${position + 1}` + break + } + + default: + break + } + } + return response +} + +const createGlobalVariableValidation = (variables: PLCVariable[], variableName: string) => { + if (checkIfGlobalVariableExists(variables, variableName)) { + const { name: variableNameWithoutNumber, number } = checkVariableName(variables, variableName) + return `${variableNameWithoutNumber}${number}` + } + return variableName +} + +/** + * This is a validation to check the name of the variable at update. + * If the variable name is invalid, create a response. + * If the variable name already exists, create or change a response. + **/ +const updateVariableValidation = ( + variables: PLCVariable[], + dataToBeUpdated: Partial, + variableToUpdate: PLCVariable, +) => { + let response: ProjectResponse = { ok: true } + + if (dataToBeUpdated.class) response.data = { class: dataToBeUpdated.class } + + if (dataToBeUpdated.name || dataToBeUpdated.name === '') { + const { name } = dataToBeUpdated + if (name === '') { + console.error('Variable name is empty') + response = { + ok: false, + title: 'Variable name is empty.', + message: 'Please make sure that the name is not empty.', + } + return response + } + + if (checkIfVariableExists(variables, name)) { + console.error(`Variable "${name}" already exists`) + response = { + ok: false, + title: 'Variable already exists', + message: 'Please make sure that the name is unique.', + } + return response + } + + if (!variableNameValidation(name)) { + console.error(`Variable "${name}" name is invalid`) + response = { + ok: false, + title: 'Variable name is invalid.', + message: `Please make sure that the name is valid. Valid names: CamelCase, PascalCase or SnakeCase.`, + } + return response + } + } + + if (dataToBeUpdated.location) { + const { location } = dataToBeUpdated + if (checkIfLocationExists(variables, location)) { + console.error(`Location "${location}" already exists`) + response = { + ok: false, + title: 'Location already exists', + message: 'Please make sure that the location is unique.', + } + return response + } + + if (!variableLocationValidation(location, variableToUpdate.type.value)) { + console.error(`Location "${location}" is invalid`) + response = { + ok: false, + title: 'Location is invalid.', + message: `Please make sure that the location is valid.\n${variableLocationValidationErrorMessage(variableToUpdate.type.value)}`, + } + return response + } + } + + if (dataToBeUpdated.type) { + if (!variableLocationValidation(variableToUpdate.location, dataToBeUpdated.type.value)) { + response.data = { ...(response.data ? response.data : {}), location: '' } + } + if (dataToBeUpdated.type.definition === 'derived') { + response.data = { ...(response.data ? response.data : {}), location: '', initialValue: '', class: 'local' } + } + } + + return response +} + +const updateGlobalVariableValidation = ( + variables: PLCVariable[], + dataToBeUpdated: Partial, +) => { + let response: ProjectResponse = { ok: true } + + if (dataToBeUpdated.name || dataToBeUpdated.name === '') { + const { name } = dataToBeUpdated + if (name === '') { + console.error('Global Variable name is empty') + response = { + ok: false, + title: 'Global Variable name is empty.', + message: 'Please make sure that the name is not empty.', + } + return response + } + + if (checkIfGlobalVariableExists(variables, name)) { + console.error(`Global Variable "${name}" already exists`) + response = { + ok: false, + title: 'Global Variable already exists', + message: 'Please make sure that the name is unique.', + } + return response + } + } + + return response +} + +export { + arrayValidation, + checkVariableName, + checkVariableNameUnit, + createGlobalVariableValidation, + createVariableValidation, + enumeratedValidation, + updateGlobalVariableValidation, + updateVariableValidation, +} From 17fd59546082054bba0f3b5a747e1facbfe3c78c Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Mon, 16 Mar 2026 16:17:56 -0300 Subject: [PATCH 07/40] fix(step-31): wire missing accelerator handlers and fix save state management Cmd+S/Cmd+Shift+S now update editingState and mark files as saved. Added missing handlers: findInProject, switchPerspective, quitApp, theme update, auto-close handshake, and darwin quit. Split window close vs darwin quit into separate port methods. Added system library data files for block definitions. Co-Authored-By: Claude Opus 4.6 (1M context) --- src2/App.tsx | 42 +- .../_templates/accelerator-handler.tsx | 136 +- src2/frontend/data/library/MQTT.ts | 197 + src2/frontend/data/library/P1AM.ts | 180 + .../library/additional-function-blocks.ts | 337 + .../data/library/arduino-function-blocks.ts | 439 + .../data/library/communication-blocks.ts | 92 + .../data/library/function/arithmetic.ts | 222 + .../data/library/function/bit-shift.ts | 152 + .../frontend/data/library/function/bitwise.ts | 147 + .../data/library/function/character-string.ts | 322 + .../data/library/function/comparison.ts | 202 + src2/frontend/data/library/function/index.ts | 9 + .../data/library/function/numerical.ts | 272 + .../data/library/function/selection.ts | 192 + src2/frontend/data/library/function/time.ts | 327 + .../data/library/function/type-conversion.ts | 7812 +++++++++++++++++ src2/frontend/data/library/index.ts | 9 + src2/frontend/data/library/jaguar.ts | 77 + .../library/sequent-microsystems-modules.ts | 346 + src2/frontend/store/slices/library/types.ts | 7 +- .../adapters/editor/accelerator-adapter.ts | 10 + .../adapters/editor/window-adapter.ts | 28 +- .../shared/ports/accelerator-port.ts | 3 + src2/middleware/shared/ports/window-port.ts | 14 + 25 files changed, 11546 insertions(+), 28 deletions(-) create mode 100644 src2/frontend/data/library/MQTT.ts create mode 100644 src2/frontend/data/library/P1AM.ts create mode 100644 src2/frontend/data/library/additional-function-blocks.ts create mode 100644 src2/frontend/data/library/arduino-function-blocks.ts create mode 100644 src2/frontend/data/library/communication-blocks.ts create mode 100644 src2/frontend/data/library/function/arithmetic.ts create mode 100644 src2/frontend/data/library/function/bit-shift.ts create mode 100644 src2/frontend/data/library/function/bitwise.ts create mode 100644 src2/frontend/data/library/function/character-string.ts create mode 100644 src2/frontend/data/library/function/comparison.ts create mode 100644 src2/frontend/data/library/function/index.ts create mode 100644 src2/frontend/data/library/function/numerical.ts create mode 100644 src2/frontend/data/library/function/selection.ts create mode 100644 src2/frontend/data/library/function/time.ts create mode 100644 src2/frontend/data/library/function/type-conversion.ts create mode 100644 src2/frontend/data/library/index.ts create mode 100644 src2/frontend/data/library/jaguar.ts create mode 100644 src2/frontend/data/library/sequent-microsystems-modules.ts diff --git a/src2/App.tsx b/src2/App.tsx index 7083d5444..83af760f4 100644 --- a/src2/App.tsx +++ b/src2/App.tsx @@ -3,12 +3,52 @@ import 'tailwindcss/tailwind.css' import './backend/styles/globals.css' import { AppLayout } from './frontend/components/_templates/app-layout' +import { + AdditionalFunctionBlocks, + ArduinoFunctionBlocks, + Arithmetic, + BitShift, + Bitwise, + CharacterString, + CommunicationBlocks, + Comparison, + Jaguar, + MQTT, + Numerical, + P1AM, + Selection, + SequentMicrosystemsModules, + StandardFunctionBlocks, + Time, + TypeConversion, +} from './frontend/data/library' import { StartScreen } from './frontend/screens/start-screen' import { WorkspaceScreen } from './frontend/screens/workspace-screen' -import { useOpenPLCStore } from './frontend/store' +import { openPLCStoreBase, useOpenPLCStore } from './frontend/store' import { editorPorts } from './middleware/editor-platform' import { PlatformProvider } from './middleware/shared/providers' +// Initialize system libraries at module load time (before first render) +openPLCStoreBase.getState().libraryActions.setSystemLibraries([ + AdditionalFunctionBlocks, + ArduinoFunctionBlocks, + CommunicationBlocks, + Jaguar, + MQTT, + P1AM, + SequentMicrosystemsModules, + StandardFunctionBlocks, + Arithmetic, + BitShift, + Bitwise, + CharacterString, + Comparison, + Numerical, + Selection, + Time, + TypeConversion, +]) + export default function App() { const { project: { diff --git a/src2/frontend/components/_templates/accelerator-handler.tsx b/src2/frontend/components/_templates/accelerator-handler.tsx index 6dbaac415..e7310c375 100644 --- a/src2/frontend/components/_templates/accelerator-handler.tsx +++ b/src2/frontend/components/_templates/accelerator-handler.tsx @@ -1,4 +1,11 @@ -import { useAccelerator, useCompiler, useProject, useWindow, useCapabilities } from '../../../middleware/shared/providers' +import { + useAccelerator, + useCompiler, + useProject, + useWindow, + useTheme, + useCapabilities, +} from '../../../middleware/shared/providers' import { useOpenPLCStore } from '../../store' import type { ModalTypes } from '../../store/slices/modal' import { useEffect, useState } from 'react' @@ -20,6 +27,7 @@ const AcceleratorHandler = () => { const compilerPort = useCompiler() const projectPort = useProject() const windowPort = useWindow() + const themePort = useTheme() const capabilities = useCapabilities() const [requestFlag, setRequestFlag] = useState(false) @@ -32,8 +40,18 @@ const AcceleratorHandler = () => { workspace: { editingState, systemConfigs, close }, modalActions: { openModal }, sharedWorkspaceActions: { closeProject }, - workspaceActions: { switchAppTheme, toggleMaximizedWindow, setCloseWindow, setCloseApp, setCloseAppDarwin }, + workspaceActions: { + switchAppTheme, + toggleMaximizedWindow, + setCloseWindow, + setCloseApp, + setCloseAppDarwin, + setEditingState, + setModalOpen, + toggleCollapse, + }, tabsActions: { removeTab }, + fileActions: { setAllToSaved }, pouActions: { deleteRequest: deletePouRequest }, datatypeActions: { deleteRequest: deleteDatatypeRequest }, snapshotActions: { undo, redo }, @@ -157,19 +175,32 @@ const AcceleratorHandler = () => { }, [editingState, accelerator, closeProject]) /** - * Save project + * Save project (Cmd+Shift+S) */ useEffect(() => { const unsub = accelerator.onSaveProject(() => { - void projectPort.saveProject({ - projectPath: project.meta.path, - projectData: project.data, - deviceConfiguration: deviceDefinitions.configuration, - devicePinMapping: deviceDefinitions.pinMapping.pins, - }) + setEditingState('save-request') + projectPort + .saveProject({ + projectPath: project.meta.path, + projectData: project.data, + deviceConfiguration: deviceDefinitions.configuration, + devicePinMapping: deviceDefinitions.pinMapping.pins, + }) + .then((res) => { + if (res.success) { + setEditingState('saved') + setAllToSaved() + } else { + setEditingState('unsaved') + } + }) + .catch(() => { + setEditingState('unsaved') + }) }) return unsub - }, [project, deviceDefinitions, accelerator, projectPort]) + }, [project, deviceDefinitions, accelerator, projectPort, setEditingState, setAllToSaved]) /** * Delete file @@ -218,19 +249,52 @@ const AcceleratorHandler = () => { }, [selectedProjectTreeLeaf, accelerator, removeTab]) /** - * Save file (saves entire project since files are part of project XML) + * Save file (Cmd+S) — saves entire project since files are part of project XML */ useEffect(() => { const unsub = accelerator.onSaveFile(() => { - void projectPort.saveProject({ - projectPath: project.meta.path, - projectData: project.data, - deviceConfiguration: deviceDefinitions.configuration, - devicePinMapping: deviceDefinitions.pinMapping.pins, - }) + setEditingState('save-request') + projectPort + .saveProject({ + projectPath: project.meta.path, + projectData: project.data, + deviceConfiguration: deviceDefinitions.configuration, + devicePinMapping: deviceDefinitions.pinMapping.pins, + }) + .then((res) => { + if (res.success) { + setEditingState('saved') + setAllToSaved() + } else { + setEditingState('unsaved') + } + }) + .catch(() => { + setEditingState('unsaved') + }) }) return unsub - }, [selectedProjectTreeLeaf, accelerator, projectPort, project, deviceDefinitions]) + }, [selectedProjectTreeLeaf, accelerator, projectPort, project, deviceDefinitions, setEditingState, setAllToSaved]) + + /** + * Find in project (Cmd+Shift+F) + */ + useEffect(() => { + const unsub = accelerator.onFindInProject(() => { + setModalOpen('findInProject', true) + }) + return unsub + }, [accelerator, setModalOpen]) + + /** + * Switch perspective (F12) + */ + useEffect(() => { + const unsub = accelerator.onSwitchPerspective(() => { + toggleCollapse() + }) + return unsub + }, [accelerator, toggleCollapse]) /** * Undo / Redo @@ -251,9 +315,36 @@ const AcceleratorHandler = () => { return unsub }, [meta.name, isMonacoFocused, accelerator, redo]) + /** + * Quit app (Ctrl+Q on Windows/Linux) + */ + useEffect(() => { + const unsub = accelerator.onQuitApp(() => { + quitAppRequest(editingState === 'unsaved', openModal) + }) + return unsub + }, [editingState, accelerator, openModal]) + + /** + * Theme update from main process + */ + useEffect(() => { + const unsub = themePort.onThemeChanged(() => { + switchAppTheme() + }) + return unsub + }, [themePort, switchAppTheme]) + /** * Window lifecycle events (editor-only, gated by capabilities) */ + useEffect(() => { + if (!capabilities.hasNativeWindowControls) return + + const unsub = windowPort.enableAutoCloseHandshake?.() + return unsub + }, [capabilities.hasNativeWindowControls, windowPort]) + useEffect(() => { if (!capabilities.hasNativeWindowControls) return @@ -263,6 +354,15 @@ const AcceleratorHandler = () => { return unsub }, [capabilities.hasNativeWindowControls, windowPort, setCloseWindow]) + useEffect(() => { + if (!capabilities.hasNativeWindowControls) return + + const unsub = windowPort.onDarwinAppQuitting?.(() => { + setCloseAppDarwin(true) + }) + return unsub + }, [capabilities.hasNativeWindowControls, windowPort, setCloseAppDarwin]) + useEffect(() => { if (!capabilities.hasNativeWindowControls) return diff --git a/src2/frontend/data/library/MQTT.ts b/src2/frontend/data/library/MQTT.ts new file mode 100644 index 000000000..0a00a01e1 --- /dev/null +++ b/src2/frontend/data/library/MQTT.ts @@ -0,0 +1,197 @@ +import { BaseLibraryPouSchema, BaseLibraryVariableSchema } from '../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const MQTTVariablesSchema = BaseLibraryVariableSchema + +const MQTTPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(MQTTVariablesSchema), +}) + +export const MQTTLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(MQTTPouSchema), +}) + +type MQTTLibrary = z.infer + +const MQTT: MQTTLibrary = { + name: 'MQTT', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'src/renderer/data/library/mqtt/st', + cPath: 'src/renderer/data/library/mqtt/c', + pous: [ + { + name: 'MQTT_RECEIVE', + type: 'function-block', + language: 'st', + variables: [ + { name: 'RECEIVE', class: 'input', type: { definition: 'base-type', value: 'BOOL' }, documentation: 'RECEIVE' }, + { name: 'TOPIC', class: 'input', type: { definition: 'base-type', value: 'STRING' }, documentation: 'TOPIC' }, + { + name: 'RECEIVED', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'RECEIVED', + }, + { + name: 'MESSAGE', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + documentation: 'MESSAGE', + }, + ], + body: 'RECEIVED := 0;', + documentation: + 'Receive MQTT messages for a particular TOPIC when RECEIVE is active. You must subscribe to a topic first before you can start receiving messages for that particular topic. Once a message is received, RECEIVED output is triggered, and MESSAGE will contain the received message as a STRING.', + }, + { + name: 'MQTT_SEND', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SEND', class: 'input', type: { definition: 'base-type', value: 'BOOL' }, documentation: 'SEND' }, + { name: 'TOPIC', class: 'input', type: { definition: 'base-type', value: 'STRING' }, documentation: 'TOPIC' }, + { + name: 'MESSAGE', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + documentation: 'MESSAGE', + }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: 'SUCCESS := 0;', + documentation: + 'Sends a MESSAGE to a particular TOPIC when SEND input is triggered. Keep in mind that SEND is not configured as a rising edge input, which means that MQTT_SEND will continuously send messages every scan cycle while SEND is TRUE. If the message was sent without errors, SUCCESS will be TRUE.', + }, + { + name: 'MQTT_CONNECT', + type: 'function-block', + language: 'st', + variables: [ + { name: 'CONNECT', class: 'input', type: { definition: 'base-type', value: 'BOOL' }, documentation: 'CONNECT' }, + { name: 'BROKER', class: 'input', type: { definition: 'base-type', value: 'STRING' }, documentation: 'BROKER' }, + { name: 'PORT', class: 'input', type: { definition: 'base-type', value: 'UINT' }, documentation: 'PORT' }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: 'SUCCESS := 0;', + documentation: + 'Connect to a BROKER at a given PORT when CONNECT is triggered. If a successful connection is made, SUCCESS is set to TRUE', + }, + { + name: 'MQTT_CONNECT_AUTH', + type: 'function-block', + language: 'st', + variables: [ + { name: 'CONNECT', class: 'input', type: { definition: 'base-type', value: 'BOOL' }, documentation: 'CONNECT' }, + { name: 'BROKER', class: 'input', type: { definition: 'base-type', value: 'STRING' }, documentation: 'BROKER' }, + { name: 'PORT', class: 'input', type: { definition: 'base-type', value: 'UINT' }, documentation: 'PORT' }, + { name: 'USER', class: 'input', type: { definition: 'base-type', value: 'STRING' }, documentation: 'USER' }, + { + name: 'PASSWORD', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + documentation: 'PASSWORD', + }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: 'SUCCESS := 0;', + documentation: + 'Connect to an authenticated BROKER at a given PORT using the credentials from USER and PASSWORD when CONNECT is triggered. If a successful connection is made, SUCCESS is set to TRUE', + }, + { + name: 'MQTT_SUBSCRIBE', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'SUBSCRIBE', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUBSCRIBE', + }, + { name: 'TOPIC', class: 'input', type: { definition: 'base-type', value: 'STRING' }, documentation: 'TOPIC' }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: 'SUCCESS := 0;', + documentation: + 'Subscribe to a given TOPIC when SUBSCRIBE input is triggered. Upon a successful subscription, SUCCESS is set to TRUE. Keep in mind that once you subscribe to a topic, OpenPLC will start receiving messages sent to that topic and storing them in a message pool. You must use the MQTT_RECEIVE block to retrieve messages from the pool and free up space to receive more messages. The maximum pool size is currently limited to 10 messages. If you let messages accumulate in the pool you will start loosing messages once the pool is full.', + }, + { + name: 'MQTT_UNSUBSCRIBE', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'UNSUBSCRIBE', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'UNSUBSCRIBE', + }, + { name: 'TOPIC', class: 'input', type: { definition: 'base-type', value: 'STRING' }, documentation: 'TOPIC' }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: 'SUCCESS := 0;', + documentation: + 'Unsubscribe to a given TOPIC when UNSUBSCRIBE input is triggered. Upon a successful unsubscription, SUCCESS is set to TRUE. Keep in mind that once you unsubscribe to a topic, OpenPLC will stop storing messages sent to that topic in the message pool. However, messages received previously and not captured with a MQTT_RECEIVE block will remain in the pool using up pool space.', + }, + { + name: 'MQTT_DISCONNECT', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'DISCONNECT', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'DISCONNECT', + }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: 'SUCCESS := 0;', + documentation: + 'Disconnects from the current broker when DISCONNECT is set to TRUE. Upon a successful disconnection, SUCCESS is set to TRUE.', + }, + ], +} + +export { MQTT } diff --git a/src2/frontend/data/library/P1AM.ts b/src2/frontend/data/library/P1AM.ts new file mode 100644 index 000000000..93889ecdb --- /dev/null +++ b/src2/frontend/data/library/P1AM.ts @@ -0,0 +1,180 @@ +import { BaseLibraryPouSchema, BaseLibraryVariableSchema } from '../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const P1AMVariablesSchema = BaseLibraryVariableSchema + +const P1AMPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(P1AMVariablesSchema), +}) + +export const P1AMLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(P1AMPouSchema), +}) + +type P1AMLibrary = z.infer + +const P1AM: P1AMLibrary = { + name: 'P1AM', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'src/renderer/data/library/p1am/st', + cPath: 'src/renderer/data/library/p1am/c', + pous: [ + { + name: 'P1AM_INIT', + type: 'function-block', + language: 'st', + variables: [ + { name: 'INIT', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'SUCCESS', class: 'output', type: { definition: 'base-type', value: 'SINT' } }, + ], + body: 'SUCCESS := 0;', + documentation: + "Initialize P1AM Modules and return the number of initialized modules on SUCCESS. If SUCCESS is zero, an error has occurred, or there aren't any modules on the bus", + }, + { + name: 'P1_16CDR', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SLOT', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'O1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O5', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O6', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O7', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O8', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I5', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I6', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I7', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I8', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + ], + body: 'I1 := 0;', + documentation: + 'Get all inputs and update all outputs from P1-16CDR module. Also works with P1-15CDD1 and P1-15CDD2', + }, + { + name: 'P1_08N', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SLOT', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'I1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I5', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I6', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I7', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I8', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + ], + body: 'I1 := 0;', + documentation: 'Get all inputs from P1-08Nxx modules. Compatible with P1-08NA, P1-08ND3, P1-08NE3 and P1-08SIM', + }, + { + name: 'P1_16N', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SLOT', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'I1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I5', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I6', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I7', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I8', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I9', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I10', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I11', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I12', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I13', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I14', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I15', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I16', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + ], + body: 'I1 := 0;', + documentation: 'Get all inputs from P1-16Nxx modules. Compatible with P1-16ND3 and P1-16NE3', + }, + { + name: 'P1_08T', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SLOT', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'O1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O5', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O6', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O7', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O8', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DUMMY', class: 'local', type: { definition: 'base-type', value: 'SINT' } }, + ], + body: 'DUMMY := SLOT;', + documentation: 'Set all outputs on P1-08Txx modules. Compatible with P1-08TA, P1-08TD1, P1-08TD2 and P1-08TRS', + }, + { + name: 'P1_16TR', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SLOT', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'O1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O5', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O6', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O7', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O8', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O9', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O10', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O11', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O12', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O13', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O14', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O15', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'O16', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DUMMY', class: 'local', type: { definition: 'base-type', value: 'SINT' } }, + ], + body: 'DUMMY := SLOT;', + documentation: 'Set all outputs on P1-16TR modules. Also compatible with P1-15TD1 and P1-15TD2', + }, + { + name: 'P1_04AD', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SLOT', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'I1', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'I2', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'I3', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'I4', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'DUMMY', class: 'local', type: { definition: 'base-type', value: 'SINT' } }, + ], + body: 'DUMMY := SLOT;', + documentation: 'Get all analog inputs from P1-04ADxx modules. Compatible with P1-04AD, P1-04ADL-1 and P1-04ADL-2', + }, + ], +} + +export { P1AM } diff --git a/src2/frontend/data/library/additional-function-blocks.ts b/src2/frontend/data/library/additional-function-blocks.ts new file mode 100644 index 000000000..de5cdc8d9 --- /dev/null +++ b/src2/frontend/data/library/additional-function-blocks.ts @@ -0,0 +1,337 @@ +import { BaseLibraryPouSchema, BaseLibraryVariableSchema, baseTypeSchema } from '../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const AdditionalFunctionBlocksVariablesSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('derived-type'), + value: z.string(), + }), + ]), + initialValue: z + .lazy((): z.Schema => AdditionalFunctionBlocksVariablesSchema.pick({ type: true })) + .optional(), +}) + +const AdditionalFunctionBlocksPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(AdditionalFunctionBlocksVariablesSchema), +}) + +export const AdditionalFunctionBlocksLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(AdditionalFunctionBlocksPouSchema), +}) + +type AdditionalFunctionBlocksLibrary = z.infer + +const AdditionalFunctionBlocks: AdditionalFunctionBlocksLibrary = { + name: 'Additional Function Blocks', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'dummypath/wichwillbereplacedlater', + cPath: 'dummypath/wichwillbereplacedlater', + pous: [ + { + name: 'RTC', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: '0 - current time, 1 - load time from PDT', + }, + { + name: 'PDT', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + documentation: 'Preset datetime', + }, + { + name: 'Q', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + initialValue: { value: 'FALSE' }, + documentation: 'Copy of IN', + }, + { + name: 'CDT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + documentation: 'Datetime, current or relative to PDT', + }, + { + name: 'PREV_IN', + class: 'local', + type: { definition: 'base-type', value: 'BOOL' }, + initialValue: { value: 'FALSE' }, + }, + { name: 'OFFSET', class: 'local', type: { definition: 'base-type', value: 'TIME' } }, + { name: 'CURRENT_TIME', class: 'local', type: { definition: 'base-type', value: 'DT' } }, + ], + body: '{__SET_VAR(data__->,CURRENT_TIME,,__CURRENT_TIME)}\n\n IF IN\n THEN\n IF NOT PREV_IN\n THEN\n OFFSET := PDT - CURRENT_TIME;\n END_IF;\n\n (* PDT + time since PDT was loaded *)\n CDT := CURRENT_TIME + OFFSET;\n ELSE\n CDT := CURRENT_TIME;\n END_IF;\n\n Q := IN;\n PREV_IN := IN;', + documentation: + 'The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on.', + }, + { + name: 'INTEGRAL', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'RUN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: '1 = integrate, 0 = hold', + }, + { + name: 'R1', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'Overriding reset', + }, + { + name: 'XIN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Input variable', + }, + { + name: 'X0', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Initial value', + }, + { + name: 'CYCLE', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + documentation: 'Sampling period', + }, + { name: 'Q', class: 'output', type: { definition: 'base-type', value: 'BOOL' }, documentation: 'NOT R1' }, + { + name: 'XOUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Integrated output', + }, + ], + body: 'Q := NOT R1 ;\nIF R1 THEN XOUT := X0;\nELSIF RUN THEN XOUT := XOUT + XIN * TIME_TO_REAL(CYCLE);\nEND_IF;', + documentation: 'The integral function block integrates the value of input XIN over time.', + }, + { + name: 'DERIVATIVE', + type: 'function-block', + language: 'st', + variables: [ + { name: 'RUN', class: 'input', type: { definition: 'base-type', value: 'BOOL' }, documentation: '0 = reset' }, + { + name: 'XIN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Input to be differentiated', + }, + { + name: 'CYCLE', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + documentation: 'Sampling period', + }, + { + name: 'XOUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Differentiated output', + }, + { name: 'X1', class: 'local', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'X2', class: 'local', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'X3', class: 'local', type: { definition: 'base-type', value: 'REAL' } }, + ], + body: 'IF RUN THEN\n XOUT := (3.0 * (XIN - X3) + X1 - X2)\n / (10.0 * TIME_TO_REAL(CYCLE));\n X3 := X2;\n X2 := X1;\n X1 := XIN;\nELSE \n XOUT := 0.0;\n X1 := XIN;\n X2 := XIN;\n X3 := XIN;\nEND_IF;', + documentation: + 'The derivative function block produces an output XOUT proportional to the rate of change of the input XIN.', + }, + { + name: 'PID', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'AUTO', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: '0 - manual, 1 - automatic', + }, + { + name: 'PV', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Process variable', + }, + { name: 'SP', class: 'input', type: { definition: 'base-type', value: 'REAL' }, documentation: 'Set point' }, + { + name: 'X0', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Manual output adjustment - Typically from transfer station', + }, + { + name: 'KP', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Proportional gain', + }, + { name: 'TR', class: 'input', type: { definition: 'base-type', value: 'REAL' }, documentation: 'Reset time' }, + { + name: 'TD', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Derivative time constant', + }, + { + name: 'CYCLE', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + documentation: 'Sampling period', + }, + { name: 'XOUT', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ERROR', class: 'local', type: { definition: 'base-type', value: 'REAL' }, documentation: 'PV - SP' }, + { + name: 'ITERM', + class: 'local', + type: { definition: 'derived-type', value: 'INTEGRAL' }, + documentation: 'FB for integral term', + }, + { + name: 'DTERM', + class: 'local', + type: { definition: 'derived-type', value: 'DERIVATIVE' }, + documentation: 'FB for derivative term', + }, + ], + body: 'ERROR := PV - SP ;\n(*** Adjust ITERM so that XOUT := X0 when AUTO = 0 ***)\nITERM(RUN := AUTO, R1 := NOT AUTO, XIN := ERROR,\n X0 := TR * (X0 - ERROR), CYCLE := CYCLE);\nDTERM(RUN := AUTO, XIN := ERROR, CYCLE := CYCLE);\nXOUT := KP * (ERROR + ITERM.XOUT/TR + DTERM.XOUT*TD);', + documentation: + 'The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control.', + }, + { + name: 'RAMP', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'RUN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: '0 - track X0, 1 - ramp to/track X1', + }, + { name: 'X0', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'X1', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { + name: 'TR', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + documentation: 'Ramp duration', + }, + { + name: 'CYCLE', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + documentation: 'Sampling period', + }, + { + name: 'BUSY', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'BUSY = 1 during ramping period', + }, + { + name: 'XOUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + initialValue: { value: '0.0' }, + }, + { + name: 'XI', + class: 'local', + type: { definition: 'base-type', value: 'REAL' }, + initialValue: { value: '0.0' }, + documentation: 'Initial value', + }, + { + name: 'T', + class: 'local', + type: { definition: 'base-type', value: 'TIME' }, + initialValue: { value: 'T#0s' }, + documentation: 'Elapsed time of ramp', + }, + ], + body: `BUSY := RUN ; +IF RUN THEN + IF T >= TR THEN + BUSY := 0; + XOUT := X1; + ELSE XOUT := XI + (X1-XI) * TIME_TO_REAL(T) + / TIME_TO_REAL(TR); + T := T + CYCLE; + END_IF; +ELSE + XOUT := X0; + XI := X0; + T := T#0s; +END_IF;`, + documentation: 'The RAMP function block is modelled on example given in the standard.', + }, + { + name: 'HYSTERESIS', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'XIN1', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'XIN2', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'EPS', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'Q', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: `IF Q THEN + IF XIN1 < (XIN2 - EPS) THEN + Q := 0; + END_IF; +ELSIF XIN1 > (XIN2 + EPS) THEN + Q := 1; +END_IF;`, + documentation: + 'The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2.', + }, + ], +} + +export { AdditionalFunctionBlocks } diff --git a/src2/frontend/data/library/arduino-function-blocks.ts b/src2/frontend/data/library/arduino-function-blocks.ts new file mode 100644 index 000000000..6b3f697b0 --- /dev/null +++ b/src2/frontend/data/library/arduino-function-blocks.ts @@ -0,0 +1,439 @@ +import { BaseLibraryPouSchema, BaseLibraryVariableSchema } from '../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const ArduinoFunctionBlocksVariableSchema = BaseLibraryVariableSchema + +const ArduinoFunctionBlocksPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(ArduinoFunctionBlocksVariableSchema), +}) + +export const ArduinoFunctionBlocksLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(ArduinoFunctionBlocksPouSchema), +}) + +type ArduinoFunctionBlocksLibrary = z.infer + +const ArduinoFunctionBlocks: ArduinoFunctionBlocksLibrary = { + name: 'Arduino Function Blocks', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'src/renderer/data/library/arduino-function-blocks/st', + cPath: 'src/renderer/data/library/arduino-function-blocks/c', + pous: [ + { + name: 'DS18B20', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'PIN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'Arduino pin where sensor is connected to', + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + ], + body: 'OUT := 0.0;', + documentation: 'Reads temperature from one DS18B20 one-wire sensor connected to the pin specified in PIN', + }, + { + name: 'DS18B20_2_OUT', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'PIN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'Arduino pin where sensor is connected to', + }, + { + name: 'OUT_0', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_1', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + ], + body: 'OUT := 0.0;', + documentation: + 'Reads temperature from two DS18B20 one-wire sensors. Both sensors must be on the same bus connected to the pin specified in PIN', + }, + { + name: 'DS18B20_3_OUT', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'PIN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'Arduino pin where sensor is connected to', + }, + { + name: 'OUT_0', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_1', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_2', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + ], + body: 'OUT := 0.0;', + documentation: + 'Reads temperature from three DS18B20 one-wire sensors. All sensors must be on the same bus connected to the pin specified in PIN', + }, + { + name: 'DS18B20_4_OUT', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'PIN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'Arduino pin where sensor is connected to', + }, + { + name: 'OUT_0', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_1', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_2', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_3', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + ], + body: 'OUT := 0.0;', + documentation: + 'Reads temperature from four DS18B20 one-wire sensors. All sensors must be on the same bus connected to the pin specified in PIN', + }, + { + name: 'DS18B20_5_OUT', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'PIN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'Arduino pin where sensor is connected to', + }, + { + name: 'OUT_0', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_1', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_2', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_3', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + { + name: 'OUT_4', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'Temperature output in Celsius', + }, + ], + body: 'OUT := 0.0;', + documentation: + 'Reads temperature from five DS18B20 one-wire sensors. All sensors must be on the same bus connected to the pin specified in PIN', + }, + { + name: 'CLOUD_ADD_BOOL', + type: 'function-block', + language: 'st', + variables: [ + { name: 'VAR_NAME', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + { name: 'BOOL_VAR', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + ], + body: 'SSID := SSID;', + documentation: + 'Add a BOOL variable to sync with the Arduino Cloud. VAR_NAME must have the same name as the variable set up in the Arduino IoT Cloud', + }, + { + name: 'CLOUD_ADD_DINT', + type: 'function-block', + language: 'st', + variables: [ + { name: 'VAR_NAME', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + { name: 'DINT_VAR', class: 'input', type: { definition: 'base-type', value: 'DINT' } }, + ], + body: 'SSID := SSID;', + documentation: + 'Add an DINT variable (Arduino int) to sync with the Arduino Cloud. VAR_NAME must have the same name as the variable set up in the Arduino IoT Cloud', + }, + { + name: 'CLOUD_ADD_REAL', + type: 'function-block', + language: 'st', + variables: [ + { name: 'VAR_NAME', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + { name: 'REAL_VAR', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + ], + body: 'SSID := SSID;', + documentation: + 'Add a REAL variable (Arduino float) to sync with the Arduino Cloud. VAR_NAME must have the same name as the variable set up in the Arduino IoT Cloud', + }, + { + name: 'CLOUD_BEGIN', + type: 'function-block', + language: 'st', + variables: [ + { name: 'THING_ID', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + { name: 'SSID', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + { name: 'PASS', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + ], + body: 'SSID := SSID;', + documentation: + 'Setup and initialize Arduino Cloud communication. Must be called before adding any variables (properties).', + }, + { + name: 'PWM_CONTROLLER', + type: 'function-block', + language: 'st', + variables: [ + { name: 'CHANNEL', class: 'input', type: { definition: 'base-type', value: 'SINT' }, documentation: 'CHANNEL' }, + { name: 'FREQ', class: 'input', type: { definition: 'base-type', value: 'REAL' }, documentation: 'FREQ' }, + { name: 'DUTY', class: 'input', type: { definition: 'base-type', value: 'REAL' }, documentation: 'DUTY' }, + { + name: 'internal_ch', + class: 'local', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'internal_ch', + }, + { + name: 'internal_freq', + class: 'local', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'internal_freq', + }, + { + name: 'internal_duty', + class: 'local', + type: { definition: 'base-type', value: 'REAL' }, + documentation: 'internal_duty', + }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: ` + IF CHANNEL < 1 THEN + SUCCESS := FALSE; + RETURN; + END_IF; + + + IF (CHANNEL <> internal_ch) OR (FREQ <> internal_freq) OR (DUTY <> internal_duty) THEN + SUCCESS := TRUE; + END_IF; + `, + documentation: + 'Configures the CPU internal PWM peripheral to generate a PWM signal through hardware. If the CPU does not have a PWM peripheral, compiling this block will result in a compilation error. CHANNEL is the PWM channel number. For most Arduino boards that number is the pin number for the PWM capable pin. FREQ is the desired PWM frequency in Hz. DUTY is the PWM duty cycle (between 0 and 100).', + }, + { + name: 'ARDUINOCAN_CONF', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'EN_PIN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + documentation: 'Arduino CAN Enable pin', + }, + { + name: 'BR', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + documentation: 'Arduino CAN Baudrate', + }, + { + name: 'DONE', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'Arduino CAN configuration Done flag', + }, + ], + body: 'DONE := FALSE;', + documentation: 'Configure Arduino CAN communication', + }, + { + name: 'ARDUINOCAN_WRITE', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'ID', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + documentation: 'Arduino CAN message ID', + }, + { + name: 'D0', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN first payload byte', + }, + { + name: 'D1', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN second payload byte', + }, + { + name: 'D2', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN third payload byte', + }, + { + name: 'D3', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN fourth payload byte', + }, + { + name: 'D4', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN fifth payload byte', + }, + { + name: 'D5', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN sixth payload byte', + }, + { + name: 'D6', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN seventh payload byte', + }, + { + name: 'D7', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + documentation: 'Arduino CAN eighth payload byte', + }, + { + name: 'DONE', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'Arduino CAN write done flag', + }, + ], + body: 'DONE := FALSE;', + documentation: 'Write data to Arduino CAN bus', + }, + { + name: 'ARDUINOCAN_WRITE_WORD', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'ID', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + documentation: 'Arduino CAN message ID', + }, + { + name: 'DATA', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + documentation: 'Arduino CAN payload', + }, + { + name: 'DONE', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'Arduino CAN write done flag', + }, + ], + body: 'DONE := FALSE;', + documentation: 'Write word data to Arduino CAN bus', + }, + { + name: 'ARDUINOCAN_READ', + type: 'function-block', + language: 'st', + variables: [ + { + name: 'DATA', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + documentation: 'Arduino CAN readed data from arduino can message', + }, + ], + body: 'DATA := 0;', + documentation: 'CAN READ', + }, + ], +} + +export { ArduinoFunctionBlocks } diff --git a/src2/frontend/data/library/communication-blocks.ts b/src2/frontend/data/library/communication-blocks.ts new file mode 100644 index 000000000..f447a4eff --- /dev/null +++ b/src2/frontend/data/library/communication-blocks.ts @@ -0,0 +1,92 @@ +import { BaseLibraryPouSchema, BaseLibraryVariableSchema } from '../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const CommunicationBlocksVariableSchema = BaseLibraryVariableSchema + +const CommunicationBlocksPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(CommunicationBlocksVariableSchema), +}) + +export const CommunicationBlocksLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(CommunicationBlocksPouSchema), +}) + +type CommunicationBlocksLibrary = z.infer + +const CommunicationBlocks: CommunicationBlocksLibrary = { + name: 'Communication Blocks', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'src/renderer/data/library/communication-blocks/st', + cPath: 'src/renderer/data/library/communication-blocks/c', + pous: [ + { + name: 'TCP_CONNECT', + type: 'function-block', + language: 'st', + variables: [ + { name: 'CONNECT', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'IP_ADDRESS', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + { name: 'PORT', class: 'input', type: { definition: 'base-type', value: 'INT' } }, + { name: 'UDP', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'SOCKET_ID', class: 'output', type: { definition: 'base-type', value: 'INT' } }, + ], + body: 'SOCKET_ID := 0;', + documentation: + 'Connect to a remote TCP server when CONNECT is TRUE. Upon success, this block returns the connection ID on SOCKET_ID. If SOCKET_ID is less than zero, then the connection was not successfull', + }, + { + name: 'TCP_SEND', + type: 'function-block', + language: 'st', + variables: [ + { name: 'SEND', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'SOCKET_ID', class: 'input', type: { definition: 'base-type', value: 'INT' } }, + { name: 'MSG', class: 'input', type: { definition: 'base-type', value: 'STRING' } }, + { name: 'BYTES_SENT', class: 'output', type: { definition: 'base-type', value: 'INT' } }, + ], + body: 'BYTES_SENT := 0;', + documentation: + 'Send a message to a remote device using TCP/IP when SEND is TRUE. SOCKET_ID must receive a connection ID from a successfull connection using the TCP_Connect block. BYTES_SENT returns the number of bytes sent to the remote device. If BYTES_SENT is less than zero then an error occurred while trying to send the message', + }, + { + name: 'TCP_RECEIVE', + type: 'function-block', + language: 'st', + variables: [ + { name: 'RECEIVE', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'SOCKET_ID', class: 'input', type: { definition: 'base-type', value: 'INT' } }, + { name: 'BYTES_RECEIVED', class: 'output', type: { definition: 'base-type', value: 'INT' } }, + { name: 'MSG', class: 'output', type: { definition: 'base-type', value: 'STRING' } }, + ], + body: 'BYTES_RECEIVED := 0;', + documentation: + 'Send a message to a remote device using TCP/IP when SEND is TRUE. SOCKET_ID must receive a connection ID from a successfull connection using the TCP_Connect block. BYTES_RECEIVED returns the number of bytes received from the remote device. MSG is a String containing the message received', + }, + { + name: 'TCP_CLOSE', + type: 'function-block', + language: 'st', + variables: [ + { name: 'CLOSE', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'SOCKET_ID', class: 'input', type: { definition: 'base-type', value: 'INT' } }, + { name: 'SUCCESS', class: 'output', type: { definition: 'base-type', value: 'INT' } }, + ], + body: 'SUCCESS := 0;', + documentation: + 'Close the TCP connection with the remote server. If SUCCESS is less than zero, then the connection was not successfully closed, or the connection does not exist anymore.', + }, + ], +} + +export { CommunicationBlocks } diff --git a/src2/frontend/data/library/function/arithmetic.ts b/src2/frontend/data/library/function/arithmetic.ts new file mode 100644 index 000000000..f7ac68d35 --- /dev/null +++ b/src2/frontend/data/library/function/arithmetic.ts @@ -0,0 +1,222 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const ArithmeticVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const ArithmeticPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(ArithmeticVariableSchema), +}) + +const ArithmeticLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(ArithmeticPouSchema), +}) + +type ArithmeticLibrary = z.infer + +const Arithmetic: ArithmeticLibrary = { + name: 'Arithmetic', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'ADD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + ], + body: 'Addition', + documentation: '(IN1: ANY_NUM, IN2:ANY_NUM) => OUT:ANY_NUM', + extensible: true, + }, + { + name: 'MUL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + ], + body: 'Multiplication', + documentation: '(IN1: ANY_NUM, IN2:ANY_NUM) => OUT:ANY_NUM', + extensible: true, + }, + { + name: 'SUB', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + ], + body: 'Subtraction', + documentation: '(IN1: ANY_NUM, IN2:ANY_NUM) => OUT:ANY_NUM', + extensible: false, + }, + { + name: 'DIV', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + ], + body: 'Division', + documentation: '(IN1: ANY_NUM, IN2:ANY_NUM) => OUT:ANY_NUM', + extensible: false, + }, + { + name: 'MOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + ], + body: 'Remainder (modulo)', + documentation: '(IN1: ANY_INT, IN2:ANY_INT) => OUT:ANY_INT', + extensible: false, + }, + { + name: 'EXPT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Exponent', + documentation: '(IN1: ANY_REAL, IN2:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'MOVE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY' }, + }, + ], + body: 'Assignment', + documentation: '(IN: ANY, OUT:ANY) => OUT:ANY', + extensible: false, + }, + ], +} + +export { Arithmetic, ArithmeticLibrarySchema } diff --git a/src2/frontend/data/library/function/bit-shift.ts b/src2/frontend/data/library/function/bit-shift.ts new file mode 100644 index 000000000..159e6e827 --- /dev/null +++ b/src2/frontend/data/library/function/bit-shift.ts @@ -0,0 +1,152 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const BitShiftVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const BitShiftPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(BitShiftVariableSchema), +}) + +const BitShiftLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(BitShiftPouSchema), +}) + +type BitShiftLibrary = z.infer + +const BitShift: BitShiftLibrary = { + name: 'BitShift', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'SHL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'N', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + ], + body: 'Shift Left', + documentation: '(IN: ANY_BIT, N:INT) => OUT:ANY_BIT', + extensible: false, + }, + { + name: 'SHR', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'N', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + ], + body: 'Shift Right', + documentation: '(IN: ANY_BIT, N:INT) => OUT:ANY_BIT', + extensible: false, + }, + { + name: 'ROR', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, // need review + }, + { + name: 'N', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, // need review + }, + ], + body: 'Rotate Right', + documentation: '(IN: ANY_NBIT, N:INT) => OUT:ANY_NBIT', + extensible: false, + }, + { + name: 'ROL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, // need review + }, + { + name: 'N', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, // need review + }, + ], + body: 'Rotate Left', + documentation: '(IN: ANY_NBIT, N:INT) => OUT:ANY_NBIT', + extensible: false, + }, + ], +} + +export { BitShift, BitShiftLibrarySchema } diff --git a/src2/frontend/data/library/function/bitwise.ts b/src2/frontend/data/library/function/bitwise.ts new file mode 100644 index 000000000..9d581b016 --- /dev/null +++ b/src2/frontend/data/library/function/bitwise.ts @@ -0,0 +1,147 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const BitwiseVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const BitwisePouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(BitwiseVariableSchema), +}) + +const BitwiseLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(BitwisePouSchema), +}) + +type BitwiseLibrary = z.infer + +const Bitwise: BitwiseLibrary = { + name: 'Bitwise', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'AND', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + ], + body: 'Bitwise AND', + documentation: '(IN1: ANY_BIT, IN2:ANY_BIT) => OUT:ANY_BIT', + extensible: true, + }, + { + name: 'OR', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + ], + body: 'Bitwise OR', + documentation: '(IN1: ANY_BIT, IN2:ANY_BIT) => OUT:ANY_BIT', + extensible: true, + }, + { + name: 'XOR', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + ], + body: 'Bitwise XOR', + documentation: '(IN1: ANY_BIT, IN2:ANY_BIT) => OUT:ANY_BIT', + extensible: true, + }, + { + name: 'NOT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_BIT' }, + }, + ], + body: 'Bitwise NOT', + documentation: '(IN: ANY_BIT) => OUT:ANY_BIT', + extensible: false, + }, + ], +} + +export { Bitwise, BitwiseLibrarySchema } diff --git a/src2/frontend/data/library/function/character-string.ts b/src2/frontend/data/library/function/character-string.ts new file mode 100644 index 000000000..ed26474a2 --- /dev/null +++ b/src2/frontend/data/library/function/character-string.ts @@ -0,0 +1,322 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const CharacterStringVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const CharacterStringPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(CharacterStringVariableSchema), +}) + +const CharacterStringLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(CharacterStringPouSchema), +}) + +type CharacterStringLibrary = z.infer + +const CharacterString: CharacterStringLibrary = { + name: 'CharacterString', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'LEN', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Length of string', + documentation: '(IN:STRING) => OUT:INT', + extensible: false, + }, + { + name: 'LEFT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'L', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'String left of', + documentation: '(IN:STRING, L:ANY_INT) => OUT:STRING', + extensible: false, + }, + { + name: 'RIGHT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'L', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'String right of', + documentation: '(IN:STRING, L:ANY_INT) => OUT:STRING', + extensible: false, + }, + { + name: 'MID', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'L', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'S', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'String from middle of', + documentation: '(IN:STRING, L:ANY_INT, S:ANY_INT) => OUT:STRING', + extensible: false, + }, + { + name: 'CONCAT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Concatenation', + documentation: '(IN1:STRING, IN2:STRING) => OUT:STRING', + extensible: true, + }, + { + name: 'CONCAT_DATE_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Time concatenation', + documentation: '(IN1:DATE, IN2:TOD) => OUT:DT', + extensible: false, + }, + { + name: 'INSERT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'P', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Insertion (into)', + documentation: '(IN1:STRING, IN2:STRING, P:ANY_INT) => OUT:STRING', + extensible: false, + }, + { + name: 'DELETE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'L', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'P', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Deletion (within)', + documentation: '(IN:STRING, L:ANY_INT, P:ANY_INT) => OUT:STRING', + extensible: false, + }, + { + name: 'REPLACE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'L', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'P', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Replacement (within)', + documentation: '(IN1:STRING, IN2:STRING, L:ANY_INT, P:ANY_INT) => OUT:STRING', + extensible: false, + }, + { + name: 'FIND', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Find position', + documentation: '(IN1:STRING, IN2:STRING) => OUT:INT', + extensible: false, + }, + ], +} + +export { CharacterString, CharacterStringLibrarySchema } diff --git a/src2/frontend/data/library/function/comparison.ts b/src2/frontend/data/library/function/comparison.ts new file mode 100644 index 000000000..fd637adee --- /dev/null +++ b/src2/frontend/data/library/function/comparison.ts @@ -0,0 +1,202 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const ComparisonVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const ComparisonPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(ComparisonVariableSchema), +}) + +const ComparisonLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(ComparisonPouSchema), +}) + +type ComparisonLibrary = z.infer + +const Comparison: ComparisonLibrary = { + name: 'Comparison', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'GT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Greater Than', + documentation: '(IN1: ANY, IN2: ANY) => OUT:BOOL', + extensible: true, + }, + { + name: 'GE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Greater Than or equal to', + documentation: '(IN1: ANY, IN2: ANY) => OUT:BOOL', + extensible: true, + }, + { + name: 'EQ', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Equal to', + documentation: '(IN1: ANY, IN2: ANY) => OUT:BOOL', + extensible: true, + }, + { + name: 'LT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Less Than', + documentation: '(IN1: ANY, IN2: ANY) => OUT:BOOL', + extensible: true, + }, + { + name: 'LE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Less Than or equal to', + documentation: '(IN1: ANY, IN2: ANY) => OUT:BOOL', + extensible: true, + }, + { + name: 'NE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Not Equal to', + documentation: '(IN1: ANY, IN2: ANY) => OUT:BOOL', + extensible: false, + }, + ], +} + +export { Comparison, ComparisonLibrarySchema } diff --git a/src2/frontend/data/library/function/index.ts b/src2/frontend/data/library/function/index.ts new file mode 100644 index 000000000..2d586bbcc --- /dev/null +++ b/src2/frontend/data/library/function/index.ts @@ -0,0 +1,9 @@ +export * from './arithmetic' +export * from './bit-shift' +export * from './bitwise' +export * from './character-string' +export * from './comparison' +export * from './numerical' +export * from './selection' +export * from './time' +export * from './type-conversion' diff --git a/src2/frontend/data/library/function/numerical.ts b/src2/frontend/data/library/function/numerical.ts new file mode 100644 index 000000000..9434de43c --- /dev/null +++ b/src2/frontend/data/library/function/numerical.ts @@ -0,0 +1,272 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const NumericalVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const NumericalPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(NumericalVariableSchema), +}) + +const NumericalLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(NumericalPouSchema), +}) + +type NumericalLibrary = z.infer + +const Numerical: NumericalLibrary = { + name: 'Numerical', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'ABS', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + ], + body: 'Absolute value', + documentation: '(IN:ANY_NUM) => OUT:ANY_NUM', + extensible: false, + }, + { + name: 'SQRT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Square root (base 2)', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'LN', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Arc tangent', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'LOG', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Logarithm to base 10', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'EXP', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Exponentiation', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'SIN', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Sine', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'COS', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Cosine', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'TAN', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Tangent', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'ASIN', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Arc sine', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'ACOS', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Arc cosine', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + { + name: 'ATAN', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + ], + body: 'Arc tangent', + documentation: '(IN:ANY_REAL) => OUT:ANY_REAL', + extensible: false, + }, + ], +} + +export { Numerical, NumericalLibrarySchema } diff --git a/src2/frontend/data/library/function/selection.ts b/src2/frontend/data/library/function/selection.ts new file mode 100644 index 000000000..4280c6a69 --- /dev/null +++ b/src2/frontend/data/library/function/selection.ts @@ -0,0 +1,192 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const SelectionVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const SelectionPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(SelectionVariableSchema), +}) + +const SelectionLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(SelectionPouSchema), +}) + +type SelectionLibrary = z.infer + +const Selection: SelectionLibrary = { + name: 'Selection', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'SEL', + type: 'function', + language: 'st', + variables: [ + { + name: 'G', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'IN0', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY' }, + }, + ], + body: 'Binary selection (1 of 2)', + documentation: '(G:BOOL, IN0:ANY, IN1:ANY) => OUT:ANY', + extensible: false, + }, + { + name: 'MAX', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY' }, + }, + ], + body: 'Maximum', + documentation: '(IN1:ANY, IN2:ANY) => OUT:ANY', + extensible: true, + }, + { + name: 'MIN', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY' }, + }, + ], + body: 'Minimum', + documentation: '(IN1:ANY, IN2:ANY) => OUT:ANY', + extensible: true, + }, + { + name: 'LIMIT', + type: 'function', + language: 'st', + variables: [ + { + name: 'MN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'MX', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY' }, + }, + ], + body: 'Limitation', + documentation: '(MN:ANY, IN:ANY, MX:ANY) => OUT:ANY', + extensible: false, + }, + { + name: 'MUX', + type: 'function', + language: 'st', + variables: [ + { + name: 'K', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'IN0', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'IN1', + class: 'input', + type: { definition: 'generic-type', value: 'ANY' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY' }, + }, + ], + body: 'Multiplexer (select 1 of N)', + documentation: '(K:INT, IN0:ANY, IN1:ANY) => OUT:ANY', + extensible: true, + }, + ], +} + +export { Selection, SelectionLibrarySchema } diff --git a/src2/frontend/data/library/function/time.ts b/src2/frontend/data/library/function/time.ts new file mode 100644 index 000000000..0c4ee05e4 --- /dev/null +++ b/src2/frontend/data/library/function/time.ts @@ -0,0 +1,327 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const TimeVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const TimePouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(TimeVariableSchema), +}) + +const TimeLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(TimePouSchema), +}) + +type TimeLibrary = z.infer + +const Time: TimeLibrary = { + name: 'Time', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'ADD_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Time addition', + documentation: '(IN1:TIME, IN2:TIME) => OUT:TIME', + extensible: false, + }, + { + name: 'ADD_TOD_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Time-of-day addition', + documentation: '(IN1:TOD, IN2:TIME) => OUT:TOD', + extensible: false, + }, + { + name: 'ADD_DT_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Date addition', + documentation: '(IN1:DT, IN2:TIME) => OUT:DT', + extensible: false, + }, + { + name: 'MUL_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Time multiplication', + documentation: '(IN1:TIME, IN2:ANY_NUM) => OUT:TIME', + extensible: false, + }, + { + name: 'SUB_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Time subtraction', + documentation: '(IN1:TIME, IN2:TIME) => OUT:TIME', + extensible: false, + }, + { + name: 'SUB_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Date subtraction', + documentation: '(IN1:DATE, IN2:DATE) => OUT:TIME', + extensible: false, + }, + { + name: 'SUB_TOD_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Time-of-day subtraction', + documentation: '(IN1:TOD, IN2:TIME) => OUT:TOD', + extensible: false, + }, + { + name: 'SUB_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Time-of-day subtraction', + documentation: '(IN1:TOD, IN2:TOD) => OUT:TIME', + extensible: false, + }, + { + name: 'SUB_DT_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Date and time subtraction', + documentation: '(IN1:DT, IN2:TIME) => OUT:DT', + extensible: false, + }, + { + name: 'SUB_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Date and time subtraction', + documentation: '(IN1:DT, IN2:DT) => OUT:TIME', + extensible: false, + }, + { + name: 'DIV_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN1', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'IN2', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_NUM' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Time division', + documentation: '(IN1:TIME, IN2:ANY_NUM) => OUT:TIME', + extensible: false, + }, + ], +} + +export { Time, TimeLibrarySchema } diff --git a/src2/frontend/data/library/function/type-conversion.ts b/src2/frontend/data/library/function/type-conversion.ts new file mode 100644 index 000000000..999b83492 --- /dev/null +++ b/src2/frontend/data/library/function/type-conversion.ts @@ -0,0 +1,7812 @@ +import { + BaseLibraryPouSchema, + BaseLibraryVariableSchema, + baseTypeSchema, + genericTypeSchema, +} from '../../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const TypeConversionVariableSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('generic-type'), + value: genericTypeSchema.keyof(), + }), + ]), +}) + +const TypeConversionPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(TypeConversionVariableSchema), +}) + +const TypeConversionLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(TypeConversionPouSchema), +}) + +type TypeConversionLibrary = z.infer + +const TypeConversion: TypeConversionLibrary = { + name: 'TypeConversion', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'path/to/st/file', + cPath: 'path/to/c/file', + pous: [ + { + name: 'BOOL_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: SINT', + extensible: false, + }, + { + name: 'BOOL_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: INT', + extensible: false, + }, + { + name: 'BOOL_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: DINT', + extensible: false, + }, + { + name: 'BOOL_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: LINT', + extensible: false, + }, + { + name: 'BOOL_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: USINT', + extensible: false, + }, + { + name: 'BOOL_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: UINT', + extensible: false, + }, + { + name: 'BOOL_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: UDINT', + extensible: false, + }, + { + name: 'BOOL_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: ULINT', + extensible: false, + }, + { + name: 'BOOL_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: REAL', + extensible: false, + }, + { + name: 'BOOL_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: LREAL', + extensible: false, + }, + { + name: 'BOOL_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: TIME', + extensible: false, + }, + { + name: 'BOOL_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: DATE', + extensible: false, + }, + { + name: 'BOOL_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: TOD', + extensible: false, + }, + { + name: 'BOOL_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: DT', + extensible: false, + }, + { + name: 'BOOL_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: STRING', + extensible: false, + }, + { + name: 'BOOL_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: BYTE', + extensible: false, + }, + { + name: 'BOOL_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: WORD', + extensible: false, + }, + { + name: 'BOOL_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: DWORD', + extensible: false, + }, + { + name: 'BOOL_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BOOL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BOOL) => OUT: LWORD', + extensible: false, + }, + { + name: 'SINT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: BOOL', + extensible: false, + }, + { + name: 'SINT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: INT', + extensible: false, + }, + { + name: 'SINT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: DINT', + extensible: false, + }, + { + name: 'SINT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: LINT', + extensible: false, + }, + { + name: 'SINT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: USINT', + extensible: false, + }, + { + name: 'SINT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: UINT', + extensible: false, + }, + { + name: 'SINT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: UDINT', + extensible: false, + }, + { + name: 'SINT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: ULINT', + extensible: false, + }, + { + name: 'SINT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: REAL', + extensible: false, + }, + { + name: 'SINT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: LREAL', + extensible: false, + }, + { + name: 'SINT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: TIME', + extensible: false, + }, + { + name: 'SINT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: DATE', + extensible: false, + }, + { + name: 'SINT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: TOD', + extensible: false, + }, + { + name: 'SINT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: DT', + extensible: false, + }, + { + name: 'SINT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: STRING', + extensible: false, + }, + { + name: 'SINT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'SINT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: WORD', + extensible: false, + }, + { + name: 'SINT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'SINT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'SINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: SINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'INT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: BOOL', + extensible: false, + }, + { + name: 'INT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: SINT', + extensible: false, + }, + { + name: 'INT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: DINT', + extensible: false, + }, + { + name: 'INT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: LINT', + extensible: false, + }, + { + name: 'INT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: USINT', + extensible: false, + }, + { + name: 'INT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: UINT', + extensible: false, + }, + { + name: 'INT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: UDINT', + extensible: false, + }, + { + name: 'INT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: ULINT', + extensible: false, + }, + { + name: 'INT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: REAL', + extensible: false, + }, + { + name: 'INT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: LREAL', + extensible: false, + }, + { + name: 'INT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: TIME', + extensible: false, + }, + { + name: 'INT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: DATE', + extensible: false, + }, + { + name: 'INT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: TOD', + extensible: false, + }, + { + name: 'INT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: DT', + extensible: false, + }, + { + name: 'INT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: STRING', + extensible: false, + }, + { + name: 'INT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: BYTE', + extensible: false, + }, + { + name: 'INT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: WORD', + extensible: false, + }, + { + name: 'INT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: DWORD', + extensible: false, + }, + { + name: 'INT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: INT) => OUT: LWORD', + extensible: false, + }, + { + name: 'DINT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: BOOL', + extensible: false, + }, + { + name: 'DINT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: SINT', + extensible: false, + }, + { + name: 'DINT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: INT', + extensible: false, + }, + { + name: 'DINT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: LINT', + extensible: false, + }, + { + name: 'DINT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: USINT', + extensible: false, + }, + { + name: 'DINT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: UINT', + extensible: false, + }, + { + name: 'DINT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: UDINT', + extensible: false, + }, + { + name: 'DINT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: ULINT', + extensible: false, + }, + { + name: 'DINT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: REAL', + extensible: false, + }, + { + name: 'DINT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: LREAL', + extensible: false, + }, + { + name: 'DINT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: TIME', + extensible: false, + }, + { + name: 'DINT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: DATE', + extensible: false, + }, + { + name: 'DINT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: TOD', + extensible: false, + }, + { + name: 'DINT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: DT', + extensible: false, + }, + { + name: 'DINT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: STRING', + extensible: false, + }, + { + name: 'DINT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'DINT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: WORD', + extensible: false, + }, + { + name: 'DINT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'DINT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'LINT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: BOOL', + extensible: false, + }, + { + name: 'LINT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: SINT', + extensible: false, + }, + { + name: 'LINT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: INT', + extensible: false, + }, + { + name: 'LINT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: DINT', + extensible: false, + }, + { + name: 'LINT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: USINT', + extensible: false, + }, + { + name: 'LINT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: UINT', + extensible: false, + }, + { + name: 'LINT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: UDINT', + extensible: false, + }, + { + name: 'LINT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: ULINT', + extensible: false, + }, + { + name: 'LINT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: REAL', + extensible: false, + }, + { + name: 'LINT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: LREAL', + extensible: false, + }, + { + name: 'LINT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: TIME', + extensible: false, + }, + { + name: 'LINT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: DATE', + extensible: false, + }, + { + name: 'LINT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: TOD', + extensible: false, + }, + { + name: 'LINT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: DT', + extensible: false, + }, + { + name: 'LINT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: STRING', + extensible: false, + }, + { + name: 'LINT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'LINT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: WORD', + extensible: false, + }, + { + name: 'LINT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'LINT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'USINT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: BOOL', + extensible: false, + }, + { + name: 'USINT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: SINT', + extensible: false, + }, + { + name: 'USINT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: INT', + extensible: false, + }, + { + name: 'USINT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: DINT', + extensible: false, + }, + { + name: 'USINT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: LINT', + extensible: false, + }, + { + name: 'USINT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: UINT', + extensible: false, + }, + { + name: 'USINT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: UDINT', + extensible: false, + }, + { + name: 'USINT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: ULINT', + extensible: false, + }, + { + name: 'USINT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: REAL', + extensible: false, + }, + { + name: 'USINT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: LREAL', + extensible: false, + }, + { + name: 'USINT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: TIME', + extensible: false, + }, + { + name: 'USINT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: DATE', + extensible: false, + }, + { + name: 'USINT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: TOD', + extensible: false, + }, + { + name: 'USINT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: DT', + extensible: false, + }, + { + name: 'USINT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: STRING', + extensible: false, + }, + { + name: 'USINT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'USINT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: WORD', + extensible: false, + }, + { + name: 'USINT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'USINT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: USINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'UINT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: BOOL', + extensible: false, + }, + { + name: 'UINT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: SINT', + extensible: false, + }, + { + name: 'UINT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: INT', + extensible: false, + }, + { + name: 'UINT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: DINT', + extensible: false, + }, + { + name: 'UINT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: LINT', + extensible: false, + }, + { + name: 'UINT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: USINT', + extensible: false, + }, + { + name: 'UINT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: UDINT', + extensible: false, + }, + { + name: 'UINT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: ULINT', + extensible: false, + }, + { + name: 'UINT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: REAL', + extensible: false, + }, + { + name: 'UINT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: LREAL', + extensible: false, + }, + { + name: 'UINT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: TIME', + extensible: false, + }, + { + name: 'UINT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: DATE', + extensible: false, + }, + { + name: 'UINT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: TOD', + extensible: false, + }, + { + name: 'UINT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: DT', + extensible: false, + }, + { + name: 'UINT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: STRING', + extensible: false, + }, + { + name: 'UINT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'UINT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: WORD', + extensible: false, + }, + { + name: 'UINT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'UINT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'UDINT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: BOOL', + extensible: false, + }, + { + name: 'UDINT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: SINT', + extensible: false, + }, + { + name: 'UDINT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: INT', + extensible: false, + }, + { + name: 'UDINT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: DINT', + extensible: false, + }, + { + name: 'UDINT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: LINT', + extensible: false, + }, + { + name: 'UDINT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: USINT', + extensible: false, + }, + { + name: 'UDINT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: UINT', + extensible: false, + }, + { + name: 'UDINT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: ULINT', + extensible: false, + }, + { + name: 'UDINT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: REAL', + extensible: false, + }, + { + name: 'UDINT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: LREAL', + extensible: false, + }, + { + name: 'UDINT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: TIME', + extensible: false, + }, + { + name: 'UDINT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: DATE', + extensible: false, + }, + { + name: 'UDINT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: TOD', + extensible: false, + }, + { + name: 'UDINT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: DT', + extensible: false, + }, + { + name: 'UDINT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: STRING', + extensible: false, + }, + { + name: 'UDINT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'UDINT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: WORD', + extensible: false, + }, + { + name: 'UDINT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'UDINT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: UDINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'ULINT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: BOOL', + extensible: false, + }, + { + name: 'ULINT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: SINT', + extensible: false, + }, + { + name: 'ULINT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: INT', + extensible: false, + }, + { + name: 'ULINT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: DINT', + extensible: false, + }, + { + name: 'ULINT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: LINT', + extensible: false, + }, + { + name: 'ULINT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: USINT', + extensible: false, + }, + { + name: 'ULINT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: UINT', + extensible: false, + }, + { + name: 'ULINT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: UDINT', + extensible: false, + }, + { + name: 'ULINT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: REAL', + extensible: false, + }, + { + name: 'ULINT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: LREAL', + extensible: false, + }, + { + name: 'ULINT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: TIME', + extensible: false, + }, + { + name: 'ULINT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: DATE', + extensible: false, + }, + { + name: 'ULINT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: TOD', + extensible: false, + }, + { + name: 'ULINT_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: DT', + extensible: false, + }, + { + name: 'ULINT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: STRING', + extensible: false, + }, + { + name: 'ULINT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'ULINT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: WORD', + extensible: false, + }, + { + name: 'ULINT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'ULINT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: ULINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'REAL_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: BOOL', + extensible: false, + }, + { + name: 'REAL_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: SINT', + extensible: false, + }, + { + name: 'REAL_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: INT', + extensible: false, + }, + { + name: 'REAL_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: DINT', + extensible: false, + }, + { + name: 'REAL_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: LINT', + extensible: false, + }, + { + name: 'REAL_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: USINT', + extensible: false, + }, + { + name: 'REAL_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: UINT', + extensible: false, + }, + { + name: 'REAL_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: UDINT', + extensible: false, + }, + { + name: 'REAL_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: ULINT', + extensible: false, + }, + { + name: 'REAL_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: LREAL', + extensible: false, + }, + { + name: 'REAL_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: TIME', + extensible: false, + }, + { + name: 'REAL_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: DATE', + extensible: false, + }, + { + name: 'REAL_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: TOD', + extensible: false, + }, + { + name: 'REAL_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: DT', + extensible: false, + }, + { + name: 'REAL_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: STRING', + extensible: false, + }, + { + name: 'REAL_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: BYTE', + extensible: false, + }, + { + name: 'REAL_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: WORD', + extensible: false, + }, + { + name: 'REAL_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: DWORD', + extensible: false, + }, + { + name: 'REAL_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: REAL) => OUT: LWORD', + extensible: false, + }, + { + name: 'LREAL_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: BOOL', + extensible: false, + }, + { + name: 'LREAL_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: SINT', + extensible: false, + }, + { + name: 'LREAL_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: INT', + extensible: false, + }, + { + name: 'LREAL_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: DINT', + extensible: false, + }, + { + name: 'LREAL_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: LINT', + extensible: false, + }, + { + name: 'LREAL_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: USINT', + extensible: false, + }, + { + name: 'LREAL_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: UINT', + extensible: false, + }, + { + name: 'LREAL_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: UDINT', + extensible: false, + }, + { + name: 'LREAL_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: ULINT', + extensible: false, + }, + { + name: 'LREAL_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: REAL', + extensible: false, + }, + { + name: 'LREAL_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: TIME', + extensible: false, + }, + { + name: 'LREAL_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: DATE', + extensible: false, + }, + { + name: 'LREAL_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: TOD', + extensible: false, + }, + { + name: 'LREAL_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: DT', + extensible: false, + }, + { + name: 'LREAL_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: STRING', + extensible: false, + }, + { + name: 'LREAL_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: BYTE', + extensible: false, + }, + { + name: 'LREAL_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: WORD', + extensible: false, + }, + { + name: 'LREAL_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: DWORD', + extensible: false, + }, + { + name: 'LREAL_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LREAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LREAL) => OUT: LWORD', + extensible: false, + }, + { + name: 'TIME_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: BOOL', + extensible: false, + }, + { + name: 'TIME_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: SINT', + extensible: false, + }, + { + name: 'TIME_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: INT', + extensible: false, + }, + { + name: 'TIME_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: DINT', + extensible: false, + }, + { + name: 'TIME_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: LINT', + extensible: false, + }, + { + name: 'TIME_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: USINT', + extensible: false, + }, + { + name: 'TIME_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: UINT', + extensible: false, + }, + { + name: 'TIME_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: UDINT', + extensible: false, + }, + { + name: 'TIME_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: ULINT', + extensible: false, + }, + { + name: 'TIME_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: REAL', + extensible: false, + }, + { + name: 'TIME_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: LREAL', + extensible: false, + }, + { + name: 'TIME_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: DATE', + extensible: false, + }, + { + name: 'TIME_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: TOD', + extensible: false, + }, + { + name: 'TIME_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: DT', + extensible: false, + }, + { + name: 'TIME_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: STRING', + extensible: false, + }, + { + name: 'TIME_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: BYTE', + extensible: false, + }, + { + name: 'TIME_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: WORD', + extensible: false, + }, + { + name: 'TIME_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: DWORD', + extensible: false, + }, + { + name: 'TIME_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TIME' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TIME) => OUT: LWORD', + extensible: false, + }, + { + name: 'DATE_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: BOOL', + extensible: false, + }, + { + name: 'DATE_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: SINT', + extensible: false, + }, + { + name: 'DATE_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: INT', + extensible: false, + }, + { + name: 'DATE_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: DINT', + extensible: false, + }, + { + name: 'DATE_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: LINT', + extensible: false, + }, + { + name: 'DATE_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: USINT', + extensible: false, + }, + { + name: 'DATE_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: UINT', + extensible: false, + }, + { + name: 'DATE_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: UDINT', + extensible: false, + }, + { + name: 'DATE_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: ULINT', + extensible: false, + }, + { + name: 'DATE_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: REAL', + extensible: false, + }, + { + name: 'DATE_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: LREAL', + extensible: false, + }, + { + name: 'DATE_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: TIME', + extensible: false, + }, + { + name: 'DATE_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: TOD', + extensible: false, + }, + { + name: 'DATE_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: DT', + extensible: false, + }, + { + name: 'DATE_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: STRING', + extensible: false, + }, + { + name: 'DATE_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: BYTE', + extensible: false, + }, + { + name: 'DATE_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: WORD', + extensible: false, + }, + { + name: 'DATE_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: DWORD', + extensible: false, + }, + { + name: 'DATE_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DATE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DATE) => OUT: LWORD', + extensible: false, + }, + { + name: 'TOD_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: BOOL', + extensible: false, + }, + { + name: 'TOD_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: SINT', + extensible: false, + }, + { + name: 'TOD_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: INT', + extensible: false, + }, + { + name: 'TOD_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: DINT', + extensible: false, + }, + { + name: 'TOD_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: LINT', + extensible: false, + }, + { + name: 'TOD_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: USINT', + extensible: false, + }, + { + name: 'TOD_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: UINT', + extensible: false, + }, + { + name: 'TOD_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: UDINT', + extensible: false, + }, + { + name: 'TOD_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: ULINT', + extensible: false, + }, + { + name: 'TOD_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: REAL', + extensible: false, + }, + { + name: 'TOD_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: LREAL', + extensible: false, + }, + { + name: 'TOD_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: TIME', + extensible: false, + }, + { + name: 'TOD_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: DATE', + extensible: false, + }, + { + name: 'TOD_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: DT', + extensible: false, + }, + { + name: 'TOD_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: STRING', + extensible: false, + }, + { + name: 'TOD_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: BYTE', + extensible: false, + }, + { + name: 'TOD_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: WORD', + extensible: false, + }, + { + name: 'TOD_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: DWORD', + extensible: false, + }, + { + name: 'TOD_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'TOD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: TOD) => OUT: LWORD', + extensible: false, + }, + { + name: 'DT_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: BOOL', + extensible: false, + }, + { + name: 'DT_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: SINT', + extensible: false, + }, + { + name: 'DT_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: INT', + extensible: false, + }, + { + name: 'DT_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: DINT', + extensible: false, + }, + { + name: 'DT_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: LINT', + extensible: false, + }, + { + name: 'DT_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: USINT', + extensible: false, + }, + { + name: 'DT_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: UINT', + extensible: false, + }, + { + name: 'DT_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: UDINT', + extensible: false, + }, + { + name: 'DT_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: ULINT', + extensible: false, + }, + { + name: 'DT_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: REAL', + extensible: false, + }, + { + name: 'DT_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: LREAL', + extensible: false, + }, + { + name: 'DT_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: TIME', + extensible: false, + }, + { + name: 'DT_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: DATE', + extensible: false, + }, + { + name: 'DT_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: TOD', + extensible: false, + }, + { + name: 'DT_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: STRING', + extensible: false, + }, + { + name: 'DT_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: BYTE', + extensible: false, + }, + { + name: 'DT_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: WORD', + extensible: false, + }, + { + name: 'DT_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: DWORD', + extensible: false, + }, + { + name: 'DT_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DT) => OUT: LWORD', + extensible: false, + }, + { + name: 'STRING_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: BOOL', + extensible: false, + }, + { + name: 'STRING_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: SINT', + extensible: false, + }, + { + name: 'STRING_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: INT', + extensible: false, + }, + { + name: 'STRING_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: DINT', + extensible: false, + }, + { + name: 'STRING_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: LINT', + extensible: false, + }, + { + name: 'STRING_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: USINT', + extensible: false, + }, + { + name: 'STRING_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: UINT', + extensible: false, + }, + { + name: 'STRING_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: UDINT', + extensible: false, + }, + { + name: 'STRING_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: ULINT', + extensible: false, + }, + { + name: 'STRING_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: REAL', + extensible: false, + }, + { + name: 'STRING_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: LREAL', + extensible: false, + }, + { + name: 'STRING_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: TIME', + extensible: false, + }, + { + name: 'STRING_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: DATE', + extensible: false, + }, + { + name: 'STRING_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: TOD', + extensible: false, + }, + { + name: 'STRING_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: DT', + extensible: false, + }, + { + name: 'STRING_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: BYTE', + extensible: false, + }, + { + name: 'STRING_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: WORD', + extensible: false, + }, + { + name: 'STRING_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: DWORD', + extensible: false, + }, + { + name: 'STRING_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'STRING' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: STRING) => OUT: LWORD', + extensible: false, + }, + { + name: 'BYTE_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: BOOL', + extensible: false, + }, + { + name: 'BYTE_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: SINT', + extensible: false, + }, + { + name: 'BYTE_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: INT', + extensible: false, + }, + { + name: 'BYTE_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: DINT', + extensible: false, + }, + { + name: 'BYTE_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: LINT', + extensible: false, + }, + { + name: 'BYTE_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: USINT', + extensible: false, + }, + { + name: 'BYTE_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: UINT', + extensible: false, + }, + { + name: 'BYTE_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: UDINT', + extensible: false, + }, + { + name: 'BYTE_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: ULINT', + extensible: false, + }, + { + name: 'BYTE_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: REAL', + extensible: false, + }, + { + name: 'BYTE_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: LREAL', + extensible: false, + }, + { + name: 'BYTE_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: TIME', + extensible: false, + }, + { + name: 'BYTE_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: DATE', + extensible: false, + }, + { + name: 'BYTE_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: TOD', + extensible: false, + }, + { + name: 'BYTE_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: DT', + extensible: false, + }, + { + name: 'BYTE_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: STRING', + extensible: false, + }, + { + name: 'BYTE_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: WORD', + extensible: false, + }, + { + name: 'BYTE_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: DWORD', + extensible: false, + }, + { + name: 'BYTE_TO_LWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: BYTE) => OUT: LWORD', + extensible: false, + }, + { + name: 'WORD_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: BOOL', + extensible: false, + }, + { + name: 'WORD_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: SINT', + extensible: false, + }, + { + name: 'WORD_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: INT', + extensible: false, + }, + { + name: 'WORD_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: DINT', + extensible: false, + }, + { + name: 'WORD_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: LINT', + extensible: false, + }, + { + name: 'WORD_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: USINT', + extensible: false, + }, + { + name: 'WORD_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: UINT', + extensible: false, + }, + { + name: 'WORD_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: UDINT', + extensible: false, + }, + { + name: 'WORD_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: ULINT', + extensible: false, + }, + { + name: 'WORD_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: REAL', + extensible: false, + }, + { + name: 'WORD_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: LREAL', + extensible: false, + }, + { + name: 'WORD_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: TIME', + extensible: false, + }, + { + name: 'WORD_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: DATE', + extensible: false, + }, + { + name: 'WORD_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: TOD', + extensible: false, + }, + { + name: 'WORD_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: DT', + extensible: false, + }, + { + name: 'WORD_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: BYTE', + extensible: false, + }, + { + name: 'WORD_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: WORD) => OUT: STRING', + extensible: false, + }, + { + name: 'DWORD_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: BOOL', + extensible: false, + }, + { + name: 'DWORD_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: SINT', + extensible: false, + }, + { + name: 'DWORD_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: INT', + extensible: false, + }, + { + name: 'DWORD_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: DINT', + extensible: false, + }, + { + name: 'DWORD_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: LINT', + extensible: false, + }, + { + name: 'DWORD_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: USINT', + extensible: false, + }, + { + name: 'DWORD_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: UINT', + extensible: false, + }, + { + name: 'DWORD_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: UDINT', + extensible: false, + }, + { + name: 'DWORD_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: ULINT', + extensible: false, + }, + { + name: 'DWORD_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: REAL', + extensible: false, + }, + { + name: 'DWORD_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: LREAL', + extensible: false, + }, + { + name: 'DWORD_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: TIME', + extensible: false, + }, + { + name: 'DWORD_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: DATE', + extensible: false, + }, + { + name: 'DWORD_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: TOD', + extensible: false, + }, + { + name: 'DWORD_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: DT', + extensible: false, + }, + { + name: 'DWORD_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: BYTE', + extensible: false, + }, + { + name: 'DWORD_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: WORD', + extensible: false, + }, + { + name: 'DWORD_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: DWORD) => OUT: STRING', + extensible: false, + }, + { + name: 'LWORD_TO_BOOL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: BOOL', + extensible: false, + }, + { + name: 'LWORD_TO_SINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'SINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: SINT', + extensible: false, + }, + { + name: 'LWORD_TO_INT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'INT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: INT', + extensible: false, + }, + { + name: 'LWORD_TO_DINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: DINT', + extensible: false, + }, + { + name: 'LWORD_TO_LINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: LINT', + extensible: false, + }, + { + name: 'LWORD_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: USINT', + extensible: false, + }, + { + name: 'LWORD_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: UINT', + extensible: false, + }, + { + name: 'LWORD_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: UDINT', + extensible: false, + }, + { + name: 'LWORD_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: ULINT', + extensible: false, + }, + { + name: 'LWORD_TO_REAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'REAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: REAL', + extensible: false, + }, + { + name: 'LWORD_TO_LREAL', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LREAL' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: LREAL', + extensible: false, + }, + { + name: 'LWORD_TO_TIME', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TIME' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: TIME', + extensible: false, + }, + { + name: 'LWORD_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: DATE', + extensible: false, + }, + { + name: 'LWORD_TO_TOD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: TOD', + extensible: false, + }, + { + name: 'LWORD_TO_DT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DT' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: DT', + extensible: false, + }, + { + name: 'LWORD_TO_BYTE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: BYTE', + extensible: false, + }, + { + name: 'LWORD_TO_WORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: WORD', + extensible: false, + }, + { + name: 'LWORD_TO_DWORD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: DWORD', + extensible: false, + }, + { + name: 'LWORD_TO_STRING', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'STRING' }, + }, + ], + body: 'Data type conversion', + documentation: '(IN: LWORD) => OUT: STRING', + extensible: false, + }, + { + name: 'TRUNC', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'generic-type', value: 'ANY_REAL' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'generic-type', value: 'ANY_INT' }, + }, + ], + body: 'Rounding up/down', + documentation: '(IN: ANY_REAL) => OUT: ANY_INT', + extensible: false, + }, + { + name: 'BCD_TO_USINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'BYTE' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'USINT' }, + }, + ], + body: 'Conversion from BCD', + documentation: '(IN: BYTE) => OUT: USINT', + extensible: false, + }, + { + name: 'BCD_TO_UINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'WORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UINT' }, + }, + ], + body: 'Conversion from BCD', + documentation: '(IN: WORD) => OUT: UINT', + extensible: false, + }, + { + name: 'BCD_TO_UDINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'UDINT' }, + }, + ], + body: 'Conversion from BCD', + documentation: '(IN: DWORD) => OUT: UDINT', + extensible: false, + }, + { + name: 'BCD_TO_ULINT', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'LWORD' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'ULINT' }, + }, + ], + body: 'Conversion from BCD', + documentation: '(IN: LWORD) => OUT: ULINT', + extensible: false, + }, + { + name: 'USINT_TO_BCD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'USINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'BYTE' }, + }, + ], + body: 'Conversion to BCD', + documentation: '(IN: USINT) => OUT: BYTE', + extensible: false, + }, + { + name: 'UINT_TO_BCD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'WORD' }, + }, + ], + body: 'Conversion to BCD', + documentation: '(IN: UINT) => OUT: WORD', + extensible: false, + }, + { + name: 'UDINT_TO_BCD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'UDINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DWORD' }, + }, + ], + body: 'Conversion to BCD', + documentation: '(IN: UDINT) => OUT: DWORD', + extensible: false, + }, + { + name: 'ULINT_TO_BCD', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'ULINT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'LWORD' }, + }, + ], + body: 'Conversion to BCD', + documentation: '(IN: ULINT) => OUT: LWORD', + extensible: false, + }, + { + name: 'DATE_AND_TIME_TO_TIME_OF_DAY', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'TOD' }, + }, + ], + body: 'Conversion to time-of-day', + documentation: '(IN: DT) => OUT: TOD', + extensible: false, + }, + { + name: 'DATE_AND_TIME_TO_DATE', + type: 'function', + language: 'st', + variables: [ + { + name: 'IN', + class: 'input', + type: { definition: 'base-type', value: 'DT' }, + }, + { + name: 'OUT', + class: 'output', + type: { definition: 'base-type', value: 'DATE' }, + }, + ], + body: 'Conversion to date', + documentation: '(IN: DT) => OUT: DATE', + extensible: false, + }, + ], +} + +export { TypeConversion, TypeConversionLibrarySchema } diff --git a/src2/frontend/data/library/index.ts b/src2/frontend/data/library/index.ts new file mode 100644 index 000000000..8646fbbc1 --- /dev/null +++ b/src2/frontend/data/library/index.ts @@ -0,0 +1,9 @@ +export * from './additional-function-blocks' +export * from './arduino-function-blocks' +export * from './communication-blocks' +export * from './function' +export * from './jaguar' +export * from './MQTT' +export * from './P1AM' +export * from './sequent-microsystems-modules' +export * from './standard-function-blocks' diff --git a/src2/frontend/data/library/jaguar.ts b/src2/frontend/data/library/jaguar.ts new file mode 100644 index 000000000..dcebc2a28 --- /dev/null +++ b/src2/frontend/data/library/jaguar.ts @@ -0,0 +1,77 @@ +import { BaseLibraryPouSchema, BaseLibraryVariableSchema } from '../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const JaguarVariablesSchema = BaseLibraryVariableSchema + +const JaguarPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(JaguarVariablesSchema), +}) + +export const JaguarLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(JaguarPouSchema), +}) + +type JaguarLibrary = z.infer + +const Jaguar: JaguarLibrary = { + name: 'Jaguar', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'src/renderer/data/library/jaguar/st', + cPath: 'src/renderer/data/library/jaguar/c', + pous: [ + { + name: 'ADC_CONFIG', + type: 'function-block', + language: 'st', + variables: [ + { name: 'ADC_CH', class: 'input', type: { definition: 'base-type', value: 'INT' }, documentation: 'ADC_CH' }, + { + name: 'ADC_TYPE', + class: 'input', + type: { definition: 'base-type', value: 'INT' }, + documentation: 'ADC_TYPE', + }, + { + name: 'ADC_CH_LOCAL', + class: 'local', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'ADC_CH_LOCAL', + }, + { + name: 'ADC_TYPE_LOCAL', + class: 'local', + type: { definition: 'base-type', value: 'SINT' }, + documentation: 'ADC_TYPE_LOCAL', + }, + { + name: 'SUCCESS', + class: 'output', + type: { definition: 'base-type', value: 'BOOL' }, + documentation: 'SUCCESS', + }, + ], + body: `IF ADC_CH <> ADC_CH_LOCAL OR ADC_TYPE <> ADC_TYPE_LOCAL THEN + ADC_CH_LOCAL := ADC_CH; + ADC_TYPE_LOCAL := ADC_TYPE; + SUCCESS := TRUE; + ELSE + SUCCESS := FALSE; + END_IF;`, + documentation: + 'Configures the analog channel inputs on the Jaguar board. ADC_CH must be between 0 - 3. ADC_TYPE must be between 0 - 3, where 0 = unipolar 10V, 1 = bipolar 10V, 2 = unipolar 5V, and 3 = bipolar 5V. Upon successful configuration of the ADC, SUCCESS is set to TRUE.', + }, + ], +} + +export { Jaguar } diff --git a/src2/frontend/data/library/sequent-microsystems-modules.ts b/src2/frontend/data/library/sequent-microsystems-modules.ts new file mode 100644 index 000000000..b40039b73 --- /dev/null +++ b/src2/frontend/data/library/sequent-microsystems-modules.ts @@ -0,0 +1,346 @@ +import { BaseLibraryPouSchema, BaseLibraryVariableSchema, baseTypeSchema } from '../../../middleware/shared/ports/plc-schemas' +import { z } from 'zod' + +/** Library-level schema (name + version + paths). Not in plc-schemas so defined locally. */ +const BaseLibrarySchema = z.object({ + name: z.string(), + version: z.string(), + author: z.string(), + stPath: z.string(), + cPath: z.string(), +}) + + +const SequentMicrosystemsModulesVariablesSchema = BaseLibraryVariableSchema.extend({ + type: z.discriminatedUnion('definition', [ + z.object({ + definition: z.literal('base-type'), + value: baseTypeSchema, + }), + z.object({ + definition: z.literal('derived-type'), + value: z.string(), + }), + ]), + initialValue: z + .lazy((): z.Schema => SequentMicrosystemsModulesVariablesSchema.pick({ type: true })) + .optional(), +}) + +const SequentMicrosystemsModulesPouSchema = BaseLibraryPouSchema.extend({ + variables: z.array(SequentMicrosystemsModulesVariablesSchema), +}) + +export const SequentMicrosystemsModulesLibrarySchema = BaseLibrarySchema.extend({ + pous: z.array(SequentMicrosystemsModulesPouSchema), +}) + +type SequentMicrosystemsModulesLibrary = z.infer + +const SequentMicrosystemsModules: SequentMicrosystemsModulesLibrary = { + name: 'Sequent Microsystems Modules', + version: '1.0.0', + author: 'Autonomy Logic', + stPath: 'dummypath/wichwillbereplacedlater', + cPath: 'dummypath/wichwillbereplacedlater', + pous: [ + { + name: 'SM_8RELAY', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: '01', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '02', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '03', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '04', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '05', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '06', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '07', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '08', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DUMMY', class: 'output', type: { definition: 'base-type', value: 'SINT' } }, + ], + body: 'DUMMY := STACK;', + documentation: 'Update all outputs from 8-relays card', + }, + { + name: 'SM_16RELAY', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: '01', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '02', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '03', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '04', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '05', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '06', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '07', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '08', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '09', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '010', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '011', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '012', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '013', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '014', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '015', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: '016', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DUMMY', class: 'output', type: { definition: 'base-type', value: 'SINT' } }, + ], + body: 'DUMMY := STACK;', + documentation: 'Update all outputs from 16-relays card', + }, + { + name: 'SM_8DIN', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'I1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I5', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I6', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I7', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I8', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + ], + body: 'I1 := 0;', + documentation: 'Get all inputs from Sequent microsystems 8 HV Inputs modules', + }, + { + name: 'SM_16DIN', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'I1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I5', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I6', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I7', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I8', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I9', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I10', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I11', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I12', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I13', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I14', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I15', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I16', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + ], + body: 'I1 := 0;', + documentation: 'Get all inputs from Sequent microsystems 16 digital inputs modules.', + }, + { + name: 'SM_4REL4IN', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'RELAY1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'AC_OPTO1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'AC_OPTO2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'AC_OPTO3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'AC_OPTO4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'PWM1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'PWM2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'PWM3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'PWM4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'FREQ1', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'FREQ2', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'FREQ3', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'FREQ4', class: 'output', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'BUTTON', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + ], + body: 'OPTO1 := 0;', + documentation: 'Get all inputs from and set all outputs to SM_4REL4IN modules', + }, + { + name: 'SM_INDUSTRIAL', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'LED1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'LED2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'LED3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'LED4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'Q0_10V1', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V2', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V3', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V4', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q4_20MA1', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q4_20MA2', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q4_20MA3', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q4_20MA4', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD1', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD2', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD3', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD4', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OPTO1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'I0_10V1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'I0_10V2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'I0_10V3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'I0_10V4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'I4_20MA1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'I4_20MA2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'I4_20MA3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'I4_20MA4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + ], + body: 'OPTO1 := 0;', + documentation: 'Get all input and set all outputs of a Sequent Microsystems Industrial Automation card.', + }, + { + name: 'SM_RTD', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'TEMP1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'TEMP2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'TEMP3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'TEMP4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'TEMP5', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'TEMP6', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'TEMP7', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'TEMP8', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + ], + body: 'TEMP1 := 0.0;', + documentation: 'Get all temperature inputs from SM_RTD modules as REAL values in deg Celsius', + }, + { + name: 'SM_BAS', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'TRIAC1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'TRIAC2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'TRIAC3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'TRIAC4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'LED1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'LED2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'LED3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'LED4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'IN1_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'IN2_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'IN3_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'IN4_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'IN5_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'IN6_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'IN7_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'IN8_T', class: 'input', type: { definition: 'base-type', value: 'UINT' } }, + { name: 'Q0_10V1', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V2', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V3', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V4', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV5', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV6', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV7', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'UNIV8', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'DRY_C1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DRY_C2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DRY_C3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DRY_C4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DRY_C5', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DRY_C6', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DRY_C7', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DRY_C8', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OWB_T1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + ], + body: 'DRY_C1 := 0;', + documentation: 'Get all input and set all outputs of a Sequent Microsystems Building Automation card.', + }, + { + name: 'SM_HOME', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'RELAY1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY5', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY6', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY7', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'RELAY8', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'Q0_10V1', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V2', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V3', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'Q0_10V4', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD1', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD2', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD3', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'QOD4', class: 'input', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OPTO1', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO2', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO3', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO4', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO5', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO6', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO7', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'OPTO8', class: 'output', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'ADC1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ADC2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ADC3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ADC4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ADC5', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ADC6', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ADC7', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'ADC8', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T1', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T2', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T3', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + { name: 'OWB_T4', class: 'output', type: { definition: 'base-type', value: 'REAL' } }, + ], + body: 'OPTO1 := 0;', + documentation: 'Get all inputs and set all outputs of a Sequent Microsystems Home Automation card.', + }, + { + name: 'SM_8MOSFET', + type: 'function-block', + language: 'st', + variables: [ + { name: 'STACK', class: 'input', type: { definition: 'base-type', value: 'SINT' } }, + { name: 'MOS1', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'MOS2', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'MOS3', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'MOS4', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'MOS5', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'MOS6', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'MOS7', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'MOS8', class: 'input', type: { definition: 'base-type', value: 'BOOL' } }, + { name: 'DUMMY', class: 'local', type: { definition: 'base-type', value: 'SINT' } }, + ], + body: 'DUMMY := STACK;', + documentation: 'Update all outputs from 8-mosfets card', + }, + ], +} + +export { SequentMicrosystemsModules } diff --git a/src2/frontend/store/slices/library/types.ts b/src2/frontend/store/slices/library/types.ts index 16b10528b..072907b7c 100644 --- a/src2/frontend/store/slices/library/types.ts +++ b/src2/frontend/store/slices/library/types.ts @@ -6,9 +6,12 @@ export type LibraryLanguage = 'il' | 'st' | 'ld' | 'sfc' | 'fbd' export interface SystemLibraryVariable { name: string class: 'input' | 'output' | 'local' - type: { definition: 'base-type'; value: string } + type: + | { definition: 'base-type'; value: string } + | { definition: 'derived-type'; value: string } + | { definition: 'generic-type'; value: string } location?: string - initialValue?: string | null + initialValue?: unknown documentation?: string } diff --git a/src2/middleware/adapters/editor/accelerator-adapter.ts b/src2/middleware/adapters/editor/accelerator-adapter.ts index 76420f347..efe548205 100644 --- a/src2/middleware/adapters/editor/accelerator-adapter.ts +++ b/src2/middleware/adapters/editor/accelerator-adapter.ts @@ -170,5 +170,15 @@ export function createEditorAcceleratorAdapter(): AcceleratorPort { active = false } }, + + onQuitApp(callback: () => void): Unsubscribe { + let active = true + window.bridge.quitAppRequest(() => { + if (active) callback() + }) + return () => { + active = false + } + }, } } diff --git a/src2/middleware/adapters/editor/window-adapter.ts b/src2/middleware/adapters/editor/window-adapter.ts index 8ca1cefc4..f81b66269 100644 --- a/src2/middleware/adapters/editor/window-adapter.ts +++ b/src2/middleware/adapters/editor/window-adapter.ts @@ -53,15 +53,29 @@ export function createEditorWindowAdapter(): WindowPort { }, onCloseRequested(callback: () => void): Unsubscribe { - const handler = () => callback() + let active = true + window.bridge.windowIsClosing(() => { + if (active) callback() + }) + return () => { + active = false + } + }, - window.bridge.windowIsClosing(handler) - window.bridge.darwinAppIsClosing(handler) + onDarwinAppQuitting(callback: () => void): Unsubscribe { + let active = true + window.bridge.darwinAppIsClosing(() => { + if (active) callback() + }) + return () => { + active = false + } + }, + enableAutoCloseHandshake(): Unsubscribe { + window.bridge.handleCloseOrHideWindowAccelerator() return () => { - // Electron IPC listeners are removed by channel name. - // The bridge does not expose a per-listener removeListener, - // so we guard with an active flag instead. + window.bridge.removeHandleCloseOrHideWindowAccelerator() } }, @@ -76,7 +90,7 @@ export function createEditorWindowAdapter(): WindowPort { window.bridge.isMaximizedWindow(handler) return () => { - // Same limitation as onCloseRequested — no per-listener unsubscribe. + // Electron IPC does not expose per-listener unsubscribe for this channel. } }, } diff --git a/src2/middleware/shared/ports/accelerator-port.ts b/src2/middleware/shared/ports/accelerator-port.ts index 650e25cfe..695921539 100644 --- a/src2/middleware/shared/ports/accelerator-port.ts +++ b/src2/middleware/shared/ports/accelerator-port.ts @@ -55,4 +55,7 @@ export interface AcceleratorPort { // --- View actions --- onSwitchPerspective(callback: () => void): Unsubscribe onAbout(callback: () => void): Unsubscribe + + // --- App lifecycle --- + onQuitApp(callback: () => void): Unsubscribe } diff --git a/src2/middleware/shared/ports/window-port.ts b/src2/middleware/shared/ports/window-port.ts index c5c47ecc0..183ca0866 100644 --- a/src2/middleware/shared/ports/window-port.ts +++ b/src2/middleware/shared/ports/window-port.ts @@ -61,4 +61,18 @@ export interface WindowPort { * Web: no-op (never fires). */ onMaximizedChanged?(callback: (isMaximized: boolean) => void): Unsubscribe + + /** + * Subscribe to macOS app-level quit events (before-quit → close → will-quit). + * Editor: fires when the macOS app receives a before-quit event. + * Web: no-op (never fires). + */ + onDarwinAppQuitting?(callback: () => void): Unsubscribe + + /** + * Register the auto-response handler for window close requests from the main process. + * Editor: listens for `window-controls:request-close` and responds with `window-controls:close`. + * Web: no-op. + */ + enableAutoCloseHandshake?(): Unsubscribe } From 6146c790e6c6ec559555429abcf1676b9a716804 Mon Sep 17 00:00:00 2001 From: Daniel Coutinho <60111446+dcoutinho1328@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:11:39 -0300 Subject: [PATCH 08/40] fix(step-31): resolve 219 webpack compilation errors across src2 shared surfaces Type definition fixes: extend ModbusRemoteTcpConfig with RTU fields, add CompileProgressEvent.level/firmwarePath, add S7CommBufferMapping.type, rename PLCInstance.taskName/pouName to task/program, make ModbusIOGroup.ioPoints optional, add PLCProject alias, add workspace loading state. Consumer fixes: configuration->configurations, SharedResponse.success->.ok, RTU casing, import.meta.env->getEnv helper, missing type re-exports from graphical-editor atoms, remove orphaned react-flow custom-nodes, null guards, type assertions, stub modules for web-only imports, and import sorting. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../webpack/webpack.config.renderer.dev.ts | 7 +- scripts/check-port-in-use.js | 2 +- src/main/utils/resolve-html-path.ts | 2 +- .../_atoms/buttons/activity-bar/index.tsx | 8 +- .../components/_organisms/explorer/index.tsx | 4 +- src/renderer/screens/workspace-screen.tsx | 4 +- src2/__architecture__/validate.ts | 3 +- src2/frontend/api/queries/orchestrators.ts | 30 ++ src2/frontend/assets/icons/Fallback.tsx | 3 +- .../frontend/assets/icons/interface/Arrow.tsx | 3 +- .../assets/icons/interface/ArrowButton.tsx | 3 +- .../assets/icons/interface/ArrowUp.tsx | 3 +- src2/frontend/assets/icons/interface/Book.tsx | 3 +- .../frontend/assets/icons/interface/Close.tsx | 3 +- .../assets/icons/interface/CodeIcon.tsx | 3 +- .../assets/icons/interface/Comment.tsx | 3 +- .../assets/icons/interface/Config.tsx | 3 +- .../assets/icons/interface/DarkTheme.tsx | 3 +- .../assets/icons/interface/Debugger.tsx | 3 +- .../assets/icons/interface/DebuggerIcon.tsx | 1 + .../assets/icons/interface/DeviceTransfer.tsx | 3 +- .../assets/icons/interface/Download.tsx | 3 +- .../assets/icons/interface/Duplicate.tsx | 3 +- .../assets/icons/interface/DuplicateIcon.tsx | 3 +- .../assets/icons/interface/EditButton.tsx | 3 +- src2/frontend/assets/icons/interface/Exit.tsx | 3 +- .../assets/icons/interface/Folder.tsx | 3 +- .../assets/icons/interface/LightTheme.tsx | 3 +- src2/frontend/assets/icons/interface/Logo.tsx | 3 +- .../assets/icons/interface/Magnifier.tsx | 3 +- .../frontend/assets/icons/interface/Minus.tsx | 3 +- .../assets/icons/interface/MoreOptions.tsx | 3 +- src2/frontend/assets/icons/interface/Next.tsx | 3 +- src2/frontend/assets/icons/interface/Path.tsx | 3 +- .../frontend/assets/icons/interface/Pause.tsx | 3 +- .../assets/icons/interface/Pencil.tsx | 3 +- src2/frontend/assets/icons/interface/Play.tsx | 3 +- src2/frontend/assets/icons/interface/Plus.tsx | 3 +- .../assets/icons/interface/Prohibited.tsx | 3 +- .../assets/icons/interface/Recent.tsx | 3 +- .../assets/icons/interface/Refresh.tsx | 3 +- .../assets/icons/interface/Search.tsx | 3 +- .../assets/icons/interface/StickArrow.tsx | 3 +- src2/frontend/assets/icons/interface/Stop.tsx | 3 +- .../assets/icons/interface/TableIcon.tsx | 3 +- .../frontend/assets/icons/interface/Timer.tsx | 3 +- .../assets/icons/interface/Transfer.tsx | 3 +- .../assets/icons/interface/TrashCan.tsx | 3 +- .../frontend/assets/icons/interface/Video.tsx | 3 +- src2/frontend/assets/icons/interface/View.tsx | 3 +- .../assets/icons/interface/ViewHidden.tsx | 3 +- .../assets/icons/interface/Warning.tsx | 3 +- src2/frontend/assets/icons/interface/Zap.tsx | 3 +- .../assets/icons/interface/ZoomInOut.tsx | 3 +- .../assets/icons/library/CloseFolder.tsx | 3 +- src2/frontend/assets/icons/library/File.tsx | 3 +- .../assets/icons/library/OpenFolder.tsx | 3 +- src2/frontend/assets/icons/oplc.tsx | 1 + src2/frontend/assets/icons/project/Array.tsx | 1 + src2/frontend/assets/icons/project/Block.tsx | 1 + src2/frontend/assets/icons/project/CExt.tsx | 1 + src2/frontend/assets/icons/project/Coil.tsx | 1 + .../frontend/assets/icons/project/Contact.tsx | 1 + src2/frontend/assets/icons/project/Cpp.tsx | 1 + .../assets/icons/project/DataType.tsx | 1 + src2/frontend/assets/icons/project/Device.tsx | 1 + src2/frontend/assets/icons/project/Enum.tsx | 1 + src2/frontend/assets/icons/project/FBD.tsx | 1 + .../assets/icons/project/Function.tsx | 1 + .../assets/icons/project/FunctionBlock.tsx | 1 + .../assets/icons/project/GenericDT.tsx | 1 + src2/frontend/assets/icons/project/IL.tsx | 1 + src2/frontend/assets/icons/project/LD.tsx | 1 + src2/frontend/assets/icons/project/Loop.tsx | 1 + .../assets/icons/project/Orchestrator.tsx | 1 + src2/frontend/assets/icons/project/PLC.tsx | 1 + .../frontend/assets/icons/project/Program.tsx | 1 + src2/frontend/assets/icons/project/Python.tsx | 1 + .../assets/icons/project/RemoteDevice.tsx | 1 + .../assets/icons/project/Resource.tsx | 1 + src2/frontend/assets/icons/project/SFC.tsx | 1 + src2/frontend/assets/icons/project/ST.tsx | 1 + src2/frontend/assets/icons/project/Server.tsx | 1 + .../assets/icons/project/ServersFolder.tsx | 1 + .../assets/icons/project/Structure.tsx | 1 + .../assets/icons/project/fbd/Block.tsx | 3 +- .../assets/icons/project/fbd/Comment.tsx | 3 +- .../assets/icons/project/fbd/Connector.tsx | 3 +- .../assets/icons/project/fbd/Continuation.tsx | 3 +- .../assets/icons/project/fbd/VariableIn.tsx | 3 +- .../icons/project/fbd/VariableInOut.tsx | 3 +- .../assets/icons/project/fbd/VariableOut.tsx | 3 +- src2/frontend/assets/icons/project/index.ts | 3 + .../assets/icons/project/ladder/Block.tsx | 3 +- .../assets/icons/project/ladder/Coil.tsx | 3 +- .../assets/icons/project/ladder/Contact.tsx | 3 +- .../components/_atoms/accordion/index.tsx | 3 +- .../_atoms/buttons/activity-bar/index.tsx | 3 +- .../_atoms/buttons/default/index.tsx | 3 +- .../_atoms/buttons/tables-actions/index.tsx | 3 +- .../_atoms/buttons/window-control/index.tsx | 3 +- .../components/_atoms/checkbox/index.tsx | 1 + .../_atoms/debug-tree-node/index.tsx | 5 +- .../array-dimensions-input/index.tsx | 3 +- .../_atoms/dimensions-modal/index.tsx | 3 +- .../frontend/components/_atoms/file/index.tsx | 3 +- .../_atoms/generic-data-type-table/index.tsx | 3 +- .../generic-button-cell.tsx | 3 +- .../generic-combobox-cell.tsx | 4 +- .../generic-select-cell.tsx | 2 +- .../generic-text-cell.tsx | 4 +- .../generic-textarea-cell.tsx | 1 + .../components/_atoms/generic-table/index.tsx | 4 +- .../graphical-editor/autocomplete/index.tsx | 5 +- .../fbd/autocomplete/index.tsx | 15 +- .../_atoms/graphical-editor/fbd/block.tsx | 4 +- .../graphical-editor/fbd/buildNodes.tsx | 25 +- .../_atoms/graphical-editor/fbd/comment.tsx | 6 +- .../graphical-editor/fbd/connection.tsx | 4 +- .../_atoms/graphical-editor/fbd/handle.tsx | 3 +- .../_atoms/graphical-editor/fbd/index.ts | 2 +- .../graphical-editor/fbd/utils/types.ts | 2 +- .../graphical-editor/fbd/utils/utils.ts | 18 +- .../ladder/autocomplete/index.tsx | 15 +- .../_atoms/graphical-editor/ladder/block.tsx | 28 +- .../graphical-editor/ladder/buildNodes.tsx | 49 +-- .../_atoms/graphical-editor/ladder/coil.tsx | 16 +- .../graphical-editor/ladder/contact.tsx | 12 +- .../_atoms/graphical-editor/ladder/handle.tsx | 3 +- .../graphical-editor/ladder/parallel.tsx | 3 +- .../graphical-editor/ladder/placeholder.tsx | 3 +- .../graphical-editor/ladder/power-rail.tsx | 2 +- .../ladder/utils/constants.tsx | 3 +- .../graphical-editor/ladder/utils/types.ts | 7 +- .../graphical-editor/ladder/utils/utils.ts | 20 +- .../_atoms/graphical-editor/types/block.ts | 3 +- .../_atoms/graphical-editor/utils/index.ts | 5 +- .../_atoms/highlighted-textarea/index.tsx | 5 +- .../components/_atoms/label/index.tsx | 3 +- .../_atoms/react-flow/custom-nodes/coil.tsx | 282 ------------------ .../react-flow/custom-nodes/contact.tsx | 281 ----------------- .../components/_atoms/react-flow/index.tsx | 3 +- .../resolution-warning-message/index.tsx | 3 +- .../components/_atoms/select/index.tsx | 3 +- src2/frontend/components/_atoms/tab/index.tsx | 9 +- .../components/_atoms/table-actions/index.tsx | 2 +- .../components/_atoms/table/index.tsx | 3 +- .../components/_atoms/tooltip/index.tsx | 3 +- .../_atoms/type-dropdown-selector/index.tsx | 2 +- .../_features/[app]/debug-manager/index.tsx | 11 +- .../_features/[app]/loading-overlay/index.tsx | 4 +- .../_features/[app]/toast/index.tsx | 5 +- .../_features/[app]/toast/use-toast.tsx | 2 +- .../_features/[start]/menu/index.tsx | 5 +- .../[start]/new-project/interval-model.tsx | 4 +- .../[start]/new-project/project-modal.tsx | 4 +- .../[start]/new-project/steps/first-step.tsx | 6 +- .../[start]/new-project/steps/second-step.tsx | 6 +- .../[start]/new-project/steps/third-step.tsx | 14 +- .../[workspace]/ai-chat/ai-chat-input.tsx | 3 +- .../[workspace]/ai-chat/ai-chat-message.tsx | 5 +- .../[workspace]/ai-chat/ai-chat-panel.tsx | 8 +- .../element-card/data-type-element.tsx | 5 +- .../create-element/element-card/index.tsx | 15 +- .../[workspace]/create-element/index.tsx | 2 +- .../_features/[workspace]/data-type/index.tsx | 9 +- .../[workspace]/editor/data-type/index.tsx | 9 +- .../components/configuration-editor.tsx | 4 +- .../editor/device/configuration/board.tsx | 18 +- .../device/configuration/communication.tsx | 8 +- .../configuration/components/modbus-rtu.tsx | 9 +- .../configuration/components/modbus-tcp.tsx | 12 +- .../components/pin-mapping-table.tsx | 7 +- .../configuration/components/static-host.tsx | 7 +- .../editor/device/configuration/index.tsx | 1 - .../elements/board-configuration/index.tsx | 7 +- .../device/elements/rtu-settings/index.tsx | 15 +- .../device/elements/tcp-settings/index.tsx | 5 +- .../[workspace]/editor/device/index.tsx | 2 +- .../orchestrators/orchestrators-list.tsx | 16 +- .../editor/device/remote-device/index.tsx | 19 +- .../editor/graphical/FBD/index.tsx | 7 +- .../graphical/elements/fbd/block/index.tsx | 36 +-- .../graphical/elements/fbd/block/library.tsx | 5 +- .../graphical/elements/ladder/block/index.tsx | 38 +-- .../elements/ladder/block/library.tsx | 5 +- .../graphical/elements/ladder/coil/index.tsx | 5 +- .../elements/ladder/contact/index.tsx | 5 +- .../editor/graphical/ladder/index.tsx | 18 +- .../ai-inline-completion-provider.ts | 23 +- .../monaco/ai-completion/context-builder.ts | 3 +- .../editor/monaco/ai-consent-modal.tsx | 2 +- .../monaco/completion/datatype.completion.ts | 5 +- .../editor/monaco/completion/fb.completion.ts | 17 +- .../editor/monaco/completion/index.ts | 10 +- .../editor/monaco/configs/languages/st/st.ts | 3 +- .../[workspace]/editor/monaco/index.tsx | 56 +--- .../[workspace]/editor/monaco/theme-utils.ts | 1 + .../editor/search-in-project/index.tsx | 6 +- .../address-mapping-reference.tsx | 5 +- .../editor/server/modbus-server/index.tsx | 44 +-- .../components/address-space-tab.tsx | 8 +- .../components/certificate-modal.tsx | 7 +- .../components/certificates-tab.tsx | 10 +- .../components/security-profile-modal.tsx | 9 +- .../components/security-profiles-tab.tsx | 6 +- .../opcua-server/components/user-modal.tsx | 9 +- .../opcua-server/components/users-tab.tsx | 6 +- .../components/variable-config-modal.tsx | 10 +- .../opcua-server/components/variable-tree.tsx | 2 +- .../hooks/use-project-variables.ts | 5 +- .../editor/server/opcua-server/index.tsx | 10 +- .../editor/server/s7comm-server/index.tsx | 29 +- .../[workspace]/search/display/tree-view.tsx | 19 +- .../_features/[workspace]/search/index.tsx | 5 +- .../_molecules/breadcrumbs/index.tsx | 13 +- .../data-types/array/header/base-type.tsx | 2 +- .../_molecules/data-types/array/index.tsx | 8 +- .../data-types/array/table/editable-cell.tsx | 2 +- .../data-types/array/table/index.tsx | 6 +- .../data-types/enumerated/index.tsx | 6 +- .../enumerated/table/editable-cell.tsx | 2 +- .../data-types/enumerated/table/index.tsx | 6 +- .../_molecules/data-types/structure/index.tsx | 6 +- .../structure/table/editable-cell.tsx | 17 +- .../structure/table/elements/array-modal.tsx | 17 +- .../data-types/structure/table/index.tsx | 6 +- .../structure/table/selectable-cell.tsx | 6 +- .../components/_molecules/file/file-root.tsx | 3 +- .../global-variables-table/editable-cell.tsx | 16 +- .../elements/array-modal.tsx | 22 +- .../global-variables-table/index.tsx | 2 +- .../selectable-cell.tsx | 16 +- .../fbd/fbd-utils/useCopyPaste.ts | 9 +- .../_molecules/graphical-editor/fbd/index.tsx | 32 +- .../graphical-editor/ladder/rung/body.tsx | 22 +- .../graphical-editor/ladder/rung/header.tsx | 2 +- .../ladder/rung/ladder-utils/edges.ts | 4 +- .../ladder-utils/elements/diagram/index.ts | 10 +- .../elements/drag-n-drop/index.ts | 4 +- .../rung/ladder-utils/elements/index.ts | 4 +- .../ladder-utils/elements/parallel/index.ts | 8 +- .../elements/placeholder/index.ts | 4 +- .../ladder-utils/elements/serial/index.ts | 6 +- .../rung/ladder-utils/elements/utils/index.ts | 6 +- .../elements/variable-block/index.ts | 6 +- .../ladder/rung/ladder-utils/nodes.ts | 5 +- .../_molecules/input-field/index.tsx | 1 - .../instances-table/editable-cell.tsx | 8 +- .../_molecules/instances-table/index.tsx | 6 +- .../instances-table/selectable-cell.tsx | 6 +- .../_molecules/library-tree/index.tsx | 3 +- .../components/_molecules/menu-bar/index.tsx | 1 - .../components/_molecules/modal/index.tsx | 5 +- .../pin-mapping-table/combobox-input.tsx | 2 +- .../pin-mapping-table/select-input.tsx | 2 +- .../pin-mapping-table/text-input.tsx | 2 +- .../_molecules/project-tree/index.tsx | 34 +-- .../_molecules/rename-impact-modal/index.tsx | 1 - .../components/_molecules/search/index.tsx | 2 +- .../_molecules/select-field/index.tsx | 1 - .../_molecules/task-table/editable-cell.tsx | 8 +- .../_molecules/task-table/index.tsx | 2 +- .../_molecules/task-table/selectable-cell.tsx | 6 +- .../_molecules/type-change-modal/index.tsx | 3 +- .../_molecules/variables-panel/index.tsx | 2 +- .../variables-table/editable-cell.tsx | 12 +- .../variables-table/elements/array-modal.tsx | 11 +- .../_molecules/variables-table/index.tsx | 2 +- .../variables-table/selectable-cell.tsx | 24 +- .../_molecules/window-controls/index.tsx | 4 +- .../workspace-activity-bar/default/chat.tsx | 1 - .../default/debugger.tsx | 3 +- .../default/download.tsx | 6 +- .../workspace-activity-bar/default/exit.tsx | 3 +- .../workspace-activity-bar/default/play.tsx | 3 +- .../workspace-activity-bar/default/search.tsx | 1 - .../workspace-activity-bar/default/zoom.tsx | 2 +- .../workspace-activity-bar/fbd/block.tsx | 3 +- .../workspace-activity-bar/fbd/comment.tsx | 3 +- .../workspace-activity-bar/fbd/connector.tsx | 3 +- .../fbd/continuation.tsx | 3 +- .../fbd/inout-variable.tsx | 3 +- .../fbd/input-variable.tsx | 3 +- .../fbd/out-variable.tsx | 3 +- .../workspace-activity-bar/ladder/block.tsx | 3 +- .../workspace-activity-bar/ladder/coil.tsx | 3 +- .../workspace-activity-bar/ladder/contact.tsx | 3 +- .../ladder/parallel.tsx | 3 +- .../_organisms/about-modal/index.tsx | 6 +- .../components/_organisms/console/filters.tsx | 13 +- .../components/_organisms/console/index.tsx | 8 +- .../components/_organisms/console/log.tsx | 3 +- .../components/_organisms/debugger/index.tsx | 6 +- .../display-recent-projects/index.tsx | 7 +- .../components/_organisms/explorer/index.tsx | 2 +- .../_organisms/explorer/library.tsx | 4 +- .../_organisms/explorer/project.tsx | 11 +- .../global-variables-editor/index.tsx | 20 +- .../graphical-editor/ladder/rung/index.tsx | 7 +- .../_organisms/instances-editor/index.tsx | 15 +- .../modals/confirm-device-switch-modal.tsx | 1 - .../modals/debugger-ip-input-modal.tsx | 2 +- .../modals/debugger-message-modal.tsx | 1 - .../modals/delete-confirmation-modal.tsx | 11 +- .../components/_organisms/modals/index.ts | 2 + .../modals/quit-application-modal.tsx | 6 +- .../modals/runtime-connection-lost-modal.tsx | 1 - .../modals/runtime-create-user-modal.tsx | 4 +- .../_organisms/modals/runtime-login-modal.tsx | 4 +- .../modals/save-changes-file-modal.tsx | 4 +- .../_organisms/modals/save-changes-modal.tsx | 6 +- .../modals/server-ip-mismatch-modal.tsx | 4 +- .../components/_organisms/panel/index.tsx | 3 +- .../_organisms/plc-logs/filters.tsx | 7 +- .../components/_organisms/plc-logs/index.tsx | 6 +- .../_organisms/project-filter-bar/index.tsx | 4 +- .../_organisms/task-editor/index.tsx | 11 +- .../title-bar/slots/center-slot.tsx | 2 +- .../_organisms/title-bar/slots/left-slot.tsx | 4 +- .../_organisms/variables-editor/index.tsx | 24 +- .../workspace-activity-bar/default.tsx | 10 +- .../workspace-activity-bar/index.tsx | 1 - .../workspace-activity-bar/ladder-toolbox.tsx | 3 +- .../_templates/[workspace]/main-content.tsx | 5 +- .../_templates/accelerator-handler.tsx | 8 +- .../components/_templates/app-layout.tsx | 16 +- .../components/ui/scroll-area/display.tsx | 3 +- .../components/ui/scroll-area/root.tsx | 3 +- .../components/ui/scroll-area/scrollbar.tsx | 3 +- .../components/ui/scroll-area/viewport.tsx | 3 +- src2/frontend/data/constants/pou-icons.ts | 2 +- src2/frontend/data/library/MQTT.ts | 3 +- src2/frontend/data/library/P1AM.ts | 3 +- .../library/additional-function-blocks.ts | 3 +- .../data/library/arduino-function-blocks.ts | 3 +- .../data/library/communication-blocks.ts | 3 +- .../data/library/function/arithmetic.ts | 3 +- .../data/library/function/bit-shift.ts | 3 +- .../frontend/data/library/function/bitwise.ts | 3 +- .../data/library/function/character-string.ts | 3 +- .../data/library/function/comparison.ts | 3 +- .../data/library/function/numerical.ts | 3 +- .../data/library/function/selection.ts | 3 +- src2/frontend/data/library/function/time.ts | 3 +- .../data/library/function/type-conversion.ts | 3 +- src2/frontend/data/library/jaguar.ts | 3 +- .../library/sequent-microsystems-modules.ts | 3 +- .../data/library/standard-function-blocks.ts | 3 +- src2/frontend/data/sources/POU.tsx | 2 +- src2/frontend/env.d.ts | 7 + .../frontend/hooks/use-debug-composite-key.ts | 3 +- src2/frontend/hooks/use-remove-tab.tsx | 3 +- src2/frontend/hooks/use-runtime-polling.ts | 2 +- src2/frontend/hooks/use-store-selectors.ts | 4 +- src2/frontend/hooks/useDebugPolling.ts | 4 +- src2/frontend/hooks/useDebugSession.ts | 10 +- src2/frontend/hooks/useWebRTCConnection.ts | 2 +- src2/frontend/screens/start-screen.tsx | 2 +- src2/frontend/screens/workspace-screen.tsx | 25 +- src2/frontend/services/ai/api-client.ts | 3 +- .../frontend/services/ai/context-collector.ts | 6 +- src2/frontend/services/ai/index.ts | 4 +- src2/frontend/services/api/axios.ts | 4 +- src2/frontend/services/api/runtime-api.ts | 19 ++ src2/frontend/services/api/webrtc/index.ts | 4 +- .../services/api/webrtc/webrtc-command.ts | 1 - .../api/webrtc/webrtc-connection-manager.ts | 7 +- .../services/api/webrtc/webrtc-signaling.ts | 3 +- src2/frontend/services/debug/debug-bridge.ts | 2 +- src2/frontend/services/debug/index.ts | 4 +- .../services/debug/transports/index.ts | 2 +- .../debug/transports/modbus-rtu-transport.ts | 4 +- .../debug/transports/webrtc-transport.ts | 4 +- .../store/__tests__/device-slice.test.ts | 2 +- .../store/__tests__/history-slice.test.ts | 4 +- .../store/__tests__/project-slice.test.ts | 46 +-- .../store/__tests__/project-utils.test.ts | 1 - .../store/__tests__/shared-slice.test.ts | 2 +- .../store/__tests__/tabs-utils.test.ts | 2 +- src2/frontend/store/index.ts | 31 +- src2/frontend/store/slices/ai/slice.ts | 3 +- src2/frontend/store/slices/clipboard.ts | 3 +- src2/frontend/store/slices/device/slice.ts | 2 +- src2/frontend/store/slices/fbd/utils/index.ts | 4 +- src2/frontend/store/slices/index.ts | 32 +- src2/frontend/store/slices/ladder/slice.ts | 1 - .../store/slices/ladder/utils/index.ts | 6 +- src2/frontend/store/slices/project/index.ts | 2 +- src2/frontend/store/slices/project/slice.ts | 11 +- .../slices/project/validation/type-change.ts | 5 +- .../slices/project/validation/variables.ts | 1 - src2/frontend/store/slices/shared/index.ts | 18 +- src2/frontend/store/slices/shared/slice.ts | 8 +- src2/frontend/store/slices/shared/utils.ts | 2 +- src2/frontend/store/slices/workspace/slice.ts | 17 +- src2/frontend/store/slices/workspace/types.ts | 7 +- .../store/slices/workspace/utils/variables.ts | 1 - .../frontend/utils/PLC/pou-file-extensions.ts | 110 +++++++ src2/frontend/utils/PLC/pou-text-parser.ts | 243 +++++++++++++++ src2/frontend/utils/__tests__/device.test.ts | 4 +- .../utils/__tests__/format-timestamp.test.ts | 2 +- src2/frontend/utils/debug-tree-builder.ts | 7 +- src2/frontend/utils/debug-tree-traversal.ts | 3 +- .../utils/generate-iec-string-to-variables.ts | 2 +- src2/frontend/utils/get-env.ts | 27 ++ .../__tests__/drag-detection.test.ts | 6 +- ...unction-block-variables-to-cleanup.test.ts | 2 +- .../sync-nodes-with-variables.test.ts | 2 +- ...get-function-block-variables-to-cleanup.ts | 3 +- .../graphical/sync-nodes-with-variables.ts | 5 +- .../modbus/generate-modbus-master-config.ts | 2 +- .../modbus/generate-modbus-slave-config.ts | 16 +- src2/frontend/utils/pou-helpers.ts | 2 +- src2/frontend/utils/variable-references.ts | 7 +- src2/middleware/shared/ports/index.ts | 116 +++---- src2/middleware/shared/ports/types.ts | 18 +- src2/middleware/shared/providers/index.ts | 12 +- .../shared/providers/platform-context.tsx | 2 +- src2/middleware/shared/providers/types.ts | 12 +- 420 files changed, 1815 insertions(+), 1770 deletions(-) create mode 100644 src2/frontend/api/queries/orchestrators.ts create mode 100644 src2/frontend/assets/icons/project/index.ts delete mode 100644 src2/frontend/components/_atoms/react-flow/custom-nodes/coil.tsx delete mode 100644 src2/frontend/components/_atoms/react-flow/custom-nodes/contact.tsx create mode 100644 src2/frontend/components/_organisms/modals/index.ts create mode 100644 src2/frontend/env.d.ts create mode 100644 src2/frontend/services/api/runtime-api.ts create mode 100644 src2/frontend/utils/PLC/pou-file-extensions.ts create mode 100644 src2/frontend/utils/PLC/pou-text-parser.ts create mode 100644 src2/frontend/utils/get-env.ts diff --git a/configs/webpack/webpack.config.renderer.dev.ts b/configs/webpack/webpack.config.renderer.dev.ts index f3f5da75f..28649a5d3 100644 --- a/configs/webpack/webpack.config.renderer.dev.ts +++ b/configs/webpack/webpack.config.renderer.dev.ts @@ -24,7 +24,7 @@ if (process.env.NODE_ENV === 'production') { checkNodeEnv('development') } -const port = process.env.PORT || 1313 +const port = process.env.PORT || 1212 const manifest = resolve(webpackPaths.dllPath, 'renderer.json') const skipDLLs = module.parent?.filename.includes('webpack.config.renderer.dev.dll') || @@ -133,6 +133,11 @@ const configuration: ICustomConfiguration = { ], }, + { + test: /\.ts?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, ], }, diff --git a/scripts/check-port-in-use.js b/scripts/check-port-in-use.js index d418ba3ab..398cbc179 100644 --- a/scripts/check-port-in-use.js +++ b/scripts/check-port-in-use.js @@ -1,7 +1,7 @@ import chalk from 'chalk'; import detectPort from 'detect-port'; -const port = process.env.PORT || '1313'; +const port = process.env.PORT || '1212'; detectPort(port, (_err, availablePort) => { if (port !== String(availablePort)) { diff --git a/src/main/utils/resolve-html-path.ts b/src/main/utils/resolve-html-path.ts index b4716c9ff..7f26b02e2 100644 --- a/src/main/utils/resolve-html-path.ts +++ b/src/main/utils/resolve-html-path.ts @@ -3,7 +3,7 @@ import { URL } from 'url' export function resolveHtmlPath(htmlFileName: string) { if (process.env.NODE_ENV === 'development') { - const port = process.env.PORT || 1313 + const port = process.env.PORT || 1212 const url = new URL(`http://localhost:${port}`) url.pathname = htmlFileName return url.href diff --git a/src/renderer/components/_atoms/buttons/activity-bar/index.tsx b/src/renderer/components/_atoms/buttons/activity-bar/index.tsx index 95c74e2f4..2e68d934a 100644 --- a/src/renderer/components/_atoms/buttons/activity-bar/index.tsx +++ b/src/renderer/components/_atoms/buttons/activity-bar/index.tsx @@ -1,17 +1,16 @@ import { cn } from '@root/utils' -import { ComponentPropsWithoutRef, forwardRef } from 'react' +import { ComponentPropsWithoutRef } from 'react' type IActivityBarButtonProps = ComponentPropsWithoutRef<'button'> & { 'data-active'?: string } -const ActivityBarButton = forwardRef((props, ref) => { +const ActivityBarButton = (props: IActivityBarButtonProps) => { const { children, className, 'data-active': dataActive, ...res } = props const isActive = dataActive === 'true' return ( ) -}) -ActivityBarButton.displayName = 'ActivityBarButton' +} export { ActivityBarButton } diff --git a/src/renderer/components/_organisms/explorer/index.tsx b/src/renderer/components/_organisms/explorer/index.tsx index 454a1a11a..2a228b968 100644 --- a/src/renderer/components/_organisms/explorer/index.tsx +++ b/src/renderer/components/_organisms/explorer/index.tsx @@ -69,14 +69,14 @@ const Explorer = ({ collapse }: ExplorerProps): ReactElement => { className="flex h-full w-[200px] max-w-lg flex-col overflow-auto rounded-lg border-2 border-inherit border-neutral-200 bg-white data-[panel-size='0.0']:hidden dark:border-neutral-850 dark:bg-neutral-950" > - + - + { ref={workspacePanelRef} id='workspacePanel' order={2} - defaultSize={84} + defaultSize={87} className='relative flex h-full w-[400px]' > { id='editorPanel' order={1} minSize={45} - defaultSize={69} + defaultSize={75} className={cn( 'relative flex flex-1 grow flex-col overflow-hidden rounded-lg border-2 border-neutral-200 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-950', { diff --git a/src2/__architecture__/validate.ts b/src2/__architecture__/validate.ts index f31f8c93e..65f1e5b72 100644 --- a/src2/__architecture__/validate.ts +++ b/src2/__architecture__/validate.ts @@ -86,6 +86,7 @@ const LAYER_RULES: Record = { // Helpers // --------------------------------------------------------------------------- +// @ts-expect-error TS1343 — this script runs via `npx tsx` (ESM), not through webpack const SRC2_ROOT = resolve(dirname(new URL(import.meta.url).pathname), '..') function collectFiles(dir: string, ext: string[]): string[] { @@ -237,7 +238,7 @@ function validate(): Violation[] { const toLayer = getLayer(resolved) if (!toLayer) continue // Can't determine target layer — skip - const rule = LAYER_RULES[fromLayer] + const rule: LayerRule = LAYER_RULES[fromLayer] if (!rule.allowedDeps.includes(toLayer) && fromLayer !== toLayer && !exceptions.includes(toLayer)) { violations.push({ file: relFile, diff --git a/src2/frontend/api/queries/orchestrators.ts b/src2/frontend/api/queries/orchestrators.ts new file mode 100644 index 000000000..fa590fe6a --- /dev/null +++ b/src2/frontend/api/queries/orchestrators.ts @@ -0,0 +1,30 @@ +/** + * Orchestrator API types and request stubs. + * These will be implemented when the Edge API integration is built. + */ + +export interface DeviceResponse { + id: string + name: string + status: string + container_status?: string + is_running?: boolean +} + +export interface OrchestratorResponse { + id: string + name: string + orchestrator_id: string + description: string | null + devices: DeviceResponse[] +} + +export interface ListOrchestratorsResponse { + data: { + orchestrators: OrchestratorResponse[] + } +} + +export function listOrchestratorsRequest(): Promise { + throw new Error('Orchestrators API not yet implemented') +} diff --git a/src2/frontend/assets/icons/Fallback.tsx b/src2/frontend/assets/icons/Fallback.tsx index 5658a7587..1bbdadee2 100644 --- a/src2/frontend/assets/icons/Fallback.tsx +++ b/src2/frontend/assets/icons/Fallback.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../utils/cn' + type IFallBackIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Arrow.tsx b/src2/frontend/assets/icons/interface/Arrow.tsx index a2f956ea2..ef3a524a0 100644 --- a/src2/frontend/assets/icons/interface/Arrow.tsx +++ b/src2/frontend/assets/icons/interface/Arrow.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithRef, ElementType } from 'react' +import { cn } from '../../../utils/cn' + type IArrowIconProps = ComponentPropsWithRef<'svg'> & { variant?: 'default' | 'primary' | 'secondary' direction?: 'up' | 'down' | 'left' | 'right' diff --git a/src2/frontend/assets/icons/interface/ArrowButton.tsx b/src2/frontend/assets/icons/interface/ArrowButton.tsx index f83e09720..c5684b94b 100644 --- a/src2/frontend/assets/icons/interface/ArrowButton.tsx +++ b/src2/frontend/assets/icons/interface/ArrowButton.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithRef, ElementType } from 'react' +import { cn } from '../../../utils/cn' + type IArrowButtonIconProps = ComponentPropsWithRef<'svg'> & { variant?: 'default' | 'primary' | 'secondary' direction?: 'up' | 'down' | 'left' | 'right' diff --git a/src2/frontend/assets/icons/interface/ArrowUp.tsx b/src2/frontend/assets/icons/interface/ArrowUp.tsx index e2b94f931..c5c4f1f17 100644 --- a/src2/frontend/assets/icons/interface/ArrowUp.tsx +++ b/src2/frontend/assets/icons/interface/ArrowUp.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IArrowUpIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Book.tsx b/src2/frontend/assets/icons/interface/Book.tsx index 83c99f2c4..f425300a8 100644 --- a/src2/frontend/assets/icons/interface/Book.tsx +++ b/src2/frontend/assets/icons/interface/Book.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IBookIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Close.tsx b/src2/frontend/assets/icons/interface/Close.tsx index c2af6b036..f1af66ded 100644 --- a/src2/frontend/assets/icons/interface/Close.tsx +++ b/src2/frontend/assets/icons/interface/Close.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + const CloseIcon = (props: ComponentPropsWithoutRef<'svg'>) => { const { className, ...rest } = props return ( diff --git a/src2/frontend/assets/icons/interface/CodeIcon.tsx b/src2/frontend/assets/icons/interface/CodeIcon.tsx index 2e1f20ebe..dcab97d35 100644 --- a/src2/frontend/assets/icons/interface/CodeIcon.tsx +++ b/src2/frontend/assets/icons/interface/CodeIcon.tsx @@ -1,6 +1,7 @@ +import { ComponentProps } from 'react' + import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' -import { ComponentProps } from 'react' type IProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' diff --git a/src2/frontend/assets/icons/interface/Comment.tsx b/src2/frontend/assets/icons/interface/Comment.tsx index 152ac3e88..19990fd1c 100644 --- a/src2/frontend/assets/icons/interface/Comment.tsx +++ b/src2/frontend/assets/icons/interface/Comment.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + const CommentIcon = (props: ComponentPropsWithoutRef<'svg'>) => { const { className, ...rest } = props return ( diff --git a/src2/frontend/assets/icons/interface/Config.tsx b/src2/frontend/assets/icons/interface/Config.tsx index 9b62bff75..c1e0af373 100644 --- a/src2/frontend/assets/icons/interface/Config.tsx +++ b/src2/frontend/assets/icons/interface/Config.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IConfigIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/DarkTheme.tsx b/src2/frontend/assets/icons/interface/DarkTheme.tsx index 49f6ac4ce..b908f9318 100644 --- a/src2/frontend/assets/icons/interface/DarkTheme.tsx +++ b/src2/frontend/assets/icons/interface/DarkTheme.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export const DarkThemeIcon = (props: IIconProps) => { const { className, size = 'md', ...res } = props diff --git a/src2/frontend/assets/icons/interface/Debugger.tsx b/src2/frontend/assets/icons/interface/Debugger.tsx index 4183920ff..4050c9e71 100644 --- a/src2/frontend/assets/icons/interface/Debugger.tsx +++ b/src2/frontend/assets/icons/interface/Debugger.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type IDebuggerIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' variant?: 'default' | 'muted' diff --git a/src2/frontend/assets/icons/interface/DebuggerIcon.tsx b/src2/frontend/assets/icons/interface/DebuggerIcon.tsx index ba59b5c21..d6dae96d5 100644 --- a/src2/frontend/assets/icons/interface/DebuggerIcon.tsx +++ b/src2/frontend/assets/icons/interface/DebuggerIcon.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type IDebuggerIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/interface/DeviceTransfer.tsx b/src2/frontend/assets/icons/interface/DeviceTransfer.tsx index 2963f80fd..6ee813432 100644 --- a/src2/frontend/assets/icons/interface/DeviceTransfer.tsx +++ b/src2/frontend/assets/icons/interface/DeviceTransfer.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IDeviceTransferIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Download.tsx b/src2/frontend/assets/icons/interface/Download.tsx index f8070266b..0d1f8c7d8 100644 --- a/src2/frontend/assets/icons/interface/Download.tsx +++ b/src2/frontend/assets/icons/interface/Download.tsx @@ -1,6 +1,5 @@ -import { cn } from '../../../utils/cn' - import { IconStyles } from '../../../data/constants/icon-styles' +import { cn } from '../../../utils/cn' import { IIconProps } from '../Types/iconTypes' export const DownloadIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/Duplicate.tsx b/src2/frontend/assets/icons/interface/Duplicate.tsx index 6c04675af..4bd739cfd 100644 --- a/src2/frontend/assets/icons/interface/Duplicate.tsx +++ b/src2/frontend/assets/icons/interface/Duplicate.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export const DuplicateIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/DuplicateIcon.tsx b/src2/frontend/assets/icons/interface/DuplicateIcon.tsx index e689d1cd3..787abb994 100644 --- a/src2/frontend/assets/icons/interface/DuplicateIcon.tsx +++ b/src2/frontend/assets/icons/interface/DuplicateIcon.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type IDuplicateIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/EditButton.tsx b/src2/frontend/assets/icons/interface/EditButton.tsx index e13510c1a..692931755 100644 --- a/src2/frontend/assets/icons/interface/EditButton.tsx +++ b/src2/frontend/assets/icons/interface/EditButton.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithRef, forwardRef } from 'react' +import { cn } from '../../../utils/cn' + type IEditButtonIconProps = ComponentPropsWithRef<'svg'> & { variant?: 'default' | 'primary' | 'secondary' direction?: 'up' | 'down' | 'left' | 'right' diff --git a/src2/frontend/assets/icons/interface/Exit.tsx b/src2/frontend/assets/icons/interface/Exit.tsx index 2a63d18a9..1c2d5de05 100644 --- a/src2/frontend/assets/icons/interface/Exit.tsx +++ b/src2/frontend/assets/icons/interface/Exit.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export const ExitIcon = (props: IIconProps) => { const { className, size = 'sm', ...res } = props diff --git a/src2/frontend/assets/icons/interface/Folder.tsx b/src2/frontend/assets/icons/interface/Folder.tsx index 97ba694cf..a6d2ff97b 100644 --- a/src2/frontend/assets/icons/interface/Folder.tsx +++ b/src2/frontend/assets/icons/interface/Folder.tsx @@ -1,5 +1,6 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' + +import { cn } from '../../../utils/cn' type IFolderIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/LightTheme.tsx b/src2/frontend/assets/icons/interface/LightTheme.tsx index 5ec05cd49..63b550150 100644 --- a/src2/frontend/assets/icons/interface/LightTheme.tsx +++ b/src2/frontend/assets/icons/interface/LightTheme.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export const LightThemeIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/Logo.tsx b/src2/frontend/assets/icons/interface/Logo.tsx index b3f92de1a..72dcb9643 100644 --- a/src2/frontend/assets/icons/interface/Logo.tsx +++ b/src2/frontend/assets/icons/interface/Logo.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type LogoIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Magnifier.tsx b/src2/frontend/assets/icons/interface/Magnifier.tsx index 5272fc511..b09b40ab7 100644 --- a/src2/frontend/assets/icons/interface/Magnifier.tsx +++ b/src2/frontend/assets/icons/interface/Magnifier.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IMagnifierIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Minus.tsx b/src2/frontend/assets/icons/interface/Minus.tsx index f2986d49d..f533acc4c 100644 --- a/src2/frontend/assets/icons/interface/Minus.tsx +++ b/src2/frontend/assets/icons/interface/Minus.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type IMinusIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/MoreOptions.tsx b/src2/frontend/assets/icons/interface/MoreOptions.tsx index 3393f6688..64f3ae98e 100644 --- a/src2/frontend/assets/icons/interface/MoreOptions.tsx +++ b/src2/frontend/assets/icons/interface/MoreOptions.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + const MoreOptionsIcon = ({ className, ...rest }: ComponentPropsWithoutRef<'svg'>) => { return ( & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Pause.tsx b/src2/frontend/assets/icons/interface/Pause.tsx index e3aa9e365..bf1c1f66b 100644 --- a/src2/frontend/assets/icons/interface/Pause.tsx +++ b/src2/frontend/assets/icons/interface/Pause.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export const PauseIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/Pencil.tsx b/src2/frontend/assets/icons/interface/Pencil.tsx index ace6722d9..4338eecdc 100644 --- a/src2/frontend/assets/icons/interface/Pencil.tsx +++ b/src2/frontend/assets/icons/interface/Pencil.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type IPencilIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Play.tsx b/src2/frontend/assets/icons/interface/Play.tsx index 4c8c7cf15..c903b8447 100644 --- a/src2/frontend/assets/icons/interface/Play.tsx +++ b/src2/frontend/assets/icons/interface/Play.tsx @@ -1,6 +1,5 @@ -import { cn } from '../../../utils/cn' - import { IconStyles } from '../../../data/constants/icon-styles' +import { cn } from '../../../utils/cn' import { IIconProps } from '../Types/iconTypes' export const PlayIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/Plus.tsx b/src2/frontend/assets/icons/interface/Plus.tsx index 43181bb0d..818909851 100644 --- a/src2/frontend/assets/icons/interface/Plus.tsx +++ b/src2/frontend/assets/icons/interface/Plus.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type IPlusIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Prohibited.tsx b/src2/frontend/assets/icons/interface/Prohibited.tsx index 8037b68b8..4aacc5ea0 100644 --- a/src2/frontend/assets/icons/interface/Prohibited.tsx +++ b/src2/frontend/assets/icons/interface/Prohibited.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type ProhibitedProps = ComponentPropsWithoutRef<'svg'> export const ProhibitedIcon = (props: ProhibitedProps) => { diff --git a/src2/frontend/assets/icons/interface/Recent.tsx b/src2/frontend/assets/icons/interface/Recent.tsx index 410f545bb..b38511971 100644 --- a/src2/frontend/assets/icons/interface/Recent.tsx +++ b/src2/frontend/assets/icons/interface/Recent.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IRecentIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Refresh.tsx b/src2/frontend/assets/icons/interface/Refresh.tsx index 5f33423e3..6b9a2ebc6 100644 --- a/src2/frontend/assets/icons/interface/Refresh.tsx +++ b/src2/frontend/assets/icons/interface/Refresh.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type RefreshIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Search.tsx b/src2/frontend/assets/icons/interface/Search.tsx index 6cfbbf211..9b2cf403f 100644 --- a/src2/frontend/assets/icons/interface/Search.tsx +++ b/src2/frontend/assets/icons/interface/Search.tsx @@ -1,6 +1,5 @@ -import { cn } from '../../../utils/cn' - import { IconStyles } from '../../../data/constants/icon-styles' +import { cn } from '../../../utils/cn' import { IIconProps } from '../Types/iconTypes' export const SearchIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/StickArrow.tsx b/src2/frontend/assets/icons/interface/StickArrow.tsx index f8a69ab9a..38a86e976 100644 --- a/src2/frontend/assets/icons/interface/StickArrow.tsx +++ b/src2/frontend/assets/icons/interface/StickArrow.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type IStickArrowIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' direction?: 'up' | 'down' | 'left' | 'right' diff --git a/src2/frontend/assets/icons/interface/Stop.tsx b/src2/frontend/assets/icons/interface/Stop.tsx index 540162e38..b17c7db4b 100644 --- a/src2/frontend/assets/icons/interface/Stop.tsx +++ b/src2/frontend/assets/icons/interface/Stop.tsx @@ -1,6 +1,5 @@ -import { cn } from '../../../utils/cn' - import { IconStyles } from '../../../data/constants/icon-styles' +import { cn } from '../../../utils/cn' import { IIconProps } from '../Types/iconTypes' export const StopIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/TableIcon.tsx b/src2/frontend/assets/icons/interface/TableIcon.tsx index e73b1d97c..497fa3c6d 100644 --- a/src2/frontend/assets/icons/interface/TableIcon.tsx +++ b/src2/frontend/assets/icons/interface/TableIcon.tsx @@ -1,7 +1,8 @@ +import { ComponentProps } from 'react' + import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' -import { ComponentProps } from 'react' type IProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' diff --git a/src2/frontend/assets/icons/interface/Timer.tsx b/src2/frontend/assets/icons/interface/Timer.tsx index 5d381a793..ea2d879b8 100644 --- a/src2/frontend/assets/icons/interface/Timer.tsx +++ b/src2/frontend/assets/icons/interface/Timer.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IFBDIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/Transfer.tsx b/src2/frontend/assets/icons/interface/Transfer.tsx index 10784c705..215e9f265 100644 --- a/src2/frontend/assets/icons/interface/Transfer.tsx +++ b/src2/frontend/assets/icons/interface/Transfer.tsx @@ -1,6 +1,5 @@ -import { cn } from '../../../utils/cn' - import { IconStyles } from '../../../data/constants/icon-styles' +import { cn } from '../../../utils/cn' import { IIconProps } from '../Types/iconTypes' export const TransferIcon = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/interface/TrashCan.tsx b/src2/frontend/assets/icons/interface/TrashCan.tsx index 461ded6b1..e07f42c93 100644 --- a/src2/frontend/assets/icons/interface/TrashCan.tsx +++ b/src2/frontend/assets/icons/interface/TrashCan.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type TrashCanProps = ComponentPropsWithoutRef<'svg'> export const TrashCanIcon = (props: TrashCanProps) => { diff --git a/src2/frontend/assets/icons/interface/Video.tsx b/src2/frontend/assets/icons/interface/Video.tsx index af31e2bcf..d77c11a5b 100644 --- a/src2/frontend/assets/icons/interface/Video.tsx +++ b/src2/frontend/assets/icons/interface/Video.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type IVideoIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/interface/View.tsx b/src2/frontend/assets/icons/interface/View.tsx index f2125d77a..b32bcb90e 100644 --- a/src2/frontend/assets/icons/interface/View.tsx +++ b/src2/frontend/assets/icons/interface/View.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export default function ViewIcon(props: IIconProps) { const { stroke, className, size = 'sm', ...res } = props diff --git a/src2/frontend/assets/icons/interface/ViewHidden.tsx b/src2/frontend/assets/icons/interface/ViewHidden.tsx index d2368062c..e0d83208b 100644 --- a/src2/frontend/assets/icons/interface/ViewHidden.tsx +++ b/src2/frontend/assets/icons/interface/ViewHidden.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export default function ViewHiddenIcon(props: IIconProps) { const { stroke, className, size = 'sm', ...res } = props diff --git a/src2/frontend/assets/icons/interface/Warning.tsx b/src2/frontend/assets/icons/interface/Warning.tsx index 5058c303d..fd80a0abf 100644 --- a/src2/frontend/assets/icons/interface/Warning.tsx +++ b/src2/frontend/assets/icons/interface/Warning.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithRef, ElementType } from 'react' +import { cn } from '../../../utils/cn' + type IWarningIconProps = ComponentPropsWithRef<'svg'> & { variant?: 'default' | 'primary' | 'secondary' direction?: 'up' | 'down' | 'left' | 'right' diff --git a/src2/frontend/assets/icons/interface/Zap.tsx b/src2/frontend/assets/icons/interface/Zap.tsx index 73a708241..5e2c06b32 100644 --- a/src2/frontend/assets/icons/interface/Zap.tsx +++ b/src2/frontend/assets/icons/interface/Zap.tsx @@ -1,7 +1,6 @@ +import { IconStyles } from '../../../data/constants/icon-styles' import { cn } from '../../../utils/cn' - import { IIconProps } from '../Types/iconTypes' -import { IconStyles } from '../../../data/constants/icon-styles' export default function ZapIcon(props: IIconProps) { const { className, size = 'sm', ...res } = props diff --git a/src2/frontend/assets/icons/interface/ZoomInOut.tsx b/src2/frontend/assets/icons/interface/ZoomInOut.tsx index a8cd056a3..16dfc1c6e 100644 --- a/src2/frontend/assets/icons/interface/ZoomInOut.tsx +++ b/src2/frontend/assets/icons/interface/ZoomInOut.tsx @@ -1,6 +1,5 @@ -import { cn } from '../../../utils/cn' - import { IconStyles } from '../../../data/constants/icon-styles' +import { cn } from '../../../utils/cn' import { IIconProps } from '../Types/iconTypes' export const ZoomInOut = (props: IIconProps) => { diff --git a/src2/frontend/assets/icons/library/CloseFolder.tsx b/src2/frontend/assets/icons/library/CloseFolder.tsx index 99b9339b1..e7343d789 100644 --- a/src2/frontend/assets/icons/library/CloseFolder.tsx +++ b/src2/frontend/assets/icons/library/CloseFolder.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type ILibraryCloseFolderIconProps = ComponentProps<'svg'> & { size: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/library/File.tsx b/src2/frontend/assets/icons/library/File.tsx index ee402ba99..480e4c7d7 100644 --- a/src2/frontend/assets/icons/library/File.tsx +++ b/src2/frontend/assets/icons/library/File.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type ILibraryFileIconProps = ComponentProps<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/library/OpenFolder.tsx b/src2/frontend/assets/icons/library/OpenFolder.tsx index 10c110177..6f069a945 100644 --- a/src2/frontend/assets/icons/library/OpenFolder.tsx +++ b/src2/frontend/assets/icons/library/OpenFolder.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentProps } from 'react' +import { cn } from '../../../utils/cn' + type ILibraryOpenFolderIconProps = ComponentProps<'svg'> & { size: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/oplc.tsx b/src2/frontend/assets/icons/oplc.tsx index 401930d24..5bfa46592 100644 --- a/src2/frontend/assets/icons/oplc.tsx +++ b/src2/frontend/assets/icons/oplc.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../utils/cn' type IOpenPLCIconProps = ComponentProps<'svg'> diff --git a/src2/frontend/assets/icons/project/Array.tsx b/src2/frontend/assets/icons/project/Array.tsx index 241ae6867..e17f813c3 100644 --- a/src2/frontend/assets/icons/project/Array.tsx +++ b/src2/frontend/assets/icons/project/Array.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type ArrayIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Block.tsx b/src2/frontend/assets/icons/project/Block.tsx index f4f80030c..e5d71ce22 100644 --- a/src2/frontend/assets/icons/project/Block.tsx +++ b/src2/frontend/assets/icons/project/Block.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/CExt.tsx b/src2/frontend/assets/icons/project/CExt.tsx index 90936aa0f..544c9dcd5 100644 --- a/src2/frontend/assets/icons/project/CExt.tsx +++ b/src2/frontend/assets/icons/project/CExt.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type ICExtIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Coil.tsx b/src2/frontend/assets/icons/project/Coil.tsx index 6bfc21db4..0e1efd390 100644 --- a/src2/frontend/assets/icons/project/Coil.tsx +++ b/src2/frontend/assets/icons/project/Coil.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type ICoilIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' diff --git a/src2/frontend/assets/icons/project/Contact.tsx b/src2/frontend/assets/icons/project/Contact.tsx index fd68c4bc0..9246bf969 100644 --- a/src2/frontend/assets/icons/project/Contact.tsx +++ b/src2/frontend/assets/icons/project/Contact.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type IContactIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' diff --git a/src2/frontend/assets/icons/project/Cpp.tsx b/src2/frontend/assets/icons/project/Cpp.tsx index b8ee83982..c8f9fb06e 100644 --- a/src2/frontend/assets/icons/project/Cpp.tsx +++ b/src2/frontend/assets/icons/project/Cpp.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type CppIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/DataType.tsx b/src2/frontend/assets/icons/project/DataType.tsx index 2f0711c7b..02c103428 100644 --- a/src2/frontend/assets/icons/project/DataType.tsx +++ b/src2/frontend/assets/icons/project/DataType.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type IDataTypeIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Device.tsx b/src2/frontend/assets/icons/project/Device.tsx index bd83078b1..9447db6ab 100644 --- a/src2/frontend/assets/icons/project/Device.tsx +++ b/src2/frontend/assets/icons/project/Device.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IDeviceIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Enum.tsx b/src2/frontend/assets/icons/project/Enum.tsx index 0a6e2f379..4afa0b788 100644 --- a/src2/frontend/assets/icons/project/Enum.tsx +++ b/src2/frontend/assets/icons/project/Enum.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type EnumIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/FBD.tsx b/src2/frontend/assets/icons/project/FBD.tsx index 5e51b9743..75bbed2e0 100644 --- a/src2/frontend/assets/icons/project/FBD.tsx +++ b/src2/frontend/assets/icons/project/FBD.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IFBDIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Function.tsx b/src2/frontend/assets/icons/project/Function.tsx index cca5d1e55..932962025 100644 --- a/src2/frontend/assets/icons/project/Function.tsx +++ b/src2/frontend/assets/icons/project/Function.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IFunctionIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/FunctionBlock.tsx b/src2/frontend/assets/icons/project/FunctionBlock.tsx index afb7eb76f..dbddd737f 100644 --- a/src2/frontend/assets/icons/project/FunctionBlock.tsx +++ b/src2/frontend/assets/icons/project/FunctionBlock.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IFunctionBlockIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/GenericDT.tsx b/src2/frontend/assets/icons/project/GenericDT.tsx index ace1dde95..538ee234b 100644 --- a/src2/frontend/assets/icons/project/GenericDT.tsx +++ b/src2/frontend/assets/icons/project/GenericDT.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type IGenericIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/IL.tsx b/src2/frontend/assets/icons/project/IL.tsx index 2c529d956..ea0baa6cf 100644 --- a/src2/frontend/assets/icons/project/IL.tsx +++ b/src2/frontend/assets/icons/project/IL.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IILIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/LD.tsx b/src2/frontend/assets/icons/project/LD.tsx index eb4bf48eb..7d6fea3c1 100644 --- a/src2/frontend/assets/icons/project/LD.tsx +++ b/src2/frontend/assets/icons/project/LD.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type ILDIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Loop.tsx b/src2/frontend/assets/icons/project/Loop.tsx index 2ba98f075..95a94bbd5 100644 --- a/src2/frontend/assets/icons/project/Loop.tsx +++ b/src2/frontend/assets/icons/project/Loop.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type ILoopIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Orchestrator.tsx b/src2/frontend/assets/icons/project/Orchestrator.tsx index f64e9fa22..cd253cd08 100644 --- a/src2/frontend/assets/icons/project/Orchestrator.tsx +++ b/src2/frontend/assets/icons/project/Orchestrator.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IOrchestratorIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/PLC.tsx b/src2/frontend/assets/icons/project/PLC.tsx index e7684f62c..51565cad9 100644 --- a/src2/frontend/assets/icons/project/PLC.tsx +++ b/src2/frontend/assets/icons/project/PLC.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IPLCIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Program.tsx b/src2/frontend/assets/icons/project/Program.tsx index 9228b4db2..a9ed063a2 100644 --- a/src2/frontend/assets/icons/project/Program.tsx +++ b/src2/frontend/assets/icons/project/Program.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IProgramIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Python.tsx b/src2/frontend/assets/icons/project/Python.tsx index dbd2dd984..527baa62a 100644 --- a/src2/frontend/assets/icons/project/Python.tsx +++ b/src2/frontend/assets/icons/project/Python.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type PythonIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/RemoteDevice.tsx b/src2/frontend/assets/icons/project/RemoteDevice.tsx index a312e7b9e..f5089bddf 100644 --- a/src2/frontend/assets/icons/project/RemoteDevice.tsx +++ b/src2/frontend/assets/icons/project/RemoteDevice.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IRemoteDeviceIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Resource.tsx b/src2/frontend/assets/icons/project/Resource.tsx index 976960b40..89faadf55 100644 --- a/src2/frontend/assets/icons/project/Resource.tsx +++ b/src2/frontend/assets/icons/project/Resource.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IResourceIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/SFC.tsx b/src2/frontend/assets/icons/project/SFC.tsx index 125b1ab97..6ad4cb0c2 100644 --- a/src2/frontend/assets/icons/project/SFC.tsx +++ b/src2/frontend/assets/icons/project/SFC.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type ISFCIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/ST.tsx b/src2/frontend/assets/icons/project/ST.tsx index 3d1974de9..637a61b50 100644 --- a/src2/frontend/assets/icons/project/ST.tsx +++ b/src2/frontend/assets/icons/project/ST.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type ISTIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Server.tsx b/src2/frontend/assets/icons/project/Server.tsx index acf06de25..57fed64a2 100644 --- a/src2/frontend/assets/icons/project/Server.tsx +++ b/src2/frontend/assets/icons/project/Server.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IServerIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/ServersFolder.tsx b/src2/frontend/assets/icons/project/ServersFolder.tsx index 852a86a6f..29346693d 100644 --- a/src2/frontend/assets/icons/project/ServersFolder.tsx +++ b/src2/frontend/assets/icons/project/ServersFolder.tsx @@ -1,4 +1,5 @@ import { ComponentProps } from 'react' + import { cn } from '../../../utils/cn' type IServersFolderIconProps = ComponentProps<'svg'> & { diff --git a/src2/frontend/assets/icons/project/Structure.tsx b/src2/frontend/assets/icons/project/Structure.tsx index b551dd862..78a351bf2 100644 --- a/src2/frontend/assets/icons/project/Structure.tsx +++ b/src2/frontend/assets/icons/project/Structure.tsx @@ -1,4 +1,5 @@ import { ComponentPropsWithoutRef } from 'react' + import { cn } from '../../../utils/cn' type StructureIconProps = ComponentPropsWithoutRef<'svg'> & { diff --git a/src2/frontend/assets/icons/project/fbd/Block.tsx b/src2/frontend/assets/icons/project/fbd/Block.tsx index 89d31ee23..fc049dabd 100644 --- a/src2/frontend/assets/icons/project/fbd/Block.tsx +++ b/src2/frontend/assets/icons/project/fbd/Block.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/fbd/Comment.tsx b/src2/frontend/assets/icons/project/fbd/Comment.tsx index 26c75eaca..4d66cc344 100644 --- a/src2/frontend/assets/icons/project/fbd/Comment.tsx +++ b/src2/frontend/assets/icons/project/fbd/Comment.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type ICommentIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/fbd/Connector.tsx b/src2/frontend/assets/icons/project/fbd/Connector.tsx index f67bfe136..2b8732dda 100644 --- a/src2/frontend/assets/icons/project/fbd/Connector.tsx +++ b/src2/frontend/assets/icons/project/fbd/Connector.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/fbd/Continuation.tsx b/src2/frontend/assets/icons/project/fbd/Continuation.tsx index 428b7560b..19dc29da2 100644 --- a/src2/frontend/assets/icons/project/fbd/Continuation.tsx +++ b/src2/frontend/assets/icons/project/fbd/Continuation.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/fbd/VariableIn.tsx b/src2/frontend/assets/icons/project/fbd/VariableIn.tsx index 92eaeceaf..d97ce93bd 100644 --- a/src2/frontend/assets/icons/project/fbd/VariableIn.tsx +++ b/src2/frontend/assets/icons/project/fbd/VariableIn.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/fbd/VariableInOut.tsx b/src2/frontend/assets/icons/project/fbd/VariableInOut.tsx index d152db399..279db7ef3 100644 --- a/src2/frontend/assets/icons/project/fbd/VariableInOut.tsx +++ b/src2/frontend/assets/icons/project/fbd/VariableInOut.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/fbd/VariableOut.tsx b/src2/frontend/assets/icons/project/fbd/VariableOut.tsx index 664cd6b5e..27012a41b 100644 --- a/src2/frontend/assets/icons/project/fbd/VariableOut.tsx +++ b/src2/frontend/assets/icons/project/fbd/VariableOut.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/index.ts b/src2/frontend/assets/icons/project/index.ts new file mode 100644 index 000000000..97fb540ed --- /dev/null +++ b/src2/frontend/assets/icons/project/index.ts @@ -0,0 +1,3 @@ +export { ArrayIcon } from './Array' +export { EnumIcon } from './Enum' +export { StructureIcon } from './Structure' diff --git a/src2/frontend/assets/icons/project/ladder/Block.tsx b/src2/frontend/assets/icons/project/ladder/Block.tsx index babb5eead..280f4056e 100644 --- a/src2/frontend/assets/icons/project/ladder/Block.tsx +++ b/src2/frontend/assets/icons/project/ladder/Block.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' + type IBlockIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/ladder/Coil.tsx b/src2/frontend/assets/icons/project/ladder/Coil.tsx index b7c44dd5e..ace9242df 100644 --- a/src2/frontend/assets/icons/project/ladder/Coil.tsx +++ b/src2/frontend/assets/icons/project/ladder/Coil.tsx @@ -1,5 +1,6 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' + +import { cn } from '../../../../utils/cn' type ICoilIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/assets/icons/project/ladder/Contact.tsx b/src2/frontend/assets/icons/project/ladder/Contact.tsx index 03468924e..aa74edbac 100644 --- a/src2/frontend/assets/icons/project/ladder/Contact.tsx +++ b/src2/frontend/assets/icons/project/ladder/Contact.tsx @@ -1,5 +1,6 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' + +import { cn } from '../../../../utils/cn' type IContactIconProps = ComponentPropsWithoutRef<'svg'> & { size?: 'sm' | 'md' | 'lg' } diff --git a/src2/frontend/components/_atoms/accordion/index.tsx b/src2/frontend/components/_atoms/accordion/index.tsx index aa4250430..881d50e6e 100644 --- a/src2/frontend/components/_atoms/accordion/index.tsx +++ b/src2/frontend/components/_atoms/accordion/index.tsx @@ -1,8 +1,9 @@ import * as AccordionPrimitive from '@radix-ui/react-accordion' import { ChevronDownIcon } from '@radix-ui/react-icons' -import { cn } from '../../../utils/cn' import { forwardRef, useEffect, useRef, useState } from 'react' +import { cn } from '../../../utils/cn' + interface AccordionItemProps { title: React.ReactNode content: React.ReactNode diff --git a/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx b/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx index d9d5fb5ba..0e31231fd 100644 --- a/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx +++ b/src2/frontend/components/_atoms/buttons/activity-bar/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef, forwardRef } from 'react' +import { cn } from '../../../../utils/cn' + type IActivityBarButtonProps = ComponentPropsWithoutRef<'button'> & { 'data-active'?: string } diff --git a/src2/frontend/components/_atoms/buttons/default/index.tsx b/src2/frontend/components/_atoms/buttons/default/index.tsx index 7500ecb69..32bb84f8e 100644 --- a/src2/frontend/components/_atoms/buttons/default/index.tsx +++ b/src2/frontend/components/_atoms/buttons/default/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithRef, ReactNode } from 'react' +import { cn } from '../../../../utils/cn' + type ButtonProps = ComponentPropsWithRef<'button'> & { ghosted?: boolean children?: ReactNode diff --git a/src2/frontend/components/_atoms/buttons/tables-actions/index.tsx b/src2/frontend/components/_atoms/buttons/tables-actions/index.tsx index d52184716..d02034934 100644 --- a/src2/frontend/components/_atoms/buttons/tables-actions/index.tsx +++ b/src2/frontend/components/_atoms/buttons/tables-actions/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef, ReactNode } from 'react' +import { cn } from '../../../../utils/cn' + type TableActionButtonProps = ComponentPropsWithoutRef<'button'> & { children: ReactNode className?: string diff --git a/src2/frontend/components/_atoms/buttons/window-control/index.tsx b/src2/frontend/components/_atoms/buttons/window-control/index.tsx index 49e347f9b..f49f4d25f 100644 --- a/src2/frontend/components/_atoms/buttons/window-control/index.tsx +++ b/src2/frontend/components/_atoms/buttons/window-control/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithRef } from 'react' +import { cn } from '../../../../utils/cn' + type WindowControlButtonProps = ComponentPropsWithRef<'button'> const WindowControlButton = (props: WindowControlButtonProps) => { diff --git a/src2/frontend/components/_atoms/checkbox/index.tsx b/src2/frontend/components/_atoms/checkbox/index.tsx index 08937c7e6..15c7d75b2 100644 --- a/src2/frontend/components/_atoms/checkbox/index.tsx +++ b/src2/frontend/components/_atoms/checkbox/index.tsx @@ -1,6 +1,7 @@ import type { CheckboxProps as PrimitiveCheckboxProps } from '@radix-ui/react-checkbox' import * as PrimitiveCheckbox from '@radix-ui/react-checkbox' import { CheckIcon } from '@radix-ui/react-icons' + import { cn } from '../../../utils/cn' type CheckboxProps = PrimitiveCheckboxProps & { diff --git a/src2/frontend/components/_atoms/debug-tree-node/index.tsx b/src2/frontend/components/_atoms/debug-tree-node/index.tsx index f089600af..b36221d5c 100644 --- a/src2/frontend/components/_atoms/debug-tree-node/index.tsx +++ b/src2/frontend/components/_atoms/debug-tree-node/index.tsx @@ -1,8 +1,9 @@ +import { ComponentPropsWithoutRef } from 'react' + +import type { DebugTreeNode } from '../../../../middleware/shared/ports/types' import { ArrowIcon } from '../../../assets/icons/interface/Arrow' import ViewIcon from '../../../assets/icons/interface/View' import { cn } from '../../../utils/cn' -import { ComponentPropsWithoutRef } from 'react' -import type { DebugTreeNode } from '../../../../middleware/shared/ports/types' type TreeNodeProps = ComponentPropsWithoutRef<'div'> & { node: DebugTreeNode diff --git a/src2/frontend/components/_atoms/dimensions-modal/array-dimensions-input/index.tsx b/src2/frontend/components/_atoms/dimensions-modal/array-dimensions-input/index.tsx index b8ffb27ee..93c3fbc73 100644 --- a/src2/frontend/components/_atoms/dimensions-modal/array-dimensions-input/index.tsx +++ b/src2/frontend/components/_atoms/dimensions-modal/array-dimensions-input/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef, useEffect, useState } from 'react' +import { cn } from '../../../../utils/cn' + type ArrayDimensionsComponentProps = ComponentPropsWithoutRef<'div'> & { id: string initialValue: string diff --git a/src2/frontend/components/_atoms/dimensions-modal/index.tsx b/src2/frontend/components/_atoms/dimensions-modal/index.tsx index 03f184be0..25828cedb 100644 --- a/src2/frontend/components/_atoms/dimensions-modal/index.tsx +++ b/src2/frontend/components/_atoms/dimensions-modal/index.tsx @@ -1,10 +1,9 @@ import { MinusIcon } from '../../../assets/icons/interface/Minus' import { PlusIcon } from '../../../assets/icons/interface/Plus' import { StickArrowIcon } from '../../../assets/icons/interface/StickArrow' -import { Button } from '../buttons/default' import { Modal, ModalContent, ModalFooter, ModalHeader, ModalTitle, ModalTrigger, } from '../../../components/_molecules/modal' import type { PLCBaseType } from '../../../utils/plc-constants' - +import { Button } from '../buttons/default' import TableActions from '../table-actions' import { TypeDropdownSelector } from '../type-dropdown-selector' import { ArrayDimensionsInput } from './array-dimensions-input' diff --git a/src2/frontend/components/_atoms/file/index.tsx b/src2/frontend/components/_atoms/file/index.tsx index d479cc84b..2294e85b8 100644 --- a/src2/frontend/components/_atoms/file/index.tsx +++ b/src2/frontend/components/_atoms/file/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type IFileProps = ComponentPropsWithoutRef<'div'> & { projectName: string projectPath: string diff --git a/src2/frontend/components/_atoms/generic-data-type-table/index.tsx b/src2/frontend/components/_atoms/generic-data-type-table/index.tsx index b957b0bfe..c40006d1b 100644 --- a/src2/frontend/components/_atoms/generic-data-type-table/index.tsx +++ b/src2/frontend/components/_atoms/generic-data-type-table/index.tsx @@ -1,7 +1,8 @@ -import { Table, TableBody, TableCell, TableRow } from '../table' import { flexRender, Table as ReactTable } from '@tanstack/react-table' import React, { RefObject } from 'react' +import { Table, TableBody, TableCell, TableRow } from '../table' + interface GenericTableProps { context: 'data-type-array' | 'data-type-enumerated' // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src2/frontend/components/_atoms/generic-table-inputs/generic-button-cell.tsx b/src2/frontend/components/_atoms/generic-table-inputs/generic-button-cell.tsx index e1a9a52a9..7d3e7fda8 100644 --- a/src2/frontend/components/_atoms/generic-table-inputs/generic-button-cell.tsx +++ b/src2/frontend/components/_atoms/generic-table-inputs/generic-button-cell.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import React from 'react' +import { cn } from '../../../utils/cn' + export const GenericButtonCell = ({ value, onClick, diff --git a/src2/frontend/components/_atoms/generic-table-inputs/generic-combobox-cell.tsx b/src2/frontend/components/_atoms/generic-table-inputs/generic-combobox-cell.tsx index 04fe29141..c346b3827 100644 --- a/src2/frontend/components/_atoms/generic-table-inputs/generic-combobox-cell.tsx +++ b/src2/frontend/components/_atoms/generic-table-inputs/generic-combobox-cell.tsx @@ -1,9 +1,9 @@ import * as PrimitiveDropdown from '@radix-ui/react-dropdown-menu' +import { useEffect, useMemo, useRef, useState } from 'react' + import { CloseIcon } from '../../../assets/icons/interface/Close' import { PlusIcon } from '../../../assets/icons/interface/Plus' import { cn } from '../../../utils/cn' -import { useEffect, useMemo, useRef, useState } from 'react' - import ScrollAreaComponent from '../../ui/scroll-area' import { InputWithRef } from '../input' diff --git a/src2/frontend/components/_atoms/generic-table-inputs/generic-select-cell.tsx b/src2/frontend/components/_atoms/generic-table-inputs/generic-select-cell.tsx index 19b0284d3..b9fde763d 100644 --- a/src2/frontend/components/_atoms/generic-table-inputs/generic-select-cell.tsx +++ b/src2/frontend/components/_atoms/generic-table-inputs/generic-select-cell.tsx @@ -1,7 +1,7 @@ -import { cn } from '../../../utils/cn' import { startCase } from 'lodash' import { useEffect, useRef, useState } from 'react' +import { cn } from '../../../utils/cn' import ScrollAreaComponent from '../../ui/scroll-area' import { Select, SelectContent, SelectItem, SelectTrigger } from '../select' diff --git a/src2/frontend/components/_atoms/generic-table-inputs/generic-text-cell.tsx b/src2/frontend/components/_atoms/generic-table-inputs/generic-text-cell.tsx index 83e831e59..13eef525e 100644 --- a/src2/frontend/components/_atoms/generic-table-inputs/generic-text-cell.tsx +++ b/src2/frontend/components/_atoms/generic-table-inputs/generic-text-cell.tsx @@ -1,8 +1,8 @@ +import { useCallback, useEffect, useRef, useState } from 'react' + import { useOpenPLCStore } from '../../../store' import { extractSearchQuery } from '../../../store/slices/search/utils' import { cn } from '../../../utils/cn' -import { useCallback, useEffect, useRef, useState } from 'react' - import { InputWithRef } from '../input' export const GenericTextCell = ({ diff --git a/src2/frontend/components/_atoms/generic-table-inputs/generic-textarea-cell.tsx b/src2/frontend/components/_atoms/generic-table-inputs/generic-textarea-cell.tsx index 7c0111de0..daa31ecaa 100644 --- a/src2/frontend/components/_atoms/generic-table-inputs/generic-textarea-cell.tsx +++ b/src2/frontend/components/_atoms/generic-table-inputs/generic-textarea-cell.tsx @@ -1,4 +1,5 @@ import * as PrimitivePopover from '@radix-ui/react-popover' + import { cn } from '../../../utils/cn' export const GenericAreaTextCell = ({ diff --git a/src2/frontend/components/_atoms/generic-table/index.tsx b/src2/frontend/components/_atoms/generic-table/index.tsx index 4b7dd5f0a..8481c9d37 100644 --- a/src2/frontend/components/_atoms/generic-table/index.tsx +++ b/src2/frontend/components/_atoms/generic-table/index.tsx @@ -1,5 +1,3 @@ -import type { ProjectResponse } from '../../../store/slices/project' -import { cn } from '../../../utils/cn' import { ColumnDef, ColumnFiltersState, @@ -11,6 +9,8 @@ import { } from '@tanstack/react-table' import { useEffect, useRef } from 'react' +import type { ProjectResponse } from '../../../store/slices/project' +import { cn } from '../../../utils/cn' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../table' type GenericTableProps = { diff --git a/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx b/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx index 2c5b4773a..64c693a7a 100644 --- a/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/autocomplete/index.tsx @@ -1,8 +1,9 @@ import * as Popover from '@radix-ui/react-popover' -import { PlusIcon } from '../../../../assets/icons/interface/Plus' +import { ComponentPropsWithRef, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' + import type { PLCVariable } from '../../../../../middleware/shared/ports/types' +import { PlusIcon } from '../../../../assets/icons/interface/Plus' import { cn } from '../../../../utils/cn' -import { ComponentPropsWithRef, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' export type GraphicalEditorAutocompleteProps = ComponentPropsWithRef<'div'> & { isOpen?: boolean diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/autocomplete/index.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/autocomplete/index.tsx index e4808426e..72fd5a6e4 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/autocomplete/index.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/autocomplete/index.tsx @@ -1,14 +1,14 @@ -import { toast } from '../../../../_features/[app]/toast/use-toast' -import { buildGenericNode } from '../../../../_molecules/graphical-editor/fbd/fbd-utils/nodes' -import { useOpenPLCStore } from '../../../../../store' -import { extractNumberAtEnd } from '../../../../../store/slices/project/validation/variables' -import { PLCVariable } from '../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../utils/cn' -import { expandArrayVariables } from '../../../../../../backend/shared/array-variable-utils' import { Node } from '@xyflow/react' import { isArray } from 'lodash' import { ComponentPropsWithRef, forwardRef, useMemo } from 'react' +import { expandArrayVariables } from '../../../../../../backend/shared/array-variable-utils' +import { PLCVariable } from '../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../store' +import { extractNumberAtEnd } from '../../../../../store/slices/project/validation/variables' +import { cn } from '../../../../../utils/cn' +import { toast } from '../../../../_features/[app]/toast/use-toast' +import { buildGenericNode } from '../../../../_molecules/graphical-editor/fbd/fbd-utils/nodes' import { GraphicalEditorAutocomplete } from '../../autocomplete' import { BlockVariant } from '../../types/block' import { getVariableRestrictionType } from '../../utils' @@ -194,7 +194,6 @@ const FBDBlockAutoComplete = forwardRef({ nodeId, data, @@ -112,9 +114,7 @@ export const BlockNodeElement = ({ } const libraryBlock = libraries.system - // @ts-expect-error - type is dynamic .flatMap((block) => block.pous) - // @ts-expect-error - type is dynamic .find((pou) => pou.name === blockNameValue) if (!libraryBlock) { diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/buildNodes.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/buildNodes.tsx index eaecab711..5f69f9791 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/buildNodes.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/buildNodes.tsx @@ -1,15 +1,9 @@ -import type { - BlockBuilderProps, - CommentBuilderProps, - CommentNode, - ConnectionBuilderProps, - ConnectionNode, - VariableBuilderProps, - VariableNode, -} from './utils/types' +import { Position } from '@xyflow/react' + import { generateNumericUUID } from '../../../../utils/generate-uuid' -import { getBlockSize, getBlockVariantAndExecutionControl } from './utils' import { BlockVariant } from '../types/block' +import { buildHandle } from './handle' +import { getBlockSize, getBlockVariantAndExecutionControl } from './utils' import { CONNECTION_ELEMENT_HEIGHT, CONNECTION_ELEMENT_WIDTH, @@ -24,8 +18,15 @@ import { VARIABLE_ELEMENT_HEIGHT, VARIABLE_ELEMENT_SIZE, } from './utils/constants' -import { buildHandle } from './handle' -import { Position } from '@xyflow/react' +import type { + BlockBuilderProps, + CommentBuilderProps, + CommentNode, + ConnectionBuilderProps, + ConnectionNode, + VariableBuilderProps, + VariableNode, +} from './utils/types' /** * diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/comment.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/comment.tsx index 438146e39..98fbb8217 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/comment.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/comment.tsx @@ -1,12 +1,12 @@ -import { useOpenPLCStore } from '../../../../store' -import { cn } from '../../../../utils/cn' import { NodeResizer } from '@xyflow/react' import { memo, useEffect, useRef, useState } from 'react' +import { useOpenPLCStore } from '../../../../store' +import { cn } from '../../../../utils/cn' import { HighlightedTextArea } from '../../highlighted-textarea' import { getFBDPouVariablesRungNodeAndEdges } from './utils' -import { CommentNode, CommentProps } from './utils/types' import { MINIMUM_ELEMENT_HEIGHT, MINIMUM_ELEMENT_WIDTH } from './utils/constants' +import { CommentNode, CommentProps } from './utils/types' const CommentElement = (block: CommentProps) => { const { id, selected, data, width, height } = block diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/connection.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/connection.tsx index 1ddc87598..5f58a0ca3 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/connection.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/connection.tsx @@ -1,7 +1,7 @@ -import { useOpenPLCStore } from '../../../../store' -import { cn } from '../../../../utils/cn' import { useEffect, useRef, useState } from 'react' +import { useOpenPLCStore } from '../../../../store' +import { cn } from '../../../../utils/cn' import { HighlightedTextArea } from '../../highlighted-textarea' import { FBDBlockAutoComplete } from './autocomplete' import { CustomHandle } from './handle' diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/handle.tsx b/src2/frontend/components/_atoms/graphical-editor/fbd/handle.tsx index 1d43cf322..66aedb42b 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/handle.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/handle.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { Handle, HandleProps } from '@xyflow/react' +import { cn } from '../../../../utils/cn' + export type CustomHandleProps = HandleProps & { glbPosition: { x: number diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/index.ts b/src2/frontend/components/_atoms/graphical-editor/fbd/index.ts index 454609db8..80007b3ba 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/index.ts +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/index.ts @@ -1,8 +1,8 @@ import * as blockNode from './block' +import * as buildNodes from './buildNodes' import * as commentNode from './comment' import * as connectionNode from './connection' import * as variableNode from './variable' -import * as buildNodes from './buildNodes' export const customNodeTypes = { block: blockNode.Block, diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/utils/types.ts b/src2/frontend/components/_atoms/graphical-editor/fbd/utils/types.ts index e5351499f..d7a899711 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/utils/types.ts +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/utils/types.ts @@ -1,6 +1,6 @@ -import type { PLCVariable } from '../../../../../../middleware/shared/ports/types' import { Node, NodeProps, XYPosition } from '@xyflow/react' +import type { PLCVariable } from '../../../../../../middleware/shared/ports/types' import { CustomHandleProps } from '../handle' export type BuilderBasicProps = { diff --git a/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts b/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts index 621c89280..8e6eadb7d 100644 --- a/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts +++ b/src2/frontend/components/_atoms/graphical-editor/fbd/utils/utils.ts @@ -1,16 +1,16 @@ +import { Position } from '@xyflow/react' + +import { resolveArrayVariableByName } from '../../../../../../backend/shared/array-variable-utils' +import type { PLCPou } from '../../../../../../middleware/shared/ports/types' +import type { PLCVariable } from '../../../../../../middleware/shared/ports/types' import type { EditorModel } from '../../../../../store/slices/editor' import type { FBDFlowType } from '../../../../../store/slices/fbd' import type { LadderFlowType } from '../../../../../store/slices/ladder' -import type { PLCPou } from '../../../../../../middleware/shared/ports/types' -import type { PLCVariable } from '../../../../../../middleware/shared/ports/types' -import { resolveArrayVariableByName } from '../../../../../../backend/shared/array-variable-utils' - -import { customNodeTypes } from '..' -import type { BasicNodeData } from './types' import { BlockVariant } from '../../types/block' -import { DEFAULT_BLOCK_CONNECTOR_Y, DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, DEFAULT_BLOCK_WIDTH } from './constants' +import { customNodeTypes } from '..' import { buildHandle } from '../handle' -import { Position } from '@xyflow/react' +import { DEFAULT_BLOCK_CONNECTOR_Y, DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, DEFAULT_BLOCK_WIDTH } from './constants' +import type { BasicNodeData } from './types' export const getFBDPouVariablesRungNodeAndEdges = ( editor: EditorModel, @@ -31,7 +31,7 @@ export const getFBDPouVariablesRungNodeAndEdges = ( const rung = fbdFlows.find((flow) => flow.name === editor.meta.name)?.rung const node = rung?.nodes.find((node) => node.id === data.nodeId) - const variables: PLCVariable[] = (pou?.interface?.variables ?? []) as PLCVariable[] + const variables: PLCVariable[] = (pou?.interface?.variables ?? []) let variable = variables.find((variable) => { if (!node) return undefined switch (node.type as keyof typeof customNodeTypes) { diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx index 3b28450f6..1b5c1838c 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/autocomplete/index.tsx @@ -1,13 +1,13 @@ -import { toast } from '../../../../_features/[app]/toast/use-toast' -import { useOpenPLCStore } from '../../../../../store' -import { extractNumberAtEnd } from '../../../../../store/slices/project/validation/variables' -import { PLCVariable } from '../../../../../../middleware/shared/ports' -import { cn } from '../../../../../utils/cn' -import { expandArrayVariables } from '../../../../../../backend/shared/array-variable-utils' import { Node } from '@xyflow/react' import { ComponentPropsWithRef, forwardRef } from 'react' import { v4 as uuidv4 } from 'uuid' +import { expandArrayVariables } from '../../../../../../backend/shared/array-variable-utils' +import { PLCVariable } from '../../../../../../middleware/shared/ports' +import { useOpenPLCStore } from '../../../../../store' +import { extractNumberAtEnd } from '../../../../../store/slices/project/validation/variables' +import { cn } from '../../../../../utils/cn' +import { toast } from '../../../../_features/[app]/toast/use-toast' import { GraphicalEditorAutocomplete } from '../../autocomplete' import { getVariableRestrictionType } from '../../utils' import { getLadderPouVariablesRungNodeAndEdges } from '../utils' @@ -72,7 +72,7 @@ const VariablesBlockAutoComplete = forwardRef({ nodeId, data, @@ -449,21 +451,29 @@ export const Block = (block: BlockProps) => { return } - if (!node || !rung) { - console.error('Node or rung not found for ID:', id) + const { + variables: freshVariables, + rung: freshRung, + node: freshNode, + } = getLadderPouVariablesRungNodeAndEdges(editor, pous, ladderFlows, { + nodeId: id, + variableName: data.variable.name, + }) + + if (!freshNode || !freshRung) { return } - const variable = variables.selected + const variable = freshVariables.selected if (!variable) { setWrongVariable(true) return } - const nodeVariable = (node.data as BasicNodeData).variable + const nodeVariable = (freshNode.data as BasicNodeData).variable const nodeVariableName = nodeVariable.name.toLowerCase() const selectedVariableName = variable.name.toLowerCase() - const nodeBlockType = (node.data as BlockNodeData).variant.name + const nodeBlockType = (freshNode.data as BlockNodeData).variant.name if (nodeVariableName === selectedVariableName) { const typeMatches = @@ -478,12 +488,12 @@ export const Block = (block: BlockProps) => { if (nodeVariable.name !== variable.name) { updateNode({ editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, + rungId: freshRung.id, + nodeId: freshNode.id, node: { - ...node, + ...freshNode, data: { - ...node.data, + ...freshNode.data, variable, }, }, diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/buildNodes.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/buildNodes.tsx index 43b300b71..ac79fc34d 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/buildNodes.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/buildNodes.tsx @@ -1,5 +1,30 @@ import { Position } from '@xyflow/react' + +import { generateNumericUUID } from '../../../../utils/generate-uuid' import { buildHandle } from './handle' +import { getBlockSize, getBlockVariantAndExecutionControl } from './utils' +import { + DEFAULT_BLOCK_TYPE, + DEFAULT_COIL_BLOCK_HEIGHT, + DEFAULT_COIL_BLOCK_WIDTH, + DEFAULT_COIL_CONNECTOR_Y, + DEFAULT_CONTACT_BLOCK_HEIGHT, + DEFAULT_CONTACT_BLOCK_WIDTH, + DEFAULT_CONTACT_CONNECTOR_Y, + DEFAULT_PARALLEL_CONNECTOR_Y, + DEFAULT_PARALLEL_HEIGHT, + DEFAULT_PARALLEL_WIDTH, + DEFAULT_PLACEHOLDER_CONNECTOR_Y, + DEFAULT_PLACEHOLDER_HEIGHT, + DEFAULT_PLACEHOLDER_WIDTH, + DEFAULT_POWER_RAIL_CONNECTOR_X, + DEFAULT_POWER_RAIL_CONNECTOR_Y, + DEFAULT_POWER_RAIL_HEIGHT, + DEFAULT_POWER_RAIL_WIDTH, + DEFAULT_VARIABLE_CONNECTOR_Y, + DEFAULT_VARIABLE_HEIGHT, + DEFAULT_VARIABLE_WIDTH, +} from './utils/constants' import type { BlockBuilderProps, BlockVariant, @@ -14,30 +39,6 @@ import type { VariableBuilderProps, VariableNode, } from './utils/types' -import { generateNumericUUID } from '../../../../utils/generate-uuid' -import { - DEFAULT_COIL_CONNECTOR_Y, - DEFAULT_COIL_BLOCK_WIDTH, - DEFAULT_COIL_BLOCK_HEIGHT, - DEFAULT_CONTACT_CONNECTOR_Y, - DEFAULT_CONTACT_BLOCK_WIDTH, - DEFAULT_CONTACT_BLOCK_HEIGHT, - DEFAULT_BLOCK_TYPE, - DEFAULT_PARALLEL_WIDTH, - DEFAULT_PARALLEL_HEIGHT, - DEFAULT_PARALLEL_CONNECTOR_Y, - DEFAULT_PLACEHOLDER_CONNECTOR_Y, - DEFAULT_PLACEHOLDER_WIDTH, - DEFAULT_PLACEHOLDER_HEIGHT, - DEFAULT_POWER_RAIL_CONNECTOR_X, - DEFAULT_POWER_RAIL_CONNECTOR_Y, - DEFAULT_POWER_RAIL_WIDTH, - DEFAULT_POWER_RAIL_HEIGHT, - DEFAULT_VARIABLE_WIDTH, - DEFAULT_VARIABLE_HEIGHT, - DEFAULT_VARIABLE_CONNECTOR_Y, -} from './utils/constants' -import { getBlockSize, getBlockVariantAndExecutionControl } from './utils' /** * diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx index 0e2d17cfd..6b71ed76d 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/coil.tsx @@ -1,16 +1,18 @@ -import { useOpenPLCStore } from '../../../../store' -import { useDebugCompositeKey } from '../../../../hooks/use-debug-composite-key' -import { useDebugger } from '../../../../../middleware/shared/providers' -import { cn } from '../../../../utils/cn' -import { useEffect, useRef, useState } from 'react' import * as Popover from '@radix-ui/react-popover' +import { useEffect, useRef, useState } from 'react' +import { useDebugger } from '../../../../../middleware/shared/providers' +import { useDebugCompositeKey } from '../../../../hooks/use-debug-composite-key' +import { useOpenPLCStore } from '../../../../store' +import { cn } from '../../../../utils/cn' import { HighlightedTextArea } from '../../highlighted-textarea' +import { VariablesBlockAutoComplete } from './autocomplete' import { CustomHandle } from './handle' import { getLadderPouVariablesRungNodeAndEdges } from './utils' -import type { BasicNodeData, CoilProps } from './utils/types' -import { VariablesBlockAutoComplete } from './autocomplete' import { DEFAULT_COIL_BLOCK_HEIGHT, DEFAULT_COIL_BLOCK_WIDTH, DEFAULT_COIL_TYPES } from './utils/constants' +import type { BasicNodeData, CoilProps } from './utils/types' + +export type { CoilNode } from './utils/types' export const Coil = (block: CoilProps) => { const { selected, data, id } = block diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx index b44b18464..0e6cd78fe 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/contact.tsx @@ -1,16 +1,18 @@ +import * as Popover from '@radix-ui/react-popover' +import { useEffect, useRef, useState } from 'react' + import { useDebugger } from '../../../../../middleware/shared/providers' import { useDebugCompositeKey } from '../../../../hooks/use-debug-composite-key' import { useOpenPLCStore } from '../../../../store' import { cn } from '../../../../utils/cn' -import { useEffect, useRef, useState } from 'react' -import * as Popover from '@radix-ui/react-popover' - import { HighlightedTextArea } from '../../highlighted-textarea' +import { VariablesBlockAutoComplete } from './autocomplete' import { CustomHandle } from './handle' import { getLadderPouVariablesRungNodeAndEdges } from './utils' +import { DEFAULT_CONTACT_BLOCK_HEIGHT,DEFAULT_CONTACT_BLOCK_WIDTH, DEFAULT_CONTACT_TYPES } from './utils/constants' import type { BasicNodeData, ContactProps } from './utils/types' -import { VariablesBlockAutoComplete } from './autocomplete' -import { DEFAULT_CONTACT_TYPES, DEFAULT_CONTACT_BLOCK_WIDTH, DEFAULT_CONTACT_BLOCK_HEIGHT } from './utils/constants' + +export type { ContactNode } from './utils/types' export const Contact = (block: ContactProps) => { const { selected, data, id } = block diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/handle.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/handle.tsx index 13718fcfc..be1b8b590 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/handle.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/handle.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../../utils/cn' import { Handle, HandleProps } from '@xyflow/react' +import { cn } from '../../../../utils/cn' + export type CustomHandleProps = HandleProps & { glbPosition: { x: number diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/parallel.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/parallel.tsx index 91f288b22..ec52da42d 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/parallel.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/parallel.tsx @@ -1,8 +1,7 @@ import { cn } from '../../../../utils/cn' - import { CustomHandle } from './handle' -import type { ParallelProps } from './utils/types' import { DEFAULT_PARALLEL_HEIGHT, DEFAULT_PARALLEL_WIDTH } from './utils/constants' +import type { ParallelProps } from './utils/types' export const Parallel = ({ selected, data }: ParallelProps) => { return ( diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/placeholder.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/placeholder.tsx index c3a7e9061..06c787b3d 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/placeholder.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/placeholder.tsx @@ -1,9 +1,8 @@ import { PlaceholderNodeFilled } from '../../../../assets/icons/flow/Placeholder' import { cn } from '../../../../utils/cn' - import { CustomHandle } from './handle' -import { PlaceholderProps } from './utils/types' import { DEFAULT_PLACEHOLDER_HEIGHT, DEFAULT_PLACEHOLDER_WIDTH } from './utils/constants' +import { PlaceholderProps } from './utils/types' export const Placeholder = ({ selected, data }: PlaceholderProps) => { return ( diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/power-rail.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/power-rail.tsx index ceab32387..eaee9906d 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/power-rail.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/power-rail.tsx @@ -1,6 +1,6 @@ import { CustomHandle } from './handle' -import { PowerRailProps } from './utils/types' import { DEFAULT_POWER_RAIL_HEIGHT, DEFAULT_POWER_RAIL_WIDTH } from './utils/constants' +import { PowerRailProps } from './utils/types' export const PowerRail = ({ data }: PowerRailProps) => { return ( diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/constants.tsx b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/constants.tsx index 9255334fd..d2d4e7f35 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/constants.tsx +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/constants.tsx @@ -1,3 +1,5 @@ +import { ReactNode } from 'react' + import { DefaultCoil, FallingEdgeCoil, @@ -9,7 +11,6 @@ import { import { DefaultContact, FallingEdgeContact, NegatedContact, RisingEdgeContact } from '../../../../../assets/icons/flow/Contact' import { cn } from '../../../../../utils/cn' import { CoilType, ContactType } from './types' -import { ReactNode } from 'react' // block diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/types.ts b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/types.ts index 219ef6aba..2aeeb04e9 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/types.ts +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/types.ts @@ -1,10 +1,9 @@ -import { PLCVariable } from '../../../../../../middleware/shared/ports' +import type { Node, NodeProps } from '@xyflow/react' +import { ReactNode } from 'react' +import { PLCVariable } from '../../../../../../middleware/shared/ports' import { CustomHandleProps } from '../handle' -import { ReactNode } from 'react' -import type { Node, NodeProps } from '@xyflow/react' - export type BuilderBasicProps = { id: string posX: number diff --git a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts index b52cec0f9..df3187eb1 100644 --- a/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts +++ b/src2/frontend/components/_atoms/graphical-editor/ladder/utils/utils.ts @@ -1,15 +1,15 @@ -import type { EditorModel } from '../../../../../store/slices/editor' -import type { LadderFlowType } from '../../../../../store/slices/ladder' -import { baseTypeSchema, genericTypeSchema } from '../../../../../../middleware/shared/ports' -import type { PLCPou } from '../../../../../../middleware/shared/ports' -import type { PLCVariable } from '../../../../../../middleware/shared/ports' -import { resolveArrayVariableByName } from '../../../../../../backend/shared/array-variable-utils' +import { Position } from '@xyflow/react' import { ZodLiteral } from 'zod' -import type { BasicNodeData, BlockVariant } from './types' -import { DEFAULT_BLOCK_CONNECTOR_Y, DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, DEFAULT_BLOCK_WIDTH } from './constants' -import { Position } from '@xyflow/react' +import { resolveArrayVariableByName } from '../../../../../../backend/shared/array-variable-utils' +import type { PLCPou } from '../../../../../../middleware/shared/ports' +import type { PLCVariable } from '../../../../../../middleware/shared/ports' +import { baseTypeSchema, genericTypeSchema } from '../../../../../../middleware/shared/ports' +import type { EditorModel } from '../../../../../store/slices/editor' +import type { LadderFlowType } from '../../../../../store/slices/ladder' import { buildHandle } from '../handle' +import { DEFAULT_BLOCK_CONNECTOR_Y, DEFAULT_BLOCK_CONNECTOR_Y_OFFSET, DEFAULT_BLOCK_WIDTH } from './constants' +import type { BasicNodeData, BlockVariant } from './types' export const getLadderPouVariablesRungNodeAndEdges = ( editor: EditorModel, @@ -34,7 +34,7 @@ export const getLadderPouVariablesRungNodeAndEdges = ( const node = rung?.nodes.find((node) => node.id === data.nodeId) - const variables: PLCVariable[] = (pou?.interface?.variables ?? []) as PLCVariable[] + const variables: PLCVariable[] = (pou?.interface?.variables ?? []) let variable = variables.find((variable) => { if (!node) return undefined const nodeVariable = (node.data as BasicNodeData).variable diff --git a/src2/frontend/components/_atoms/graphical-editor/types/block.ts b/src2/frontend/components/_atoms/graphical-editor/types/block.ts index 700d62ed9..f17092144 100644 --- a/src2/frontend/components/_atoms/graphical-editor/types/block.ts +++ b/src2/frontend/components/_atoms/graphical-editor/types/block.ts @@ -1,10 +1,11 @@ +import { z } from 'zod' + import { BaseLibraryPouSchema, BaseLibraryVariableSchema, baseTypeSchema, genericTypeSchema, } from '../../../../../middleware/shared/ports/plc-schemas' -import { z } from 'zod' const blockVariantVariableSchema = BaseLibraryVariableSchema.extend({ id: z.string().optional(), diff --git a/src2/frontend/components/_atoms/graphical-editor/utils/index.ts b/src2/frontend/components/_atoms/graphical-editor/utils/index.ts index 0938ce831..df32438ef 100644 --- a/src2/frontend/components/_atoms/graphical-editor/utils/index.ts +++ b/src2/frontend/components/_atoms/graphical-editor/utils/index.ts @@ -1,10 +1,9 @@ -import type { PLCVariable } from '../../../../../middleware/shared/ports/types' import { resolveArrayVariableByName } from '../../../../../backend/shared/array-variable-utils' import { - validateVariableType as _validateVariableType, getVariableRestrictionType, + validateVariableType as _validateVariableType, } from '../../../../../backend/shared/validate-variable-type' - +import type { PLCVariable } from '../../../../../middleware/shared/ports/types' import { BlockVariant } from '../ladder/utils/types' import { BlockVariant as newBlockVariant } from '../types/block' diff --git a/src2/frontend/components/_atoms/highlighted-textarea/index.tsx b/src2/frontend/components/_atoms/highlighted-textarea/index.tsx index cccf9aeae..43ee3ffac 100644 --- a/src2/frontend/components/_atoms/highlighted-textarea/index.tsx +++ b/src2/frontend/components/_atoms/highlighted-textarea/index.tsx @@ -1,8 +1,9 @@ +import type { ChangeEvent, ComponentPropsWithRef, UIEvent } from 'react' +import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react' + import { useOpenPLCStore } from '../../../store' import { extractSearchQuery } from '../../../store/slices/search/utils' import { cn } from '../../../utils/cn' -import type { ChangeEvent, ComponentPropsWithRef, UIEvent } from 'react' -import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react' type HighlightedTextAreaProps = ComponentPropsWithRef<'textarea'> & { textAreaValue: string diff --git a/src2/frontend/components/_atoms/label/index.tsx b/src2/frontend/components/_atoms/label/index.tsx index bb4572f6b..19dafbe74 100644 --- a/src2/frontend/components/_atoms/label/index.tsx +++ b/src2/frontend/components/_atoms/label/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../utils/cn' + type LabelProps = ComponentPropsWithoutRef<'label'> const Label = (props: LabelProps) => { diff --git a/src2/frontend/components/_atoms/react-flow/custom-nodes/coil.tsx b/src2/frontend/components/_atoms/react-flow/custom-nodes/coil.tsx deleted file mode 100644 index 2fdaa3e9c..000000000 --- a/src2/frontend/components/_atoms/react-flow/custom-nodes/coil.tsx +++ /dev/null @@ -1,282 +0,0 @@ -import { useOpenPLCStore } from '../../../../store' -import { cn } from '../../../../utils/cn' -import { useEffect, useRef, useState } from 'react' - -import { HighlightedTextArea } from '../../highlighted-textarea' -import { CustomHandle } from './handle' -import { getPouVariablesRungNodeAndEdges } from './utils' -import type { BasicNodeData, CoilProps } from './utils/types' -import { VariablesBlockAutoComplete } from './variables-block-autocomplete' -import { DEFAULT_COIL_BLOCK_HEIGHT, DEFAULT_COIL_BLOCK_WIDTH, DEFAULT_COIL_TYPES } from './utils/constants' - -export const Coil = (block: CoilProps) => { - const { selected, data, id } = block - - const { - editor, - project: { - data: { pous }, - }, - flows, - flowActions: { updateNode }, - } = useOpenPLCStore() - - const coil = DEFAULT_COIL_TYPES[data.variant] - const [coilVariableValue, setCoilVariableValue] = useState(data.variable.name) - const [wrongVariable, setWrongVariable] = useState(false) - - const inputWrapperRef = useRef(null) - const inputVariableRef = useRef< - HTMLTextAreaElement & { - blur: ({ submit }: { submit?: boolean }) => void - isFocused: boolean - } - >(null) - const autocompleteRef = useRef< - HTMLDivElement & { - focus: () => void - isFocused: boolean - selectedVariable: { positionInArray: number; variableName: string } - } - >(null) - - const [openAutocomplete, setOpenAutocomplete] = useState(false) - const [keyPressedAtTextarea, setKeyPressedAtTextarea] = useState('') - - useEffect(() => { - if (inputVariableRef.current && inputWrapperRef.current) { - inputWrapperRef.current.style.top = inputVariableRef.current.scrollHeight >= 24 ? '-24px' : '-20px' - } - }, [coilVariableValue]) - - /** - * useEffect to focus the variable input when the block is selected - */ - useEffect(() => { - if (data.variable && data.variable.name !== '') { - setCoilVariableValue(data.variable.name) - return - } - - if (inputVariableRef.current && selected) { - inputVariableRef.current.focus() - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - /** - * Update wrongVariable state when the table of variables is updated - */ - useEffect(() => { - const { variables, node, rung } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id, - variableName: coilVariableValue, - }) - - if (!rung || !node) return - - const variable = variables.selected - if (!variable) { - setWrongVariable(true) - return - } - - if (variable && (variable.type.definition !== 'base-type' || variable.type.value.toUpperCase() !== 'BOOL')) { - setWrongVariable(true) - return - } - - if ((node.data as BasicNodeData).variable.id !== variable.id) { - setCoilVariableValue(variable.name) - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable, - }, - }, - }) - setWrongVariable(false) - return - } - - if ((node.data as BasicNodeData).variable.id === variable.id && variable.name !== coilVariableValue) { - if ((node.data as BasicNodeData).variable.name !== variable.name) { - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable: { - ...variable, - name: variable.name, - }, - }, - }, - }) - } - setCoilVariableValue(variable.name) - setWrongVariable(false) - return - } - - setWrongVariable(false) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pous]) - - /** - * Handle with the variable input onBlur event - */ - const handleSubmitCoilVariableOnTextareaBlur = (variableName?: string) => { - const variableNameToSubmit = variableName || coilVariableValue - const { variables, rung, node } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id, - variableName: variableNameToSubmit, - }) - if (!rung || !node) return - - const variable = variables.selected - if ( - !variable || - variable.name !== variableNameToSubmit || - variable.type.definition !== 'base-type' || - variable.type.value.toUpperCase() !== 'BOOL' - ) { - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable: { name: variableNameToSubmit }, - }, - }, - }) - setWrongVariable(true) - return - } - - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable: variable, - }, - }, - }) - setWrongVariable(false) - } - - const onChangeHandler = () => { - if (!openAutocomplete) { - setOpenAutocomplete(true) - } - } - - return ( -
-
- {coil.svg(wrongVariable)} -
- { - e.target.select() - const { node, rung } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id ?? '', - }) - if (!node || !rung) return - updateNode({ - editorName: editor.meta.name, - nodeId: node.id, - rungId: rung.id, - node: { - ...node, - draggable: false, - }, - }) - return - }} - onBlur={() => { - const { node, rung } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id ?? '', - }) - if (!node || !rung) return - updateNode({ - editorName: editor.meta.name, - nodeId: node.id, - rungId: rung.id, - node: { - ...node, - draggable: node.data.draggable as boolean, - }, - }) - return - }} - onChange={onChangeHandler} - onKeyDown={(e) => { - if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Tab') e.preventDefault() - if (e.key === 'Enter' && autocompleteRef.current?.selectedVariable.positionInArray !== -1) { - inputVariableRef.current?.blur({ submit: false }) - } - setKeyPressedAtTextarea(e.key) - }} - onKeyUp={() => setKeyPressedAtTextarea('')} - /> - {openAutocomplete && ( -
-
- setOpenAutocomplete(value)} - keyPressed={keyPressedAtTextarea} - /> -
-
- )} -
-
- {data.handles.map((handle, index) => ( - - ))} -
- ) -} diff --git a/src2/frontend/components/_atoms/react-flow/custom-nodes/contact.tsx b/src2/frontend/components/_atoms/react-flow/custom-nodes/contact.tsx deleted file mode 100644 index 5e7d68e2d..000000000 --- a/src2/frontend/components/_atoms/react-flow/custom-nodes/contact.tsx +++ /dev/null @@ -1,281 +0,0 @@ -import { useOpenPLCStore } from '../../../../store' -import { cn } from '../../../../utils/cn' -import { useEffect, useRef, useState } from 'react' - -import { HighlightedTextArea } from '../../highlighted-textarea' -import { CustomHandle } from './handle' -import { getPouVariablesRungNodeAndEdges } from './utils' -import type { BasicNodeData, ContactProps } from './utils/types' -import { VariablesBlockAutoComplete } from './variables-block-autocomplete' -import { DEFAULT_CONTACT_TYPES, DEFAULT_CONTACT_BLOCK_WIDTH, DEFAULT_CONTACT_BLOCK_HEIGHT } from './utils/constants' - -export const Contact = (block: ContactProps) => { - const { selected, data, id } = block - const { - editor, - project: { - data: { pous }, - }, - flows, - flowActions: { updateNode }, - } = useOpenPLCStore() - - const contact = DEFAULT_CONTACT_TYPES[data.variant] - const [contactVariableValue, setContactVariableValue] = useState(data.variable.name) - const [wrongVariable, setWrongVariable] = useState(false) - - const inputWrapperRef = useRef(null) - const inputVariableRef = useRef< - HTMLTextAreaElement & { - blur: ({ submit }: { submit?: boolean }) => void - isFocused: boolean - } - >(null) - const autocompleteRef = useRef< - HTMLDivElement & { - focus: () => void - isFocused: boolean - selectedVariable: { positionInArray: number; variableName: string } - } - >(null) - - const [openAutocomplete, setOpenAutocomplete] = useState(false) - const [keyPressedAtTextarea, setKeyPressedAtTextarea] = useState('') - - useEffect(() => { - if (inputVariableRef.current && inputWrapperRef.current) { - // top - inputWrapperRef.current.style.top = inputVariableRef.current.scrollHeight >= 24 ? '-24px' : '-20px' - } - }, [contactVariableValue]) - - /** - * useEffect to focus the variable input when the block is selected - */ - useEffect(() => { - if (data.variable && data.variable.name !== '') { - setContactVariableValue(data.variable.name) - return - } - - if (inputVariableRef.current && selected) { - inputVariableRef.current.focus() - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - /** - * Update wrongVariable state when the table of variables is updated - */ - useEffect(() => { - const { variables, node, rung } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id, - variableName: contactVariableValue, - }) - if (!node || !rung) return - - const variable = variables.selected - - if (!variable) { - setWrongVariable(true) - return - } - if (variable && (variable.type.definition !== 'base-type' || variable.type.value.toUpperCase() !== 'BOOL')) { - setWrongVariable(true) - return - } - - // Variable of the node is different from the selected variable - if ((node.data as BasicNodeData).variable.id !== variable.id) { - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable, - }, - }, - }) - setWrongVariable(false) - return - } - - if ((node.data as BasicNodeData).variable.id === variable.id && variable.name !== contactVariableValue) { - if ((node.data as BasicNodeData).variable.name !== variable.name) { - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable: { - ...variable, - name: variable.name, - }, - }, - }, - }) - } - setContactVariableValue(variable.name) - setWrongVariable(false) - return - } - - setWrongVariable(false) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pous]) - - /** - * Handle with the variable input onBlur event - */ - const handleSubmitContactVariableOnTextareaBlur = (variableName?: string) => { - const variableNameToSubmit = variableName || contactVariableValue - const { rung, node, variables } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id, - variableName: variableNameToSubmit, - }) - if (!rung || !node) return - - const variable = variables.selected - if ( - !variable || - variable.name !== variableNameToSubmit || - variable.type.definition !== 'base-type' || - variable.type.value.toUpperCase() !== 'BOOL' - ) { - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable: { name: variableNameToSubmit }, - }, - }, - }) - setWrongVariable(true) - return - } - - updateNode({ - editorName: editor.meta.name, - rungId: rung.id, - nodeId: node.id, - node: { - ...node, - data: { - ...node.data, - variable, - }, - }, - }) - setWrongVariable(false) - } - - const onChangeHandler = () => { - if (!openAutocomplete) { - setOpenAutocomplete(true) - } - } - - return ( -
-
- {contact.svg(wrongVariable)} -
- { - e.target.select() - const { node, rung } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id ?? '', - }) - if (!node || !rung) return - updateNode({ - editorName: editor.meta.name, - nodeId: node.id, - rungId: rung.id, - node: { - ...node, - draggable: false, - }, - }) - return - }} - onBlur={() => { - const { node, rung } = getPouVariablesRungNodeAndEdges(editor, pous, flows, { - nodeId: id ?? '', - }) - if (!node || !rung) return - updateNode({ - editorName: editor.meta.name, - nodeId: node.id, - rungId: rung.id, - node: { - ...node, - draggable: node.data.draggable as boolean, - }, - }) - return - }} - onChange={onChangeHandler} - onKeyDown={(e) => { - if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Tab') e.preventDefault() - if (e.key === 'Enter' && autocompleteRef.current?.selectedVariable.positionInArray !== -1) { - inputVariableRef.current?.blur({ submit: false }) - } - setKeyPressedAtTextarea(e.key) - }} - onKeyUp={() => setKeyPressedAtTextarea('')} - /> - {openAutocomplete && ( -
-
- setOpenAutocomplete(value)} - keyPressed={keyPressedAtTextarea} - /> -
-
- )} -
-
- {data.handles.map((handle, index) => ( - - ))} -
- ) -} diff --git a/src2/frontend/components/_atoms/react-flow/index.tsx b/src2/frontend/components/_atoms/react-flow/index.tsx index 3c13a89c6..56bcc59b3 100644 --- a/src2/frontend/components/_atoms/react-flow/index.tsx +++ b/src2/frontend/components/_atoms/react-flow/index.tsx @@ -1,8 +1,9 @@ -import { cn } from '../../../utils/cn' import type { BackgroundProps, ControlProps, ReactFlowProps } from '@xyflow/react' import { Background, Controls, ReactFlow } from '@xyflow/react' import { PropsWithChildren } from 'react' +import { cn } from '../../../utils/cn' + type ReactFlowPanelProps = PropsWithChildren & { background?: boolean controls?: boolean diff --git a/src2/frontend/components/_atoms/resolution-warning-message/index.tsx b/src2/frontend/components/_atoms/resolution-warning-message/index.tsx index 62da7e10b..5b33f1e47 100644 --- a/src2/frontend/components/_atoms/resolution-warning-message/index.tsx +++ b/src2/frontend/components/_atoms/resolution-warning-message/index.tsx @@ -1,6 +1,7 @@ -import { LogoIcon } from '../../../assets/icons/interface/Logo' import { useEffect, useState } from 'react' +import { LogoIcon } from '../../../assets/icons/interface/Logo' + const ResolutionWarning = () => { const [isPortrait, setIsPortrait] = useState(window.innerHeight > window.innerWidth) diff --git a/src2/frontend/components/_atoms/select/index.tsx b/src2/frontend/components/_atoms/select/index.tsx index a4fdbd3a3..5822c6f0f 100644 --- a/src2/frontend/components/_atoms/select/index.tsx +++ b/src2/frontend/components/_atoms/select/index.tsx @@ -1,7 +1,8 @@ import * as PrimitiveSelect from '@radix-ui/react-select' +import { ComponentPropsWithoutRef, ElementRef, forwardRef, ReactElement } from 'react' + import { ArrowIcon } from '../../../assets/icons/interface/Arrow' import { cn } from '../../../utils/cn' -import { ComponentPropsWithoutRef, ElementRef, forwardRef, ReactElement } from 'react' const Select = PrimitiveSelect.Root diff --git a/src2/frontend/components/_atoms/tab/index.tsx b/src2/frontend/components/_atoms/tab/index.tsx index e6d707fd1..fdd4bbe3f 100644 --- a/src2/frontend/components/_atoms/tab/index.tsx +++ b/src2/frontend/components/_atoms/tab/index.tsx @@ -1,8 +1,10 @@ -import { ArrayIcon } from '../../../assets/icons/project/Array' +import { ComponentPropsWithoutRef, useCallback } from 'react' + import { CloseIcon } from '../../../assets/icons/interface/Close' import { ConfigIcon } from '../../../assets/icons/interface/Config' -import { CppIcon } from '../../../assets/icons/project/Cpp' import { DeviceTransferIcon } from '../../../assets/icons/interface/DeviceTransfer' +import { ArrayIcon } from '../../../assets/icons/project/Array' +import { CppIcon } from '../../../assets/icons/project/Cpp' import { EnumIcon } from '../../../assets/icons/project/Enum' import { FBDIcon } from '../../../assets/icons/project/FBD' import { ILIcon } from '../../../assets/icons/project/IL' @@ -15,11 +17,10 @@ import { ServerIcon } from '../../../assets/icons/project/Server' import { SFCIcon } from '../../../assets/icons/project/SFC' import { STIcon } from '../../../assets/icons/project/ST' import { StructureIcon } from '../../../assets/icons/project/Structure' -import type { TabsProps } from '../../../store/slices/tabs' import { useOpenPLCStore } from '../../../store' +import type { TabsProps } from '../../../store/slices/tabs' import { cn } from '../../../utils/cn' import { isUnsaved, unsavedLabel } from '../../../utils/unsaved-label' -import { ComponentPropsWithoutRef, useCallback } from 'react' type ITabProps = ComponentPropsWithoutRef<'div'> & { fileName: string diff --git a/src2/frontend/components/_atoms/table-actions/index.tsx b/src2/frontend/components/_atoms/table-actions/index.tsx index db05e4551..601158f2c 100644 --- a/src2/frontend/components/_atoms/table-actions/index.tsx +++ b/src2/frontend/components/_atoms/table-actions/index.tsx @@ -1,6 +1,6 @@ -import { cn } from '../../../utils/cn' import React, { ButtonHTMLAttributes, ReactNode } from 'react' +import { cn } from '../../../utils/cn' import { TableActionButton } from '../buttons/tables-actions' type TableAction = { diff --git a/src2/frontend/components/_atoms/table/index.tsx b/src2/frontend/components/_atoms/table/index.tsx index 309ddf244..81bdcd465 100644 --- a/src2/frontend/components/_atoms/table/index.tsx +++ b/src2/frontend/components/_atoms/table/index.tsx @@ -1,6 +1,7 @@ -import { cn } from '../../../utils/cn' import { ComponentPropsWithRef, forwardRef } from 'react' +import { cn } from '../../../utils/cn' + const Table = forwardRef & { context?: string }>( ({ className, context, ...res }, ref) => (
diff --git a/src2/frontend/components/_atoms/tooltip/index.tsx b/src2/frontend/components/_atoms/tooltip/index.tsx index 564f60c00..256979c64 100644 --- a/src2/frontend/components/_atoms/tooltip/index.tsx +++ b/src2/frontend/components/_atoms/tooltip/index.tsx @@ -1,7 +1,8 @@ import * as PrimitiveTooltip from '@radix-ui/react-tooltip' -import { cn } from '../../../utils/cn' import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react' +import { cn } from '../../../utils/cn' + const TooltipProvider = PrimitiveTooltip.Provider const Tooltip = PrimitiveTooltip.Root diff --git a/src2/frontend/components/_atoms/type-dropdown-selector/index.tsx b/src2/frontend/components/_atoms/type-dropdown-selector/index.tsx index 03e9b9ed7..b3e8c93b8 100644 --- a/src2/frontend/components/_atoms/type-dropdown-selector/index.tsx +++ b/src2/frontend/components/_atoms/type-dropdown-selector/index.tsx @@ -1,8 +1,8 @@ import * as PrimitiveDropdown from '@radix-ui/react-dropdown-menu' -import { ArrowIcon } from '../../../assets/icons/interface/Arrow' import _ from 'lodash' import { useState } from 'react' +import { ArrowIcon } from '../../../assets/icons/interface/Arrow' import { InputWithRef } from '../input' type TypeDropdownSelectorProps = { diff --git a/src2/frontend/components/_features/[app]/debug-manager/index.tsx b/src2/frontend/components/_features/[app]/debug-manager/index.tsx index 050daf664..c18bff61c 100644 --- a/src2/frontend/components/_features/[app]/debug-manager/index.tsx +++ b/src2/frontend/components/_features/[app]/debug-manager/index.tsx @@ -18,16 +18,17 @@ */ import { useCallback, useEffect, useRef } from 'react' -import { useOpenPLCStore } from '../../../../store' -import { useWebRTCConnection } from '../../../../hooks/useWebRTCConnection' -import { useDebugSession } from '../../../../hooks/useDebugSession' + import { useDebugPolling } from '../../../../hooks/useDebugPolling' +import { useDebugSession } from '../../../../hooks/useDebugSession' +import { useWebRTCConnection } from '../../../../hooks/useWebRTCConnection' import { debugBridge } from '../../../../services/debug/debug-bridge' import { ModbusRtuTransport } from '../../../../services/debug/transports/modbus-rtu-transport' -import { WebRTCTransport } from '../../../../services/debug/transports/webrtc-transport' import type { WebRTCTransportConfig } from '../../../../services/debug/transports/webrtc-transport' +import { WebRTCTransport } from '../../../../services/debug/transports/webrtc-transport' +import { clearDebugSessionControls,setDebugSessionControls } from '../../../../services/debug-session-controls' import { simulatorService } from '../../../../services/simulator/simulator-service' -import { setDebugSessionControls, clearDebugSessionControls } from '../../../../services/debug-session-controls' +import { useOpenPLCStore } from '../../../../store' export const DebugManager = () => { const { runtimeConnection, workspaceActions, consoleActions, webrtcActions } = useOpenPLCStore() diff --git a/src2/frontend/components/_features/[app]/loading-overlay/index.tsx b/src2/frontend/components/_features/[app]/loading-overlay/index.tsx index 625eabc18..9b906e370 100644 --- a/src2/frontend/components/_features/[app]/loading-overlay/index.tsx +++ b/src2/frontend/components/_features/[app]/loading-overlay/index.tsx @@ -1,7 +1,9 @@ import { useOpenPLCStore } from '../../../../store' export const LoadingOverlay = () => { - const { isProjectLoading, projectLoadingMessage } = useOpenPLCStore() + const { + workspace: { isProjectLoading, projectLoadingMessage }, + } = useOpenPLCStore() if (!isProjectLoading) { return null diff --git a/src2/frontend/components/_features/[app]/toast/index.tsx b/src2/frontend/components/_features/[app]/toast/index.tsx index f7c654b03..ff3395197 100644 --- a/src2/frontend/components/_features/[app]/toast/index.tsx +++ b/src2/frontend/components/_features/[app]/toast/index.tsx @@ -1,9 +1,10 @@ import * as PrimitiveToast from '@radix-ui/react-toast' -import { CloseIcon } from '../../../../assets/icons/interface/Close' -import { cn } from '../../../../utils/cn' import { cva, type VariantProps } from 'cva' import { ComponentPropsWithoutRef, ElementRef, forwardRef, ReactElement } from 'react' +import { CloseIcon } from '../../../../assets/icons/interface/Close' +import { cn } from '../../../../utils/cn' + const ToastProvider = PrimitiveToast.ToastProvider const ToastViewport = forwardRef< diff --git a/src2/frontend/components/_features/[app]/toast/use-toast.tsx b/src2/frontend/components/_features/[app]/toast/use-toast.tsx index e5d1a6432..4924254a9 100644 --- a/src2/frontend/components/_features/[app]/toast/use-toast.tsx +++ b/src2/frontend/components/_features/[app]/toast/use-toast.tsx @@ -18,7 +18,7 @@ type ToasterToast = ToastProps & { action?: ToastActionElement } -// eslint-disable-next-line @typescript-eslint/no-unused-vars + const _actionTypes = { ADD_TOAST: 'ADD_TOAST', UPDATE_TOAST: 'UPDATE_TOAST', diff --git a/src2/frontend/components/_features/[start]/menu/index.tsx b/src2/frontend/components/_features/[start]/menu/index.tsx index b621e7066..a12c920da 100644 --- a/src2/frontend/components/_features/[start]/menu/index.tsx +++ b/src2/frontend/components/_features/[start]/menu/index.tsx @@ -1,7 +1,8 @@ -import { Button } from '../../../_atoms/buttons/default' -import { cn } from '../../../../utils/cn' import { ComponentPropsWithoutRef } from 'react' +import { cn } from '../../../../utils/cn' +import { Button } from '../../../_atoms/buttons/default' + type IMenuRootProps = ComponentPropsWithoutRef<'div'> const MenuRoot = (props: IMenuRootProps) => { const { children, ...res } = props diff --git a/src2/frontend/components/_features/[start]/new-project/interval-model.tsx b/src2/frontend/components/_features/[start]/new-project/interval-model.tsx index df37404b1..f48f5c5d4 100644 --- a/src2/frontend/components/_features/[start]/new-project/interval-model.tsx +++ b/src2/frontend/components/_features/[start]/new-project/interval-model.tsx @@ -1,7 +1,7 @@ -import { Modal, ModalContent, ModalTitle, ModalTrigger } from '../../../_molecules/modal' -import { cn } from '../../../../utils/cn' import { useEffect, useMemo, useState } from 'react' +import { cn } from '../../../../utils/cn' +import { Modal, ModalContent, ModalTitle, ModalTrigger } from '../../../_molecules/modal' import ArrowButtonGroup from '../../[workspace]/editor/graphical/elements/arrow-button-group' type IntervalModalProps = { diff --git a/src2/frontend/components/_features/[start]/new-project/project-modal.tsx b/src2/frontend/components/_features/[start]/new-project/project-modal.tsx index 890b6470a..a5529d692 100644 --- a/src2/frontend/components/_features/[start]/new-project/project-modal.tsx +++ b/src2/frontend/components/_features/[start]/new-project/project-modal.tsx @@ -1,7 +1,7 @@ -import { Modal, ModalContent, ModalTitle } from '../../../_molecules/modal' -import { useOpenPLCStore } from '../../../../store' import { useEffect, useState } from 'react' +import { useOpenPLCStore } from '../../../../store' +import { Modal, ModalContent, ModalTitle } from '../../../_molecules/modal' import { Step1 } from './steps/first-step' import { Step2 } from './steps/second-step' import { Step3 } from './steps/third-step' diff --git a/src2/frontend/components/_features/[start]/new-project/steps/first-step.tsx b/src2/frontend/components/_features/[start]/new-project/steps/first-step.tsx index 847006529..694a1663f 100644 --- a/src2/frontend/components/_features/[start]/new-project/steps/first-step.tsx +++ b/src2/frontend/components/_features/[start]/new-project/steps/first-step.tsx @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { BookIcon } from '../../../../../assets/icons/interface/Book' -import { FolderIcon } from '../../../../../assets/icons/interface/Folder' -import { cn } from '../../../../../utils/cn' import { useEffect, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' +import { BookIcon } from '../../../../../assets/icons/interface/Book' +import { FolderIcon } from '../../../../../assets/icons/interface/Folder' +import { cn } from '../../../../../utils/cn' import { NewProjectStore } from '../store' const Step1 = ({ onNext, onClose }: { onNext: () => void; onClose: () => void }) => { diff --git a/src2/frontend/components/_features/[start]/new-project/steps/second-step.tsx b/src2/frontend/components/_features/[start]/new-project/steps/second-step.tsx index 1088c90d2..27f18cf9d 100644 --- a/src2/frontend/components/_features/[start]/new-project/steps/second-step.tsx +++ b/src2/frontend/components/_features/[start]/new-project/steps/second-step.tsx @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { PathIcon } from '../../../../../assets/icons/interface/Path' -import { cn } from '../../../../../utils/cn' -import { useCapabilities, useProject } from '../../../../../../middleware/shared/providers/platform-context' import { useEffect, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' +import { useCapabilities, useProject } from '../../../../../../middleware/shared/providers/platform-context' +import { PathIcon } from '../../../../../assets/icons/interface/Path' +import { cn } from '../../../../../utils/cn' import { NewProjectStore } from '../store' const Step2 = ({ onNext, onPrev }: { onNext: () => void; onPrev: () => void }) => { diff --git a/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx b/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx index 9266cc586..f4e00ed28 100644 --- a/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx +++ b/src2/frontend/components/_features/[start]/new-project/steps/third-step.tsx @@ -1,14 +1,14 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { PouLanguageSources } from '../../../../../data/sources/POU' +import { useState } from 'react' +import { Controller, SubmitHandler, useForm } from 'react-hook-form' + +import { useProject } from '../../../../../../middleware/shared/providers' import { TimerIcon } from '../../../../../assets/icons/interface/Timer' -import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../_atoms/select' +import { PouLanguageSources } from '../../../../../data/sources/POU' import { useOpenPLCStore } from '../../../../../store' import { cn } from '../../../../../utils/cn' import { ConvertToLangShortenedFormat } from '../../../../../utils/formatters/POU' -import { useProject } from '../../../../../../middleware/shared/providers' -import { useState } from 'react' -import { Controller, SubmitHandler, useForm } from 'react-hook-form' - +import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../_atoms/select' import { useToast } from '../../../[app]/toast/use-toast' import { IntervalModal } from '../interval-model' import { NewProjectStore } from '../store' @@ -85,7 +85,7 @@ const Step3 = ({ onPrev, onFinish, onClose }: { onPrev: () => void; onFinish: () }) } setEditingState('saved') - // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (_error) { toast({ title: 'Cannot create a project!', diff --git a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-input.tsx b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-input.tsx index 986baecef..2db47f3de 100644 --- a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-input.tsx +++ b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-input.tsx @@ -1,6 +1,7 @@ -import { useOpenPLCStore } from '../../../../store' import { useCallback, useRef, useState } from 'react' +import { useOpenPLCStore } from '../../../../store' + type AIChatInputProps = { onSend: (message: string) => void onCancel: () => void diff --git a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-message.tsx b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-message.tsx index 8fce6d046..72fc4d04b 100644 --- a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-message.tsx +++ b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-message.tsx @@ -1,7 +1,8 @@ -import { useOpenPLCStore } from '../../../../store' +import Markdown from 'react-markdown' + import { trackChatRating } from '../../../../services/ai/telemetry' +import { useOpenPLCStore } from '../../../../store' import type { ChatMessage } from '../../../../store/slices/ai/types' -import Markdown from 'react-markdown' import { AICodeBlock } from './ai-code-block' type AIMessageProps = { diff --git a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx index cb47f2f07..805e4624e 100644 --- a/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx +++ b/src2/frontend/components/_features/[workspace]/ai-chat/ai-chat-panel.tsx @@ -1,11 +1,11 @@ +import { useCallback, useEffect, useRef, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + import { useAIChat } from '../../../../hooks/useAI' import { collectProjectContext } from '../../../../services/ai/context-collector' import { trackChatMessage } from '../../../../services/ai/telemetry' import type { AIChatMessage } from '../../../../services/ai/types' import { openPLCStoreBase, useOpenPLCStore } from '../../../../store' -import { useCallback, useEffect, useRef, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' - import { AIChatInput } from './ai-chat-input' import { AIChatMessage as ChatMessageComponent } from './ai-chat-message' @@ -128,7 +128,7 @@ export const AIChatPanel = () => { { messages: apiMessages, pouContext, - language: language as 'st' | 'il' | 'python' | 'cpp' | undefined, + language: language, }, (token) => { accumulated += token diff --git a/src2/frontend/components/_features/[workspace]/create-element/element-card/data-type-element.tsx b/src2/frontend/components/_features/[workspace]/create-element/element-card/data-type-element.tsx index 2b8ff164e..f4eaabe90 100644 --- a/src2/frontend/components/_features/[workspace]/create-element/element-card/data-type-element.tsx +++ b/src2/frontend/components/_features/[workspace]/create-element/element-card/data-type-element.tsx @@ -1,6 +1,7 @@ -import { ComponentPropsWithoutRef } from 'react' -import { EnumIcon, StructureIcon, ArrayIcon } from '../../../../../assets/icons/project' import { startCase } from 'lodash' +import { ComponentPropsWithoutRef } from 'react' + +import { ArrayIcon,EnumIcon, StructureIcon } from '../../../../../assets/icons/project' type CreateDataTypeProps = ComponentPropsWithoutRef<'div'> & { derivation: 'enumerated' | 'structure' | 'array' diff --git a/src2/frontend/components/_features/[workspace]/create-element/element-card/index.tsx b/src2/frontend/components/_features/[workspace]/create-element/element-card/index.tsx index b3027dc13..fc4276bdd 100644 --- a/src2/frontend/components/_features/[workspace]/create-element/element-card/index.tsx +++ b/src2/frontend/components/_features/[workspace]/create-element/element-card/index.tsx @@ -1,24 +1,25 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { CreatePouSources, PouLanguageSources } from '../../../../../data/sources/POU' import * as Popover from '@radix-ui/react-popover' + +import type { PLCDataType } from '../../../../../../middleware/shared/ports/types' import { ArrowIcon } from '../../../../../assets/icons/interface/Arrow' -import { InputWithRef } from '../../../../_atoms/input' -import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../_atoms/select' import { DatatypeDerivationSources } from '../../../../../data/sources/data-type' +import { CreatePouSources, PouLanguageSources } from '../../../../../data/sources/POU' import { useOpenPLCStore } from '../../../../../store' -import type { PLCDataType } from '../../../../../../middleware/shared/ports/types' +import { InputWithRef } from '../../../../_atoms/input' +import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../_atoms/select' type PLCArrayDatatype = Extract type PLCEnumeratedDatatype = Extract type PLCStructureDatatype = Extract -import { cn } from '../../../../../utils/cn' -import { isArduinoTarget as checkIsArduinoTarget, isOpenPLCRuntimeV4Target, isSimulatorTarget } from '../../../../../utils/device' -import { ConvertToLangShortenedFormat } from '../../../../../utils/formatters/POU' import { startCase } from 'lodash' import { Dispatch, ReactNode, SetStateAction, useState } from 'react' import { Controller, SubmitHandler, useForm } from 'react-hook-form' +import { cn } from '../../../../../utils/cn' +import { isArduinoTarget as checkIsArduinoTarget, isOpenPLCRuntimeV4Target, isSimulatorTarget } from '../../../../../utils/device' +import { ConvertToLangShortenedFormat } from '../../../../../utils/formatters/POU' import { useToast } from '../../../[app]/toast/use-toast' type ElementCardProps = { diff --git a/src2/frontend/components/_features/[workspace]/create-element/index.tsx b/src2/frontend/components/_features/[workspace]/create-element/index.tsx index f0684f5cc..5824423a1 100644 --- a/src2/frontend/components/_features/[workspace]/create-element/index.tsx +++ b/src2/frontend/components/_features/[workspace]/create-element/index.tsx @@ -2,8 +2,8 @@ import * as Popover from '@radix-ui/react-popover' import { useState } from 'react' import { PlusIcon } from '../../../../assets/icons/interface/Plus' -import { cn } from '../../../../utils/cn' import { useOpenPLCStore } from '../../../../store' +import { cn } from '../../../../utils/cn' import { ElementCard } from './element-card' const CreatePLCElement = () => { diff --git a/src2/frontend/components/_features/[workspace]/data-type/index.tsx b/src2/frontend/components/_features/[workspace]/data-type/index.tsx index 37a19ac70..5d7c6004c 100644 --- a/src2/frontend/components/_features/[workspace]/data-type/index.tsx +++ b/src2/frontend/components/_features/[workspace]/data-type/index.tsx @@ -1,11 +1,12 @@ +import { ComponentPropsWithoutRef, useEffect, useState } from 'react' + +import type { PLCDataType } from '../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../store' +import { extractSearchQuery } from '../../../../store/slices/search/utils' import { InputWithRef } from '../../../_atoms/input' import { ArrayDataType } from '../../../_molecules/data-types/array' import { EnumeratorDataType } from '../../../_molecules/data-types/enumerated' import { StructureDataType } from '../../../_molecules/data-types/structure' -import { useOpenPLCStore } from '../../../../store' -import { extractSearchQuery } from '../../../../store/slices/search/utils' -import type { PLCDataType } from '../../../../../middleware/shared/ports/types' -import { ComponentPropsWithoutRef, useEffect, useState } from 'react' type DatatypeEditorProps = ComponentPropsWithoutRef<'div'> & { dataTypeName: string diff --git a/src2/frontend/components/_features/[workspace]/editor/data-type/index.tsx b/src2/frontend/components/_features/[workspace]/editor/data-type/index.tsx index b38de48b6..e358cbdba 100644 --- a/src2/frontend/components/_features/[workspace]/editor/data-type/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/data-type/index.tsx @@ -1,11 +1,12 @@ +import { ComponentPropsWithoutRef, useEffect, useState } from 'react' + +import type { PLCDataType } from '../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../store' +import { extractSearchQuery } from '../../../../../store/slices/search/utils' import { InputWithRef } from '../../../../_atoms/input' import { ArrayDataType } from '../../../../_molecules/data-types/array' import { EnumeratorDataType } from '../../../../_molecules/data-types/enumerated' import { StructureDataType } from '../../../../_molecules/data-types/structure' -import { useOpenPLCStore } from '../../../../../store' -import { extractSearchQuery } from '../../../../../store/slices/search/utils' -import type { PLCDataType } from '../../../../../../middleware/shared/ports/types' -import { ComponentPropsWithoutRef, useEffect, useState } from 'react' type DatatypeEditorProps = ComponentPropsWithoutRef<'div'> & { dataTypeName: string diff --git a/src2/frontend/components/_features/[workspace]/editor/device/components/configuration-editor.tsx b/src2/frontend/components/_features/[workspace]/editor/device/components/configuration-editor.tsx index 3eb8b6930..ce410bf24 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/components/configuration-editor.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/components/configuration-editor.tsx @@ -1,7 +1,7 @@ -import { Checkbox } from '../../../../../_atoms/checkbox' -import { useOpenPLCStore } from '../../../../../../store' import { useEffect, useState } from 'react' +import { useOpenPLCStore } from '../../../../../../store' +import { Checkbox } from '../../../../../_atoms/checkbox' import { BoardConfiguration } from '../elements/board-configuration' import { RTUSettings } from '../elements/rtu-settings' import { TCPSettings } from '../elements/tcp-settings' diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx index 54bb1bbed..360a6f8f3 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/board.tsx @@ -1,22 +1,22 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { boardSelectors, compileOnlySelectors, pinSelectors } from '../../../../../../hooks/use-store-selectors' +import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' + +import type { TimingStats } from '../../../../../../../middleware/shared/ports/types' +import { useDevice, useRuntime } from '../../../../../../../middleware/shared/providers/platform-context' import { MinusIcon } from '../../../../../../assets/icons/interface/Minus' import { PlusIcon } from '../../../../../../assets/icons/interface/Plus' import { RefreshIcon } from '../../../../../../assets/icons/interface/Refresh' +import { boardSelectors, compileOnlySelectors, pinSelectors } from '../../../../../../hooks/use-store-selectors' +import { useOpenPLCStore } from '../../../../../../store' +import type { RuntimeConnection } from '../../../../../../store/slices/device/types' +import { cn } from '../../../../../../utils/cn' +import { isArduinoTarget, isOpenPLCRuntimeTarget, isOpenPLCRuntimeV4Target, isSimulatorTarget, validateRuntimeVersion } from '../../../../../../utils/device' import { Checkbox } from '../../../../../_atoms/checkbox' import { Label } from '../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../_atoms/select' import TableActions from '../../../../../_atoms/table-actions' import { Modal, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '../../../../../_molecules/modal' import { DeviceEditorSlot } from '../../../../../_templates/[editors]/device-editor-slot' -import { useOpenPLCStore } from '../../../../../../store' -import type { RuntimeConnection } from '../../../../../../store/slices/device/types' -import type { TimingStats } from '../../../../../../../middleware/shared/ports/types' -import { useDevice, useRuntime } from '../../../../../../../middleware/shared/providers/platform-context' -import { cn } from '../../../../../../utils/cn' -import { isArduinoTarget, isOpenPLCRuntimeTarget, isOpenPLCRuntimeV4Target, isSimulatorTarget, validateRuntimeVersion } from '../../../../../../utils/device' -import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' - import { PinMappingTable } from './components/pin-mapping-table' const Board = memo(function () { diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/communication.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/communication.tsx index bcbb6c2b1..71c05212e 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/communication.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/communication.tsx @@ -1,11 +1,11 @@ +import { useEffect, useMemo } from 'react' + import { communicationSelectors } from '../../../../../../hooks/use-store-selectors' +import { useOpenPLCStore } from '../../../../../../store' +import { cn } from '../../../../../../utils/cn' import { Checkbox } from '../../../../../_atoms/checkbox' import { Label } from '../../../../../_atoms/label' import { DeviceEditorSlot } from '../../../../../_templates/[editors]/device-editor-slot' -import { useOpenPLCStore } from '../../../../../../store' -import { cn } from '../../../../../../utils/cn' -import { useEffect, useMemo } from 'react' - import { ModbusRTUComponent } from './components/modbus-rtu' import { ModbusTCPComponent } from './components/modbus-tcp' diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-rtu.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-rtu.tsx index 1477aae46..5aeac9d59 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-rtu.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-rtu.tsx @@ -1,13 +1,14 @@ import { zodResolver } from '@hookform/resolvers/zod' +import { memo, useEffect, useRef, useState } from 'react' +import { Controller, useForm } from 'react-hook-form' +import { z } from 'zod' + import { rtuSelectors } from '../../../../../../../hooks/use-store-selectors' +import { cn } from '../../../../../../../utils/cn' import { Checkbox } from '../../../../../../_atoms/checkbox' import { InputWithRef } from '../../../../../../_atoms/input' import { Label } from '../../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../../_atoms/select' -import { cn } from '../../../../../../../utils/cn' -import { memo, useEffect, useRef, useState } from 'react' -import { Controller, useForm } from 'react-hook-form' -import { z } from 'zod' const INPUT_STYLES = { default: diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-tcp.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-tcp.tsx index 7cb991819..b8d6cadc2 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-tcp.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/modbus-tcp.tsx @@ -1,15 +1,15 @@ import { zodResolver } from '@hookform/resolvers/zod' +import { ComponentPropsWithoutRef, memo } from 'react' +import { Controller, useForm } from 'react-hook-form' +import { z } from 'zod' + import { tcpSelectors } from '../../../../../../../hooks/use-store-selectors' +import { useOpenPLCStore } from '../../../../../../../store' +import { cn } from '../../../../../../../utils/cn' import { Checkbox } from '../../../../../../_atoms/checkbox' import { InputWithRef } from '../../../../../../_atoms/input' import { Label } from '../../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../../_atoms/select' -import { useOpenPLCStore } from '../../../../../../../store' -import { cn } from '../../../../../../../utils/cn' -import { ComponentPropsWithoutRef, memo } from 'react' -import { Controller, useForm } from 'react-hook-form' -import { z } from 'zod' - import { StaticHostConfigurationComponent } from './static-host' const INPUT_STYLES = { diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/pin-mapping-table.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/pin-mapping-table.tsx index d46fc4c0a..31f40ed50 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/pin-mapping-table.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/pin-mapping-table.tsx @@ -1,10 +1,11 @@ +import { createColumnHelper } from '@tanstack/react-table' + +import type { DevicePin } from '../../../../../../../../middleware/shared/ports/types' +import { pinSelectors } from '../../../../../../../hooks/use-store-selectors' import { GenericTable } from '../../../../../../_atoms/generic-table' import { PinComboboxInputCell } from '../../../../../../_molecules/pin-mapping-table/combobox-input' import { PinSelectInputCell } from '../../../../../../_molecules/pin-mapping-table/select-input' import { PinTextInputCell } from '../../../../../../_molecules/pin-mapping-table/text-input' -import { pinSelectors } from '../../../../../../../hooks/use-store-selectors' -import type { DevicePin } from '../../../../../../../../middleware/shared/ports/types' -import { createColumnHelper } from '@tanstack/react-table' const columnHelper = createColumnHelper() diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/static-host.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/static-host.tsx index 32596eadc..ac87f7dea 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/static-host.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/components/static-host.tsx @@ -1,11 +1,12 @@ import { zodResolver } from '@hookform/resolvers/zod' -import { staticHostSelectors } from '../../../../../../../hooks/use-store-selectors' -import { InputWithRef } from '../../../../../../_atoms/input' -import { Label } from '../../../../../../_atoms/label' import { ComponentPropsWithoutRef, memo } from 'react' import { Controller, useForm } from 'react-hook-form' import { z } from 'zod' +import { staticHostSelectors } from '../../../../../../../hooks/use-store-selectors' +import { InputWithRef } from '../../../../../../_atoms/input' +import { Label } from '../../../../../../_atoms/label' + const INPUT_STYLES = { default: 'flex h-[30px] w-full items-center justify-between gap-1 rounded-md border border-neutral-100 bg-white px-2 py-1 font-caption text-cp-sm font-medium text-neutral-850 outline-none focus:border-brand-medium-dark dark:border-neutral-850 dark:bg-neutral-950 dark:text-neutral-300', diff --git a/src2/frontend/components/_features/[workspace]/editor/device/configuration/index.tsx b/src2/frontend/components/_features/[workspace]/editor/device/configuration/index.tsx index 30aeb5028..b03d30354 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/configuration/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/configuration/index.tsx @@ -1,5 +1,4 @@ import { DeviceEditorTemplate } from '../../../../../_templates/[editors]/device-editor-template' - import { Board } from './board' import { Communication } from './communication' diff --git a/src2/frontend/components/_features/[workspace]/editor/device/elements/board-configuration/index.tsx b/src2/frontend/components/_features/[workspace]/editor/device/elements/board-configuration/index.tsx index 2cb91af16..525d7e363 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/elements/board-configuration/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/elements/board-configuration/index.tsx @@ -1,7 +1,8 @@ +import { useCallback, useMemo, useState } from 'react' + import { RefreshIcon } from '../../../../../../../assets/icons/interface/Refresh' -import { SelectField } from '../../../../../../_molecules/select-field' import { useOpenPLCStore } from '../../../../../../../store' -import { useCallback, useMemo, useState } from 'react' +import { SelectField } from '../../../../../../_molecules/select-field' const BoardConfiguration = () => { const [isPressed, setIsPressed] = useState(false) @@ -60,7 +61,7 @@ const BoardConfiguration = () => {
p.name)} setSelectedOption={setCommunicationPort} label='Programming Port' placeholder={communicationPort} diff --git a/src2/frontend/components/_features/[workspace]/editor/device/elements/rtu-settings/index.tsx b/src2/frontend/components/_features/[workspace]/editor/device/elements/rtu-settings/index.tsx index 3a4b472de..73f53cc5f 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/elements/rtu-settings/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/elements/rtu-settings/index.tsx @@ -1,8 +1,9 @@ -import { InputField } from '../../../../../../_molecules/input-field' -import { SelectField } from '../../../../../../_molecules/select-field' +import { ComponentPropsWithoutRef } from 'react' + import { useOpenPLCStore } from '../../../../../../../store' import { cn } from '../../../../../../../utils/cn' -import { ComponentPropsWithoutRef } from 'react' +import { InputField } from '../../../../../../_molecules/input-field' +import { SelectField } from '../../../../../../_molecules/select-field' type RTUSettingsProps = ComponentPropsWithoutRef<'div'> & { userEnabled?: boolean @@ -10,11 +11,11 @@ type RTUSettingsProps = ComponentPropsWithoutRef<'div'> & { const RTUSettings = ({ userEnabled, ...props }: RTUSettingsProps) => { const { - deviceAvailableOptions: { availableRTUInterfaces, availableRTUBaudrates }, + deviceAvailableOptions: { availableRTUInterfaces, availableRTUBaudRates }, deviceDefinitions: { configuration: { communicationConfiguration: { - modbusRTU: { rtuInterface, rtuBaudrate }, + modbusRTU: { rtuInterface, rtuBaudRate }, }, }, }, @@ -44,8 +45,8 @@ const RTUSettings = ({ userEnabled, ...props }: RTUSettingsProps) => { & { userEnabled: boolean diff --git a/src2/frontend/components/_features/[workspace]/editor/device/index.tsx b/src2/frontend/components/_features/[workspace]/editor/device/index.tsx index 931466758..3670dd0d3 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/index.tsx @@ -1,6 +1,6 @@ -import { useOpenPLCStore } from '../../../../../store' import { useEffect } from 'react' +import { useOpenPLCStore } from '../../../../../store' import { DeviceConfigurationEditor } from './configuration' const DeviceEditor = () => { diff --git a/src2/frontend/components/_features/[workspace]/editor/device/orchestrators/orchestrators-list.tsx b/src2/frontend/components/_features/[workspace]/editor/device/orchestrators/orchestrators-list.tsx index 2ab5a2165..eff41d905 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/orchestrators/orchestrators-list.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/orchestrators/orchestrators-list.tsx @@ -1,14 +1,16 @@ -import { DeviceEditorSlot } from '../../../../../_templates/[editors]/device-editor-slot' -import { cn } from '../../../../../../utils/cn' import { useCallback, useEffect, useRef, useState } from 'react' + +import { type DeviceResponse,listOrchestratorsRequest, type OrchestratorResponse, } from '../../../../../../api/queries/orchestrators' import { ArrowIcon } from '../../../../../../assets/icons/interface/Arrow' import { RefreshIcon } from '../../../../../../assets/icons/interface/Refresh' -import { useOpenPLCStore } from '../../../../../../store' +import { WarningIcon } from '../../../../../../assets/icons/interface/Warning' import { runtimeGetUsersInfo, runtimeLogout } from '../../../../../../services/api/runtime-api' -import { RuntimeLoginModal, RuntimeCreateUserModal } from '../../../../../_organisms/modals' +import { useOpenPLCStore } from '../../../../../../store' +import { cn } from '../../../../../../utils/cn' +import { isDev } from '../../../../../../utils/get-env' import { Modal, ModalContent, ModalTitle } from '../../../../../_molecules/modal' -import { WarningIcon } from '../../../../../../assets/icons/interface/Warning' -import { listOrchestratorsRequest, type OrchestratorResponse, type DeviceResponse, } from '../../../../../../api/queries/orchestrators' +import { RuntimeCreateUserModal,RuntimeLoginModal } from '../../../../../_organisms/modals' +import { DeviceEditorSlot } from '../../../../../_templates/[editors]/device-editor-slot' // Note: Status and timing stats polling is handled globally by useRuntimePolling hook. // This component sets includeTimingStatsInPolling=true on mount to request timing stats. @@ -212,7 +214,7 @@ const OrchestratorsList = () => { console.error('[Orchestrators] Edge API fetch failed', error) // In development mode, fall back to mock data for local testing - if (import.meta.env?.DEV) { + if (isDev()) { console.info('[Orchestrators] Using mock data for development') await new Promise((resolve) => setTimeout(resolve, 300)) setOrchestrators(MOCK_ORCHESTRATORS) diff --git a/src2/frontend/components/_features/[workspace]/editor/device/remote-device/index.tsx b/src2/frontend/components/_features/[workspace]/editor/device/remote-device/index.tsx index d25f2848d..64d15662a 100644 --- a/src2/frontend/components/_features/[workspace]/editor/device/remote-device/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/device/remote-device/index.tsx @@ -1,19 +1,20 @@ import * as Popover from '@radix-ui/react-popover' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + +import type { ModbusIOGroup, ModbusIOPoint } from '../../../../../../../middleware/shared/ports/types' +import { useRuntime } from '../../../../../../../middleware/shared/providers/platform-context' import { ArrowIcon } from '../../../../../../assets/icons/interface/Arrow' import { MinusIcon } from '../../../../../../assets/icons/interface/Minus' import { PlusIcon } from '../../../../../../assets/icons/interface/Plus' +import { useOpenPLCStore } from '../../../../../../store' +import { cn } from '../../../../../../utils/cn' import { InputWithRef } from '../../../../../_atoms/input' import { Label } from '../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../_atoms/select' import TableActions from '../../../../../_atoms/table-actions' -import ScrollAreaComponent from '../../../../../ui/scroll-area' import { Modal, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '../../../../../_molecules/modal' -import { useOpenPLCStore } from '../../../../../../store' -import { useRuntime } from '../../../../../../../middleware/shared/providers/platform-context' -import type { ModbusIOGroup, ModbusIOPoint } from '../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../utils/cn' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' +import ScrollAreaComponent from '../../../../../ui/scroll-area' type FunctionCodeOption = { value: '1' | '2' | '3' | '4' | '5' | '6' | '15' | '16' @@ -490,7 +491,7 @@ const IOGroupRow = ({ onEdit, onUpdateAlias, }: IOGroupRowProps) => { - const firstIOPoint = ioGroup.ioPoints[0] + const firstIOPoint = ioGroup.ioPoints?.[0] const groupType = firstIOPoint?.type || '-' const groupAddress = firstIOPoint?.iecLocation || '-' const groupOffset = ioGroup.offset @@ -529,7 +530,7 @@ const IOGroupRow = ({ - {isExpanded && - ioGroup.ioPoints.map((ioPoint: ModbusIOPoint, index: number) => ( + (ioGroup.ioPoints ?? []).map((ioPoint: ModbusIOPoint, index: number) => ( state.editor) diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx index eeeb9d8da..ee6e10343 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/index.tsx @@ -1,36 +1,36 @@ import * as Switch from '@radix-ui/react-switch' -import { InputWithRef } from '../../../../../../../_atoms/input' +import { useEffect, useRef, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + +import { PLCPou } from '../../../../../../../../../middleware/shared/ports' +import { useOpenPLCStore } from '../../../../../../../../store' +import type { EditorModel } from '../../../../../../../../store/slices/editor' +import type { LibraryState } from '../../../../../../../../store/slices/library' +import { cn } from '../../../../../../../../utils/cn' +import { + assembleVariables, + buildNextExtensibleInput, + classifyBlockVariables, + getMinInputCount, + rebuildVariablesForInputCount, + removeLastExtensibleInput, +} from '../../../../../../../../utils/PLC/extensible-block-variables' import { BlockNode, BlockNodeData, BlockNodeElement, } from '../../../../../../../_atoms/graphical-editor/fbd/block' import { buildBlockNode } from '../../../../../../../_atoms/graphical-editor/fbd/buildNodes' -import { getBlockSize } from '../../../../../../../_atoms/graphical-editor/fbd/utils/utils' import { BasicNodeData } from '../../../../../../../_atoms/graphical-editor/fbd/utils/types' +import { getBlockSize } from '../../../../../../../_atoms/graphical-editor/fbd/utils/utils' import { getFBDPouVariablesRungNodeAndEdges } from '../../../../../../../_atoms/graphical-editor/fbd/utils/utils' import { BlockVariant } from '../../../../../../../_atoms/graphical-editor/types/block' import { getBlockDocumentation, getVariableRestrictionType, } from '../../../../../../../_atoms/graphical-editor/utils' +import { InputWithRef } from '../../../../../../../_atoms/input' import { Modal, ModalContent, ModalTitle } from '../../../../../../../_molecules/modal' -import { useOpenPLCStore } from '../../../../../../../../store' -import type { EditorModel } from '../../../../../../../../store/slices/editor' -import type { LibraryState } from '../../../../../../../../store/slices/library' -import { PLCPou } from '../../../../../../../../../middleware/shared/ports' -import { cn } from '../../../../../../../../utils/cn' -import { - assembleVariables, - buildNextExtensibleInput, - classifyBlockVariables, - getMinInputCount, - rebuildVariablesForInputCount, - removeLastExtensibleInput, -} from '../../../../../../../../utils/PLC/extensible-block-variables' -import { useEffect, useRef, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' - import ArrowButtonGroup from '../../arrow-button-group' import { ModalBlockLibrary } from './library' diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx index 35d06c5ad..d8af480cf 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/fbd/block/library.tsx @@ -1,8 +1,9 @@ +import { useState } from 'react' + import { MagnifierIcon } from '../../../../../../../../assets/icons/interface/Magnifier' +import { useOpenPLCStore } from '../../../../../../../../store' import { InputWithRef } from '../../../../../../../_atoms/input' import { LibraryFile, LibraryFolder, LibraryRoot } from '../../../../../../../_molecules/library-tree' -import { useOpenPLCStore } from '../../../../../../../../store' -import { useState } from 'react' export const ModalBlockLibrary = ({ selectedFileKey, diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx index 9d68ff8e7..1e716d9b7 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/index.tsx @@ -1,5 +1,20 @@ import * as Switch from '@radix-ui/react-switch' -import { InputWithRef } from '../../../../../../../_atoms/input' +import { useEffect, useRef, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + +import { PLCPou } from '../../../../../../../../../middleware/shared/ports' +import { useOpenPLCStore } from '../../../../../../../../store' +import type { EditorModel } from '../../../../../../../../store/slices/editor' +import type { LibraryState } from '../../../../../../../../store/slices/library' +import { cn } from '../../../../../../../../utils/cn' +import { + assembleVariables, + buildNextExtensibleInput, + classifyBlockVariables, + getMinInputCount, + rebuildVariablesForInputCount, + removeLastExtensibleInput, +} from '../../../../../../../../utils/PLC/extensible-block-variables' import { BlockNode, BlockNodeData, @@ -7,32 +22,17 @@ import { BlockVariant as LadderBlockVariant, } from '../../../../../../../_atoms/graphical-editor/ladder/block' import { buildBlockNode } from '../../../../../../../_atoms/graphical-editor/ladder/buildNodes' +import { BasicNodeData } from '../../../../../../../_atoms/graphical-editor/ladder/utils/types' import { getBlockSize } from '../../../../../../../_atoms/graphical-editor/ladder/utils/utils' import { getLadderPouVariablesRungNodeAndEdges } from '../../../../../../../_atoms/graphical-editor/ladder/utils/utils' -import { BasicNodeData } from '../../../../../../../_atoms/graphical-editor/ladder/utils/types' import { BlockVariant } from '../../../../../../../_atoms/graphical-editor/types/block' import { getBlockDocumentation, getVariableRestrictionType, } from '../../../../../../../_atoms/graphical-editor/utils' -import { Modal, ModalContent, ModalTitle } from '../../../../../../../_molecules/modal' +import { InputWithRef } from '../../../../../../../_atoms/input' import { updateDiagramElementsPosition } from '../../../../../../../_molecules/graphical-editor/ladder/rung/ladder-utils/elements/diagram' -import { useOpenPLCStore } from '../../../../../../../../store' -import type { EditorModel } from '../../../../../../../../store/slices/editor' -import type { LibraryState } from '../../../../../../../../store/slices/library' -import { PLCPou } from '../../../../../../../../../middleware/shared/ports' -import { cn } from '../../../../../../../../utils/cn' -import { - assembleVariables, - buildNextExtensibleInput, - classifyBlockVariables, - getMinInputCount, - rebuildVariablesForInputCount, - removeLastExtensibleInput, -} from '../../../../../../../../utils/PLC/extensible-block-variables' -import { useEffect, useRef, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' - +import { Modal, ModalContent, ModalTitle } from '../../../../../../../_molecules/modal' import ArrowButtonGroup from '../../arrow-button-group' import { ModalBlockLibrary } from './library' diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx index 35d06c5ad..d8af480cf 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/block/library.tsx @@ -1,8 +1,9 @@ +import { useState } from 'react' + import { MagnifierIcon } from '../../../../../../../../assets/icons/interface/Magnifier' +import { useOpenPLCStore } from '../../../../../../../../store' import { InputWithRef } from '../../../../../../../_atoms/input' import { LibraryFile, LibraryFolder, LibraryRoot } from '../../../../../../../_molecules/library-tree' -import { useOpenPLCStore } from '../../../../../../../../store' -import { useState } from 'react' export const ModalBlockLibrary = ({ selectedFileKey, diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/coil/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/coil/index.tsx index 3ec3061cf..0c1d5624f 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/coil/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/coil/index.tsx @@ -1,9 +1,10 @@ +import { useState } from 'react' + +import { useOpenPLCStore } from '../../../../../../../../store' import { CoilNode } from '../../../../../../../_atoms/graphical-editor/ladder/coil' import { DEFAULT_COIL_TYPES } from '../../../../../../../_atoms/graphical-editor/ladder/utils/constants' import { getLadderPouVariablesRungNodeAndEdges } from '../../../../../../../_atoms/graphical-editor/ladder/utils/utils' import { Modal, ModalContent, ModalTitle } from '../../../../../../../_molecules/modal' -import { useOpenPLCStore } from '../../../../../../../../store' -import { useState } from 'react' type CoilElementProps = { isOpen: boolean diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/contact/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/contact/index.tsx index dc0d23624..33f45d5fa 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/contact/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/elements/ladder/contact/index.tsx @@ -1,9 +1,10 @@ +import { useState } from 'react' + +import { useOpenPLCStore } from '../../../../../../../../store' import { ContactNode } from '../../../../../../../_atoms/graphical-editor/ladder/contact' import { DEFAULT_CONTACT_TYPES } from '../../../../../../../_atoms/graphical-editor/ladder/utils/constants' import { getLadderPouVariablesRungNodeAndEdges } from '../../../../../../../_atoms/graphical-editor/ladder/utils/utils' import { Modal, ModalContent, ModalTitle } from '../../../../../../../_molecules/modal' -import { useOpenPLCStore } from '../../../../../../../../store' -import { useState } from 'react' type ContactElementProps = { isOpen: boolean diff --git a/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx b/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx index ce862911e..8ded155ef 100644 --- a/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/graphical/ladder/index.tsx @@ -15,21 +15,21 @@ import { import { restrictToParentElement } from '@dnd-kit/modifiers' import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable' import * as Portal from '@radix-ui/react-portal' +import { useCallback, useEffect, useRef, useState } from 'react' +import { createPortal } from 'react-dom' +import { v4 as uuidv4 } from 'uuid' + +import { ladderSelectors } from '../../../../../../hooks/use-store-selectors' +import { openPLCStoreBase, useOpenPLCStore } from '../../../../../../store' +import { RungLadderState, zodLadderFlowSchema } from '../../../../../../store/slices/ladder' +import type { PouHistorySnapshot } from '../../../../../../store/slices/shared/types' +import { cn } from '../../../../../../utils/cn' import { BlockNode, BlockNodeData } from '../../../../../_atoms/graphical-editor/ladder/block' import { CoilNode } from '../../../../../_atoms/graphical-editor/ladder/coil' import { ContactNode } from '../../../../../_atoms/graphical-editor/ladder/contact' import { BlockVariant } from '../../../../../_atoms/graphical-editor/types/block' import { CreateRung } from '../../../../../_molecules/graphical-editor/ladder/rung/create-rung' import { Rung } from '../../../../../_organisms/graphical-editor/ladder/rung' -import { ladderSelectors } from '../../../../../../hooks/use-store-selectors' -import { openPLCStoreBase, useOpenPLCStore } from '../../../../../../store' -import { RungLadderState, zodLadderFlowSchema } from '../../../../../../store/slices/ladder' -import type { PouHistorySnapshot } from '../../../../../../store/slices/shared/types' -import { cn } from '../../../../../../utils/cn' -import { useCallback, useEffect, useRef, useState } from 'react' -import { createPortal } from 'react-dom' -import { v4 as uuidv4 } from 'uuid' - import BlockElement from '../elements/ladder/block' import CoilElement from '../elements/ladder/coil' import ContactElement from '../elements/ladder/contact' diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/ai-inline-completion-provider.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/ai-inline-completion-provider.ts index 9e261193e..9931b18f5 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/ai-inline-completion-provider.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/ai-inline-completion-provider.ts @@ -1,5 +1,7 @@ -import { openPLCStoreBase } from '../../../../../../store' import type * as monaco from 'monaco-editor' + +import { openPLCStoreBase } from '../../../../../../store' +import { getEnv } from '../../../../../../utils/get-env' import { buildFIMContext } from './context-builder' // --------------------------------------------------------------------------- @@ -63,17 +65,17 @@ function startTimer(): TelemetryTimer { return { elapsed: () => Math.round(performance.now() - start) } } -// eslint-disable-next-line @typescript-eslint/no-unused-vars + function trackCompletionRequested(_data: Record): void {} -// eslint-disable-next-line @typescript-eslint/no-unused-vars + function trackCompletionShown(_data: Record): void {} -// eslint-disable-next-line @typescript-eslint/no-unused-vars + function trackCompletionAccepted(_data: Record): void {} -// eslint-disable-next-line @typescript-eslint/no-unused-vars + function trackCompletionDismissed(_data: Record): void {} -// eslint-disable-next-line @typescript-eslint/no-unused-vars + function trackCompletionError(_data: Record): void {} -// eslint-disable-next-line @typescript-eslint/no-unused-vars + function trackCompletionTimeout(_data: Record): void {} // --------------------------------------------------------------------------- @@ -120,8 +122,7 @@ async function* streamAIRequest( ): AsyncGenerator { // Determine the base URL. In the web app, this comes from VITE_EDGE_API_URL. // Falls back to a relative path if the env var is not set. - const envBase = - typeof import.meta !== 'undefined' && (import.meta as Record>).env?.VITE_EDGE_API_URL + const envBase = getEnv('VITE_EDGE_API_URL') const baseUrl = envBase ? `${envBase}/ai` : '/api/ai' const response = await fetch(`${baseUrl}${endpoint}`, { @@ -354,12 +355,12 @@ export class AIInlineCompletionProvider implements monaco.languages.InlineComple } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars + freeInlineCompletions(_completions: monaco.languages.InlineCompletions): void { this.trackAcceptOrDismiss() } - // eslint-disable-next-line @typescript-eslint/no-unused-vars + disposeInlineCompletions(_completions: monaco.languages.InlineCompletions): void { this.trackAcceptOrDismiss() } diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts index ad366dab5..a7e5af0c6 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-completion/context-builder.ts @@ -1,6 +1,7 @@ -import { openPLCStoreBase } from '../../../../../../store' import type * as monaco from 'monaco-editor' +import { openPLCStoreBase } from '../../../../../../store' + /** Maximum characters to extract before cursor for FIM prefix */ const MAX_PREFIX_CHARS = 3000 /** Maximum characters to extract after cursor for FIM suffix */ diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-consent-modal.tsx b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-consent-modal.tsx index 1938ce81c..be494b8dd 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/ai-consent-modal.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/ai-consent-modal.tsx @@ -1,5 +1,5 @@ -import { Modal, ModalContent, ModalTitle } from '../../../../_molecules/modal' import { useOpenPLCStore } from '../../../../../store' +import { Modal, ModalContent, ModalTitle } from '../../../../_molecules/modal' const AIConsentModal = () => { const { diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/datatype.completion.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/datatype.completion.ts index 024ea5f27..3cbfd3a43 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/datatype.completion.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/datatype.completion.ts @@ -1,8 +1,9 @@ -import type { VariableDTO } from '../../../../../../store/slices/project' -import { PLCDataType, PLCStructureVariable } from '../../../../../../../middleware/shared/ports/types' import { escapeRegExp } from 'lodash' import * as monaco from 'monaco-editor' +import { PLCDataType, PLCStructureVariable } from '../../../../../../../middleware/shared/ports/types' +import type { VariableDTO } from '../../../../../../store/slices/project' + interface DataTypeCompletionContext { isAfterDot: boolean instancePath: string[] // Changed: array to support multi-level like ["B", "J"] diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts index 1e8e83a86..6ff3ecf33 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/fb.completion.ts @@ -1,9 +1,10 @@ -import { StandardFunctionBlocks } from '../../../../../../data/library/standard-function-blocks' -import type { VariableDTO } from '../../../../../../store/slices/project' -import { PLCProject } from '../../../../../../../middleware/shared/ports/types' import { escapeRegExp } from 'lodash' import * as monaco from 'monaco-editor' +import { PLCProject } from '../../../../../../../middleware/shared/ports/types' +import { StandardFunctionBlocks } from '../../../../../../data/library/standard-function-blocks' +import type { VariableDTO } from '../../../../../../store/slices/project' + interface FBCompletionContext { isAfterDot: boolean instancePath: string[] // Changed: array to support multi-level @@ -48,7 +49,7 @@ function analyzeContext(model: monaco.editor.ITextModel, position: monaco.IPosit function findFinalType( instancePath: string[], pouVariables: VariableDTO['data'][] = [], - customFBs: PLCProject['data']['pous'] = [], + customFBs: PLCProject['pous'] = [], ): { type: string; isStandard: boolean } | null { if (instancePath.length === 0) return null @@ -114,7 +115,7 @@ function findFBType( code: string, instanceName: string, pouVariables: VariableDTO['data'][] = [], - customFBs: PLCProject['data']['pous'] = [], + customFBs: PLCProject['pous'] = [], ): { type: string; isStandard: boolean } | null { // First, check in POU variables (from the store) const pouVariable = pouVariables.find((variable) => { @@ -197,7 +198,7 @@ function getStandardFBVariableSuggestions( */ function getCustomFBVariableSuggestions( fbType: string, - customFBs: PLCProject['data']['pous'], + customFBs: PLCProject['pous'], range: monaco.IRange, editorName: string, ): monaco.languages.CompletionItem[] { @@ -227,7 +228,7 @@ function getCustomFBVariableSuggestions( * Get custom function block instance suggestions (for direct FB calls) */ function getCustomFBInstanceSuggestions( - customFBs: PLCProject['data']['pous'], + customFBs: PLCProject['pous'], range: monaco.IRange, editorName: string, ): monaco.languages.CompletionItem[] { @@ -282,7 +283,7 @@ export const fbCompletion = ({ range: monaco.IRange editorName: string pouVariables?: VariableDTO['data'][] - customFBs?: PLCProject['data']['pous'] + customFBs?: PLCProject['pous'] }) => { const context = analyzeContext(model, position) const code = model.getValue() diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts index 7bf7d32fb..1b90f1637 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/completion/index.ts @@ -1,9 +1,9 @@ -import type { EditorState } from '../../../../../../store/slices/editor' -import type { LibraryState } from '../../../../../../store/slices/library' -import { PLCVariable } from '../../../../../../../middleware/shared/ports/types' -import { PLCProject } from '../../../../../../../middleware/shared/ports/types' import * as monaco from 'monaco-editor' +import { PLCVariable } from '../../../../../../../middleware/shared/ports/types' +import { PLCProject } from '../../../../../../../middleware/shared/ports/types' +import type { EditorState } from '../../../../../../store/slices/editor' +import type { LibraryState } from '../../../../../../store/slices/library' import { pythonSnippets } from '../configs/languages/python/python.snippets' import { stSnippets } from '../configs/languages/st/st.snippets' import { parsePouToStText } from '../drag-and-drop/st' @@ -330,7 +330,7 @@ export const libraryCompletion = ({ }: { range: monaco.IRange library: LibraryState['libraries'] - pous: PLCProject['data']['pous'] + pous: PLCProject['pous'] editor: EditorState['editor'] }) => { const systemSuggestions = library.system diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/configs/languages/st/st.ts b/src2/frontend/components/_features/[workspace]/editor/monaco/configs/languages/st/st.ts index cda15fc8a..fc47741e8 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/configs/languages/st/st.ts +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/configs/languages/st/st.ts @@ -1,7 +1,8 @@ /* eslint-disable no-useless-escape */ -import { PLCDataType } from '../../../../../../../../../middleware/shared/ports/types' import { languages } from 'monaco-editor' +import { PLCDataType } from '../../../../../../../../../middleware/shared/ports/types' + type PLCEnumeratedDatatype = Extract type PLCStructureDatatype = Extract diff --git a/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx b/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx index f158cd05b..3feeeb65d 100644 --- a/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/monaco/index.tsx @@ -1,15 +1,20 @@ import './configs' import { Editor as PrimitiveEditor } from '@monaco-editor/react' -import { Modal, ModalContent, ModalTitle } from '../../../../_molecules/modal' -import { openPLCStoreBase, useOpenPLCStore } from '../../../../../store' -import type { PLCVariable, PLCPou } from '../../../../../../middleware/shared/ports/types' -import { baseTypeSchema } from '../../../../../../middleware/shared/ports/plc-schemas' -import { useCapabilities, useProject } from '../../../../../../middleware/shared/providers' import * as monaco from 'monaco-editor' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { baseTypeSchema } from '../../../../../../middleware/shared/ports/plc-schemas' +import type { PLCPou,PLCVariable } from '../../../../../../middleware/shared/ports/types' +import { useCapabilities, useProject } from '../../../../../../middleware/shared/providers' +import { openPLCStoreBase, useOpenPLCStore } from '../../../../../store' +import { getExtensionFromLanguage, getFolderFromPouType } from '../../../../../utils/PLC/pou-file-extensions' +import { parseHybridPouFromString,parseTextualPouFromString } from '../../../../../utils/PLC/pou-text-parser' +import { Modal, ModalContent, ModalTitle } from '../../../../_molecules/modal' import { toast } from '../../../[app]/toast/use-toast' +import { AIInlineCompletionProvider } from './ai-completion/ai-inline-completion-provider' +import { AIConsentModal } from './ai-consent-modal' +import { AIStatusIndicator } from './ai-status-indicator' import { arduinoApiCompletion, cppSignatureHelp, @@ -31,9 +36,6 @@ import { import { parsePouToStText } from './drag-and-drop/st' import { cleanupPythonLSP, initPythonLSP, setupPythonLSPForEditor } from './python-lsp' import { applyThemeNow, ensureOpenplcThemes } from './theme-utils' -import { AIInlineCompletionProvider } from './ai-completion/ai-inline-completion-provider' -import { AIConsentModal } from './ai-consent-modal' -import { AIStatusIndicator } from './ai-status-indicator' type monacoEditorProps = { path: string @@ -252,29 +254,6 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType string) | null = null - let getFolderFromPouType: ((type: string) => string) | null = null - let parseTextualPouFromString: ((...args: unknown[]) => { data: { body: { value: unknown } } }) | null = null - let parseHybridPouFromString: ((...args: unknown[]) => { data: { body: { value: unknown } } }) | null = null - - try { - // These utils exist in the editor's src/ tree; they're accessed at runtime only - // eslint-disable-next-line @typescript-eslint/no-require-imports - const pouExtensions = require('@root/utils/PLC/pou-file-extensions') - getExtensionFromLanguage = pouExtensions.getExtensionFromLanguage - getFolderFromPouType = pouExtensions.getFolderFromPouType - // eslint-disable-next-line @typescript-eslint/no-require-imports - const pouParser = require('@root/utils/PLC/pou-text-parser') - parseTextualPouFromString = pouParser.parseTextualPouFromString - parseHybridPouFromString = pouParser.parseHybridPouFromString - } catch { - // Not available in this platform - return - } - - if (!getExtensionFromLanguage || !getFolderFromPouType) return - const actualExtension = getExtensionFromLanguage(language) const pouFolder = getFolderFromPouType(pou.pouType) const fullPath = `${currentProjectPath}/pous/${pouFolder}/${name}${actualExtension}` @@ -297,12 +276,12 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType { const suggestions = tableVariablesCompletion({ range, - variables: pouVariables as PLCVariable[], + variables: pouVariables, }).suggestions const uniqueSuggestions = Array.from(new Map(suggestions.map((s) => [s.label, s])).values()) const labels = uniqueSuggestions.map((suggestion) => suggestion.label) @@ -434,7 +413,7 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType { const suggestions = tableGlobalVariablesCompletion({ range, - variables: globalVariables as PLCVariable[], + variables: globalVariables, }).suggestions const uniqueSuggestions = Array.from(new Map(suggestions.map((s) => [s.label, s])).values()) const labels = uniqueSuggestions.map((suggestion) => suggestion.label) @@ -829,11 +808,6 @@ const MonacoEditor = (props: monacoEditorProps): ReturnType() diff --git a/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx b/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx index 531121084..d23cdc8f1 100644 --- a/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/search-in-project/index.tsx @@ -1,10 +1,10 @@ import * as Checkbox from '@radix-ui/react-checkbox' import { CheckIcon } from '@radix-ui/react-icons' -import { InputWithRef } from '../../../../_atoms/input' -import { useOpenPLCStore } from '../../../../../store' import { useEffect, useState } from 'react' import { v4 as uuidv4 } from 'uuid' +import { useOpenPLCStore } from '../../../../../store' +import { InputWithRef } from '../../../../_atoms/input' import { useToast } from '../../../[app]/toast/use-toast' type OptionProps = { @@ -250,7 +250,7 @@ export default function SearchInProject({ onClose }: SearchInProjectModalProps) const pouVariables = pou.interface?.variables ?? [] acc[pouType].push({ name: pou.name, - language: pou.body.language, + language: pou.body.language as 'ld' | 'sfc' | 'fbd' | 'il' | 'st' | 'python' | 'cpp', pouType: pou.pouType, body: (['st', 'il'].includes(pou.body.language) && diff --git a/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/address-mapping-reference.tsx b/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/address-mapping-reference.tsx index faf8701ff..7142922d1 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/address-mapping-reference.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/address-mapping-reference.tsx @@ -1,8 +1,9 @@ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible' import { ChevronDownIcon } from '@radix-ui/react-icons' -import { cn } from '../../../../../../utils/cn' import { useMemo, useState } from 'react' +import { cn } from '../../../../../../utils/cn' + // --------------------------------------------------------------------------- // Types (self-contained — modbus utils not yet migrated to src2) // --------------------------------------------------------------------------- @@ -325,4 +326,4 @@ const AddressMappingReference = ({ bufferMapping, defaultExpanded = false }: Add } export { AddressMappingReference } -export type { ModbusSlaveBufferMapping, AddressMappingRow, AddressMappingSection } +export type { AddressMappingRow, AddressMappingSection,ModbusSlaveBufferMapping } diff --git a/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/index.tsx b/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/index.tsx index fb85bad01..00a973347 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/modbus-server/index.tsx @@ -1,12 +1,14 @@ import * as AccordionPrimitive from '@radix-ui/react-accordion' import { ChevronDownIcon } from '@radix-ui/react-icons' -import { InputWithRef } from '../../../../../_atoms/input' -import { Label } from '../../../../../_atoms/label' -import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../_atoms/select' +import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react' + +import type { ModbusBufferMapping } from '../../../../../../../middleware/shared/ports/types' import { useOpenPLCStore } from '../../../../../../store' import { cn } from '../../../../../../utils/cn' import { DEFAULT_BUFFER_MAPPING } from '../../../../../../utils/modbus/generate-modbus-slave-config' -import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react' +import { InputWithRef } from '../../../../../_atoms/input' +import { Label } from '../../../../../_atoms/label' +import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../_atoms/select' const DEFAULT_NETWORK_INTERFACE_OPTIONS = [ { value: '0.0.0.0', label: 'All Interfaces (0.0.0.0)' }, @@ -135,15 +137,15 @@ const ModbusServerEditor = () => { const [iwCount, setIwCount] = useState(DEFAULT_BUFFER_MAPPING.inputRegisters.iwCount.toString()) // Helper function to reset buffer mapping state to defaults or provided values - const setBufferMappingState = useCallback((bufferMapping: typeof DEFAULT_BUFFER_MAPPING = DEFAULT_BUFFER_MAPPING) => { - setQwCount(bufferMapping.holdingRegisters.qwCount.toString()) - setMwCount(bufferMapping.holdingRegisters.mwCount.toString()) - setMdCount(bufferMapping.holdingRegisters.mdCount.toString()) - setMlCount(bufferMapping.holdingRegisters.mlCount.toString()) - setQxBits(bufferMapping.coils.qxBits.toString()) - setMxBits(bufferMapping.coils.mxBits.toString()) - setIxBits(bufferMapping.discreteInputs.ixBits.toString()) - setIwCount(bufferMapping.inputRegisters.iwCount.toString()) + const setBufferMappingState = useCallback((bufferMapping: ModbusBufferMapping | typeof DEFAULT_BUFFER_MAPPING = DEFAULT_BUFFER_MAPPING) => { + setQwCount((bufferMapping.holdingRegisters?.qwCount ?? DEFAULT_BUFFER_MAPPING.holdingRegisters.qwCount).toString()) + setMwCount((bufferMapping.holdingRegisters?.mwCount ?? DEFAULT_BUFFER_MAPPING.holdingRegisters.mwCount).toString()) + setMdCount((bufferMapping.holdingRegisters?.mdCount ?? DEFAULT_BUFFER_MAPPING.holdingRegisters.mdCount).toString()) + setMlCount((bufferMapping.holdingRegisters?.mlCount ?? DEFAULT_BUFFER_MAPPING.holdingRegisters.mlCount).toString()) + setQxBits((bufferMapping.coils?.qxBits ?? DEFAULT_BUFFER_MAPPING.coils.qxBits).toString()) + setMxBits((bufferMapping.coils?.mxBits ?? DEFAULT_BUFFER_MAPPING.coils.mxBits).toString()) + setIxBits((bufferMapping.discreteInputs?.ixBits ?? DEFAULT_BUFFER_MAPPING.discreteInputs.ixBits).toString()) + setIwCount((bufferMapping.inputRegisters?.iwCount ?? DEFAULT_BUFFER_MAPPING.inputRegisters.iwCount).toString()) }, []) // Accordion state @@ -213,56 +215,56 @@ const ModbusServerEditor = () => { 'holdingRegisters', 'qwCount', qwCount, - bufferMapping.holdingRegisters.qwCount, + bufferMapping.holdingRegisters?.qwCount, MAX_REGISTER_COUNT, ) const handleMwCountBlur = createBufferMappingHandler( 'holdingRegisters', 'mwCount', mwCount, - bufferMapping.holdingRegisters.mwCount, + bufferMapping.holdingRegisters?.mwCount, MAX_REGISTER_COUNT, ) const handleMdCountBlur = createBufferMappingHandler( 'holdingRegisters', 'mdCount', mdCount, - bufferMapping.holdingRegisters.mdCount, + bufferMapping.holdingRegisters?.mdCount, MAX_REGISTER_COUNT, ) const handleMlCountBlur = createBufferMappingHandler( 'holdingRegisters', 'mlCount', mlCount, - bufferMapping.holdingRegisters.mlCount, + bufferMapping.holdingRegisters?.mlCount, MAX_REGISTER_COUNT, ) const handleQxBitsBlur = createBufferMappingHandler( 'coils', 'qxBits', qxBits, - bufferMapping.coils.qxBits, + bufferMapping.coils?.qxBits, MAX_BIT_COUNT, ) const handleMxBitsBlur = createBufferMappingHandler( 'coils', 'mxBits', mxBits, - bufferMapping.coils.mxBits, + bufferMapping.coils?.mxBits, MAX_BIT_COUNT, ) const handleIxBitsBlur = createBufferMappingHandler( 'discreteInputs', 'ixBits', ixBits, - bufferMapping.discreteInputs.ixBits, + bufferMapping.discreteInputs?.ixBits, MAX_BIT_COUNT, ) const handleIwCountBlur = createBufferMappingHandler( 'inputRegisters', 'iwCount', iwCount, - bufferMapping.inputRegisters.iwCount, + bufferMapping.inputRegisters?.iwCount, MAX_REGISTER_COUNT, ) diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/address-space-tab.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/address-space-tab.tsx index 2383dca7c..73bee8634 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/address-space-tab.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/address-space-tab.tsx @@ -1,9 +1,9 @@ -import { InputWithRef } from '../../../../../../_atoms/input' -import { Label } from '../../../../../../_atoms/label' -import { useOpenPLCStore } from '../../../../../../../store' -import type { OpcUaNodeConfig, OpcUaServerConfig } from '../../../../../../../../middleware/shared/ports/types' import { useCallback, useMemo, useState } from 'react' +import type { OpcUaNodeConfig, OpcUaServerConfig } from '../../../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../../../store' +import { InputWithRef } from '../../../../../../_atoms/input' +import { Label } from '../../../../../../_atoms/label' import { findTreeNodeById, getSelectableDescendantIds, diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx index 09907e423..0a5dd2f0b 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx @@ -1,9 +1,10 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' + +import type { OpcUaTrustedCertificate } from '../../../../../../../../middleware/shared/ports/types' +import { cn } from '../../../../../../../utils/cn' import { InputWithRef } from '../../../../../../_atoms/input' import { Label } from '../../../../../../_atoms/label' import { Modal, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '../../../../../../_molecules/modal' -import type { OpcUaTrustedCertificate } from '../../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../../utils/cn' -import { useCallback, useEffect, useMemo, useState } from 'react' interface CertificateModalProps { isOpen: boolean diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificates-tab.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificates-tab.tsx index df4d37755..e2fde7e92 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificates-tab.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificates-tab.tsx @@ -1,10 +1,10 @@ -import { Label } from '../../../../../../_atoms/label' -import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../../_atoms/select' -import { useOpenPLCStore } from '../../../../../../../store' -import type { OpcUaServerConfig, OpcUaTrustedCertificate } from '../../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../../utils/cn' import { useCallback, useMemo, useState } from 'react' +import type { OpcUaServerConfig, OpcUaTrustedCertificate } from '../../../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../../../store' +import { cn } from '../../../../../../../utils/cn' +import { Label } from '../../../../../../_atoms/label' +import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../../_atoms/select' import { CertificateModal } from './certificate-modal' interface CertificatesTabProps { diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profile-modal.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profile-modal.tsx index 3046ef508..30460f5a9 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profile-modal.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profile-modal.tsx @@ -1,12 +1,13 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + +import type { OpcUaSecurityProfile } from '../../../../../../../../middleware/shared/ports/types' +import { cn } from '../../../../../../../utils/cn' import { Checkbox } from '../../../../../../_atoms/checkbox' import { InputWithRef } from '../../../../../../_atoms/input' import { Label } from '../../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../../_atoms/select' import { Modal, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '../../../../../../_molecules/modal' -import type { OpcUaSecurityProfile } from '../../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../../utils/cn' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' type SecurityPolicy = 'None' | 'Basic128Rsa15' | 'Basic256' | 'Basic256Sha256' type SecurityMode = 'None' | 'Sign' | 'SignAndEncrypt' diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profiles-tab.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profiles-tab.tsx index 018ce484f..0ae20145f 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profiles-tab.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/security-profiles-tab.tsx @@ -1,8 +1,8 @@ -import { useOpenPLCStore } from '../../../../../../../store' -import type { OpcUaSecurityProfile, OpcUaServerConfig } from '../../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../../utils/cn' import { useCallback, useMemo, useState } from 'react' +import type { OpcUaSecurityProfile, OpcUaServerConfig } from '../../../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../../../store' +import { cn } from '../../../../../../../utils/cn' import { SecurityProfileModal } from './security-profile-modal' interface SecurityProfilesTabProps { diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/user-modal.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/user-modal.tsx index c571aed4e..5e69cc5cb 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/user-modal.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/user-modal.tsx @@ -1,13 +1,14 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + +import type { OpcUaTrustedCertificate, OpcUaUser } from '../../../../../../../../middleware/shared/ports/types' import ViewIcon from '../../../../../../../assets/icons/interface/View' import ViewHiddenIcon from '../../../../../../../assets/icons/interface/ViewHidden' +import { cn } from '../../../../../../../utils/cn' import { InputWithRef } from '../../../../../../_atoms/input' import { Label } from '../../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../../_atoms/select' import { Modal, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '../../../../../../_molecules/modal' -import type { OpcUaTrustedCertificate, OpcUaUser } from '../../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../../utils/cn' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' type AuthType = 'password' | 'certificate' type UserRole = 'viewer' | 'operator' | 'engineer' diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx index 1337db2d8..276d54774 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx @@ -1,8 +1,8 @@ -import { useOpenPLCStore } from '../../../../../../../store' -import type { OpcUaServerConfig, OpcUaUser } from '../../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../../utils/cn' import { useCallback, useMemo, useState } from 'react' +import type { OpcUaServerConfig, OpcUaUser } from '../../../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../../../store' +import { cn } from '../../../../../../../utils/cn' import { UserModal } from './user-modal' interface UsersTabProps { diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx index bdb5d7fe0..a33683b6b 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx @@ -1,12 +1,12 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { v4 as uuidv4 } from 'uuid' + +import type { OpcUaFieldConfig, OpcUaNodeConfig, OpcUaPermissions } from '../../../../../../../../middleware/shared/ports/types' +import { cn } from '../../../../../../../utils/cn' import { InputWithRef } from '../../../../../../_atoms/input' import { Label } from '../../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../../_atoms/select' import { Modal, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '../../../../../../_molecules/modal' -import type { OpcUaFieldConfig, OpcUaNodeConfig, OpcUaPermissions } from '../../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../../utils/cn' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' - import { isComplexType, type VariableTreeNode } from '../hooks/use-project-variables' interface VariableConfigModalProps { diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx index 534997b0a..454001ae6 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx @@ -1,6 +1,6 @@ -import { cn } from '../../../../../../../utils/cn' import { useCallback, useState } from 'react' +import { cn } from '../../../../../../../utils/cn' import type { VariableTreeNode } from '../hooks/use-project-variables' interface VariableTreeProps { diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts index 59aaea8f3..897c29ea3 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/hooks/use-project-variables.ts @@ -1,5 +1,7 @@ -import { useOpenPLCStore } from '../../../../../../../store' +import { useMemo } from 'react' + import type { PLCDataType, PLCPou, PLCVariable } from '../../../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../../../store' import { findFunctionBlockVariables, findStructureVariables, @@ -8,7 +10,6 @@ import { isFunctionBlockType, type PouVariable, } from '../../../../../../../utils/pou-helpers' -import { useMemo } from 'react' /** * Type for array data extracted from PLCVariable array type. diff --git a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/index.tsx b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/index.tsx index 14a5302f0..059d6894d 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/opcua-server/index.tsx @@ -1,12 +1,12 @@ import * as Tabs from '@radix-ui/react-tabs' +import { useCallback, useEffect, useMemo, useState } from 'react' + +import type { OpcUaServerConfig } from '../../../../../../../middleware/shared/ports/types' +import { useOpenPLCStore } from '../../../../../../store' +import { cn } from '../../../../../../utils/cn' import { InputWithRef } from '../../../../../_atoms/input' import { Label } from '../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../_atoms/select' -import { useOpenPLCStore } from '../../../../../../store' -import type { OpcUaServerConfig } from '../../../../../../../middleware/shared/ports/types' -import { cn } from '../../../../../../utils/cn' -import { useCallback, useEffect, useMemo, useState } from 'react' - import { AddressSpaceTab } from './components/address-space-tab' import { CertificatesTab } from './components/certificates-tab' import { SecurityProfilesTab } from './components/security-profiles-tab' diff --git a/src2/frontend/components/_features/[workspace]/editor/server/s7comm-server/index.tsx b/src2/frontend/components/_features/[workspace]/editor/server/s7comm-server/index.tsx index a282c80c0..7eb077763 100644 --- a/src2/frontend/components/_features/[workspace]/editor/server/s7comm-server/index.tsx +++ b/src2/frontend/components/_features/[workspace]/editor/server/s7comm-server/index.tsx @@ -1,11 +1,12 @@ import * as AccordionPrimitive from '@radix-ui/react-accordion' import { ChevronDownIcon, Pencil1Icon, PlusIcon, TrashIcon } from '@radix-ui/react-icons' +import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react' + +import { useOpenPLCStore } from '../../../../../../store' +import { cn } from '../../../../../../utils/cn' import { InputWithRef } from '../../../../../_atoms/input' import { Label } from '../../../../../_atoms/label' import { Select, SelectContent, SelectItem, SelectTrigger } from '../../../../../_atoms/select' -import { useOpenPLCStore } from '../../../../../../store' -import { cn } from '../../../../../../utils/cn' -import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react' // UI-specific types for S7Comm buffer mapping. // These differ from the middleware S7CommDataBlock which uses startByte/endByte/iecAddresses. @@ -26,7 +27,7 @@ type S7CommBufferType = | 'lint_output' | 'lint_memory' -type S7CommDataBlock = { +type S7CommUIDataBlock = { dbNumber: number description: string sizeBytes: number @@ -142,9 +143,9 @@ AccordionContent.displayName = 'AccordionContent' interface DataBlockModalProps { isOpen: boolean onClose: () => void - onSave: (dataBlock: S7CommDataBlock) => void + onSave: (dataBlock: S7CommUIDataBlock) => void existingDbNumbers: number[] - editingBlock?: S7CommDataBlock + editingBlock?: S7CommUIDataBlock } const DataBlockModal = ({ isOpen, onClose, onSave, existingDbNumbers, editingBlock }: DataBlockModalProps) => { @@ -383,7 +384,7 @@ const S7CommServerEditor = () => { // Modal state const [isModalOpen, setIsModalOpen] = useState(false) - const [editingBlock, setEditingBlock] = useState(undefined) + const [editingBlock, setEditingBlock] = useState(undefined) // Sync state from config useEffect(() => { @@ -500,17 +501,19 @@ const S7CommServerEditor = () => { setIsModalOpen(true) }, []) - const handleEditDataBlock = useCallback((dataBlock: S7CommDataBlock) => { + const handleEditDataBlock = useCallback((dataBlock: S7CommUIDataBlock) => { setEditingBlock(dataBlock) setIsModalOpen(true) }, []) const handleSaveDataBlock = useCallback( - (dataBlock: S7CommDataBlock) => { + (dataBlock: S7CommUIDataBlock) => { + // Cast UI data block to middleware type — the extra UI fields (startBuffer, bitAddressing) are ignored by the store + const middlewareBlock = dataBlock as unknown as import('../../../../../../../middleware/shared/ports/types').S7CommDataBlock if (editingBlock) { - projectActions.updateS7CommDataBlock(serverName, editingBlock.dbNumber, dataBlock) + projectActions.updateS7CommDataBlock(serverName, editingBlock.dbNumber, middlewareBlock) } else { - projectActions.addS7CommDataBlock(serverName, dataBlock) + projectActions.addS7CommDataBlock(serverName, middlewareBlock) } handleFileAndWorkspaceSavedState(serverName) }, @@ -728,12 +731,12 @@ const S7CommServerEditor = () => { {db.sizeBytes} bytes - {BUFFER_TYPE_OPTIONS.find((o) => o.value === db.mapping.type)?.label || db.mapping.type} + {BUFFER_TYPE_OPTIONS.find((o) => o.value === db.mapping.type)?.label || db.mapping.type || 'Unknown'}