Skip to content

fsmes scenario: play a product through a line by writing tags, Simulator channels only - #222

Merged
kalwei merged 4 commits into
mainfrom
feat/kepware-scenario
Nov 4, 2026
Merged

kalwei merged 4 commits into
mainfrom
feat/kepware-scenario

Conversation

@kalwei

@kalwei kalwei commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

What

fsmes scenario — a player that writes timed tag values into any OPC UA server, Kepware first, so a product can be run through a line and every system reading that server (this MES, another MES, a script under test) sees the same thing.

  • fsmes scenario play <file> --endpoint ... [--security --user --password] [--speed] [--from mm:ss] — writes each value at its time, as the node's own type, and prints a line per write. Ctrl-C stops.
  • fsmes scenario check <file> [--endpoint ...] [--timeline] — offline, names the line of each mistake in the file; connected, reports every tag's type, access and the driver rule, writing nothing.
  • fsmes scenario serve <file> — a rehearsal server holding the file's tags as writable memory tags at their own addresses, so the whole thing can be tried without Kepware.

The file is JSON (PyYAML is not a dependency of the product): tags as short names for node ids or browse paths, drivers, steps with value or ramp, and product — stations in order, advance_s apart, each bumping a count from what it holds, setting a state, an id, or other tags.

The safety rule, in code

Before anything is written, every target is read. A tag whose channel is not declared Simulator or Memory Based in the file is refused, with the channel and the reason; so is a declared Advanced Simulator or PLC driver. Kepware's address space does not name a channel's driver (its System Tags page lists none), so the declaration is required — and on a Kepware server the player also looks for _System._ComId, _System._NetworkAdapter and an IP-address _DeviceId, any of which refuses the channel whatever the file says. A read-only tag is reported, not written. Nothing is written unless every target passes. There is no override flag.

Shadow mode refuses it. MES_SHADOW=true promises this MES changes nothing outside its own database; a scenario changes tags on another server, so play refuses before it connects and again at the write. Both new paths are in fsmes.shadow.REGISTER (now 38 entries: opc.scenario_play refused, sim.scenario_rehearsal allowed), and the register's source scan now matches .write_attribute(, which it did not see before.

Writes carry no source timestamp (Kepware refuses those) and use the node's own variant type (Kepware answers a wrong one with BadTypeMismatch).

How verified

  • tests/test_scenario_player.py, 19 tests (one: play in shadow mode writes nothing and never connects), plus a proof in tests/test_shadow_mode.py: the file's mistakes named by line (bad time, unknown tag, unknown key, invalid JSON, a duplicated key); ramp and product expansion; the driver rule; type coercion with ranges and array lengths; and against in-test asyncua servers (slow tier): string, bool, Int16, UInt32, Int16[4] and Float written as their own types at the right times at 2x; a PLC-driver channel refused with nothing at all written; a Kepware-shaped server whose device is addressed by IP overruling a "Simulator" declaration; read-only and missing tags reported; the lab's product walk played into its own rehearsal server.
  • By hand, on loopback port 4945: setpoint-drill.json checked and played into fsmes run-opc-sim --replay (its setpoints are the tags it lets a client write); product-through-the-line.json played into fsmes scenario serve and read back with fsmes opc-browse.
  • Not verified here: a real KEPServerEX. Whether a Simulator channel shows none of the three device signs is what Scott's first check --endpoint against his server will say.
  • ruff check . and pytest green locally, except test_the_calibration_the_supervisor_records_is_on_the_gauges_own_register, which fails on this machine in the evening when the local date and the UTC date differ; it does not touch this change.

Docs: docs/operate/scenarios.md (in the nav), with the Kepware notes from Kepware's Simulator Driver Help and System Tags page, and which points remain to check on a real server.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

Thanks for the first pull request. Checks: every commit signed off (git commit -s), ruff and pytest green, and the house-rules checklist in the template. The maintainer reviews within a few days.

kalwei and others added 4 commits November 3, 2026 20:05
…rver

A scenario file (JSON) names tags once, then says when each takes which
value, with a ramp and a product-through-stations shorthand. play writes
each value as the node's own type at its time; check names the line of
each mistake and, connected, reports type, access and the driver rule.

Only Simulator or Memory Based channels, declared in the file, are
written; Kepware showing a serial or Ethernet device behind a channel
refuses it regardless, and a read-only tag is reported. Nothing is
written unless every target passes. serve holds a scenario's tags as
writable memory tags for rehearsal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scottkalw <scottkalwei@gmail.com>
A product through the six KepSim stations on a scratch Simulator channel,
and a set-and-ramp drill on the replay server's writable setpoints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scottkalw <scottkalwei@gmail.com>
…egister

The player changes tags on another server, so in shadow mode it is refused
before it connects (the CLI) and again at the call site (play). The
rehearsal server is registered as allowed: its own loopback server.

The register's scan now matches .write_attribute( as an OPC UA node write;
without it the player's write was invisible to the ratchet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scottkalw <scottkalwei@gmail.com>
The shadow-mode CLI test reached _scenario_settings, whose
setup_logging("ERROR") stayed configured for the rest of the test
process; three floor tests that capture structlog events then saw none on
CI. The refusal now comes first, and the test restores structlog's config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: scottkalw <scottkalwei@gmail.com>
@kalwei
kalwei force-pushed the feat/kepware-scenario branch from c563008 to faaa95d Compare November 4, 2026 02:06
@kalwei
kalwei merged commit a341c35 into main Nov 4, 2026
47 of 48 checks passed
@kalwei
kalwei deleted the feat/kepware-scenario branch November 4, 2026 03:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant