# plant-weave **Repository Path**: gyb98/plant-weave ## Basic Information - **Project Name**: plant-weave - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-11-06 - **Last Updated**: 2026-11-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AyMON **An industrial monitoring and automation engine, for Windows and Linux.** It speaks the field protocols, keeps the whole plant as one live, addressable XML-shaped tree, runs scripts and timers on that tree, raises alarms, writes history and serves the web pages. One engine runs on a small box and on a server; the difference is configuration, not a different build. - **One command, nothing to install.** Every example here starts with `run.bat` or `./run.sh` — no licence, no hardware, no database. The machines they talk to come with them, simulated. - **Protocols as configuration.** Modbus, OPC UA, MQTT, SNMP, SQL, HTTP / REST and WebSocket, with TLS, are elements in the XML, not code to write. - **Two engines, one job.** Replication, and a cluster that fails over: kill the master and the spare carries on from the same unit within a second or two. - **AI assistants, inside rules.** An MCP endpoint lets Claude or any MCP client read the plant and change what it is allowed to — and nothing else. - **In production.** The same engine runs data-centre monitoring (DCIM) systems: one of them 1 837 devices, 272 000 points and three nodes. See [In the field](docs/08_In_the_Field.md). - **Free to run.** The FREE edition never expires and allows production use within 5 documents, 8 connections, 8 devices and 4 logged-in users.
Two cluster nodes side by side: the master is stopped, the spare becomes master and the count goes on
PRJEDU_cluster — two engines, one job. The master is stopped; a second later the spare is master and goes on from the same unit.
A filling line on camera: each bottle inspected, a bottle without a cap rejected
PRJEDU_vision — a camera inspects bottles inside the engine: a missing cap, the wrong cap, a short fill. Twenty lines of JScript; no Python, no OpenCV.
A six-axis arm picking and placing parts, started by an AI assistant over MCP
PRJEDU_robot_mcp — an AI assistant runs a robot cell over MCP. It may load, play and pause; power, brakes and a protective stop stay with a person.
Aircraft on a radar-style map around Istanbul Airport, one squawking an emergency
PRJPER_adsb — the aircraft overhead, from an ADS-B receiver or the simulated one around Istanbul Airport. An emergency squawk is an alarm, set in configuration.
This repository is where you get AyMON: the engine for Windows and Linux in `bin/`, and runnable projects for learning, teaching and hobby use. Every example starts with one command and needs **no licence, no hardware and no database** unless its own README says otherwise. The same pages as a searchable site: **https://docs.aymon.ca/** ``` AyMON/ bin/win-x64/ the engine (AyXDB.exe) and its runtime, Windows x64 bin/linux-x64/ the same engine for Linux x86-64 (AyXDB + lib*.so) bin/lic/ the education / personal licence - one file, every example and every platform examples/ the examples, one folder per group: start/ first steps (PRJEDU_101) protocols/ Modbus, MQTT, SNMP, OPC UA - both ends, no hardware data/ databases, mail and files web/ live pages, pages the engine writes, logins, a maintenance shell, a plant to harden cluster/ two engines: replication, and a cluster that fails over labs/ course labs: a PLC program, PID, OEE, a robot arm ai-vision/ AI assistants over MCP, Python, machine vision hobby/ a drone, the aircraft overhead, your phone (PRJPER_*) docs/ six short guides, the real-hardware page and the field page media/ the pictures in this README licenses/ third-party licence texts ``` ## Run one ```bat cd examples\start\PRJEDU_101 run.bat ``` `run.bat` finds the engine in `..\..\..\bin\win-x64` and hands it the licence in `..\..\..\bin\lic` with `-lic:` — one licence per process, so projects under different licences can run side by side on one machine. Stop it with CTRL-C. Everything the engine did is in that project's `log\` directory and on the console. To point at an engine installed somewhere else: ```bat run.bat "C:\Program Files\AyMON\bin" ``` ## On Linux Every example has a `run.sh` beside its `run.bat` (and `run_plant.sh` / `run_centre.sh`, `shell.sh`, `try.sh` where there is a `.bat` of that name): ```bash cd examples/start/PRJEDU_101 ./run.sh ``` It does what `run.bat` does: finds the engine for the machine — `../../../bin/linux-x64`, or `../../../bin/linux-arm64` on 64-bit ARM such as a Raspberry Pi — hands it the licence in `../../../bin/lic` with `-lic:`, and starts it in the project directory. Stop it with Ctrl-C (or ESC); Ctrl-X opens the maintenance prompt. The same education licence works on both platforms. | | | |---|---| | **Machine** | x86-64 with glibc 2.34 or newer and OpenSSL 3: Ubuntu 22.04+, Debian 12+, Fedora 35+, RHEL / Rocky 9, and WSL on Windows. The engine takes `libssl.so.3` from the system (`sudo apt install libssl3` if `run.sh` says it is missing); everything else ships in `bin/linux-x64`. | | **Raspberry Pi / ARM** | 64-bit ARM (aarch64) with the same glibc and OpenSSL: Raspberry Pi 4 or 5 with **64-bit** Raspberry Pi OS (Bookworm or Trixie), or another arm64 board. `run.sh` picks `bin/linux-arm64` by itself. A 32-bit system has no engine here. This engine has been checked in an arm64 Debian 12 container; it has not yet been run on a real Raspberry Pi. | | **Helpers** | `examples/web/PRJEDU_telnet/shell.sh` and `examples/ai-vision/PRJEDU_mcp/try.sh` run small Python 3 scripts (standard library only) — the counterparts of `shell.ps1` / `try.ps1`. | | **Python in the engine** | only `PRJEDU_gauge` and `PRJEDU_pycall`: `bin/linux-x64` (and `bin/linux-arm64`, like `bin\win-x64` on Windows) holds a Python module for **Python 3.11** (`libAyLIB_Python311.so`: Debian 12, Raspberry Pi OS Bookworm; deadsnakes on Ubuntu) and one for **Python 3.13** (`libAyLIB_Python313.so`: Debian 13, Raspberry Pi OS Trixie); the engine takes the one whose Python the machine has — the gauge with numpy installed for that version, pycall with the standard library only. The engine needs Python's **shared library**, a package of its own that `python3` does not bring: `sudo apt install libpython3.11` on Bookworm, `libpython3.13` on Trixie (and `python3-numpy` for the gauge). Every other example runs without it — the module is loaded only when a document has a Python script. | | **ODBC** | `PRJEDU_odbc` runs on its embedded SQLite out of the box. For PostgreSQL or SQL Server install unixODBC and the driver (`sudo apt install unixodbc odbc-postgresql`); on Linux the drivers are named in `/etc/odbcinst.ini` — `{PostgreSQL Unicode}`, not Windows' `{PostgreSQL Unicode(x64)}`. | | **Paths** | the projects write `%PRJ%` / `%DB%` paths with `/`, which both platforms read; a file database written as `Database=log\x.db` works too — on Linux the engine reads the `\` as a separator. | | **Copied from Windows?** | a zip loses the executable bits: `chmod +x examples/*/*/*.sh bin/linux-*/AyXDB`. A `git clone` keeps them. | ## On a Mac There is no macOS build of the engine. A Mac runs the Linux engine of this repository in a small Linux virtual machine made with [Lima](https://lima-vm.io) (free): on Apple silicon (M1 and later) that is `bin/linux-arm64` at native speed, on an Intel Mac `bin/linux-x64`. ```bash brew install lima git clone https://github.com/akya-dev/AyMON.git cd AyMON tools/mac/aymon-vm.sh examples/start/PRJEDU_101 ``` The first run creates a VM named `aymon` (Debian 12, whose Python 3.11 is one of the two versions the engine's Python modules are built for), mounts this repository into it, and installs `libssl3`, `libpython3.11` and `python3-numpy` — a few minutes and a ~300 MB download, once. After that the script starts the example's `run.sh` inside the VM; stop it with Ctrl-C. | | | |---|---| | **Another script** | name it after the folder: `tools/mac/aymon-vm.sh examples/cluster/PRJEDU_cluster run_S02.sh` (the second engine, in a second terminal), `tools/mac/aymon-vm.sh examples/web/PRJEDU_telnet shell.sh`. | | **Web pages** | Lima forwards the ports the engine listens on: the addresses in the READMEs (`http://127.0.0.1:/`) open in the Mac's browser. | | **The VM** | `tools/mac/aymon-vm.sh --shell` opens a shell in it, `--stop` stops it (the next run starts it again), `--delete` removes it. | | **Devices** | examples that talk over the network work as on Linux. USB and serial devices and a USB camera are on the Mac, not in the VM: `PRJEDU_vision` runs on its recorded frames. | | **Status** | the script has not yet been tried on a real Mac. If something goes wrong, please [open an issue](https://github.com/akya-dev/AyMON/issues/new/choose) with what it printed. | ## The examples They live in [examples/](examples/), one folder per group; each group's folder has a README of its own. Run an example from its own folder. ### [First steps](examples/start/) | Project | What it teaches | Needs | |---|---|---| | [**PRJEDU_101**](examples/start/PRJEDU_101/) | the document tree, a thread and its clock, a timer-driven value, a calculated value, an alarm with limits, reading the log | nothing | ### [Field protocols](examples/protocols/) | Project | What it teaches | Needs | |---|---|---| | [**PRJEDU_modbus**](examples/protocols/PRJEDU_modbus/) | `` / `` / `` / ``, the block as the unit of communication, register and BIT addressing, scaling and alarms as configuration — and the **slave** side, so the plant and the monitoring side are two documents of one engine; a live page at http://127.0.0.1:8103/ | nothing | | [**PRJEDU_mqtt**](examples/protocols/PRJEDU_mqtt/) | MQTT with nothing to install: AyMON as the broker that publishes its own document and takes writes on `.../set`, and as a client whose ``/``/`` are a publisher, a subscription and a topic — retain from the document, what stays local, writing back without an echo, and pointing it at mosquitto instead; a live page at http://127.0.0.1:8104/ | nothing; mosquitto to try the optional steps | | [**PRJEDU_snmp**](examples/protocols/PRJEDU_snmp/) | SNMP with nothing to install: the manager side as you would write it for a real device — one request carrying every point of a block, the OID split between `` and ``, types and scaling — and the device side written in script on `PRO.raw`, which is how any protocol the engine does not speak can be tried out first; a live page at http://127.0.0.1:8105/ | nothing | | [**PRJEDU_opcua**](examples/protocols/PRJEDU_opcua/) | an OPC UA client against a machine that comes with its own server — a simulated injection moulding machine on asyncua, somebody else's stack: NodeIds by namespace URI (`nsu=`) rather than index, a poll and a subscription of the same value side by side (30 against 135 changes in 30 s), why the subscription gets a connection of its own (a Publish waits at the server and held the 1 s poll to 0.65 reads a second), one session one thread so the page's requests are carried out by a timer on the session's thread, writes and method calls (`OPC.call`) signed and encrypted as a user, the anonymous write the server refuses, certificates trusted in both directions (`OPC.cert`, the machine's rejected folder, `pro_trust`), and `OPC.browse` / `OPC.gen` for where the NodeIds come from | Python 3.10+ for the machine (`setup.bat` / `setup.sh` installs asyncua into the example's own folder) | ### [Data, history, mail and files](examples/data/) | Project | What it teaches | Needs | |---|---|---| | [**PRJEDU_sqlite**](examples/data/PRJEDU_sqlite/) | the SQL surface: `exec_direct` vs `prepare`/`parameter`/`execute` and why a value is never pasted into a statement, `SQL.qry` turning rows into addressable elements, what an SQL NULL is in script, and a history that survives a restart; a live page at http://127.0.0.1:8106/ | nothing | | [**PRJEDU_odbc**](examples/data/PRJEDU_odbc/) | connection strings kept in the root document and handed to the `` in `xdb_init`, the ODBC and SQLite drivers attaching themselves, one script whose SQL runs unchanged on three databases — and the traps between them (LIMIT/TOP, `round()`, a Turkish collation); a live page at http://127.0.0.1:8107/ | nothing for SQLite; SQL Server or PostgreSQL to try those | | [**PRJEDU_net**](examples/data/PRJEDU_net/) | the engine writes mail and files: a tank's level alarm sends one mail when it comes and one when it is gone — a mail is a **write to a point** of a `PRO.smtp` connection and the server's answer comes back **into the same point**, the connection is made for the message and let go when quiet, sending runs on a thread of its own; a shift report is written, zipped (`FILE.zip`) and uploaded by `PRO.ftp` as `.part` then renamed, and the directory listed; the mail and file servers at the other end are written in script on `PRO.raw` — they speak first (`pro_onaccept`) and hear a connection end (`pro_onclose`, the end of an FTP upload), so nothing is installed; how to point it at a real mailbox or FTP server and the licence's 20 operations a day; a live page at http://127.0.0.1:8114/ | nothing; engine 4.3.4.568 or later and its module `AyLIB_net` (in `bin`) | ### [Web pages, access and security](examples/web/) | Project | What it teaches | Needs | |---|---|---| | [**PRJEDU_web**](examples/web/PRJEDU_web/) | a live page in a browser: static files over HTTP, a JSON snapshot over REST, live values and a write-back over MQTT inside a WebSocket — one port for all three, the same again over TLS (https / wss), and why the web side is configuration, not code | a browser | | [**PRJEDU_pages**](examples/web/PRJEDU_pages/) | web pages the engine writes: a file ending in `.ayp` is a template (`<% %>`, `<%= %>` escaped, `<%- %>` raw, `include`, as in ASP / JSP / EJS) that the engine runs on every request with the same `evt.GetValue` every script uses — no JavaScript in the browser, no API to design; a tank farm's overview, a form that changes set points (checked on the server, POST then redirect), the same data as JSON for a program, a page that shows it runs with the **visitor's** rights (`xdb_acl`), and a **login**: a plain form the engine answers, a cookie per login, set points only an operator may change, a CSRF token on the form; a live page at http://127.0.0.1:8115/ | nothing; engine 4.3.4.594 or later | | [**PRJEDU_telnet**](examples/web/PRJEDU_telnet/) | getting inside an engine that has no console: the maintenance shell over plain TCP and over TLS, the three gates in front of it (`con_acl` at accept, `con_auth` at login, `xdb_acl` per element), what four different roles see of the same plant, project verbs through `pro_oncmd`, and following the log live — with a terminal for it in the box | nothing | | [**PRJEDU_hardening**](examples/web/PRJEDU_hardening/) | a security course's lab, the defending side: a small plant (tank, pump, high-level trip, a secret recipe) delivered with seven weaknesses in who may do what — no address filter, no login and every connection an Operator, no default deny, the safety trip open to operators, the recipe open to all, passwords in clear text, unlimited guesses — to be fixed in its XML; the plant checks its own configuration, and the page logs in as four identities and tries what each may and may not do; a solution file marks every fix; measured: without a password the tank could be made to overflow, after hardening 8 of 8 | nothing | ### [Two engines](examples/cluster/) | Project | What it teaches | Needs | |---|---|---| | [**PRJEDU_rep**](examples/cluster/PRJEDU_rep/) | replication between two engines on one machine: the mirror that has no file of its own, direction decided by the document and not the connection, one connection row that works out its own end, and why an unlicensed feature is a *local* feature; a live page at http://127.0.0.1:8108/ | nothing | | [**PRJEDU_cluster**](examples/cluster/PRJEDU_cluster/) | two engines that share one job: an election (`PRO.clt`) over a link on each node, the role written into the job document's `sta` (TRANSIT, master, spare), replication (`PRO.rep`) whose direction follows the role, one set of files for both nodes told apart by `tid`, `CLT.info` for the cluster's state in a script, a write on the spare forwarded to the master, a planned hand-over — and a page that shows both nodes side by side while you kill one; measured (engine 4.3.4.622): master again 1–3 s after the kill, nothing skipped, at most the last step made again; both copies of the batch list the same, in the same order | nothing | ### [Course labs](examples/labs/) | Project | What it teaches | Needs | |---|---|---| | [**PRJEDU_plc**](examples/labs/PRJEDU_plc/) | an automation course's lab: a bottle filling line (conveyor, photo-eyes, nozzle, level switch, supply tank, START / STOP / RESET / EMERGENCY STOP wired normally closed) whose signals are a Modbus TCP remote I/O, and a PLC program in script that scans it every 100 ms — a step sequence, edges, timers, an interlock on the valve, a watchdog on the fieldbus and a life bit the other way — with an HMI page that shows the line, the sequence and the I/O image | nothing | | [**PRJEDU_pid**](examples/labs/PRJEDU_pid/) | a control course's lab: a simulated heater block (first order plus dead time, sensor noise, a fan as the disturbance) in one document and a PID controller in another — ISA form, derivative on the measurement with a filter, anti-windup you can switch off, bumpless AUTO/MANUAL, a trip — that measures every set-point step (rise, overshoot, settling, IAE), runs a step test, fits a model and suggests a SIMC tuning; a lab page with a trend, the three terms and a CSV for the report | nothing | | [**PRJEDU_oee**](examples/labs/PRJEDU_oee/) | an industrial engineering course's lab: a simulated line (products in lots, breakdowns, jams, slow spells, missing material, changeovers, bad parts) that tells the world only seven Modbus registers, and a small MES that turns them into time and OEE — 16-bit counters that wrap, the ideal cycle as master data, minor stops, stops the machine cannot name and the operator names on the page, the planned break taken from the shift plan, a stop split when its reason changes; a waterfall of where the shift's time went, A × P × Q checked against fully productive time / planned time, a Pareto of downtime, every stop and shift in SQLite, a CSV, and a printable shift report written by the engine from the database (`report.ayp`, a server page) | nothing | | [**PRJEDU_robotarm**](examples/labs/PRJEDU_robotarm/) | a robotics or automation course's lab: a six-axis collaborative arm built after a UR5e (Denavit-Hartenberg forward kinematics, joint moves with a speed ramp, programs as data) that speaks a UR's two cell interfaces — the dashboard server (text, TCP 29999) and the Modbus TCP server (joints, tool position and general purpose registers at UR's addresses) — and a cell controller that runs it: a dashboard command queue with a greeting to set aside, a part handshake over registers, a feeder, the rules in the engine; protective and safeguard stops; a page that draws the arm from its joint angles | nothing; URSim or a UR robot for a real one | ### [AI assistants, Python and vision](examples/ai-vision/) | Project | What it teaches | Needs | |---|---|---| | [**PRJEDU_mcp**](examples/ai-vision/PRJEDU_mcp/) | an AI assistant (Claude Code, Claude Desktop, any MCP client) on the plant: publishing a document with `mcp_pub`, a read-only and a read+write endpoint, why the endpoint's role and each element's `xdb_acl` decide everything, the four answers to a write (`OKY` / `refused` / `PARTIAL` / `dry_run`), and tools a project adds in script — with every call runnable without an AI; a live page at http://127.0.0.1:8109/ | nothing; an MCP client to try it with an assistant | | [**PRJEDU_robot_mcp**](examples/ai-vision/PRJEDU_robot_mcp/) | the robot cell of PRJEDU_robotarm with an MCP endpoint an AI assistant (Claude Code, Claude Desktop, any MCP client) comes in through: three project tools (`cell_status`, `robot_command`, `set_speed`), a separate assistant channel kept apart from the person's by `xdb_acl` in both directions, the robot's document off the MCP whitelist, and rules the assistant cannot change — power, brakes and a protective stop stay with a person, a speed ceiling, a switch on the page that turns the assistant off; `try.bat` makes every call without an assistant | an MCP client to connect an assistant; seven connections, inside FREE | | [**PRJEDU_gauge**](examples/ai-vision/PRJEDU_gauge/) | Python beside the engine: a `