kapps-semantic-middleware 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kapps_semantic_middleware/AGENTS.md +170 -0
- kapps_semantic_middleware/CONTEXT-MAP.md +59 -0
- kapps_semantic_middleware/CONTEXT.md +349 -0
- kapps_semantic_middleware/__init__.py +38 -0
- kapps_semantic_middleware/activity.py +384 -0
- kapps_semantic_middleware/connectors/__init__.py +42 -0
- kapps_semantic_middleware/connectors/knowledge_graph_connector.py +140 -0
- kapps_semantic_middleware/connectors/mqtt_binding.py +371 -0
- kapps_semantic_middleware/connectors/rest_binding.py +437 -0
- kapps_semantic_middleware/connectors/semantic.py +346 -0
- kapps_semantic_middleware/connectors/wiring.py +506 -0
- kapps_semantic_middleware/credentials.py +90 -0
- kapps_semantic_middleware/demonstrations/__init__.py +9 -0
- kapps_semantic_middleware/demonstrations/transferunits/CONTEXT.md +77 -0
- kapps_semantic_middleware/demonstrations/transferunits/README.md +220 -0
- kapps_semantic_middleware/demonstrations/transferunits/__init__.py +24 -0
- kapps_semantic_middleware/demonstrations/transferunits/__main__.py +43 -0
- kapps_semantic_middleware/demonstrations/transferunits/algorithm.py +398 -0
- kapps_semantic_middleware/demonstrations/transferunits/control_station.py +206 -0
- kapps_semantic_middleware/demonstrations/transferunits/controller.py +1105 -0
- kapps_semantic_middleware/demonstrations/transferunits/factory.ttl +16 -0
- kapps_semantic_middleware/demonstrations/transferunits/index.py +170 -0
- kapps_semantic_middleware/demonstrations/transferunits/launcher.py +485 -0
- kapps_semantic_middleware/demonstrations/transferunits/middleware.py +187 -0
- kapps_semantic_middleware/demonstrations/transferunits/plc/__init__.py +10 -0
- kapps_semantic_middleware/demonstrations/transferunits/plc/__main__.py +87 -0
- kapps_semantic_middleware/demonstrations/transferunits/plc/panel.py +202 -0
- kapps_semantic_middleware/demonstrations/transferunits/plc/static/transferunit.svg +99 -0
- kapps_semantic_middleware/demonstrations/transferunits/plc/templates/panel.html +181 -0
- kapps_semantic_middleware/demonstrations/transferunits/plc/transfer_unit.py +449 -0
- kapps_semantic_middleware/demonstrations/transferunits/seed.py +259 -0
- kapps_semantic_middleware/demonstrations/transferunits/station_board.py +631 -0
- kapps_semantic_middleware/demonstrations/transferunits/templates/index.html +311 -0
- kapps_semantic_middleware/demonstrations/transferunits/templates/station_board.html +404 -0
- kapps_semantic_middleware/demonstrations/transferunits/transferunit.ttl +114 -0
- kapps_semantic_middleware/docs/mechanics/01-instantiation-and-lifecycle.md +75 -0
- kapps_semantic_middleware/docs/mechanics/02-workflow-registration.md +103 -0
- kapps_semantic_middleware/docs/mechanics/03-state-and-parameters.md +79 -0
- kapps_semantic_middleware/docs/mechanics/04-connector-binding.md +131 -0
- kapps_semantic_middleware/docs/mechanics/05-operation-coordination.md +108 -0
- kapps_semantic_middleware/docs/mechanics/06-views-and-projection.md +86 -0
- kapps_semantic_middleware/docs/mechanics/07-writing-to-the-graph-and-to-devices.md +48 -0
- kapps_semantic_middleware/docs/mechanics/08-provisioning-and-seeding.md +109 -0
- kapps_semantic_middleware/docs/mqtt-payloads.md +98 -0
- kapps_semantic_middleware/examples/CONTEXT.md +45 -0
- kapps_semantic_middleware/examples/__init__.py +8 -0
- kapps_semantic_middleware/examples/demo_handover.ttl +42 -0
- kapps_semantic_middleware/examples/demo_scenario1.ttl +66 -0
- kapps_semantic_middleware/examples/demo_scenario2.ttl +75 -0
- kapps_semantic_middleware/examples/docker/docker-compose.yml +36 -0
- kapps_semantic_middleware/examples/docker/graphdb-repo-config.ttl +74 -0
- kapps_semantic_middleware/examples/docs/transferunit-ontology.md +275 -0
- kapps_semantic_middleware/examples/handlers.py +64 -0
- kapps_semantic_middleware/examples/scenario1_hello_world.ipynb +432 -0
- kapps_semantic_middleware/examples/scenario1_hello_world.py +282 -0
- kapps_semantic_middleware/examples/scenario2_door.ipynb +438 -0
- kapps_semantic_middleware/examples/scenario2_door.py +285 -0
- kapps_semantic_middleware/examples/seed.py +269 -0
- kapps_semantic_middleware/examples/transferunit.ttl +114 -0
- kapps_semantic_middleware/examples_cli.py +199 -0
- kapps_semantic_middleware/middleware.py +1255 -0
- kapps_semantic_middleware/modes.py +47 -0
- kapps_semantic_middleware/ontology/core.ttl +755 -0
- kapps_semantic_middleware/ontology/mes.ttl +90 -0
- kapps_semantic_middleware/ontology/service.ttl +178 -0
- kapps_semantic_middleware/projection.py +314 -0
- kapps_semantic_middleware/py.typed +0 -0
- kapps_semantic_middleware/registration.py +877 -0
- kapps_semantic_middleware/rest_router.py +356 -0
- kapps_semantic_middleware/seeding.py +99 -0
- kapps_semantic_middleware/shacl_interop/CONTEXT.md +33 -0
- kapps_semantic_middleware/shacl_interop/__init__.py +5 -0
- kapps_semantic_middleware/shacl_interop/shape_from_typehints.py +159 -0
- kapps_semantic_middleware/vocabulary.py +213 -0
- kapps_semantic_middleware-0.1.0.dist-info/METADATA +142 -0
- kapps_semantic_middleware-0.1.0.dist-info/RECORD +79 -0
- kapps_semantic_middleware-0.1.0.dist-info/WHEEL +4 -0
- kapps_semantic_middleware-0.1.0.dist-info/entry_points.txt +3 -0
- kapps_semantic_middleware-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Orientation for an agent building against `kapps_semantic_middleware`.
|
|
4
|
+
|
|
5
|
+
This library lets a piece of Python code running on or next to a shopfloor resource expose its
|
|
6
|
+
functionality through an RDF knowledge graph, and lets other middleware instances discover and
|
|
7
|
+
invoke that functionality through the graph rather than through hardcoded network references.
|
|
8
|
+
|
|
9
|
+
If you are prototyping against this middleware, read the consumption rules below first. They are
|
|
10
|
+
constraints that **do not surface in type signatures** — most of them fail silently, producing
|
|
11
|
+
structurally wrong behavior rather than an exception. Then read the mechanics pages for whichever
|
|
12
|
+
part you are touching.
|
|
13
|
+
|
|
14
|
+
## Where to read what
|
|
15
|
+
|
|
16
|
+
**Vocabulary — what the words mean.** Read these before the mechanics pages; the mechanics pages
|
|
17
|
+
assume their terms and do not re-define them.
|
|
18
|
+
|
|
19
|
+
| File | Covers |
|
|
20
|
+
|---|---|
|
|
21
|
+
| [`CONTEXT-MAP.md`](CONTEXT-MAP.md) | The five contexts, how they depend on each other, and the three-module ontology layering |
|
|
22
|
+
| [`src/kapps_semantic_middleware/CONTEXT.md`](src/kapps_semantic_middleware/CONTEXT.md) | The core vocabulary: Service, Workflow, Parameter, ClassScope, Projection, Binding descriptor, Operation, Mode |
|
|
23
|
+
| [`src/kapps_semantic_middleware/shacl_interop/CONTEXT.md`](src/kapps_semantic_middleware/shacl_interop/CONTEXT.md) | Precondition and outcome shapes derived from a function's type hints |
|
|
24
|
+
| [`examples/CONTEXT.md`](examples/CONTEXT.md) | The self-contained scenario notebooks |
|
|
25
|
+
| [`demo/transferunits/CONTEXT.md`](demo/transferunits/CONTEXT.md) | The runnable multi-process factory demonstration |
|
|
26
|
+
|
|
27
|
+
**Mechanics — how to use it.** Written in construction order; a reader can go start to finish
|
|
28
|
+
without forward references.
|
|
29
|
+
|
|
30
|
+
| Page | Covers |
|
|
31
|
+
|---|---|
|
|
32
|
+
| [`docs/mechanics/01-instantiation-and-lifecycle.md`](docs/mechanics/01-instantiation-and-lifecycle.md) | Constructing an instance, choosing a mode, what appears in the graph at startup, heartbeat, deregistration |
|
|
33
|
+
| [`docs/mechanics/02-workflow-registration.md`](docs/mechanics/02-workflow-registration.md) | Declaring what a Service exposes, the ontology prerequisites, signature-derived shapes, address vs. endpoint |
|
|
34
|
+
| [`docs/mechanics/03-state-and-parameters.md`](docs/mechanics/03-state-and-parameters.md) | Modelling resource state as Parameters, committed value vs. locator, where a parameter's shape comes from |
|
|
35
|
+
| [`docs/mechanics/04-connector-binding.md`](docs/mechanics/04-connector-binding.md) | The recognition chain, adding a protocol the library does not ship, connector wiring, transports |
|
|
36
|
+
| [`docs/mechanics/05-operation-coordination.md`](docs/mechanics/05-operation-coordination.md) | Dispatch, the event trigger, pull-and-run, the status lifecycle, handover, recovery |
|
|
37
|
+
| [`docs/mechanics/06-views-and-projection.md`](docs/mechanics/06-views-and-projection.md) | Defining a ClassScope, what a view cannot select, what the northbound projection removes |
|
|
38
|
+
| [`docs/mechanics/07-writing-to-the-graph-and-to-devices.md`](docs/mechanics/07-writing-to-the-graph-and-to-devices.md) | Every path that causes a write, naming the field that moved, northbound vs. southbound, IRI handling |
|
|
39
|
+
| [`docs/mechanics/08-provisioning-and-seeding.md`](docs/mechanics/08-provisioning-and-seeding.md) | **Read first if `ogm=` is a mystery.** The bootstrap order, loading the shared ontologies, authoring the domain TBox, and seeding the instance data a Parameter needs |
|
|
40
|
+
|
|
41
|
+
### A note on the citations you will find in the glossaries
|
|
42
|
+
|
|
43
|
+
The `CONTEXT.md` files cite architecture decision records as **ADR 00nn** and **root ADR
|
|
44
|
+
000n**. Those records are development-repository material and are **not part of this
|
|
45
|
+
distribution** — no copy of them ships, and the release rewrites every path that pointed
|
|
46
|
+
at one, so a citation here is never a link you can follow.
|
|
47
|
+
|
|
48
|
+
**Do not go looking for them.** The citations are provenance markers, not links. Every mechanism a
|
|
49
|
+
glossary cites a record for is explained in full by the mechanics page covering it — ADR 0004's
|
|
50
|
+
subject is the "Address and endpoint" section of
|
|
51
|
+
[`02-workflow-registration.md`](docs/mechanics/02-workflow-registration.md), ADR 0006's is "The
|
|
52
|
+
graph-facing connector" in [`04-connector-binding.md`](docs/mechanics/04-connector-binding.md), and
|
|
53
|
+
so on. If a citation seems to point at something the mechanics pages do not cover, that is a
|
|
54
|
+
documentation bug worth reporting, not a missing file worth hunting.
|
|
55
|
+
|
|
56
|
+
## Where the names come from
|
|
57
|
+
|
|
58
|
+
This library re-exports much of its surface from its three dependencies, so the import you need is
|
|
59
|
+
often *not* from `kapps_semantic_middleware`. Use this table rather than guessing.
|
|
60
|
+
|
|
61
|
+
| Name | Import from |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `SemanticMiddleware`, `Mode` | `kapps_semantic_middleware` |
|
|
64
|
+
| `IRI`, `GraphDB` | `kapps_triplestore_interface` |
|
|
65
|
+
| `OGM` | `kapps_ogm` |
|
|
66
|
+
| `ClassScope` | `kapps_ogm.utils.class_scope` |
|
|
67
|
+
| `SyncDirection` | `transitional_sync_middleware.middleware.sync.synced_connector` |
|
|
68
|
+
| `ConnectionInfo` | `transitional_sync_middleware.middleware.registries` |
|
|
69
|
+
| `INF` (the interface vocabulary) | `kapps_semantic_middleware.vocabulary` |
|
|
70
|
+
| `graphdb_for`, `credentials_for` | `kapps_semantic_middleware.credentials` |
|
|
71
|
+
| `DataModel`, `Reference`, `Identifier` | `kapps_semantic_middleware` (re-exported from `transitional_sync_middleware`) |
|
|
72
|
+
|
|
73
|
+
Binding internals, if you are adding a protocol, live under
|
|
74
|
+
`kapps_semantic_middleware.connectors` — `semantic`, `mqtt_binding`, `rest_binding`,
|
|
75
|
+
`knowledge_graph_connector`, `wiring`.
|
|
76
|
+
|
|
77
|
+
> `kapps_semantic_middleware/ontology/` is **not** a Python module. It is the directory holding the
|
|
78
|
+
> three vocabulary files (`core.ttl`, `mes.ttl`, `service.ttl`). Importing from it fails.
|
|
79
|
+
|
|
80
|
+
## Consumption rules
|
|
81
|
+
|
|
82
|
+
### These fail silently
|
|
83
|
+
|
|
84
|
+
**`GraphDB.from_env()` connects to whatever `GRAPHDB_REPOSITORY` names, and seeding destroys what it
|
|
85
|
+
connects to.** `seeding.clear_repository` clears the default graph of the client's current
|
|
86
|
+
repository, so a value left over in a shell — from another project, another checkout, a `.bashrc`
|
|
87
|
+
written months ago — silently redirects a wipe. Nothing validates that the repository was the one you
|
|
88
|
+
meant; it only has to exist. Use `credentials.graphdb_for("name")`, which names the repository in
|
|
89
|
+
code and ignores the variable, for anything that seeds, clears, or re-seeds. Note this hazard belongs
|
|
90
|
+
to the *variable*, not the server: pinning the repository does not stop `GRAPHDB_URL` pointing at a
|
|
91
|
+
shared instance.
|
|
92
|
+
|
|
93
|
+
**A `ClassScope` terminates at a Parameter and cannot select within one.** Any chain element below
|
|
94
|
+
a complex property is silently discarded during fetch. A view that tries to reach inside a
|
|
95
|
+
parameter blanknode materializes only as far as the parameter itself, with no error raised. A scope
|
|
96
|
+
chooses *which* parameters, never *which parts* of one.
|
|
97
|
+
|
|
98
|
+
**Connector bindings must be registered at construction, not later.** The framework calls
|
|
99
|
+
`connect()` on everything in the connection registry before it runs `on_start_up` callbacks. A
|
|
100
|
+
connector registered after construction never has `connect()` called, so inbound traffic dies
|
|
101
|
+
silently — the listener task never starts and the queue is never fed — while outbound may limp
|
|
102
|
+
along, making the fault one-directional and quiet.
|
|
103
|
+
|
|
104
|
+
**A Parameter's shape comes from the TBox restriction on its property's range, not from the
|
|
105
|
+
instance data.** Anything the restriction does not declare is dropped at materialization with only
|
|
106
|
+
a warning logged. Metadata a connector needs must be declared in the restriction or it never
|
|
107
|
+
arrives, and the connector fails with nothing raised.
|
|
108
|
+
|
|
109
|
+
**Under the locator pattern a Parameter's value is not in the graph at all.** Fast-changing
|
|
110
|
+
parameters keep only metadata in the graph; the live value exists only in the datamodel and over
|
|
111
|
+
REST. An unobserved parameter materializes as an empty list, which means *not yet read* — not zero,
|
|
112
|
+
and not null. Handle the empty case.
|
|
113
|
+
|
|
114
|
+
**A persistence write must name the region of the model that changed.** The fan-out notifies only
|
|
115
|
+
the connectors that region covers. A write that does not name its field notifies every synced
|
|
116
|
+
connector, so devices are written that nobody touched — and for a settable parameter that
|
|
117
|
+
fabricates a command rather than merely wasting traffic.
|
|
118
|
+
|
|
119
|
+
**The northbound REST payload never carries protocol connection metadata.** Broker addresses,
|
|
120
|
+
topics and endpoints are pruned from the served datamodel *before* any data is fetched. This is
|
|
121
|
+
structural, not a permission check: the northbound model has no field able to carry a broker
|
|
122
|
+
address. It stops a peer *learning* an address from this surface; it does not stop someone who
|
|
123
|
+
already knows it.
|
|
124
|
+
|
|
125
|
+
**OWL existential restrictions do not make a field required.** Only SHACL shapes enforce
|
|
126
|
+
requiredness, and only at admission. A parameter with no observed value does not raise a validation
|
|
127
|
+
error on materialization merely because its restriction declares `owl:someValuesFrom`. Absence of a
|
|
128
|
+
triple means *unknown*, never *false*.
|
|
129
|
+
|
|
130
|
+
**Any prettified or shortened IRI is display only.** Production code carries fully back-resolvable
|
|
131
|
+
IRIs in their mangled form — REST path segments, datamodel field names, `svc:endpoint` triples. A
|
|
132
|
+
consumer that parses a displayed IRI, or round-trips one back into a query, gets a wrong answer
|
|
133
|
+
with no exception raised.
|
|
134
|
+
|
|
135
|
+
### These fail loudly
|
|
136
|
+
|
|
137
|
+
**Every class you register against must already exist in the ontology.** The middleware creates
|
|
138
|
+
instances automatically but never mints classes. A domain-specific subclass of `svc:Service`,
|
|
139
|
+
`svc:Workflow` or `svc:StateProperty`, and the `cfc:Capability` subclass a Workflow realizes, must
|
|
140
|
+
all pre-exist. Startup fails immediately, naming the missing class.
|
|
141
|
+
|
|
142
|
+
**All knowledge-graph writes go through the OGM.** Raw SPARQL `UPDATE` or direct
|
|
143
|
+
`kapps_triplestore_interface` mutation calls bypass the validated write path, so the written node passes no
|
|
144
|
+
shape check and a property replacement loses its atomicity — the intermediate state fails
|
|
145
|
+
validation. Reads may use the access module directly; only writes are constrained.
|
|
146
|
+
|
|
147
|
+
**`@mw.workflow` and `@mw.state` are resource-mode only.** Each raises `RuntimeError` when called
|
|
148
|
+
on an instance in any other mode, and `ValueError` when a required class IRI is missing.
|
|
149
|
+
|
|
150
|
+
### Structural facts
|
|
151
|
+
|
|
152
|
+
**Build against `src/kapps_semantic_middleware/`. `examples/` and `demo/` are illustrations, not
|
|
153
|
+
API.** They are teaching vehicles seeded against throwaway repositories, not production patterns to
|
|
154
|
+
copy.
|
|
155
|
+
|
|
156
|
+
**The demo imports as `kapps_semantic_middleware.demonstrations`, not `demo`.** The wheel remaps
|
|
157
|
+
the `demo/` directory into the library namespace. `import demo.transferunits` works only in a
|
|
158
|
+
development checkout.
|
|
159
|
+
|
|
160
|
+
**There is one Service per middleware *instance*, not per Resource.** Several instances may wrap
|
|
161
|
+
one Resource — a controller and a monitor, say — each owning its own Service node, address and
|
|
162
|
+
heartbeat, all linked by `svc:isServiceOf`. Discovery may therefore return several Services for one
|
|
163
|
+
Resource; select by address or by advertised capability rather than assuming exactly one.
|
|
164
|
+
|
|
165
|
+
**Only `resource` and `watchdog` modes are implemented.** `server` is reserved and raises
|
|
166
|
+
`NotImplementedError`. A graph-*consuming* participant — a planner, a controller, a mobile robot —
|
|
167
|
+
is a resource-mode instance with its own Resource, not a server-mode one.
|
|
168
|
+
|
|
169
|
+
**Docker is a prerequisite for running the examples, never for using the library.** The bundled
|
|
170
|
+
`docker compose` file provides the GraphDB the scenarios and the factory need.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Context Map
|
|
2
|
+
|
|
3
|
+
## Contexts
|
|
4
|
+
|
|
5
|
+
- [Core Middleware](./src/kapps_semantic_middleware/CONTEXT.md) — the middleware library itself.
|
|
6
|
+
It holds the Service/Workflow/Capability/Operation/Mode registration machinery and the
|
|
7
|
+
execution machinery.
|
|
8
|
+
- [SHACL Interop](./src/kapps_semantic_middleware/shacl_interop/CONTEXT.md) — the temporary SHACL
|
|
9
|
+
seam for workflow precondition shapes and outcome shapes. It generates and parses them. It is
|
|
10
|
+
explicit scaffolding, will move to `kapps_ogm` (see its ADR 0001).
|
|
11
|
+
- [Example Scenarios](./examples/CONTEXT.md) — self-contained demonstration notebooks, plus the
|
|
12
|
+
seed-data logic and the ontology-provisioning logic under them. That logic makes each notebook
|
|
13
|
+
reproducible against a dummy repository, and never against the production state.
|
|
14
|
+
- [TransferUnit Factory](./demo/transferunits/CONTEXT.md) — the runnable multi-process demo. A
|
|
15
|
+
launcher seeds N units and starts one process per participant (ADR 0029). Its decisions live
|
|
16
|
+
with the Core Middleware ADRs, next to ADR 0029.
|
|
17
|
+
- **Module Requirements** — requirements this project places on sibling KAPPS-family
|
|
18
|
+
modules (`kapps_ogm`, the not-yet-created visual-toolbox GUI). Not a code context, and its
|
|
19
|
+
documents are development-repository material that does not ship.
|
|
20
|
+
|
|
21
|
+
## Relationships
|
|
22
|
+
|
|
23
|
+
- **Core Middleware → SHACL Interop**: Core Middleware calls into SHACL Interop to read or write
|
|
24
|
+
a shape. The shape belongs to a Workflow class or a StateProperty class. The call happens when
|
|
25
|
+
`@mw.workflow` or `@mw.state` registers or resolves one. SHACL Interop has no dependency back
|
|
26
|
+
on Core Middleware. It operates on class IRIs and shapes, and not on types of the Core Middleware.
|
|
27
|
+
- **Example Scenarios → Core Middleware**: Example Scenarios instantiate and exercise Core
|
|
28
|
+
Middleware end-to-end, against the ontology and the instance data they seed themselves. Core Middleware
|
|
29
|
+
has no dependency on Example Scenarios.
|
|
30
|
+
- **TransferUnit Factory → Core Middleware**: the factory runs several instances of the library
|
|
31
|
+
in separate processes. That is one instance per unit, plus a controller. A monitor is
|
|
32
|
+
milestone 2 (ADR 0032) and is not built. Core Middleware has no dependency on the factory.
|
|
33
|
+
- **Module Requirements** records obligations that Core Middleware and SHACL Interop place on
|
|
34
|
+
`kapps_ogm` and the visual-toolbox repo. It does not depend on the code contexts, and no code
|
|
35
|
+
context depends on it. It is a record for work that belongs elsewhere.
|
|
36
|
+
|
|
37
|
+
## Ontology-module layering
|
|
38
|
+
|
|
39
|
+
The vocabulary of the project is layered across three modules (Core Middleware ADR 0012):
|
|
40
|
+
|
|
41
|
+
- **`cfc:` — Core** (`.../Core#`): published, external, superior — `Operation`/`Capability`/
|
|
42
|
+
`Resource`/`Task`. The system imports and specializes it. The system does not modify it.
|
|
43
|
+
- **`mes:` — MES** (`.../MES#`, `src/kapps_semantic_middleware/ontology/mes.ttl`): a
|
|
44
|
+
**domain** ontology that imports Core and details it out — possession + handover ability.
|
|
45
|
+
What domain experts touch.
|
|
46
|
+
- **`svc:` — Service** (`.../Service#`, `ontology/service.ttl`): middleware-to-middleware
|
|
47
|
+
**reachability and coordination only**, domain code does not touch it. It holds Service, Workflow and
|
|
48
|
+
StateProperty, the address, the endpoint and the heartbeat. It also holds the resolution chain,
|
|
49
|
+
and an Operation's status and provenance.
|
|
50
|
+
|
|
51
|
+
`mes:` and `svc:` are siblings that both import Core. `mes:` is domain-facing, `svc:` is
|
|
52
|
+
middleware-facing.
|
|
53
|
+
|
|
54
|
+
## Where the decisions are written down
|
|
55
|
+
|
|
56
|
+
The architecture decision records are development-repository material and are not part of
|
|
57
|
+
this distribution. What they decided is described, without the deliberation, in
|
|
58
|
+
[`docs/mechanics/`](docs/mechanics/); [`AGENTS.md`](AGENTS.md) indexes those pages.
|
|
59
|
+
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# Core Middleware
|
|
2
|
+
|
|
3
|
+
One of five contexts in this repo — see `/CONTEXT-MAP.md` at the repo root for the others
|
|
4
|
+
(SHACL Interop, Example Scenarios, Module Requirements).
|
|
5
|
+
|
|
6
|
+
> This file defines the **vocabulary**. For how to *use* the mechanisms it names — and for the
|
|
7
|
+
> constraints that do not surface in a type signature — read `AGENTS.md` and the `docs/mechanics/`
|
|
8
|
+
> set beside it. The `ADR 00nn` citations below are provenance markers for the development
|
|
9
|
+
> repository; those records are not distributed, and `docs/mechanics/` is their shipped
|
|
10
|
+
> replacement.
|
|
11
|
+
|
|
12
|
+
The reference implementation of the KAPPS architecture's Semantic Middleware Runtime: the
|
|
13
|
+
Interface-Layer component that lets a piece of Python code running on or next to a shopfloor
|
|
14
|
+
resource (or as a standalone service) expose functionality through the knowledge graph, and
|
|
15
|
+
lets other middleware instances discover and invoke that functionality via the graph rather
|
|
16
|
+
than via hardcoded network references. This context owns the Service/Workflow/Capability/
|
|
17
|
+
Operation/Mode registration and execution machinery. It delegates the actual generation and
|
|
18
|
+
parsing of workflow precondition/outcome SHACL shapes to the SHACL Interop context.
|
|
19
|
+
|
|
20
|
+
Built on `transitional_sync_middleware` (forked by inheritance, being incrementally reimplemented locally),
|
|
21
|
+
`kapps_ogm` (all knowledge graph reads/writes), and `kapps_triplestore_interface` (raw triple store
|
|
22
|
+
access, used directly where `kapps_ogm` has no equivalent yet, e.g. instance discovery).
|
|
23
|
+
|
|
24
|
+
## Language
|
|
25
|
+
|
|
26
|
+
**Service**:
|
|
27
|
+
A distributed runtime entity wrapped by a single middleware instance (e.g. a door
|
|
28
|
+
controller, a screwing-resource controller, a planning service). Typed via a
|
|
29
|
+
domain-specific subclass of `svc:Service` that must pre-exist in the ontology.
|
|
30
|
+
There is **one Service per middleware instance, not per Resource**: several instances may be bound to
|
|
31
|
+
one Resource, each owning its own Service node, address and heartbeat, all linked by
|
|
32
|
+
`svc:isServiceOf` (ADR 0022). Discovery may therefore return several Services for one Resource.
|
|
33
|
+
_Avoid_: Middleware instance (that is the Python object; Service is its graph representation), Resource (Service *wraps* a Resource, it is not one); "the service of a resource" (there may be several).
|
|
34
|
+
|
|
35
|
+
**Connector wiring** (of a resource-mode instance):
|
|
36
|
+
A configuration of the one library, not a distinct class. Resource mode is a **library woven into a
|
|
37
|
+
domain expert's Python package**, never a monolithic server. A wiring is two facts: a **protocol**
|
|
38
|
+
and a **direction**. This is the only axis that describes an instance (ADR 0033).
|
|
39
|
+
|
|
40
|
+
*Direction.* **Driving**: connectors wired bidirectionally, writes as well as reads. **Observing**:
|
|
41
|
+
connectors wired `TO_PERSISTENCE`, reads live values, structurally unable to write. **Inspecting**:
|
|
42
|
+
`autoregister_connectors=False`, nothing connected, structure and graph content only.
|
|
43
|
+
|
|
44
|
+
*Protocol.* **MQTT** reaches a device, recognised on the parameter (`inf:hasMQTTTopic`). **REST**
|
|
45
|
+
reaches another middleware instance over its ADR 0017 routes, recognised through the resource's
|
|
46
|
+
Service (`svc:address`). A peer middleware is a device as far as the seam is concerned.
|
|
47
|
+
|
|
48
|
+
Recognition and the **Projection** run identically in every combination, so connection metadata
|
|
49
|
+
never reaches a **served datamodel** (ADR 0020, ADR 0032, ADR 0033).
|
|
50
|
+
|
|
51
|
+
**The log path is held to the same claim** (issue #76, closed 2026-08-09). `activity.py` serves
|
|
52
|
+
this package's INFO records over HTTP, which for a while made `/activity` a way around the
|
|
53
|
+
projection: `connectors/mqtt_binding.py` logged each value with its topic at INFO. The topic now
|
|
54
|
+
sits at DEBUG on both legs, and the feed's handler filters at its own level rather than the
|
|
55
|
+
logger's — so widening the package logger to DEBUG for a debugging session still cannot put a
|
|
56
|
+
topic on the page. The INFO line names the parameter and the value, which is what the feed is
|
|
57
|
+
for.
|
|
58
|
+
_Avoid_: Flavour, retired by ADR 0032. **Role**, retired by ADR 0033 — an instance has no property
|
|
59
|
+
beyond its wiring. Consumer and Resource middleware as *defined* terms; they survive only as informal
|
|
60
|
+
shorthand. Read-only mode, because a **Mode** is `resource`/`server`/`watchdog`, and a connector
|
|
61
|
+
wiring is a configuration *within* resource mode. Monitor mode.
|
|
62
|
+
|
|
63
|
+
**Transport**:
|
|
64
|
+
The thing a connector dials, as opposed to the connector that dials it — an MQTT broker, and
|
|
65
|
+
nothing else so far. A Parameter's connection metadata *names* a transport by address; it never
|
|
66
|
+
provides one. A middleware may be asked to **ensure** a transport exists at a declared address
|
|
67
|
+
before it registers the first connector aimed there, but it never holds a transport
|
|
68
|
+
implementation: it states the need and the deployment meets it (ADR 0034). So a transport is the
|
|
69
|
+
one part of the southbound path that is neither in the graph nor in this library.
|
|
70
|
+
_Avoid_: Connector (the client this middleware constructs, one per topic — it *uses* a transport);
|
|
71
|
+
Binding / Binding descriptor (the recognition rule that builds connectors, ADR 0023); Broker as a
|
|
72
|
+
general term, since it is one transport and the seam is not MQTT-shaped; Endpoint and Address,
|
|
73
|
+
which are northbound (`svc:address`, the ADR 0017 routes).
|
|
74
|
+
|
|
75
|
+
**Workflow**:
|
|
76
|
+
An invokable function exposed by a Service, registered with `@mw.workflow(...)`. Realizes
|
|
77
|
+
exactly one Capability. Typed via a domain-specific subclass of `svc:Workflow` that must
|
|
78
|
+
pre-exist in the ontology, carrying a SHACL shape describing its arguments (precondition)
|
|
79
|
+
and return value (outcome) — see the SHACL Interop context for how that shape is read/
|
|
80
|
+
written.
|
|
81
|
+
_Avoid_: Skill, Action (AAS-tradition terms for the same invocation-interface idea; KAPPS
|
|
82
|
+
reserves "Service" for the deployable middleware-wrapped entity and "Workflow" for what it
|
|
83
|
+
exposes — see the paper's explicit divergence from the AAS capability-skill-service model).
|
|
84
|
+
|
|
85
|
+
**Parameter** (interface-accessible parameter) — _supersedes StateProperty (ADR 0015)_:
|
|
86
|
+
A readable and/or settable state of a Resource, modelled as **one graph node** carrying its
|
|
87
|
+
value/unit **and** the metadata a protocol connector needs to reach the device (e.g. an MQTT
|
|
88
|
+
topic + broker). It carries **no named `rdf:type`** — its only types are anonymous restriction nodes,
|
|
89
|
+
which exist by inference and never survive an explicit-graph fetch — so it is recognised by the
|
|
90
|
+
**Interface property** its domain property specializes, never by its class (ADR 0020).
|
|
91
|
+
The middleware's former "readable state" and the shop-floor "parameter" are one thing seen from
|
|
92
|
+
two directions — southbound (how the middleware reaches the device) and northbound (how peers
|
|
93
|
+
reach the middleware). Whether it is externally settable is a **facet** (an access mode), not a
|
|
94
|
+
subclass. Northbound it is **atomic**: value, unit
|
|
95
|
+
and access mode are read and written together as one dict, because they share one blanknode — the
|
|
96
|
+
locked circular-factory pattern for metadata about a property, RDF having no properties-about-
|
|
97
|
+
properties (ADR 0017). Its **shape is the TBox restriction** on its property's `rdfs:range`, not the
|
|
98
|
+
instance data: anything the restriction does not declare is dropped at materialization with only a
|
|
99
|
+
warning, so metadata a connector needs must be declared there or it never arrives (ADR 0028).
|
|
100
|
+
A complex property that matches no registered connector is **not** a Parameter — it is ordinary
|
|
101
|
+
data the consumer asked for, displayed and readable, with nothing wired (ADR 0020).
|
|
102
|
+
Whether its value lives in the graph is the domain's choice — see **Committed value** / **Locator**. It is
|
|
103
|
+
also the deepest thing a binding can address: `ConnectionInfo` bottoms out at the Parameter, never at the
|
|
104
|
+
value inside it (ADR 0023).
|
|
105
|
+
_Avoid_: StateProperty (retired term, ADR 0015); Sensor value / Observation (those describe the
|
|
106
|
+
data, not the graph node); Capability (states have none — there is no "light-barrier capability").
|
|
107
|
+
|
|
108
|
+
**Interface class** — _retired term, do not use_:
|
|
109
|
+
A protocol-specific parameter *class* (`inf:MQTTParameter`, `inf:OPCUAParameter`) that a connector was
|
|
110
|
+
paired with one-to-one, resolved by the parameter's `rdf:type`. **The parameter node has no named
|
|
111
|
+
type**, so nothing could ever match on it (ADR 0020, measured). The concept it reached for — the
|
|
112
|
+
protocol-extensibility seam — survives intact as the **Interface property**, and the ontology terms it
|
|
113
|
+
named are gone from the scenario-3 TBox. Use **Interface property**.
|
|
114
|
+
_Avoid_: the term itself. Also Adapter, Driver.
|
|
115
|
+
|
|
116
|
+
**ClassScope**:
|
|
117
|
+
A **projection — a view** — over the graph, expressed (in the OGM) as a tree of property-chains
|
|
118
|
+
rooted at a class. A view **belongs to its consumer** and is rooted at the node that consumer cares
|
|
119
|
+
about. There is no single "the datamodel" for a resource. This is how one central ontology serves
|
|
120
|
+
both the IT-OT boundary and the control/factory layer without duplicating concepts (ADR 0018).
|
|
121
|
+
A view **terminates at a Parameter and cannot select within one**: below a complex property the
|
|
122
|
+
chain is silently discarded, and the blanknode's contents are fixed by the TBox restriction, the
|
|
123
|
+
same for every consumer (ADR 0028). A scope chooses *which* parameters, never *which parts* of one.
|
|
124
|
+
An **empty** projection is legitimate — the graph holds the information, and a resource whose view
|
|
125
|
+
is a bare individual serves a one-field datamodel and starts normally.
|
|
126
|
+
_Avoid_: Filter, Query (a ClassScope is a reusable named view of which metadata to materialise).
|
|
127
|
+
|
|
128
|
+
**Root** (of a view):
|
|
129
|
+
The node a ClassScope is rooted at. Being a root is what makes a Resource a *top-level* thing rather
|
|
130
|
+
than a component — TransferUnit1 is a unit and ConveyorBelt1_left is a part of it **because a view is
|
|
131
|
+
rooted at the unit and reaches the belt**, not because of any part-of relation in the graph. There is
|
|
132
|
+
no composition property in Core, and none is needed.
|
|
133
|
+
_Avoid_: Top-level resource, Aggregate (both suggest an intrinsic property of the resource; rootedness
|
|
134
|
+
is a property of the view).
|
|
135
|
+
|
|
136
|
+
**User view**:
|
|
137
|
+
The ClassScope a resource-mode middleware is constructed with, and the one it materializes into the
|
|
138
|
+
datamodel it REST-exposes: the **northbound** projection. Stated by the domain code that embeds the
|
|
139
|
+
library, since only that code knows what the instance is for. Omitting it falls back to an unscoped
|
|
140
|
+
fetch, which materializes the `id` alone — a legitimate, if minimal, projection.
|
|
141
|
+
Connection metadata is absent from it not because the view declines to fetch it (it cannot — see
|
|
142
|
+
**ClassScope**) but because the **Projection** removes it (ADR 0028), so a peer cannot learn the
|
|
143
|
+
broker address and bypass the middleware.
|
|
144
|
+
_Avoid_: The datamodel, Schema (it is one view among many; a connector's view of the same resource is
|
|
145
|
+
a different one).
|
|
146
|
+
|
|
147
|
+
**Projection** (northbound):
|
|
148
|
+
What keeps connection metadata out of the served datamodel: the middleware **removes the protocol
|
|
149
|
+
properties from the ClassSpec before fetching**, and materializes the pruned spec (ADR 0028). What
|
|
150
|
+
counts as protocol metadata is **read from the ontology**, per Parameter, at every startup: everything
|
|
151
|
+
contributed by an **Interface property** strictly between the Parameter's own property and
|
|
152
|
+
`inf:isInterfaceAccessibleParameter`. The Parameter's own range (value, unit) and the root's own range
|
|
153
|
+
(`inf:accessMode`) stay. It runs for **every connector wiring**, including one that wires nothing — gating it
|
|
154
|
+
would make the least-privileged instance the one that leaks.
|
|
155
|
+
|
|
156
|
+
Deriving the set from the **registry** instead was tried and **fails open**: it knows only the
|
|
157
|
+
protocols this middleware has code for, so a Parameter reachable over an unregistered protocol had its
|
|
158
|
+
endpoint served (measured). A **Binding descriptor**'s connection metadata is now a *cross-check*
|
|
159
|
+
against the ontology, not the source. A keep-list — naming what is safe — was rejected: it is a second
|
|
160
|
+
closed-world moment (ADR 0025 allows exactly one) and it hides new domain content by default.
|
|
161
|
+
_Avoid_: Deny-list *of field names* (the point is that the list is derived from the authoritative
|
|
162
|
+
ontology, not enumerated); Access control (the Projection stops a peer *learning* the broker address
|
|
163
|
+
from this REST surface, not someone who already knows it — that is future work).
|
|
164
|
+
|
|
165
|
+
Earlier this was recorded as *not* a middleware step at all: a Parameter materializes to exactly what its
|
|
166
|
+
property's restriction declares, so on the merge-depth reading a broker address physically could not
|
|
167
|
+
reach a peer. That premise died when the `inf:` interface properties gained their own ranges (#53) —
|
|
168
|
+
necessary so provisioning can write connection metadata through the OGM — because `PropertySpec` merges
|
|
169
|
+
the entire `rdfs:subPropertyOf*` chain with no depth parameter. Measured: the unpruned belt materializes
|
|
170
|
+
carrying topic, set topic and broker. Merge depth remains the right *description* of the two views; the
|
|
171
|
+
middleware has to realize the shallow one itself (ADR 0019 stays retired as written; ADR 0026's
|
|
172
|
+
projection claim is superseded).
|
|
173
|
+
_Avoid_: Filter, Stripping *of data* (the prune is on the shape, before any data is read — the northbound
|
|
174
|
+
model has no field to carry a broker address in); View (the view is the ClassScope, which selects *which*
|
|
175
|
+
Parameters, not which parts of one).
|
|
176
|
+
|
|
177
|
+
**Interface property**:
|
|
178
|
+
The property a **Semantic connector** binds to — `inf:isInterfaceAccessibleMQTTParameter` and its
|
|
179
|
+
siblings, under `inf:isInterfaceAccessibleParameter`. A resource's parameter declares its protocol by
|
|
180
|
+
being a **subproperty** of one, which is how the authoritative upstream ontology already models it.
|
|
181
|
+
Recognition is `rdfs:subPropertyOf*` against the registry. The parameter blanknode itself carries no
|
|
182
|
+
named class to match on (ADR 0020).
|
|
183
|
+
_Avoid_: Interface class (retained for the ontology concept, but the *match* is on the property).
|
|
184
|
+
|
|
185
|
+
**Known primitives**:
|
|
186
|
+
The bounded form of the system's flexibility: novel *combinations* are handled, novel *vocabulary* is
|
|
187
|
+
not. A task assembled from grip/move/place may never have been seen in that combination, and a product
|
|
188
|
+
may combine a screw and a gear never before combined — but grip, move, place, screw and gear are all
|
|
189
|
+
known. Views and discovery listings are configured over known classes for this reason. It is a
|
|
190
|
+
deliberate boundary, not a limitation to be engineered away.
|
|
191
|
+
_Avoid_: Zero-configuration, Fully generic (both overclaim — the system does not discover concepts it
|
|
192
|
+
has never been taught).
|
|
193
|
+
|
|
194
|
+
**Semantic connector**:
|
|
195
|
+
Any connector able to **register itself from the knowledge graph**. Every connector transitional_sync_middleware ships
|
|
196
|
+
(MQTT, OPC-UA, HTTP, websocket, webhook, AAS client, model) is a candidate. Only the bare `Connector`
|
|
197
|
+
protocol is not, being the interface specification itself. Realized as a **Binding descriptor**, not as a
|
|
198
|
+
connector subclass (ADR 0023).
|
|
199
|
+
_Avoid_: Connector (the bare transitional_sync_middleware protocol, without the metadata ontology); Adapter, Driver;
|
|
200
|
+
MQTT connector as the archetype (MQTT is the first instance, not the shape of the concept).
|
|
201
|
+
|
|
202
|
+
**Binding descriptor**:
|
|
203
|
+
The object that makes a connector semantic: it names the **connector class** it builds, the **Interface
|
|
204
|
+
property** it binds to, the connection-metadata properties its protocol needs — which are also exactly
|
|
205
|
+
what the **Projection** hides northbound — and how to turn one Parameter's metadata into one or more
|
|
206
|
+
framework registrations. It references its connector class rather than subclassing it, so a connector
|
|
207
|
+
nobody here owns can still be made semantic. One binding may yield **two** connectors (a read topic and a
|
|
208
|
+
write topic) against **one** binding target, differing only in direction. Built and registered **at
|
|
209
|
+
construction**, from the ClassSpec and the graph — registering later means the framework never connects
|
|
210
|
+
them and inbound traffic dies silently (ADR 0023).
|
|
211
|
+
_Avoid_: Connector factory, Plugin (the descriptor is declarative — it states what a protocol needs, and
|
|
212
|
+
building is one method on it).
|
|
213
|
+
|
|
214
|
+
**Static facet**:
|
|
215
|
+
A part of a Parameter that does not change with a reading — unit, access mode. Captured by the **Binding
|
|
216
|
+
descriptor** at wiring time and reassembled into the payload on every inbound message, because
|
|
217
|
+
`setattr` replaces the whole Parameter node and `Formatter.deserialize` sees only the payload, with no
|
|
218
|
+
access to the current value. Free to carry, since `OGM.commit` filters unchanged triples (ADR 0018,
|
|
219
|
+
ADR 0023).
|
|
220
|
+
|
|
221
|
+
ADR 0027 retired the *graph* reason for this — a skolemised Parameter node is addressable, so a commit
|
|
222
|
+
diffs per triple and an unchanged facet cannot be wiped. The **in-memory** reason stands and is why the
|
|
223
|
+
reassembly remains: without it, a bare inbound scalar blanks the unit in the very model that is served
|
|
224
|
+
over REST.
|
|
225
|
+
_Avoid_: Constant, Config (a facet belongs to the Parameter and is authored in the ontology, not to the
|
|
226
|
+
deployment).
|
|
227
|
+
|
|
228
|
+
**Connector registry**:
|
|
229
|
+
The universal map from **Interface property** to **Binding descriptor**. Built at middleware
|
|
230
|
+
initialization from the known, tested descriptors shipped in the middleware, and extensible after init by
|
|
231
|
+
injecting a domain-built one. Resolution is
|
|
232
|
+
`parameter property rdfs:subPropertyOf* → interface property → binding descriptor`. Supporting a new
|
|
233
|
+
protocol is registering a new entry, never a core change. Recognition runs over the **ClassSpec and the
|
|
234
|
+
graph**, not over materialized instance data, which is what allows registration to happen early enough
|
|
235
|
+
for the framework to connect them (ADR 0020, ADR 0023).
|
|
236
|
+
_Avoid_: Connector factory, Plugin loader (the registry keys specifically on the interface property);
|
|
237
|
+
keying on `rdf:type` (superseded — the parameter blanknode has no named type).
|
|
238
|
+
|
|
239
|
+
**Committed value** / **Locator**:
|
|
240
|
+
The two legitimate ways a domain may treat a Parameter's value, chosen per subproject — the middleware is
|
|
241
|
+
agnostic and enforces neither. **Committed value**: the data point changes slowly, so the domain code
|
|
242
|
+
commits it and the graph holds the value. `@state` is not involved. **Locator**: the data point changes
|
|
243
|
+
fast, so the graph holds only *where the value lives* — unit, access mode, topic, broker — and never the
|
|
244
|
+
value itself, which exists only in the datamodel and over REST. Scenario 3 is a locator, which is why its
|
|
245
|
+
instance data carries no `inf:hasValue` literals. The restriction still declares the field, so an
|
|
246
|
+
unobserved Parameter reads as `[]` (ADR 0024).
|
|
247
|
+
_Avoid_: Cached value, Stale value (a committed value is authoritative for its update rate, not a stale
|
|
248
|
+
copy); "the live value is never persisted" as a middleware rule (it is the locator pattern's property).
|
|
249
|
+
|
|
250
|
+
**Capability**:
|
|
251
|
+
Defined in Core (`cfc:Capability`, subclasses `EquippedCapability`/`FlexibilityCapability`/
|
|
252
|
+
`ChangeabilityCapability`). An ability a Resource currently has. In this project's usage,
|
|
253
|
+
every Capability instance is created automatically by the middleware from a pre-existing
|
|
254
|
+
Capability *type* the moment a matching Workflow or StateProperty is registered — it is
|
|
255
|
+
never instantiated by hand.
|
|
256
|
+
_Avoid_: Skill (AAS term for a related but not identical concept).
|
|
257
|
+
|
|
258
|
+
**Operation**:
|
|
259
|
+
Defined in Core (`cfc:Operation`, subclass of `Task`). The executable, resource-assigned
|
|
260
|
+
form of a task; links to a Capability via `cfc:implementsCapability`. A caller creates an
|
|
261
|
+
Operation in the graph and dispatches it to the resource that will carry it out. That
|
|
262
|
+
resource queues it, pulls it, runs it, and the outcome is recorded back onto it. The
|
|
263
|
+
graph-level unit of work exchanged between middleware instances.
|
|
264
|
+
_Avoid_: Workflow invocation, Job (Operation is the graph-level unit of work; a single
|
|
265
|
+
Operation resolves to exactly one Workflow via its Capability).
|
|
266
|
+
|
|
267
|
+
**Event trigger** (`execute()`):
|
|
268
|
+
The receiver-side built-in Workflow that every resource-mode middleware exposes on its REST
|
|
269
|
+
API. A caller "triggers" it to signal that an Operation addressed to that resource now exists in
|
|
270
|
+
the graph. The receiver enqueues the Operation, `ogm.fetch`es it, and hands it to an optional
|
|
271
|
+
domain callback (else leaves it `queued`). The trigger carries only the Operation IRI — the
|
|
272
|
+
payload lives in the graph — and does not block on the work or return a business result.
|
|
273
|
+
_Avoid_: RPC call, Invoke (the event trigger notifies; it does not run the work synchronously).
|
|
274
|
+
|
|
275
|
+
**Dispatch**:
|
|
276
|
+
The caller-side act of handing an Operation to another resource: create the Operation
|
|
277
|
+
individual in the graph (through the OGM-routed write path) and then fire the receiver's
|
|
278
|
+
event trigger. Accessed from domain Python as a transaction context manager — the body populates
|
|
279
|
+
the Operation, the atomic exit performs the create-and-notify.
|
|
280
|
+
_Avoid_: Send, Publish (there is no message bus; dispatch is a graph write plus an event trigger).
|
|
281
|
+
|
|
282
|
+
**Operation queue**:
|
|
283
|
+
A resource-mode middleware's pending-work list — the Operations addressed to its Resource
|
|
284
|
+
that await or are in progress. Held in memory as a cache and reconstructed at startup by
|
|
285
|
+
querying the graph (own `queued` operations, plus own orphaned `running` operations to
|
|
286
|
+
reclaim); filled live by event triggers. A dead resource's stranded operations are swept
|
|
287
|
+
centrally by a watchdog, not by per-resource polling.
|
|
288
|
+
_Avoid_: Message queue, Broker (there is no broker; the queue is a view over graph state).
|
|
289
|
+
|
|
290
|
+
**Operation status**:
|
|
291
|
+
The lifecycle state of an Operation: `queued` (created and addressed, awaiting pull) →
|
|
292
|
+
`running` (pulled, in progress) → `done` or `failed` (terminal). Drives coordination and
|
|
293
|
+
recovery. Execution provenance (which Workflow ran it, when, the result) is written as part
|
|
294
|
+
of the terminal transition, so the status is itself the provenance record — there is no
|
|
295
|
+
separate success flag.
|
|
296
|
+
|
|
297
|
+
**Pull-and-run**:
|
|
298
|
+
The receiver-side transaction context manager by which domain code takes the next `queued`
|
|
299
|
+
Operation, sets it `running` (re-fetching it under a domain-supplied `ClassScope`), runs the
|
|
300
|
+
work in the body, and on atomic exit records the terminal `done`/`failed` state and
|
|
301
|
+
provenance — dumping the Resource's datamodel to the graph on failure.
|
|
302
|
+
_Avoid_: Poll, Consume (pull-and-run is the guarded unit of work, not the delivery mechanism).
|
|
303
|
+
|
|
304
|
+
**Resource**:
|
|
305
|
+
Defined in Core (`cfc:Resource`). The physical or logical thing a Service wraps (a door, a
|
|
306
|
+
transformer cell, a screwing tool). Required at construction time in resource mode.
|
|
307
|
+
|
|
308
|
+
**Mode**:
|
|
309
|
+
A `SemanticMiddleware` construction-time choice governing what the instance is *for*:
|
|
310
|
+
- `"resource"` — wraps one Resource; the REST surface is the user-registered Workflows/
|
|
311
|
+
StateProperties, the built-in `execute()` event trigger, and a CRUD REST API generated from
|
|
312
|
+
the resource's own datamodel (`generate_rest_api_for_data_model`; ADR 0005 #13 amendment). The
|
|
313
|
+
transactional context-manager surface (dispatch/`request`, pull-and-run, handover) and the
|
|
314
|
+
graph-write helpers stay Python-only, not REST-exposed.
|
|
315
|
+
- `"server"` — wraps no Resource. CRUD/`execute` themselves are the REST surface (e.g. a
|
|
316
|
+
future data-serving "product server"). Not yet implemented, and deliberately still reserved: a
|
|
317
|
+
graph-*consuming* participant (a planner, a mobile robot, a controller) is a resource-mode
|
|
318
|
+
planner with its own Resource, not a server (ADR 0005, #32 amendment).
|
|
319
|
+
- `"watchdog"` — wraps no Resource, exposes little to no REST surface; runs a sweep loop
|
|
320
|
+
that removes stale `svc:address`/`svc:endpoint` triples left by resource-mode instances
|
|
321
|
+
that stopped heartbeating.
|
|
322
|
+
_Avoid_: Deployment type, Role (Mode is specifically the constructor discriminator).
|
|
323
|
+
|
|
324
|
+
**Heartbeat**:
|
|
325
|
+
A resource-mode Service's periodic re-assertion of its own liveness — refreshing
|
|
326
|
+
`svc:lastHeartbeat` on its Service individual via an internal interval-based Workflow. Read
|
|
327
|
+
by watchdog-mode instances to decide staleness.
|
|
328
|
+
|
|
329
|
+
**Address vs. Endpoint**:
|
|
330
|
+
`svc:address` is a Service's base URL, set on startup and removed on
|
|
331
|
+
deregistration/staleness. `svc:endpoint` is the full, directly callable URL for one specific
|
|
332
|
+
Workflow or StateProperty, also set on startup and removed on deregistration/staleness. Both
|
|
333
|
+
being present is a deliberate divergence from the paper's literal "address on Service only"
|
|
334
|
+
wording — see the "Address and endpoint" section of
|
|
335
|
+
`docs/mechanics/02-workflow-registration.md`.
|
|
336
|
+
_Avoid_: URL, endpoint URL used interchangeably for both — they are distinct properties on
|
|
337
|
+
distinct entity types.
|
|
338
|
+
|
|
339
|
+
**KnowledgeGraphConnector**:
|
|
340
|
+
An `transitional_sync_middleware`-protocol `Connector` (`connect`/`disconnect`/`provide`/`consume`) whose
|
|
341
|
+
`provide`/`consume` wrap `kapps_ogm.OGM.fetch`/`commit`. The mechanism by which any
|
|
342
|
+
`transitional_sync_middleware` construct (workflows, synced connectors) can read/write the knowledge graph
|
|
343
|
+
without going around the OGM's validated write path. See "The graph-facing connector"
|
|
344
|
+
in `docs/mechanics/04-connector-binding.md`.
|
|
345
|
+
|
|
346
|
+
**Deregistration**:
|
|
347
|
+
The reverse of registration: on shutdown (or, for watchdog-mode-detected staleness), removing
|
|
348
|
+
a Service's `svc:address` and its Workflows'/StateProperties' `svc:endpoint` triples while
|
|
349
|
+
preserving the individuals themselves, for provenance.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Re-export full transitional_sync_middleware public API.
|
|
2
|
+
# As each layer is migrated locally, swap the import below to point at the
|
|
3
|
+
# local implementation instead of transitional_sync_middleware.
|
|
4
|
+
from transitional_sync_middleware import (
|
|
5
|
+
Middleware,
|
|
6
|
+
AasMiddleware,
|
|
7
|
+
Reference,
|
|
8
|
+
Identifier,
|
|
9
|
+
DataModel,
|
|
10
|
+
DataModelRebuilder,
|
|
11
|
+
AAS,
|
|
12
|
+
Submodel,
|
|
13
|
+
SubmodelElementCollection,
|
|
14
|
+
Blob,
|
|
15
|
+
File,
|
|
16
|
+
formatting,
|
|
17
|
+
connectors,
|
|
18
|
+
)
|
|
19
|
+
from kapps_semantic_middleware.middleware import SemanticMiddleware
|
|
20
|
+
from kapps_semantic_middleware.modes import Mode
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"SemanticMiddleware",
|
|
24
|
+
"Mode",
|
|
25
|
+
"Middleware",
|
|
26
|
+
"AasMiddleware",
|
|
27
|
+
"Reference",
|
|
28
|
+
"Identifier",
|
|
29
|
+
"DataModel",
|
|
30
|
+
"DataModelRebuilder",
|
|
31
|
+
"AAS",
|
|
32
|
+
"Submodel",
|
|
33
|
+
"SubmodelElementCollection",
|
|
34
|
+
"Blob",
|
|
35
|
+
"File",
|
|
36
|
+
"formatting",
|
|
37
|
+
"connectors",
|
|
38
|
+
]
|