Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 0 additions & 10 deletions package-lock.json

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

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"@tailwindcss/forms": "^0.5.10",
"@tanstack/react-query": "^5.90.21",
"@tanstack/react-table": "^8.21.2",
"@xmldom/xmldom": "^0.9.10",
"@xyflow/react": "^12.0.1",
"auto-zustand-selectors-hook": "^2.0.0",
"avr8js": "0.20.0",
Expand Down
93 changes: 9 additions & 84 deletions src/backend/shared/transpilers/st-transpiler/emit/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,22 @@
/**
* IR-native `CONFIGURATION … END_CONFIGURATION` block emitter.
*
* JSON-direct port of `src/PLCGenerator/configuration.ts` — walks
* `TranspileProject.configuration` instead of the parsed DOM, but
* emits byte-identical chunks. Mirrors Python's
* `ProgramGenerator.GenerateConfiguration` + `GenerateResource`
* (PLCGenerator.py:334-628).
* Walks `TranspileProject.configuration` and emits byte-identical
* chunks against the python oracle's `ProgramGenerator.GenerateConfiguration`
* + `GenerateResource` (PLCGenerator.py:334-628).
*
* The IR carries exactly one configuration with one resource, named
* `Config0` / `Res0` — same hardcoded names the XML-based emitter
* produced. Global vars are emitted under the configuration block
* (not under the resource), matching `irToPlcOpenXml`'s layout.
*
* CTN-globals provider is honoured: `kind: 'variable'` entries
* (tuple-shape globals) are appended after the IR's
* `globalVariables`. `kind: 'varlist'` entries (raw DOM `<globalVars>`
* elements) are silently ignored on the IR-native path — they only
* come from the legacy DOM-injection flow which has no caller after
* Phase 1.
* `Config0` / `Res0` — same hardcoded names the python oracle
* produces. Global vars are emitted under the configuration block
* (not under the resource).
*/

import type { ConfigurationExtraVariablesProvider } from '../helpers/ctn-globals'
import type { ProgramChunk } from '../helpers/program'
import { computeConfigurationName, computeConfigurationResourceName } from '../helpers/text-helpers'
import type { TranspileProject, TranspileVariable } from '../types'
import { declaredTypeName, getTypeAsText } from './type-text'
import { computeValue } from './value'

export interface GenerateConfigurationOptions {
/**
* Beremiz CTN-injected globals provider. Mirrors
* `Controler.GetConfigurationExtraVariables` (PLCControler.py:1248).
*/
extraVarsProvider?: ConfigurationExtraVariablesProvider | null
}

const CONFIG_NAME = 'Config0'
const RESOURCE_NAME = 'Res0'

Expand All @@ -44,10 +26,7 @@
* globals (caller may still want the keyword shell — Python always
* emits the block; we mirror that).
*/
export function generateConfigurations(
project: TranspileProject,
options: GenerateConfigurationOptions = {},
): ProgramChunk[] {
export function generateConfigurations(project: TranspileProject): ProgramChunk[] {
const configTagname = computeConfigurationName(CONFIG_NAME)
const resourceTagname = computeConfigurationResourceName(CONFIG_NAME, RESOURCE_NAME)

Expand All @@ -57,11 +36,10 @@
out.push([CONFIG_NAME, [configTagname, 'name']])
out.push(['\n', []])

// Configuration-level global variables. IR-shape globals first,
// then any CTN-injected tuple-shape globals.
// Configuration-level global variables.
emitGlobalVarList(
out,
collectConfigGlobals(project, options.extraVarsProvider),
project.configuration.globalVariables,
configTagname,
/*indent=*/ ' ',
/*varIndent=*/ ' ',
Expand Down Expand Up @@ -142,59 +120,6 @@

/* ────────────────────── helpers ─────────────────────────────────────────── */

function collectConfigGlobals(
project: TranspileProject,
provider: ConfigurationExtraVariablesProvider | null | undefined,
): TranspileVariable[] {
const out: TranspileVariable[] = [...project.configuration.globalVariables]
if (!provider) return out
const entries = provider()
for (const entry of entries) {
if (entry.kind !== 'variable') continue
const tuple = entry.variable
out.push({
name: tuple.name,
type: tupleTypeToIr(tuple.type),
...(tuple.initial ? { initialValue: tuple.initial } : {}),
})
}
return out
}

function tupleTypeToIr(typeName: string): TranspileVariable['type'] {
// CTN-globals tuples carry the type as a bare string. IEC base
// types resolve to `base-type`; anything else becomes a derived
// reference — mirrors PLCControler.py:1258-1273.
const upper = typeName.toUpperCase()
const elementaryTypes = new Set([
'BOOL',
'SINT',
'INT',
'DINT',
'LINT',
'USINT',
'UINT',
'UDINT',
'ULINT',
'REAL',
'LREAL',
'TIME',
'DATE',
'TOD',
'DT',
'STRING',
'WSTRING',
'BYTE',
'WORD',
'DWORD',
'LWORD',
])
if (elementaryTypes.has(upper)) {
return { definition: 'base-type', value: upper }
}
return { definition: 'derived', value: typeName }
}

function emitGlobalVarList(
out: ProgramChunk[],
variables: TranspileVariable[],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/**
* IR-native graphical-POU emitter — LD / FBD bodies.
*
Expand All @@ -9,8 +9,8 @@
* declaration order matches what the python oracle produces.
*/

import { PLC_BASE_TYPES } from '../helpers/base-types'
import { resolveBlockType } from '../helpers/block-library'
import { PLC_BASE_TYPES } from '../helpers/ctn-globals'
import type { ProgramChunk } from '../helpers/program'
import { computePouName } from '../helpers/text-helpers'
import { varTypeNames } from '../helpers/type-text'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/**
* IR-native textual-POU emitter — ST / IL / Python / C++.
*
Expand All @@ -11,7 +11,7 @@
* dispatch.
*/

import { PLC_BASE_TYPES } from '../helpers/ctn-globals'
import { PLC_BASE_TYPES } from '../helpers/base-types'
import type { ProgramChunk } from '../helpers/program'
import { reIndentText } from '../helpers/text-helpers'
import { computePouName } from '../helpers/text-helpers'
Expand Down
33 changes: 33 additions & 0 deletions src/backend/shared/transpilers/st-transpiler/helpers/base-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* IEC 61131-3 elementary base types — mirrors python's
* `Controler.GetBaseTypes()` (derived from `TypeHierarchy_list` in
* `plcopen/definitions.py:84`). Used by the emit pipeline to
* decide whether a type name resolves to an elementary `<TYPE/>`
* element or a `<derived name="…"/>` wrapper.
*
* `WSTRING` is intentionally absent — matches python's `# TODO`
* comment at `definitions.py:118`.
*/

export const PLC_BASE_TYPES: ReadonlySet<string> = new Set([
'BOOL',
'SINT',
'INT',
'DINT',
'LINT',
'USINT',
'UINT',
'UDINT',
'ULINT',
'REAL',
'LREAL',
'TIME',
'DATE',
'TOD',
'DT',
'STRING',
'BYTE',
'WORD',
'DWORD',
'LWORD',
])

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
/**
* Text-manipulation helpers used by the body dispatcher and downstream phases.
*
* Mirrors:
* - `ReIndentText` (PLCGenerator.py:66)
* - `ComputePouName` (plcopen/types_enums.py:112)
* - `ComputePouTransitionName` (plcopen/types_enums.py:117)
* - `ComputePouActionName` (plcopen/types_enums.py:122)
* - `ComputeConfigurationName` (plcopen/types_enums.py:127)
* Text-manipulation helpers — mirrors python's `ReIndentText`
* (`PLCGenerator.py:66`) and the `Compute*Name` family in
* `plcopen/types_enums.py:112-132`.
*/

/**
Expand Down Expand Up @@ -71,16 +66,6 @@
return `P::${name}`
}

/** `"T::" + pou + "::" + transition`. */
export function computePouTransitionName(pou: string, transition: string): string {
return `T::${pou}::${transition}`
}

/** `"A::" + pou + "::" + action`. */
export function computePouActionName(pou: string, action: string): string {
return `A::${pou}::${action}`
}

/** `"C::" + name`. */
export function computeConfigurationName(name: string): string {
return `C::${name}`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/**
* IEC 61131-3 type hierarchy + compatibility predicate.
*
Expand Down Expand Up @@ -59,38 +59,3 @@
LWORD: 'ANY_NBIT',
// WSTRING intentionally absent — matches Python's `# TODO` comment.
}

/**
* Returns `true` iff `type` is `reference` or any of its subtypes (walking
* the parent chain). `null` `reference` returns `true` ("matches anything"),
* mirroring Python's `if reference is None: return True`.
*
* Unknown types (not in the hierarchy) return `false` against any
* reference except themselves. Same behavior as Python where the unknown
* type raises `KeyError` on the hierarchy lookup — we soften to `false` so
* derived user types (e.g. `Irrigation_State`) don't crash overload checks.
*/
export function isOfType(type: string, reference: string | null): boolean {
if (reference === null) return true
if (type === reference) return true
const parent = TypeHierarchy[type]
if (parent === undefined) return false // user-defined type, not in hierarchy
if (parent === null) return false // reached ANY without matching
return isOfType(parent, reference)
}

/**
* Concrete (non-ANY-prefixed) types that are subtypes of `metaType`.
* Used by `get_standard_funtions` to expand polymorphic CSV signatures into
* concrete overload entries (e.g. `(ANY_NUM, ANY_NUM)` becomes one entry per
* `INT`, `SINT`, `DINT`, …). Catalog already pre-expanded; this stays in TS
* for completeness and future use.
*/
export function getSubTypes(metaType: string): string[] {
const out: string[] = []
for (const typename of Object.keys(TypeHierarchy)) {
if (typename.startsWith('ANY')) continue
if (isOfType(typename, metaType)) out.push(typename)
}
return out
}
9 changes: 3 additions & 6 deletions src/backend/shared/transpilers/st-transpiler/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/**
* Project IR → Structured Text transpiler.
*
Expand All @@ -14,15 +14,14 @@
* `PLCProjectData` the editor's IPC delivers) — see `from-schema.ts`.
*/

import { type GenerateConfigurationOptions, generateConfigurations } from './emit/configuration'
import { generateConfigurations } from './emit/configuration'
import { generateDataTypes } from './emit/data-types'
import { generateGraphicalPou } from './emit/pou-graphical'
import { generateTextualPou } from './emit/pou-textual'
import { buildPouEmissionOrder } from './pou-emission-order'
import type { TranspileProject } from './types'

export { fromSchemaShape, type SchemaProjectData } from './from-schema'
export type { ConfigurationExtraVariablesProvider, CtnGlobalEntry, CtnGlobalVarTuple } from './helpers/ctn-globals'
export type {
TranspileBody,
TranspileBodyLanguage,
Expand All @@ -38,8 +37,6 @@
TranspileVariableType,
} from './types'

export type TranspileOptions = GenerateConfigurationOptions

export interface TranspileResult {
/** Concatenated Structured Text, or `null` if no POU compiled. */
programSt: string | null
Expand All @@ -65,7 +62,7 @@
* compile errors land in `result.errors` and the rest of the
* program still emits.
*/
export function transpileToSt(project: TranspileProject, options: TranspileOptions = {}): TranspileResult {
export function transpileToSt(project: TranspileProject): TranspileResult {
const errors: string[] = []
const warnings: string[] = []
const pouNames: string[] = []
Expand Down Expand Up @@ -106,7 +103,7 @@
}

// Trailing CONFIGURATION block — emit IR-native.
for (const [text] of generateConfigurations(project, options)) {
for (const [text] of generateConfigurations(project)) {
pieces.push(text)
}

Expand Down
Loading
Loading