Fixture renamed off ignitionoee's otopcua-focas-sim, port 8193 published, project label, default endpoint 10.100.0.35:8193; infra/dev-environment docs. FOCAS.md: MACRO:3901 parts count, PMC override + cycle-time recipes, won't-do/blocked backlog. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -510,7 +510,7 @@ These are consumed by the driver **integration-test fixtures** (under `tests/Dri
|
||||
| `AB_SERVER_ENDPOINT` | `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbServerFixture.cs` | `10.100.0.35:44818` |
|
||||
| `S7_SIM_ENDPOINT` | `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/Snap7ServerFixture.cs` | `10.100.0.35:1102` (non-privileged; not S7-standard 102) |
|
||||
| `OPCUA_SIM_ENDPOINT` | `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcPlcFixture.cs` | `opc.tcp://10.100.0.35:50000` |
|
||||
| `OTOPCUA_FOCAS_SIM_ENDPOINT` | `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/FocasSimFixture.cs` | `localhost:8193` |
|
||||
| `OTOPCUA_FOCAS_SIM_ENDPOINT` | `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/FocasSimFixture.cs` | `10.100.0.35:8193` |
|
||||
|
||||
> Additional harness/parity/soak env vars (`OTOPCUA_FOCAS_*`, `OTOPCUA_PARITY_*`, `OTOPCUA_SOAK_*`, `OTOPCUA_HARNESS_USE_SQL`) exist only in the test/parity/soak harnesses, not in production source, and are out of scope for this reference.
|
||||
|
||||
|
||||
@@ -52,8 +52,9 @@ message naming the CNC series + documented limit.
|
||||
managed `FocasDriver` end-to-end. A single gate:
|
||||
|
||||
**Docker compose up** — tests skip when the TCP probe fails, with a
|
||||
pointer to the compose command. The endpoint defaults to `localhost:8193`
|
||||
and is overridable via `OTOPCUA_FOCAS_SIM_ENDPOINT`.
|
||||
pointer to the compose command. The endpoint defaults to `10.100.0.35:8193`
|
||||
(the OtOpcUa-owned `otopcua-focas-fixture` container, stack `/opt/otopcua-focas`; see
|
||||
`infra/README.md`) and is overridable via `OTOPCUA_FOCAS_SIM_ENDPOINT`.
|
||||
|
||||
When the mock is up, `WireFocasClient` dials it over TCP exactly like a
|
||||
real CNC, and the mock's native FOCAS Ethernet responder replies with
|
||||
|
||||
+91
-12
@@ -286,22 +286,69 @@ fallen back to `PositionDecimalPlaces` and published raw counts.
|
||||
numbers whose meaning is MTB-specific, and which no browse can discover. Those
|
||||
go under the device folder alongside the fixed subtree.
|
||||
|
||||
### Parts count (`PARAM:6711`)
|
||||
### Parts count (`MACRO:3901`, or `PARAM:6711`)
|
||||
|
||||
There is deliberately **no** dedicated parts-count node (#258): the count is an ordinary CNC parameter, so it
|
||||
is already readable as a hand-authored `PARAM:` tag (Int32), and a fixed `Parts/` subtree would hard-code
|
||||
parameter numbers that are control- and MTB-specific. On the standard FANUC 0i / 16i / 30i parameter map:
|
||||
There is deliberately **no** dedicated parts-count node (#258): the count is an ordinary CNC value, so it
|
||||
is already readable as a hand-authored tag, and a fixed `Parts/` subtree would hard-code numbers that are
|
||||
control- and MTB-specific. Two addresses carry it; **which one works depends on the control**:
|
||||
|
||||
| Address | Parameter | Meaning |
|
||||
| Address | What | Use when |
|
||||
|---|---|---|
|
||||
| `PARAM:6711` | No. 6711 | Number of parts machined (resets with the operator's clear) |
|
||||
| `PARAM:6712` | No. 6712 | Total number of parts machined (cumulative) |
|
||||
| `PARAM:6713` | No. 6713 | Number of parts required (the target) |
|
||||
| `MACRO:3901` | Custom macro system variable #3901 — parts machined (`#3902` = parts required) | **Recommended.** Works over the wire on a Makino 31i-B (Z-34184, G431), where `cnc_rdparam` is refused |
|
||||
| `PARAM:6711` | CNC parameter No. 6711 — number of parts machined (`6712` total, `6713` required) | Controls that answer `cnc_rdparam` (standard FANUC 0i / 16i / 30i parameter map) |
|
||||
|
||||
The count-up trigger (which M code or cycle end increments it) is set by No. 6710 and bit 0 of No. 6700,
|
||||
so confirm in the machine's parameter manual that the counter is actually enabled and that 6711–6713 mean
|
||||
what the MTB says they mean before relying on them. Cycle time per part is not provided; the `Timers/`
|
||||
subtree exposes `CycleSeconds` and a client can difference it across a `PARAM:6711` change.
|
||||
⚠️ **`PARAM:` reads do not work on the Makino.** Its 31i-B answers `cnc_rdparam` (0x000E) with `EW_FUNC`, so a
|
||||
`PARAM:6711` tag reads `BadNotSupported` there. `cnc_rdmacro` is accepted, so `MACRO:3901` is the address to
|
||||
author on that machine. Both are Int32 tags.
|
||||
|
||||
The count-up trigger (which M code or cycle end increments the counter) is set by No. 6710 and bit 0 of No.
|
||||
6700, so confirm in the machine's manual that the counter is actually enabled before relying on either
|
||||
address — a disabled counter reads a constant `0` (a *vacant* macro reads `BadNoData`, never `0`; see
|
||||
[Wire decode corrections](#wire-decode-corrections-2026-10)). A `0` from `MACRO:3901` is therefore a valid
|
||||
decode of "counter not counting", not a driver fault. Cycle time per part is not provided as a node — see the
|
||||
[virtual-tag recipe](#recipe-cycle-time-per-part-virtual-tag-272).
|
||||
|
||||
### Recipe: feed / rapid / spindle override via PMC (#259)
|
||||
|
||||
The CNC's override values are ordinary PMC signals (the MTB ladder copies the operator-panel switches into the
|
||||
G area), so they are readable today with plain PMC tags — read-only, no new driver surface. On the standard
|
||||
FANUC signal map:
|
||||
|
||||
| Signal | PMC address | Notes |
|
||||
|---|---|---|
|
||||
| Feed override `*FV0-*FV7` | `G12` (Byte) | **Inverted**: the byte holds the one's complement. Percent = `255 - value` for a binary-coded switch (`255` = 0%, `155` = 100%); gray-coded/ladder-converted panels differ |
|
||||
| Rapid override `ROV1`, `ROV2` | `G14.0`, `G14.1` (Bit) | Two bits select 100% / 50% / 25% / F0 |
|
||||
| Spindle override `SOV0-SOV7` | `G30` (Byte) | Percent, usually a direct 0-150 value |
|
||||
|
||||
Author them as normal PMC tags (`G12`, `G14.0`, `G30` — G is accepted on the 30i family and the 16i). To publish a
|
||||
percentage rather than the raw byte, derive it with a virtual tag (`255 - ctx.GetTag("{{equip}}/FeedOverrideRaw")`).
|
||||
|
||||
⚠️ **The addresses above are the FANUC *standard* signal assignment; the meaning is defined by the machine
|
||||
builder's ladder.** Confirm each against the machine's own PMC signal list before trusting the number, and
|
||||
validate with the panel switches at known positions (read-only observation — never write PMC). **Modal G/M/T
|
||||
codes are not available**: `cnc_modal` has no known wire command id here (see
|
||||
[Not ported](#not-ported-633-backlog)).
|
||||
|
||||
### Recipe: cycle time per part (virtual tag, #272)
|
||||
|
||||
A per-part cycle time is **not** a driver feature: `FIXED:Timers/CycleSeconds` and the parts count
|
||||
(`MACRO:3901` / `PARAM:6711`) are both already readable, and the result is a pure function of them. Author the
|
||||
count as an equipment tag named `PartsCount` and `FIXED:Timers/CycleSeconds` as `CycleSeconds`, then add a
|
||||
virtual tag (Double, change-triggered by those two references):
|
||||
|
||||
```csharp
|
||||
// Average cycle seconds per part since the counters were last cleared.
|
||||
var parts = (double)ctx.GetTag("{{equip}}/PartsCount").Value;
|
||||
var cycle = (double)ctx.GetTag("{{equip}}/CycleSeconds").Value;
|
||||
return parts > 0 ? cycle / parts : null; // no parts yet: no average
|
||||
```
|
||||
|
||||
This is a **running average**, valid only while both counters accumulate from the same reset (an operator clearing
|
||||
one but not the other skews it until they are cleared together). The exact *last-cycle delta* (difference
|
||||
`CycleSeconds` across a parts-count step) needs the script to remember the previous sample, and virtual-tag scripts
|
||||
have no documented persistent state (see [`docs/ScriptEditor.md`](../ScriptEditor.md)) — compute that in the
|
||||
consuming system (e.g. ScadaBridge) from the two raw tags instead. Both inputs hold while either is Bad, per the
|
||||
quality contract.
|
||||
|
||||
## PMC range coalescing
|
||||
|
||||
@@ -448,6 +495,38 @@ re-interpretation of `cnc_rdparam` (no successful `cnc_rdparam` capture exists o
|
||||
the reference 31i-B refuses it with `EW_FUNC`, which already reads `BadNotSupported` here); and
|
||||
the all-axes multi-block `cnc_rddynamic2` batching (a latency optimisation, not a correctness fix).
|
||||
|
||||
## Not ported (#633 backlog)
|
||||
|
||||
The #607 porting backlog (#257-#272) was triaged against this read-only driver. Disposition:
|
||||
|
||||
**Done / documented:** #257 `Status/` fixed subtree, #258 parts count ([`MACRO:3901`](#parts-count-macro3901-or-param6711)),
|
||||
#259 overrides ([PMC recipe](#recipe-feed--rapid--spindle-override-via-pmc-259)), #265 16i PMC F/G, #266 PMC range
|
||||
coalescing, #272 ([virtual-tag recipe](#recipe-cycle-time-per-part-virtual-tag-272)).
|
||||
|
||||
**Won't do — the read-only policy (#268-#271).** The FOCAS driver is read-only by design
|
||||
(`WireFocasClient.WriteAsync` returns `BadNotWritable`) and that is a safety decision for real CNCs, not a
|
||||
gap: no write infrastructure / per-tag opt-in (#268), no `cnc_wrmacro` / `cnc_wrparam` (#269), no `pmc_wrpmcrng`
|
||||
or PMC bit read-modify-write (#270), no password / unlock parameter (#271). Nothing in the driver or CLI should be
|
||||
read as a path toward them.
|
||||
|
||||
**Blocked — need an authoritative wire capture first.** None of the following has a wire command id known to this
|
||||
repo or to the sister ignitionoee client, and neither mock serves them over the binary wire:
|
||||
|
||||
| Item | Capability | Blocked by |
|
||||
|---|---|---|
|
||||
| #259 (modal part) | Modal G / M / T codes (`cnc_modal`) | no wire id |
|
||||
| #260 | Tool number / tool life, work-coordinate and tool offsets (`cnc_rdtofs` / `rdzofs`) | no wire ids |
|
||||
| #261 | Operator messages (`cnc_rdopmsg`) and the currently-executing block text (`cnc_rdexecprog`) | no wire ids |
|
||||
| #263 | `DIAG:` address area (`cnc_diagnoss`) — the highest-value one: a single generic area would unlock many fields | no wire id |
|
||||
| #264 | Multi-path / multi-channel (`cnc_getpath`) — the wire layer has `PathId` plumbing but no per-device/tag option | no wire id, and no multi-path control to validate against (the Makino is single-path) |
|
||||
| #267 | Alarm history (`cnc_rdalmhistry`) | no wire id; the live alarm surface ([#647](#alarm-projection)) comes first |
|
||||
|
||||
⚠️ **Do not discover these ids by probing the production Makino.** An unknown or guessed command id could map to a
|
||||
function that is not a read, and even known ids misbehave on the 31i-B (`cnc_rdparam` 0x000E answers `EW_FUNC`). The
|
||||
0x00D3 `cnc_getfigure` lesson (see Wire decode corrections) already showed that an id agreed between two mocks can be
|
||||
fiction. The prerequisite is an authoritative capture: real FWLIB on a Windows host against a **non-production**
|
||||
control, or a passive capture of an existing FWLIB application's traffic.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [`docs/v2/driver-specs.md §7`](../v2/driver-specs.md) — full OPC UA node
|
||||
|
||||
@@ -67,7 +67,7 @@ Running record of v2 dev services on the Windows dev VM. Updated on every instal
|
||||
| Dev Galaxy (AVEVA System Platform) | Local install on this dev box — full ArchestrA + Historian + OI-Server stack | v1 baseline | Local COM via MXAccess (`C:\Program Files (x86)\ArchestrA\Framework\bin\ArchestrA.MXAccess.dll`); Historian via `aaH*` services; SuiteLink via `slssvc` | Windows Auth | Galaxy repository DB `ZB` on local SQL Server (separate instance from `otopcua-mssql` — legacy v1 Galaxy DB, not related to v2 config DB) | ✅ **Fully available — Phase 2 lift unblocked.** 27 ArchestrA / AVEVA / Wonderware services running incl. `aaBootstrap`, `aaGR` (Galaxy Repository), `aaLogger`, `aaUserValidator`, `aaPim`, `ArchestrADataStore`, `AsbServiceManager`, `AutoBuild_Service`; full Historian set (`aahClientAccessPoint`, `aahGateway`, `aahInSight`, `aahSearchIndexer`, `aahSupervisor`, `InSQLStorage`, `InSQLConfiguration`, `InSQLEventSystem`, `InSQLIndexing`, `InSQLIOServer`, `InSQLManualStorage`, `InSQLSystemDriver`, `HistorianSearch-x64`); `slssvc` (Wonderware SuiteLink); `OI-Gateway` install present at `C:\Program Files (x86)\Wonderware\OI-Server\OI-Gateway\` (decision #142 AppServer-via-OI-Gateway smoke test now also unblocked) |
|
||||
| GLAuth (LDAP) | Docker container `zb-shared-glauth` on the Linux Docker host — managed via `scadaproj/infra/glauth/` | v2.4.0 | `10.100.0.35:3893` (LDAP plaintext; LDAPS disabled) | Bind account `cn=serviceaccount,dc=zb,dc=local` / `serviceaccount123`; all test users password `password`; baseDN `dc=zb,dc=local` | `scadaproj/infra/glauth/` (source of truth + deploy/verify runbook) | ✅ Running on Docker host. Shared across OtOpcUa, MxAccessGateway, ScadaBridge. OtOpcUa groups: `OtOpcUa-Admins`→Administrator, `OtOpcUa-Designers`→Designer, `OtOpcUa-Viewers`→Viewer. The per-VM NSSM service at `C:\publish\glauth\` and old base DNs `dc=lmxopcua,dc=local` / `dc=otopcua,dc=local` are obsolete. |
|
||||
| OPC Foundation reference server | Not yet built | — | `10.100.0.35:62541` (target) | `user1` / `password1` (reference-server defaults) | — | Pending (needed for Phase 5 OPC UA Client driver testing) |
|
||||
| FOCAS TCP stub | Not yet built | — | `10.100.0.35:8193` (target) | n/a | — | Pending (built in Phase 5; runs on Docker host) |
|
||||
| FOCAS fixture (`otopcua-focas-fixture`, vendored focas-mock) | Docker compose at `/opt/otopcua-focas/` on Docker host (source: `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Docker/`) | vendored snapshot | `10.100.0.35:8193` (published; `OTOPCUA_FOCAS_SIM_ENDPOINT`) | n/a | n/a | Owned by OtOpcUa. NB `otopcua-focas-sim` on the same host is **ignitionoee's** mock (#651) — do not touch it |
|
||||
| Modbus simulator (`otopcua-pymodbus:3.13.0`) | Docker compose at `/opt/otopcua-modbus/` on Docker host | pinned 3.13.0 | `10.100.0.35:5020` | n/a | n/a | Stack staged; bring up with `lmxopcua-fix up modbus <profile>` from this VM |
|
||||
| AB CIP fixture (`otopcua-ab-server:libplctag-release`) | Docker compose at `/opt/otopcua-abcip/` on Docker host | source-pinned `release` tag | `10.100.0.35:44818` | n/a | n/a | Stack staged; bring up with `lmxopcua-fix up abcip <profile>` from this VM |
|
||||
| S7 fixture (`otopcua-python-snap7:1.0`) | Docker compose at `/opt/otopcua-s7/` on Docker host | python-snap7 ≥2.0 | `10.100.0.35:1102` | n/a | n/a | Stack staged; bring up with `lmxopcua-fix up s7 s7_1500` from this VM |
|
||||
|
||||
Reference in New Issue
Block a user