Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 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
b0939c1
fix: resolve all CI lint, format, and architecture errors
marconetsf Apr 16, 2026
b6bb9c8
fix: align fast-xml-parser version with openplc-web (^5.6.0)
marconetsf Apr 16, 2026
7f9b72a
fix: register EtherCAT slave devices as first-class file entries
marconetsf Apr 16, 2026
6639776
Merge remote-tracking branch 'origin/development' into feat/ethercat-…
marconetsf Apr 17, 2026
8446a1f
refactor(esi): adopt shared loadedAt: string contract
marconetsf Apr 17, 2026
44c3515
fix(ethercat): cascade delete of bus children
marconetsf Apr 17, 2026
1e968a5
Merge branch 'development' into feat/ethercat-esi-backend
marconetsf Apr 17, 2026
11e1261
fix(ethercat): address CodeRabbit review on PR #731
marconetsf Apr 20, 2026
e414fbc
Merge branch 'feat/ethercat-esi-backend' of https://github.com/Autono…
marconetsf Apr 20, 2026
c85b290
style: apply prettier to ipc main.ts
marconetsf Apr 20, 2026
435aae4
chore: add sync-shared-surfaces Claude Code skill
marconetsf Apr 20, 2026
8edf1cf
fix(ethercat): address João's review comments on PR #731
marconetsf Apr 20, 2026
bcae26f
docs: add Phase 4 debugger plan (scalable per-leaf addressing)
thiagoralves Apr 20, 2026
e02ec7c
Merge branch 'development' into feat/ethercat-esi-backend
marconetsf Apr 20, 2026
7aee1b9
chore: untrack sync-shared-surfaces Claude Code skill
marconetsf Apr 22, 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.

441 changes: 441 additions & 0 deletions docs/strucpp-migration/04-debugger.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.6.0",
"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
97 changes: 68 additions & 29 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 { join } from 'node:path'
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,13 @@ class CompilerModule {

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 or installed VPP packages

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

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

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 +1454,9 @@ class CompilerModule {
})

// --- 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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition flipped from boardTarget === 'OpenPLC Runtime v4' to boardRuntime === 'openplc-compiler' && !isRuntimeV3. Any existing v4 board whose runtime string isn't literally 'openplc-compiler' will now be misclassified as non-v4 and skip the conf/* generation. Will be nice if we can confirm that all current hals.json v4 entries use that runtime name.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifiquei: hoje as três entradas do hals.json com openplc-compiler são OpenPLC Runtime v4, OpenPLC Runtime v3 e Raspberry Pi — e a classificação atual
acerta as três (v3 é excluída explicitamente; Pi entra como v4 porque roda Linux e hospeda o Runtime v4 nativamente, igual aos VPP packages com
target.type === 'runtime-v4'). A heurística é intencional: qualquer board com esse compilador é capaz de executar o Runtime v4 embarcado, então herda os
artefatos conf/*.json — comportamento desejado para novos boards Linux que venham a ser adicionados.

const hasServers = projectData.servers && projectData.servers.length > 0
const hasRemoteDevices = projectData.remoteDevices && projectData.remoteDevices.length > 0

Expand Down Expand Up @@ -1683,6 +1710,43 @@ class CompilerModule {
message: 'Source files generated successfully at: ' + sourceTargetFolderPath,
})

// Generate Runtime v4 conf/* files for BOTH compile-only and upload flows.
// Without this, compile-only never produces ethercat.json (and other configs),
// so users who only want the generated sources miss runtime configuration.
if (isRuntimeV4) {
try {
await this.cleanConfFolder(sourceTargetFolderPath, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})
await this.handleGenerateModbusSlaveConfig(sourceTargetFolderPath, projectData, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})
await this.handleGenerateModbusMasterConfig(sourceTargetFolderPath, projectData, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})
await this.handleGenerateS7CommConfig(sourceTargetFolderPath, projectData, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})
await this.handleGenerateOpcUaConfig(sourceTargetFolderPath, projectData, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})
await this.handleGenerateEthercatConfig(sourceTargetFolderPath, projectData, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})
} catch (error) {
_mainProcessPort.postMessage({
logLevel: 'error',
message: `Error generating Runtime v4 configs: ${error instanceof Error ? error.message : String(error)}`,
})
_mainProcessPort.postMessage({
logLevel: 'error',
message: 'Stopping compilation process.',
})
_mainProcessPort.close()
return
}
}

if (compileOnly) {
_mainProcessPort.postMessage({
logLevel: 'info',
Expand Down Expand Up @@ -1714,8 +1778,6 @@ class CompilerModule {
}

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

let fileBuffer: Buffer
let filename: string
let contentType: string
Expand All @@ -1737,31 +1799,8 @@ class CompilerModule {
filename = 'program.st'
contentType = 'text/plain'
} else {
// Clean conf folder from previous compilations to avoid stale config files
await this.cleanConfFolder(sourceTargetFolderPath, (data, logLevel) => {
_mainProcessPort.postMessage({ logLevel, message: data })
})

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

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

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

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

// Runtime v4 conf/* files were already generated above, before the
// compile-only early return, so compile-only flows also get them.
_mainProcessPort.postMessage({
logLevel: 'info',
message: 'Compressing source files for OpenPLC Runtime v4...',
Expand Down
Loading
Loading