Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b7ca2d3
feat: add EtherCAT types and shared backend logic
thiagoralves Apr 10, 2026
03e9bdc
feat: extend RuntimePort with EtherCAT discovery and create EsiPort
thiagoralves Apr 9, 2026
5906b45
feat: add EtherCAT store actions, selectors, and device config hook
thiagoralves Apr 9, 2026
83bf21c
feat: port all EtherCAT UI components to shared frontend
thiagoralves Apr 10, 2026
9b27556
feat: integrate EtherCAT config generation into v4 compilation
thiagoralves Apr 10, 2026
f1a15b9
fix: remove VPP code remnants and fix corrupted import in renderer
thiagoralves Apr 10, 2026
6213a2f
feat: auto-create system task when EtherCAT device is added
thiagoralves Apr 10, 2026
67d0dd1
fix: remove circular store import from editor-platform
thiagoralves Apr 10, 2026
e3d500e
feat: port remaining ethercat-task-selection commits (13 commits)
thiagoralves Apr 10, 2026
dfd45b7
feat: add ProjectTreeExpandableLeaf for EtherCAT bus device tree
thiagoralves Apr 10, 2026
a24a6b7
feat: port remaining ethercat-task-selection commits (6 commits)
marconetsf Apr 13, 2026
668c8a9
refactor: remove subtitle from EtherCAT slave device editor header
marconetsf Apr 13, 2026
a5d297b
fix: use DeviceTransferIcon for EtherCAT slave tabs
marconetsf Apr 13, 2026
7f868e8
refactor: move Add Selected button into Scanned Devices header
marconetsf Apr 13, 2026
239e42b
fix: populate EtherCAT channelMappings at device add-time
marconetsf Apr 13, 2026
8ea31e6
Merge branch 'development' into feat/ethercat-support-v2
thiagoralves Apr 13, 2026
abfb9dd
fix: close EtherCAT slave tabs without phantom save prompt
marconetsf Apr 13, 2026
dd77cc9
fix: update PLC type imports after move to backend/shared
marconetsf Apr 13, 2026
ea64666
Merge remote-tracking branch 'origin/development' into feat/ethercat-…
marconetsf Apr 15, 2026
1777a51
Merge remote-tracking branch 'origin/development' into feat/ethercat-…
marconetsf Apr 16, 2026
23cd396
fix: move ESI types to shared ports surface and fix formatting
marconetsf Apr 16, 2026
69ea68f
fix: move ESI types to ports surface and update all imports
marconetsf Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
565 changes: 565 additions & 0 deletions docs/ethercat-architecture.md

Large diffs are not rendered by default.

76 changes: 76 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"electron-store": "^8.1.0",
"electron-updater": "^6.1.4",
"embla-carousel-react": "^8.0.0-rc17",
"fast-xml-parser": "^5.5.11",
"i18next": "^24.2.2",
"immer": "^10.1.1",
"lodash": "^4.17.21",
Expand Down
8 changes: 7 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
import { StartScreen } from './frontend/screens/start-screen'
import { WorkspaceScreen } from './frontend/screens/workspace-screen'
import { openPLCStoreBase, useOpenPLCStore } from './frontend/store'
import { editorPorts, setRuntimeIpAddress } from './middleware/editor-platform'
import { editorPorts, setProjectPath, setRuntimeIpAddress } from './middleware/editor-platform'
import { PlatformProvider } from './middleware/shared/providers'

// Initialize system libraries at module load time (before first render)
Expand Down Expand Up @@ -66,6 +66,12 @@ export default function App() {
setRuntimeIpAddress(runtimeIpAddress)
}, [runtimeIpAddress])

// Sync project path to the platform adapter so the ESI port can access it
const projectPath = useOpenPLCStore((state) => state.project.meta.path)
useEffect(() => {
setProjectPath(projectPath)
}, [projectPath])

return (
<PlatformProvider ports={editorPorts}>
<AppLayout>{path === '' ? <StartScreen /> : <WorkspaceScreen />}</AppLayout>
Expand Down
42 changes: 38 additions & 4 deletions src/backend/editor/compiler/compiler-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import { promisify } from 'node:util'

import { getRuntimeHttpsOptions } from '@root/backend/editor/utils/runtime-https-config'
import { generateEthercatConfig } from '@root/backend/shared/ethercat/generate-ethercat-config'
import type { DeviceConfiguration, DevicePin } from '@root/backend/shared/types/PLC/devices'
import type { PLCProjectData } from '@root/backend/shared/types/PLC/open-plc'
import {
Expand Down Expand Up @@ -183,7 +184,17 @@

async #getBoardRuntime(board: string) {
const halsFileContent = await CompilerModule.readJSONFile<HalsFile>(this.halsFilePath)
return halsFileContent[board]['compiler']
if (halsFileContent[board]) {
return halsFileContent[board]['compiler']
}

// Board not found in hals.json
try {

Check failure on line 192 in src/backend/editor/compiler/compiler-module.ts

View workflow job for this annotation

GitHub Actions / Lint Check

Empty block statement

Check failure on line 192 in src/backend/editor/compiler/compiler-module.ts

View workflow job for this annotation

GitHub Actions / Lint Check

Empty block statement
} catch {
// ignore package manager errors
}

throw new Error(`Board "${board}" not found in hals.json or installed VPP packages`)
}

#executeXml2st(args: string[]) {
Expand Down Expand Up @@ -1328,6 +1339,24 @@
}
}

async handleGenerateEthercatConfig(
sourceTargetFolderPath: string,
projectData: PLCProjectData,
handleOutputData: HandleOutputDataCallback,
): Promise<void> {
const ethercatConfig = generateEthercatConfig(projectData.remoteDevices)

if (ethercatConfig) {
const confFolderPath = join(sourceTargetFolderPath, 'conf')
await mkdir(confFolderPath, { recursive: true })
const configFilePath = join(confFolderPath, 'ethercat.json')
await writeFile(configFilePath, ethercatConfig, 'utf-8')
handleOutputData('Generated conf/ethercat.json', 'info')
} else {
handleOutputData('No EtherCAT devices configured, skipping ethercat.json generation', 'info')
}
}

async embedCBlocksInProgramSt(
sourceTargetFolderPath: string,
handleOutputData: HandleOutputDataCallback,
Expand Down Expand Up @@ -1429,7 +1458,9 @@
})

// --- Check for unsupported features on non-v4 targets ---
const isRuntimeV4 = boardTarget === 'OpenPLC Runtime v4'
// VPP boards with runtime-v4 target type use openplc-compiler and are also v4-capable
const isRuntimeV3 = boardTarget === 'OpenPLC Runtime v3'
const isRuntimeV4 = boardRuntime === 'openplc-compiler' && !isRuntimeV3
const hasServers = projectData.servers && projectData.servers.length > 0
const hasRemoteDevices = projectData.remoteDevices && projectData.remoteDevices.length > 0

Expand Down Expand Up @@ -1714,8 +1745,6 @@
}

try {
const isRuntimeV3 = boardTarget === 'OpenPLC Runtime v3'

let fileBuffer: Buffer
let filename: string
let contentType: string
Expand Down Expand Up @@ -1762,6 +1791,11 @@
_mainProcessPort.postMessage({ logLevel, message: data })
})

// Generate EtherCAT config for Runtime v4
await this.handleGenerateEthercatConfig(sourceTargetFolderPath, projectData, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})

_mainProcessPort.postMessage({
logLevel: 'info',
message: 'Compressing source files for OpenPLC Runtime v4...',
Expand Down
Loading
Loading