Commit Graph

2 Commits

Author SHA1 Message Date
Klaus Landsdorf e0ab811c03 Scales and Machinery: advertise only the facets and units the server satisfies (#4615)
# Description

The Scales and Machinery node managers publish their facets and
conformance units through `IConformanceContributor`. This aligns what
they advertise with the published tables.

**Machinery** (`fix(machinery): advertise the ProcessIrregularity job
result unit`)
- Adds *Machinery Job Management Result ProcessIrregularity* to the
units
  reported for `WithPredefinedParameters()`. OPC 40001-3 Table 24 lists
  it, the Jobs NodeSet carries it as a `<Category>`, and
  `MachineryJobParameters` already recognises the parameter.
- New test
`PredefinedParametersAdvertiseEveryParameterUnitOfTheBaseFacetAsync`
  checks all Table 24 parameter units.

**Scales** (`fix(scales): advertise only the facets the runtime
satisfies`)
- Base Scale (and every kind facet), Scale System, Feeder Module and
  Printer Module include the Machinery Machine Identification facet
  (OPC 40200 Tables 164 to 167). While every published scale and scale
  system is organized into the `Machines` folder, the server now also
advertises that facet and its mandatory units *Machinery Find Machines*
and *Machinery Machine Identification*. When a scale is not in the
folder
  (`OrganizeIntoMachinesFolder = false`, or the folder is missing) those
facets are no longer advertised. Production preset and SI unit facets do
  not include Machine Identification and are unaffected.
- The Totalizing Hopper, Catchweigher and AutomaticWeightPriceLabeler
kind
  facets and units are no longer claimed: no controller drives them yet
(known limitations in `docs/Scales.md`). The scales still count towards
  Base Scale.
- *PackML State Information* (Table 164, optional) is advertised when a
  scale or scale system runs a PackML state machine, qualified with the
PackML namespace as the table writes it. Machinery units use namespace
0,
  as `MachineryNodeManager` reports them.
- `docs/Scales.md` updated (Machines folder, options table, conformance
  matrix, known limitations).
- New tests: `ScaleFacetsBringTheMachineIdentificationTheyIncludeAsync`,
  `ScaleFacetsAreNotClaimedOutsideTheMachinesFolderAsync`,
  `KindFacetsWithoutAControllerAreNotClaimedAsync`,
  `PackMLStateInformationIsClaimedWithAPackMLStateMachineAsync`.

Each new test fails against master and passes with the change.

Fork CI (full matrix incl. macOS) and CodeQL: green — [CI run
36779541067](https://github.com/biancode/UA-.NETStandard/actions/runs/36779541067),
[CodeQL run
36779544537](https://github.com/biancode/UA-.NETStandard/actions/runs/36779544537).

## Related Issues

- Fixes #4614

## Checklist

- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
2026-10-01 15:12:44 +02:00
Klaus Landsdorf dadd609ec0 Scales: the OPC 40200 companion SDK with the OPC 30050 PackML model (#4580)
> Tracking issue #4576. #4577, #4578 and #4579 are merged, so this
branch is
> rebased onto `master` and contains only the Scales and PackML commits.

## Summary

Last of the four PRs of #4576. Adds OPC 40200 Weighing Technology 2.00
as a
model package plus a server and a client SDK, and the OPC 30050 PackML
model
it composes.

| Package | Content |
|---|---|
| `Opc.Ua.PackML` | OPC 30050 PackML 1.01, plus
`PackMLStateMachineController`, which drives a base state machine
together with its nested machine and execute state machines (activation
and deactivation of the sub-state machines, the PackML methods with an
`Executable` attribute that follows the state, acting states left
automatically or by the equipment) |
| `Opc.Ua.Scales` | OPC 40200 2.00
(`http://opcfoundation.org/UA/Scales/V2/`), generated from the
unmodified UA-Nodeset publication, plus the contracts client and server
share |
| `Opc.Ua.Scales.Server` | `AddScales()` / `ConfigureScales(...)`; all
fourteen scale kinds and scale systems below `DeviceSet` and `Machines`;
a runtime that does the weighing arithmetic (ranges, rounding to d or e,
zero band, overload/underload, zero, tare, preset tare, registration),
production presets with DI locking, statistics, recipes and recipe
files, Annex C events and alarms over a `HasNotifier` hierarchy, and the
methods of every scale type |
| `Opc.Ua.Scales.Client` | discovery, weight reads and streaming,
zero/tare/register, products, PackML state and commands, type-specific
methods, events and alarms, snapshots |

`docs/Scales.md` includes a conformance matrix against the 21 OPC 40200
server
facets with test evidence, the places where the specification and the
NodeSet disagree, and known limitations.

## Review follow-ups

Separate commits after the docs/samples commit, each with a test that
fails without it where the defect can be observed.

- Copilot review: `CurrentProductsChanged` fires only when the set of
  current products changes; the new public collection APIs
  (`ScalesServerOptions.AdditionalNamespaceUris`,
  `ScaleNamespaceIndices.SearchOrder`, `ScalesModel.AllKinds`) are
  `ArrayOf<T>`.
- Own review with the rubric in `.github/copilot-instructions.md`:
  - PackML raises `StateChanged` after its lock is released (queued, in
    order), reads `StartParameters` under the lock, and reports the
    handler's status when an acting state cannot complete.
- Scales: concurrent first creations share one runtime (the lock service
    refused a second attach); two creations of the same name cannot both
publish; a removed product id stays reserved until its node is gone; the
    zone, target-count and vehicle handlers change products under the
preset lock; the product factory and `configure` delegate run outside
    it; the recipe upload reads `ActiveRecipe` under the lock; resetting
    the statistics also resets the tolerance percentage.
- The client lets a failed browse call propagate instead of reading it
as
    "no children".

## Testing

- `tests/Opc.Ua.Scales.Tests`: 115 tests on net8 and net10 and with the
netstandard2.1 libraries, including the PackML runtime; the
client/server
  tests connect a real session over `opc.tcp` to a hosted
  `ScalesNodeManager`.
- `UA.slnx` builds for net472, net48, netstandard2.0/2.1 and net8/9/10
with
  SDK 10.0.401 and no new warnings.
- Changed-line coverage of this layer (`check-coverage.ps1`): 94.59 %
(4585 of 4847 changed lines, against `master`).
- Fork CI, full PR matrix incl. macOS: in progress, [run
36676067489](https://github.com/biancode/UA-.NETStandard/actions/runs/36676067489);
CodeQL: in progress, [run
36676070427](https://github.com/biancode/UA-.NETStandard/actions/runs/36676070427).
- On the current `master` (with Pumps merged), `UA.slnx` builds for
every target
  framework and the Scales and Pumps tests pass.
- Beyond the in-repo tests the implementation has been validated end to
end
  against a separate, non-public OPC UA client and server.

## Reviewing

Commits: PackML → Scales model → server → client → tests → docs and
samples
→ review follow-ups.
2026-09-30 10:54:48 +02:00