# PLC_Simulator WebAssembly core

This folder contains the actual C++ PLC compiler and executor from
[ironhero1544/PLC_Simulator](https://github.com/ironhero1544/PLC_Simulator),
pinned to commit `4aba081cc3ec8152f64698ade1c666d47a314c5a`.
The upstream sources and this derivative adapter are GPL-3.0; see
[upstream/LICENSE](upstream/LICENSE). The web page uses the same
`ParseCellInput`, `CompileLadder`, `CompiledPLCExecutor`, `ImportGXCSV` and
`ExportGXCSV` implementations as that revision. No JavaScript PLC instruction
interpreter is substituted.

## Rebuild

Install the official **Emscripten 6.0.13** SDK and provide its `em++.py` path.
Python 3 and Node.js must be on `PATH`. No GUI dependencies, CMake, or upstream
setup scripts are required.

```powershell
python plc/native/build.py --emxx C:/path/to/emscripten/em++.py
node --test tests/plc-engine.test.js
```

The build compiles all 27 upstream programming core translation units and the
three project compiler/codec translation units with C++20. It generates
`plc/runtime.js`, `plc/runtime.wasm` and `build-manifest.json`. The build script
uses only the local compiler; it does not install packages or fetch source.
`.emscripten` and `.build-tmp` are local configuration/build artifacts and must
not be deployed.

The official prebuilt compiler used for this build is recorded in
`upstream-manifest.json` with its source URL and SHA-256. The SDK itself is not
part of the shipped application. Source hashes permit verifying the vendored
copy against the pinned commit.

## Changes to upstream

Only two upstream files are modified:

- `compiled_plc_executor.h`: add an optional `simulation_elapsed_ms = -1`
  argument to `ExecuteScanCycle`.
- `compiled_plc_executor.cpp`: use a supplied nonnegative duration for
  `current_elapsed_ms_`. The default `-1` retains the desktop clock behavior.

This lets every browser board have one scan clock and makes tests deterministic.
Instruction execution, timer time bases, input filtering, memory rules, scan
order, compiler validation and device limits remain upstream behavior.
Timer accumulation starts after the scan that enables the timer, as upstream
specifies. RTC/calendar commands retain the upstream real wall clock.

`bridge.cpp`, `build.py` and `../engine.js` are integration code. The bridge owns
one independent executor per runtime, accepts grid data, calls upstream
compilation, supplies 16 inputs, and returns device/coil/cell monitoring data.

## Browser and Node API

Load `engine.js`, then await `PlcEngine.ready()`. It loads the generated
`runtime.js` and `runtime.wasm` from the same directory. In Node, use
`require('./plc/engine.js')`.

- `compile(program)` returns `{ok, errors, compiled?, instructionCount?}`.
  Error entries include zero-based `row`, `col`, and `message`; `-1` means
  the upstream error has no particular coordinate.
- `createRuntime(program)` returns an independent runtime. Invalid programs
  throw an error with its `errors` array.
- `runtime.scan(inputs, dt)` supplies 16 booleans and seconds, and returns a
  snapshot. It is synchronous after `ready()`. Fractional milliseconds carry
  into the next scan. `dt` must be finite and between 0 and 60 seconds.
- `runtime.read(address)`, `snapshot()`, `reset()`, `dispose()` are synchronous.
- `importCSV(text)` returns `{ok, program?, errors}`.
- `exportCSV(program)` returns `{ok, text?, errors}` with Unicode text. The
  file layer encodes UTF-16LE/BOM when creating a GX-compatible file.

Snapshots contain `inputs`, `outputs`, `values`, `live`, `timers`, `counters`,
and an `error` string if execution failed. Timer `value`/`preset` are device
counts; `unitMs` is the original timer's time base and `elapsedMs` is elapsed
milliseconds. `read(Tn)`/`read(Cn)` are contact booleans.

P00–P0F and P20–P2F are aliases for X0–X17 and Y0–Y17 (octal).
Inside a typed APP instruction, `P` retains its upstream jump-pointer meaning;
use X/Y names for APP device operands.

## Deliberate web integration bounds

- Editor programs support 1–256 rows and 2–32 columns. The C++ core has larger
  limits, but the web adapter refuses oversized input explicitly.
- The complete upstream instruction dispatch is linked. The board exposes only
  its 16 physical inputs/outputs. Virtual serial, Modbus, CF, inverter, motion
  and extension instructions do not connect a browser to real equipment; their
  original virtual-device configuration APIs are not exposed by this adapter.
- CSV loading uses the upstream parser and materializer. It rejects wrapping
  symbols, nonadjacent vertical buses, and any canonical instruction topology
  whose reconstructed execution DAG serialization differs, rather than silently
  changing the program. The comparison removes original spelling metadata so
  equivalent AND/ANB and OR/ORB spellings do not cause false rejections; it
  retains saved-condition evaluation groups and instruction order.
- Runtime/compiler messages originate in the upstream English core. The UI
  can add localized context around them.
