eyeling 1.26.6 → 1.27.0
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.
- package/HANDBOOK.md +100 -3
- package/dist/browser/eyeling.browser.js +396 -3
- package/examples/deck/act-barley-seed-lineage.md +1 -1
- package/examples/deck/faltings-genus2-finiteness.md +1 -1
- package/examples/deck/high-trust-rdf-bloom-envelope.md +1 -1
- package/examples/deck/odrl-dpv-risk-ranked.md +1 -1
- package/examples/deck/rdf-message-cold-chain-recall.md +71 -0
- package/examples/deck/rdf-message-flow.md +1 -1
- package/examples/deck/rdf-message-ldes-incremental.md +94 -0
- package/examples/deck/rdf-message-window-repair.md +175 -0
- package/examples/deck/schema-foaf-mapping.md +2 -0
- package/examples/input/rdf-message-cold-chain-recall.trig +668 -0
- package/examples/input/rdf-message-flow.trig +3 -0
- package/examples/input/rdf-message-ldes-incremental.trig +564 -0
- package/examples/input/rdf-message-microgrid.trig +3 -0
- package/examples/input/rdf-message-window-repair.trig +82 -0
- package/examples/input/rdf-messages.trig +3 -0
- package/examples/output/rdf-message-cold-chain-recall.md +13 -0
- package/examples/output/rdf-message-ldes-incremental.md +13 -0
- package/examples/output/rdf-message-window-repair.md +13 -0
- package/examples/rdf-message-cold-chain-recall.n3 +229 -0
- package/examples/rdf-message-flow.n3 +3 -0
- package/examples/rdf-message-ldes-incremental.n3 +231 -0
- package/examples/rdf-message-microgrid.n3 +3 -0
- package/examples/rdf-message-window-repair.n3 +166 -0
- package/examples/rdf-messages.n3 +3 -0
- package/eyeling.js +396 -3
- package/lib/cli.js +396 -3
- package/package.json +5 -3
- package/test/examples.test.js +25 -14
- package/test/fixtures/marc-rules-stream-messages.n3 +192 -0
- package/test/stream_messages.test.js +243 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# =================================
|
|
2
|
+
# RDF Message Cold Chain Recall
|
|
3
|
+
# =================================
|
|
4
|
+
#
|
|
5
|
+
# Run as:
|
|
6
|
+
#
|
|
7
|
+
# eyeling -r examples/rdf-message-cold-chain-recall.n3 examples/input/rdf-message-cold-chain-recall.trig
|
|
8
|
+
#
|
|
9
|
+
# See the handbook section on RDF Message Log replay under -r:
|
|
10
|
+
# https://eyereasoner.github.io/eyeling/HANDBOOK#rdf-message-log-replay-under--r
|
|
11
|
+
#
|
|
12
|
+
# This example is meant to be operationally obvious: a cold-chain shipment has
|
|
13
|
+
# many historical telemetry messages, a checkpoint says which prefix was already
|
|
14
|
+
# processed, and the new tail contains inconsistent evidence about whether a
|
|
15
|
+
# medicine batch stayed below its temperature limit. The repair keeps the
|
|
16
|
+
# calibrated logger reading before materializing a recall/quarantine decision.
|
|
17
|
+
|
|
18
|
+
@prefix : <https://eyereasoner.github.io/eyeling/examples/rdf-message-cold-chain-recall#>.
|
|
19
|
+
@prefix eymsg: <https://eyereasoner.github.io/eyeling/vocab/message#>.
|
|
20
|
+
@prefix ldes: <https://w3id.org/ldes#>.
|
|
21
|
+
@prefix tree: <https://w3id.org/tree#>.
|
|
22
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#>.
|
|
23
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
24
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
25
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#>.
|
|
26
|
+
|
|
27
|
+
# Parser replay gives us an ordered RDF Message stream. Keep this visible in
|
|
28
|
+
# the derived data so the report can say how many messages were processed.
|
|
29
|
+
{
|
|
30
|
+
?Stream a eymsg:RDFMessageStream;
|
|
31
|
+
eymsg:envelope ?Envelope.
|
|
32
|
+
?Envelope a eymsg:MessageEnvelope;
|
|
33
|
+
eymsg:offset ?Offset.
|
|
34
|
+
} => {
|
|
35
|
+
?Envelope :messageOffset ?Offset;
|
|
36
|
+
:messageBoundaryExplicit true.
|
|
37
|
+
}.
|
|
38
|
+
|
|
39
|
+
# Message 1 carries stream metadata and the consumer checkpoint. Everything up
|
|
40
|
+
# to sequence 42 was already materialized by a previous synchronization run.
|
|
41
|
+
{
|
|
42
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
43
|
+
?Payload log:nameOf ?PayloadContext.
|
|
44
|
+
?PayloadContext log:includes {
|
|
45
|
+
:telemetryStream a ldes:EventStream;
|
|
46
|
+
ldes:timestampPath :observedAt;
|
|
47
|
+
ldes:sequencePath :sequence;
|
|
48
|
+
tree:view :telemetryPage;
|
|
49
|
+
:lastCommittedSequence ?Bookmark;
|
|
50
|
+
:currentWindowFromSequence ?WindowStart;
|
|
51
|
+
:temperatureLimitTenthC ?Limit;
|
|
52
|
+
:temperatureLimitLabel ?LimitLabel;
|
|
53
|
+
:repairPolicy :preferCalibratedLogger.
|
|
54
|
+
:quarantineDecision :decisionText ?DecisionText.
|
|
55
|
+
}.
|
|
56
|
+
} => {
|
|
57
|
+
:telemetryStream a ldes:EventStream;
|
|
58
|
+
ldes:timestampPath :observedAt;
|
|
59
|
+
ldes:sequencePath :sequence;
|
|
60
|
+
tree:view :telemetryPage;
|
|
61
|
+
:lastCommittedSequence ?Bookmark;
|
|
62
|
+
:currentWindowFromSequence ?WindowStart;
|
|
63
|
+
:temperatureLimitTenthC ?Limit;
|
|
64
|
+
:temperatureLimitLabel ?LimitLabel;
|
|
65
|
+
:repairPolicy :preferCalibratedLogger.
|
|
66
|
+
:quarantineDecision :decisionText ?DecisionText.
|
|
67
|
+
}.
|
|
68
|
+
|
|
69
|
+
# Each RDF Message payload contributes one LDES telemetry member. The payloads
|
|
70
|
+
# stay scoped to their message graphs; the reasoner lifts only the fields needed
|
|
71
|
+
# for incremental classification, repair, and reporting.
|
|
72
|
+
{
|
|
73
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
74
|
+
?Payload log:nameOf ?PayloadContext.
|
|
75
|
+
?PayloadContext log:includes {
|
|
76
|
+
:telemetryStream tree:member ?Member.
|
|
77
|
+
?Member a :ColdChainObservation;
|
|
78
|
+
:sequence ?Sequence;
|
|
79
|
+
:observedAt ?ObservedAt;
|
|
80
|
+
:batch ?Batch;
|
|
81
|
+
:temperatureTenthC ?Temperature;
|
|
82
|
+
:temperatureLabel ?TemperatureLabel;
|
|
83
|
+
:priority ?Priority;
|
|
84
|
+
:source ?Source.
|
|
85
|
+
}.
|
|
86
|
+
} => {
|
|
87
|
+
?Member :ldesMember true;
|
|
88
|
+
:envelope ?Envelope;
|
|
89
|
+
:sequence ?Sequence;
|
|
90
|
+
:observedAt ?ObservedAt;
|
|
91
|
+
:batch ?Batch;
|
|
92
|
+
:temperatureTenthC ?Temperature;
|
|
93
|
+
:temperatureLabel ?TemperatureLabel;
|
|
94
|
+
:priority ?Priority;
|
|
95
|
+
:source ?Source.
|
|
96
|
+
?Envelope :emitsLDESMember ?Member.
|
|
97
|
+
}.
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Carry optional calibration metadata when a telemetry member has it. Most
|
|
101
|
+
# sensors do not; the calibrated logger in the noisy tail does.
|
|
102
|
+
{
|
|
103
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
104
|
+
?Payload log:nameOf ?PayloadContext.
|
|
105
|
+
?PayloadContext log:includes {
|
|
106
|
+
?Member :calibrationClass ?CalibrationClass.
|
|
107
|
+
}.
|
|
108
|
+
} => {
|
|
109
|
+
?Member :calibrationClass ?CalibrationClass.
|
|
110
|
+
}.
|
|
111
|
+
|
|
112
|
+
# The checkpoint separates the large committed prefix from the new tail.
|
|
113
|
+
{
|
|
114
|
+
:telemetryStream :lastCommittedSequence ?Bookmark.
|
|
115
|
+
?Member :ldesMember true;
|
|
116
|
+
:sequence ?Sequence.
|
|
117
|
+
?Sequence math:notGreaterThan ?Bookmark.
|
|
118
|
+
} => {
|
|
119
|
+
?Member :alreadyCommitted true.
|
|
120
|
+
}.
|
|
121
|
+
|
|
122
|
+
{
|
|
123
|
+
:telemetryStream :lastCommittedSequence ?Bookmark.
|
|
124
|
+
?Member :ldesMember true;
|
|
125
|
+
:sequence ?Sequence.
|
|
126
|
+
?Sequence math:greaterThan ?Bookmark.
|
|
127
|
+
} => {
|
|
128
|
+
?Member :newlyEmitted true.
|
|
129
|
+
}.
|
|
130
|
+
|
|
131
|
+
{
|
|
132
|
+
:telemetryStream :currentWindowFromSequence ?WindowStart.
|
|
133
|
+
?Member :newlyEmitted true;
|
|
134
|
+
:sequence ?Sequence.
|
|
135
|
+
?Sequence math:notLessThan ?WindowStart.
|
|
136
|
+
} => {
|
|
137
|
+
:currentColdChainWindow :contains ?Member.
|
|
138
|
+
?Member :inCurrentColdChainWindow true.
|
|
139
|
+
}.
|
|
140
|
+
|
|
141
|
+
# The raw tail is inconsistent for batchA: some new telemetry says the batch is
|
|
142
|
+
# still within range, while another message says it crossed the cold-chain limit.
|
|
143
|
+
{
|
|
144
|
+
:telemetryStream :temperatureLimitTenthC ?Limit.
|
|
145
|
+
:currentColdChainWindow :contains ?InRangeMember, ?ExcursionMember.
|
|
146
|
+
?InRangeMember :batch ?Batch;
|
|
147
|
+
:temperatureTenthC ?InRangeTemperature.
|
|
148
|
+
?ExcursionMember :batch ?Batch;
|
|
149
|
+
:temperatureTenthC ?ExcursionTemperature.
|
|
150
|
+
?InRangeTemperature math:notGreaterThan ?Limit.
|
|
151
|
+
?ExcursionTemperature math:greaterThan ?Limit.
|
|
152
|
+
?InRangeMember log:notEqualTo ?ExcursionMember.
|
|
153
|
+
} => {
|
|
154
|
+
:currentColdChainWindow :rawConflict :batchBothInRangeAndInExcursion;
|
|
155
|
+
:inconsistentBatch ?Batch.
|
|
156
|
+
}.
|
|
157
|
+
|
|
158
|
+
# Preferred repair: calibrated cold-chain logger evidence wins for recall
|
|
159
|
+
# decisions. In this example it says batchA exceeded the temperature limit.
|
|
160
|
+
{
|
|
161
|
+
:telemetryStream :temperatureLimitTenthC ?Limit.
|
|
162
|
+
:currentColdChainWindow :rawConflict :batchBothInRangeAndInExcursion;
|
|
163
|
+
:contains ?PreferredMember.
|
|
164
|
+
?PreferredMember :batch ?Batch;
|
|
165
|
+
:temperatureTenthC ?PreferredTemperature;
|
|
166
|
+
:temperatureLabel ?PreferredTemperatureLabel;
|
|
167
|
+
:priority ?PreferredPriority;
|
|
168
|
+
:source ?PreferredSource;
|
|
169
|
+
:sequence ?PreferredSequence;
|
|
170
|
+
:calibrationClass :calibratedLogger.
|
|
171
|
+
?PreferredTemperature math:greaterThan ?Limit.
|
|
172
|
+
} => {
|
|
173
|
+
:currentColdChainWindow :preferredRepair :calibratedLoggerWins;
|
|
174
|
+
:repairedBatch ?Batch;
|
|
175
|
+
:preferredEvidence ?PreferredMember;
|
|
176
|
+
:preferredSource ?PreferredSource;
|
|
177
|
+
:preferredSequence ?PreferredSequence;
|
|
178
|
+
:preferredPriority ?PreferredPriority;
|
|
179
|
+
:repairedTemperatureTenthC ?PreferredTemperature;
|
|
180
|
+
:repairedTemperatureLabel ?PreferredTemperatureLabel;
|
|
181
|
+
:materializedDecision :quarantineDecision.
|
|
182
|
+
}.
|
|
183
|
+
|
|
184
|
+
# Count the whole stream and the incremental tail. The interesting point is
|
|
185
|
+
# that the input is large enough to make streaming useful, while the repair is
|
|
186
|
+
# local to the new messages after the checkpoint.
|
|
187
|
+
{
|
|
188
|
+
( ?Member { ?Member :ldesMember true. } ?AllMembers ) log:collectAllIn _:coldChainCounts.
|
|
189
|
+
?AllMembers list:length ?AllCount.
|
|
190
|
+
( ?Member { ?Member :alreadyCommitted true. } ?CommittedMembers ) log:collectAllIn _:coldChainCounts.
|
|
191
|
+
?CommittedMembers list:length ?CommittedCount.
|
|
192
|
+
( ?Member { ?Member :newlyEmitted true. } ?NewMembers ) log:collectAllIn _:coldChainCounts.
|
|
193
|
+
?NewMembers list:length ?NewCount.
|
|
194
|
+
( ?Member { ?Member :inCurrentColdChainWindow true. } ?WindowMembers ) log:collectAllIn _:coldChainCounts.
|
|
195
|
+
?WindowMembers list:length ?WindowCount.
|
|
196
|
+
} => {
|
|
197
|
+
:incrementalColdChainRun :totalTelemetryMembers ?AllCount;
|
|
198
|
+
:alreadyCommittedMembers ?CommittedCount;
|
|
199
|
+
:newlyEmittedMembers ?NewCount;
|
|
200
|
+
:windowMemberCount ?WindowCount.
|
|
201
|
+
}.
|
|
202
|
+
|
|
203
|
+
# Emit one compact report when replay, checkpointing, conflict detection, repair,
|
|
204
|
+
# and final materialization are all present.
|
|
205
|
+
{
|
|
206
|
+
?Stream a eymsg:RDFMessageStream;
|
|
207
|
+
eymsg:orderedEnvelopes ?Envelopes.
|
|
208
|
+
?Envelopes list:length ?EnvelopeCount.
|
|
209
|
+
:telemetryStream :lastCommittedSequence ?Bookmark;
|
|
210
|
+
:temperatureLimitTenthC ?Limit;
|
|
211
|
+
:temperatureLimitLabel ?LimitLabel.
|
|
212
|
+
:incrementalColdChainRun :totalTelemetryMembers ?AllCount;
|
|
213
|
+
:alreadyCommittedMembers ?CommittedCount;
|
|
214
|
+
:newlyEmittedMembers ?NewCount;
|
|
215
|
+
:windowMemberCount ?WindowCount.
|
|
216
|
+
:currentColdChainWindow :rawConflict :batchBothInRangeAndInExcursion;
|
|
217
|
+
:inconsistentBatch :batchA;
|
|
218
|
+
:preferredRepair :calibratedLoggerWins;
|
|
219
|
+
:preferredSource ?PreferredSource;
|
|
220
|
+
:preferredSequence ?PreferredSequence;
|
|
221
|
+
:preferredPriority ?PreferredPriority;
|
|
222
|
+
:repairedTemperatureLabel ?PreferredTemperatureLabel;
|
|
223
|
+
:materializedDecision :quarantineDecision.
|
|
224
|
+
:quarantineDecision :decisionText ?DecisionText.
|
|
225
|
+
("# rdf-message-cold-chain-recall\n\n## Source files\n\n- [N3 rules](../rdf-message-cold-chain-recall.n3)\n- [Input RDF Message Log](../input/rdf-message-cold-chain-recall.trig)\n- [Story deck](../deck/rdf-message-cold-chain-recall.md)\n\n## Answer\nCold-chain recall reasoning accepted: %d RDF Message envelopes contained %d append-only telemetry members. The consumer checkpoint was sequence %d, so %d committed history members were skipped and only %d newly emitted members entered the repair window. The tail window has %d members and says batchA is both within range and over the %s limit. The repair keeps the calibrated logger evidence at sequence %d from %s with priority %d, resolves the repaired temperature to %s, and materializes the decision: %s.\n\n## Explanation\nThis is the streaming shape that matters operationally. A cold-chain consumer can replay a long RDF Message Log, use LDES sequence metadata to resume after its checkpoint, and inspect only the new tail. The raw tail is contradictory, so the rules do not materialize from the flattened data. They first choose the calibrated logger reading, then derive the quarantine decision from that repaired state." ?EnvelopeCount ?AllCount ?Bookmark ?CommittedCount ?NewCount ?WindowCount ?LimitLabel ?PreferredSequence ?PreferredSource ?PreferredPriority ?PreferredTemperatureLabel ?DecisionText) string:format ?Block.
|
|
226
|
+
} => {
|
|
227
|
+
:rdfMessageColdChainRecallExample log:outputString ?Block.
|
|
228
|
+
:rdfMessageColdChainRecallExample :demonstrates :RDFMessageLogReplay, :LDESMemberStream, :CheckpointedSynchronization, :IncrementalColdChainReasoning, :TelemetryConflictRepair, :QuarantineMaterialization.
|
|
229
|
+
}.
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
#
|
|
7
7
|
# eyeling -r examples/rdf-message-flow.n3 examples/input/rdf-message-flow.trig
|
|
8
8
|
#
|
|
9
|
+
# See the handbook section on RDF Message Log replay under -r:
|
|
10
|
+
# https://eyereasoner.github.io/eyeling/HANDBOOK#rdf-message-log-replay-under--r
|
|
11
|
+
#
|
|
9
12
|
# Motivation
|
|
10
13
|
# ----------
|
|
11
14
|
# The RDF Messages draft describes an RDF Message as an RDF dataset interpreted
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# =====================================
|
|
2
|
+
# RDF Message LDES Incremental Repair
|
|
3
|
+
# =====================================
|
|
4
|
+
#
|
|
5
|
+
# Run as:
|
|
6
|
+
#
|
|
7
|
+
# eyeling -r examples/rdf-message-ldes-incremental.n3 examples/input/rdf-message-ldes-incremental.trig
|
|
8
|
+
#
|
|
9
|
+
# See the handbook section on RDF Message Log replay under -r:
|
|
10
|
+
# https://eyereasoner.github.io/eyeling/HANDBOOK#rdf-message-log-replay-under--r
|
|
11
|
+
#
|
|
12
|
+
# This example combines two streaming ideas:
|
|
13
|
+
#
|
|
14
|
+
# - RDF Messages give parser-level message boundaries for a potentially long
|
|
15
|
+
# stream. Eyeling replays those boundaries as eymsg: envelopes and keeps each
|
|
16
|
+
# payload graph separate.
|
|
17
|
+
# - LDES describes an append-only stream of members with a timestamp/sequence
|
|
18
|
+
# order. The rules below model an incremental consumer that has already
|
|
19
|
+
# committed sequence 34, so it only reasons over the new tail of the log.
|
|
20
|
+
#
|
|
21
|
+
# The input intentionally contains forty LDES members. Only the six members
|
|
22
|
+
# after the checkpoint are part of the current incremental repair window. That
|
|
23
|
+
# tail contains a concrete inconsistency: the same door is both open and closed.
|
|
24
|
+
# The repair keeps the highest-priority assertion before materializing the
|
|
25
|
+
# operational action.
|
|
26
|
+
|
|
27
|
+
@prefix : <https://eyereasoner.github.io/eyeling/examples/rdf-message-ldes-incremental#>.
|
|
28
|
+
@prefix eymsg: <https://eyereasoner.github.io/eyeling/vocab/message#>.
|
|
29
|
+
@prefix ldes: <https://w3id.org/ldes#>.
|
|
30
|
+
@prefix tree: <https://w3id.org/tree#>.
|
|
31
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#>.
|
|
32
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
33
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
34
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#>.
|
|
35
|
+
|
|
36
|
+
# Eyeling's RDF-message replay gives us a static, ordered view of a message log.
|
|
37
|
+
{
|
|
38
|
+
?Stream a eymsg:RDFMessageStream;
|
|
39
|
+
eymsg:envelope ?Envelope.
|
|
40
|
+
?Envelope a eymsg:MessageEnvelope;
|
|
41
|
+
eymsg:offset ?Offset.
|
|
42
|
+
} => {
|
|
43
|
+
?Envelope :messageOffset ?Offset;
|
|
44
|
+
:messageBoundaryExplicit true.
|
|
45
|
+
}.
|
|
46
|
+
|
|
47
|
+
# Message 1 carries LDES context and the incremental consumer state. The LDES
|
|
48
|
+
# bookmark is the key point: everything up to sequence 34 was already committed
|
|
49
|
+
# by the previous synchronization run.
|
|
50
|
+
{
|
|
51
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
52
|
+
?Payload log:nameOf ?PayloadContext.
|
|
53
|
+
?PayloadContext log:includes {
|
|
54
|
+
:doorStream a ldes:EventStream;
|
|
55
|
+
ldes:timestampPath :observedAt;
|
|
56
|
+
ldes:sequencePath :sequence;
|
|
57
|
+
tree:view :doorStreamPage;
|
|
58
|
+
:lastCommittedSequence ?Bookmark;
|
|
59
|
+
:currentWindowFromSequence ?WindowStart;
|
|
60
|
+
:windowSize ?WindowSize;
|
|
61
|
+
:repairPolicy :preferHigherPriority.
|
|
62
|
+
:open :stateText ?OpenStateText;
|
|
63
|
+
:actionText ?OpenAction.
|
|
64
|
+
:closed :stateText ?ClosedStateText;
|
|
65
|
+
:actionText ?ClosedAction.
|
|
66
|
+
}.
|
|
67
|
+
} => {
|
|
68
|
+
:doorStream a ldes:EventStream;
|
|
69
|
+
ldes:timestampPath :observedAt;
|
|
70
|
+
ldes:sequencePath :sequence;
|
|
71
|
+
tree:view :doorStreamPage;
|
|
72
|
+
:lastCommittedSequence ?Bookmark;
|
|
73
|
+
:currentWindowFromSequence ?WindowStart;
|
|
74
|
+
:windowSize ?WindowSize;
|
|
75
|
+
:repairPolicy :preferHigherPriority.
|
|
76
|
+
:open :stateText ?OpenStateText;
|
|
77
|
+
:actionText ?OpenAction.
|
|
78
|
+
:closed :stateText ?ClosedStateText;
|
|
79
|
+
:actionText ?ClosedAction.
|
|
80
|
+
}.
|
|
81
|
+
|
|
82
|
+
# Each non-configuration RDF Message payload contributes one LDES member. The
|
|
83
|
+
# rules lift just the metadata needed by the incremental reasoner; the payloads
|
|
84
|
+
# themselves stay scoped to their message graphs.
|
|
85
|
+
{
|
|
86
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
87
|
+
?Payload log:nameOf ?PayloadContext.
|
|
88
|
+
?PayloadContext log:includes {
|
|
89
|
+
:doorStream tree:member ?Member.
|
|
90
|
+
?Member a :DoorObservation;
|
|
91
|
+
:sequence ?Sequence;
|
|
92
|
+
:observedAt ?ObservedAt;
|
|
93
|
+
:door ?Door;
|
|
94
|
+
:doorState ?State;
|
|
95
|
+
:priority ?Priority;
|
|
96
|
+
:source ?Source.
|
|
97
|
+
}.
|
|
98
|
+
} => {
|
|
99
|
+
?Member :ldesMember true;
|
|
100
|
+
:envelope ?Envelope;
|
|
101
|
+
:sequence ?Sequence;
|
|
102
|
+
:observedAt ?ObservedAt;
|
|
103
|
+
:door ?Door;
|
|
104
|
+
:doorState ?State;
|
|
105
|
+
:priority ?Priority;
|
|
106
|
+
:source ?Source.
|
|
107
|
+
?Envelope :emitsLDESMember ?Member.
|
|
108
|
+
}.
|
|
109
|
+
|
|
110
|
+
# The LDES checkpoint lets an incremental consumer skip the large, already
|
|
111
|
+
# committed history.
|
|
112
|
+
{
|
|
113
|
+
:doorStream :lastCommittedSequence ?Bookmark.
|
|
114
|
+
?Member :ldesMember true;
|
|
115
|
+
:sequence ?Sequence.
|
|
116
|
+
?Sequence math:notGreaterThan ?Bookmark.
|
|
117
|
+
} => {
|
|
118
|
+
?Member :alreadyCommitted true.
|
|
119
|
+
}.
|
|
120
|
+
|
|
121
|
+
{
|
|
122
|
+
:doorStream :lastCommittedSequence ?Bookmark.
|
|
123
|
+
?Member :ldesMember true;
|
|
124
|
+
:sequence ?Sequence.
|
|
125
|
+
?Sequence math:greaterThan ?Bookmark.
|
|
126
|
+
} => {
|
|
127
|
+
?Member :newlyEmitted true.
|
|
128
|
+
}.
|
|
129
|
+
|
|
130
|
+
# The current incremental repair window is the new tail after the checkpoint.
|
|
131
|
+
{
|
|
132
|
+
:doorStream :currentWindowFromSequence ?WindowStart.
|
|
133
|
+
?Member :newlyEmitted true;
|
|
134
|
+
:sequence ?Sequence.
|
|
135
|
+
?Sequence math:notLessThan ?WindowStart.
|
|
136
|
+
} => {
|
|
137
|
+
:currentLDESWindow :contains ?Member.
|
|
138
|
+
?Member :inCurrentLDESWindow true.
|
|
139
|
+
}.
|
|
140
|
+
|
|
141
|
+
# Raw tail materialization is inconsistent: the same door is open and closed in
|
|
142
|
+
# the new LDES members.
|
|
143
|
+
{
|
|
144
|
+
:currentLDESWindow :contains ?OpenMember, ?ClosedMember.
|
|
145
|
+
?OpenMember :door ?Door;
|
|
146
|
+
:doorState :open.
|
|
147
|
+
?ClosedMember :door ?Door;
|
|
148
|
+
:doorState :closed.
|
|
149
|
+
?OpenMember log:notEqualTo ?ClosedMember.
|
|
150
|
+
} => {
|
|
151
|
+
:currentLDESWindow :rawConflict :doorOpenAndClosed;
|
|
152
|
+
:inconsistentDoor ?Door.
|
|
153
|
+
}.
|
|
154
|
+
|
|
155
|
+
# Preferred repair: keep the higher-priority assertion when the current tail
|
|
156
|
+
# contains conflicting LDES members for the same door.
|
|
157
|
+
{
|
|
158
|
+
:currentLDESWindow :contains ?PreferredMember, ?RejectedMember;
|
|
159
|
+
:rawConflict :doorOpenAndClosed.
|
|
160
|
+
?PreferredMember :doorState ?PreferredState;
|
|
161
|
+
:priority ?PreferredPriority;
|
|
162
|
+
:source ?PreferredSource;
|
|
163
|
+
:sequence ?PreferredSequence.
|
|
164
|
+
?RejectedMember :doorState ?RejectedState;
|
|
165
|
+
:priority ?RejectedPriority.
|
|
166
|
+
?PreferredState log:notEqualTo ?RejectedState.
|
|
167
|
+
?PreferredPriority math:greaterThan ?RejectedPriority.
|
|
168
|
+
} => {
|
|
169
|
+
:currentLDESWindow :preferredRepairState ?PreferredState;
|
|
170
|
+
:preferredEvidence ?PreferredMember;
|
|
171
|
+
:preferredSource ?PreferredSource;
|
|
172
|
+
:preferredSequence ?PreferredSequence;
|
|
173
|
+
:preferredPriority ?PreferredPriority;
|
|
174
|
+
:rejectsLowerPriority ?RejectedMember;
|
|
175
|
+
:repairsInconsistency :doorOpenAndClosed;
|
|
176
|
+
:resolvedDoorState ?PreferredState.
|
|
177
|
+
}.
|
|
178
|
+
|
|
179
|
+
# Materialize from the repaired state, not from the inconsistent raw window.
|
|
180
|
+
{
|
|
181
|
+
:currentLDESWindow :preferredRepairState ?State.
|
|
182
|
+
?State :actionText ?ActionText.
|
|
183
|
+
} => {
|
|
184
|
+
:currentLDESWindow :materializedActionText ?ActionText.
|
|
185
|
+
}.
|
|
186
|
+
|
|
187
|
+
# Count the stream and the incremental tail. These counts are useful in the
|
|
188
|
+
# report because they show that the example input has lots of historical data,
|
|
189
|
+
# but the reasoning focus is the tail after the LDES bookmark.
|
|
190
|
+
{
|
|
191
|
+
( ?Member { ?Member :ldesMember true. } ?AllMembers ) log:collectAllIn _:ldesCounts.
|
|
192
|
+
?AllMembers list:length ?AllCount.
|
|
193
|
+
( ?Member { ?Member :alreadyCommitted true. } ?CommittedMembers ) log:collectAllIn _:ldesCounts.
|
|
194
|
+
?CommittedMembers list:length ?CommittedCount.
|
|
195
|
+
( ?Member { ?Member :newlyEmitted true. } ?NewMembers ) log:collectAllIn _:ldesCounts.
|
|
196
|
+
?NewMembers list:length ?NewCount.
|
|
197
|
+
( ?Member { ?Member :inCurrentLDESWindow true. } ?WindowMembers ) log:collectAllIn _:ldesCounts.
|
|
198
|
+
?WindowMembers list:length ?WindowCount.
|
|
199
|
+
} => {
|
|
200
|
+
:incrementalRun :totalLDESMembers ?AllCount;
|
|
201
|
+
:alreadyCommittedMembers ?CommittedCount;
|
|
202
|
+
:newlyEmittedMembers ?NewCount;
|
|
203
|
+
:windowMemberCount ?WindowCount.
|
|
204
|
+
}.
|
|
205
|
+
|
|
206
|
+
# Emit a compact human-readable result only once the parser replay, LDES
|
|
207
|
+
# checkpoint, count aggregation, inconsistency repair, and final materialization
|
|
208
|
+
# are all present.
|
|
209
|
+
{
|
|
210
|
+
?Stream a eymsg:RDFMessageStream;
|
|
211
|
+
eymsg:orderedEnvelopes ?Envelopes.
|
|
212
|
+
?Envelopes list:length ?EnvelopeCount.
|
|
213
|
+
:doorStream :lastCommittedSequence ?Bookmark;
|
|
214
|
+
:windowSize ?WindowSize.
|
|
215
|
+
:incrementalRun :totalLDESMembers ?AllCount;
|
|
216
|
+
:alreadyCommittedMembers ?CommittedCount;
|
|
217
|
+
:newlyEmittedMembers ?NewCount;
|
|
218
|
+
:windowMemberCount ?WindowCount.
|
|
219
|
+
:currentLDESWindow :rawConflict :doorOpenAndClosed;
|
|
220
|
+
:inconsistentDoor :doorA;
|
|
221
|
+
:preferredSource ?PreferredSource;
|
|
222
|
+
:preferredSequence ?PreferredSequence;
|
|
223
|
+
:preferredPriority ?PreferredPriority;
|
|
224
|
+
:resolvedDoorState ?State;
|
|
225
|
+
:materializedActionText ?ActionText.
|
|
226
|
+
?State :stateText ?StateText.
|
|
227
|
+
("# rdf-message-ldes-incremental\n\n## Source files\n\n- [N3 rules](../rdf-message-ldes-incremental.n3)\n- [Input RDF Message Log](../input/rdf-message-ldes-incremental.trig)\n- [Story deck](../deck/rdf-message-ldes-incremental.md)\n\n## Answer\nIncremental LDES reasoning accepted: %d RDF Message envelopes contained %d append-only LDES members. The consumer checkpoint was sequence %d, so %d committed history members were skipped and only %d newly emitted members entered the repair window. The tail window has %d members, contains a raw open/closed inconsistency for doorA, repairs it by keeping the priority-%d member at sequence %d from %s, resolves the door state to %s, and materializes the action: %s.\n\n## Explanation\nThe input is deliberately larger than the reasoning task: it serializes many LDES members as RDF Messages, but the LDES sequence bookmark tells the consumer which prefix was already materialized. Eyeling's -r mode replays the RDF Message Log into ordered eymsg: envelopes; the rules inspect each payload with log:includes, classify old versus new members from the LDES sequence path, and run the inconsistency repair only over the new tail. This mirrors an incremental streaming pipeline: append-only publication, resumable synchronization, small repair window, and monotonic output after the noisy tail is fixed." ?EnvelopeCount ?AllCount ?Bookmark ?CommittedCount ?NewCount ?WindowCount ?PreferredPriority ?PreferredSequence ?PreferredSource ?StateText ?ActionText) string:format ?Block.
|
|
228
|
+
} => {
|
|
229
|
+
:rdfMessageLDESIncrementalExample log:outputString ?Block.
|
|
230
|
+
:rdfMessageLDESIncrementalExample :demonstrates :RDFMessageLogReplay, :LDESMemberStream, :CheckpointedSynchronization, :IncrementalReasoning, :TailWindowRepair, :RawDoorInconsistency, :PreferredRepair.
|
|
231
|
+
}.
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
#
|
|
7
7
|
# eyeling -r examples/rdf-message-microgrid.n3 examples/input/rdf-message-microgrid.trig
|
|
8
8
|
#
|
|
9
|
+
# See the handbook section on RDF Message Log replay under -r:
|
|
10
|
+
# https://eyereasoner.github.io/eyeling/HANDBOOK#rdf-message-log-replay-under--r
|
|
11
|
+
#
|
|
9
12
|
# A clinic is running as an islanded microgrid after a storm. Several small
|
|
10
13
|
# systems send RDF data: one message describes life-safety loads, another
|
|
11
14
|
# describes battery and solar status, another describes flexible demand that may
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# =========================
|
|
2
|
+
# RDF Message Window Repair
|
|
3
|
+
# =========================
|
|
4
|
+
#
|
|
5
|
+
# Run as:
|
|
6
|
+
#
|
|
7
|
+
# eyeling -r examples/rdf-message-window-repair.n3 examples/input/rdf-message-window-repair.trig
|
|
8
|
+
#
|
|
9
|
+
# See the handbook section on RDF Message Log replay under -r:
|
|
10
|
+
# https://eyereasoner.github.io/eyeling/HANDBOOK#rdf-message-log-replay-under--r
|
|
11
|
+
#
|
|
12
|
+
# This example is inspired by incremental, inconsistency-resilient reasoning over
|
|
13
|
+
# sliding ABox streams. The input is a true RDF Message Log: each MESSAGE
|
|
14
|
+
# boundary is parsed by Eyeling into an eymsg: envelope with one payload graph per
|
|
15
|
+
# non-empty message.
|
|
16
|
+
#
|
|
17
|
+
# The rules below keep the message boundaries visible, select two overlapping
|
|
18
|
+
# three-message windows, detect that the current window says the same fire door
|
|
19
|
+
# is both open and closed, and repair the inconsistency by keeping the
|
|
20
|
+
# higher-priority safety-controller assertion for the final materialization.
|
|
21
|
+
|
|
22
|
+
@prefix : <https://eyereasoner.github.io/eyeling/examples/rdf-message-window-repair#>.
|
|
23
|
+
@prefix eymsg: <https://eyereasoner.github.io/eyeling/vocab/message#>.
|
|
24
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
25
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#>.
|
|
26
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
27
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#>.
|
|
28
|
+
|
|
29
|
+
# Eyeling's parser-level replay gives a static view of the message log: an
|
|
30
|
+
# ordered list, explicit offsets, and next-envelope links.
|
|
31
|
+
{
|
|
32
|
+
?Stream a eymsg:RDFMessageStream;
|
|
33
|
+
eymsg:envelope ?Message.
|
|
34
|
+
?Message a eymsg:MessageEnvelope;
|
|
35
|
+
eymsg:offset ?Offset.
|
|
36
|
+
} => {
|
|
37
|
+
?Message :boundaryExplicit true.
|
|
38
|
+
?Message :offset ?Offset.
|
|
39
|
+
}.
|
|
40
|
+
|
|
41
|
+
# The first payload carries stream policy, like a fixed TBox/configuration that
|
|
42
|
+
# is not itself part of the changing ABox window.
|
|
43
|
+
{
|
|
44
|
+
?Message eymsg:payloadGraph ?Payload.
|
|
45
|
+
?Payload log:nameOf ?PayloadContext.
|
|
46
|
+
?PayloadContext log:includes {
|
|
47
|
+
:doorA :windowSize ?WindowSize;
|
|
48
|
+
:repairPolicy :preferHigherPriority.
|
|
49
|
+
:open :actionText ?OpenAction;
|
|
50
|
+
:stateText ?OpenStateText.
|
|
51
|
+
:closed :actionText ?ClosedAction;
|
|
52
|
+
:stateText ?ClosedStateText.
|
|
53
|
+
}.
|
|
54
|
+
} => {
|
|
55
|
+
:doorA :windowSize ?WindowSize;
|
|
56
|
+
:repairPolicy :preferHigherPriority.
|
|
57
|
+
:open :actionText ?OpenAction;
|
|
58
|
+
:stateText ?OpenStateText.
|
|
59
|
+
:closed :actionText ?ClosedAction;
|
|
60
|
+
:stateText ?ClosedStateText.
|
|
61
|
+
}.
|
|
62
|
+
|
|
63
|
+
# With five messages, message 1 is configuration and messages 2-5 are ABox
|
|
64
|
+
# assertions. The previous and current sliding windows overlap on messages 3
|
|
65
|
+
# and 4; message 2 expires and message 5 enters.
|
|
66
|
+
{
|
|
67
|
+
?Stream a eymsg:RDFMessageStream;
|
|
68
|
+
eymsg:firstEnvelope ?M1.
|
|
69
|
+
?M1 eymsg:nextEnvelope ?M2.
|
|
70
|
+
?M2 eymsg:nextEnvelope ?M3.
|
|
71
|
+
?M3 eymsg:nextEnvelope ?M4.
|
|
72
|
+
?M4 eymsg:nextEnvelope ?M5.
|
|
73
|
+
} => {
|
|
74
|
+
:previousWindow :contains ?M2, ?M3, ?M4.
|
|
75
|
+
:currentWindow :contains ?M3, ?M4, ?M5.
|
|
76
|
+
:windowSlide :expired ?M2;
|
|
77
|
+
:retained ?M3, ?M4;
|
|
78
|
+
:entered ?M5.
|
|
79
|
+
}.
|
|
80
|
+
|
|
81
|
+
# Inspect each message payload inside its own replayed RDF Message graph, then
|
|
82
|
+
# lift only the ABox assertion metadata needed for window reasoning.
|
|
83
|
+
{
|
|
84
|
+
?Window :contains ?Message.
|
|
85
|
+
?Message eymsg:payloadGraph ?Payload.
|
|
86
|
+
?Payload log:nameOf ?PayloadContext.
|
|
87
|
+
?PayloadContext log:includes {
|
|
88
|
+
?Reading :about :doorA;
|
|
89
|
+
:doorState ?State;
|
|
90
|
+
:priority ?Priority;
|
|
91
|
+
:source ?Source.
|
|
92
|
+
}.
|
|
93
|
+
} => {
|
|
94
|
+
?Message :windowAssertion ?Reading;
|
|
95
|
+
:doorState ?State;
|
|
96
|
+
:priority ?Priority;
|
|
97
|
+
:source ?Source.
|
|
98
|
+
?Window :hasDoorState ?State.
|
|
99
|
+
}.
|
|
100
|
+
|
|
101
|
+
# The current graph-level window is inconsistent: after raw materialization it
|
|
102
|
+
# says the same door is both open and closed.
|
|
103
|
+
{
|
|
104
|
+
:currentWindow :contains ?OpenMessage, ?ClosedMessage.
|
|
105
|
+
?OpenMessage :doorState :open.
|
|
106
|
+
?ClosedMessage :doorState :closed.
|
|
107
|
+
} => {
|
|
108
|
+
:currentWindow :rawConflict :doorOpenAndClosed;
|
|
109
|
+
:inconsistentDoor :doorA.
|
|
110
|
+
}.
|
|
111
|
+
|
|
112
|
+
# Preferred repair: if a current-window assertion has higher priority than a
|
|
113
|
+
# conflicting current-window assertion, keep the higher-priority assertion as the
|
|
114
|
+
# one used by materialization.
|
|
115
|
+
{
|
|
116
|
+
:currentWindow :contains ?PreferredMessage, ?RejectedMessage;
|
|
117
|
+
:rawConflict :doorOpenAndClosed.
|
|
118
|
+
?PreferredMessage :doorState ?PreferredState;
|
|
119
|
+
:priority ?PreferredPriority;
|
|
120
|
+
:source ?PreferredSource.
|
|
121
|
+
?RejectedMessage :doorState ?RejectedState;
|
|
122
|
+
:priority ?RejectedPriority.
|
|
123
|
+
?PreferredState log:notEqualTo ?RejectedState.
|
|
124
|
+
?PreferredPriority math:greaterThan ?RejectedPriority.
|
|
125
|
+
} => {
|
|
126
|
+
:currentWindow :preferredRepairState ?PreferredState;
|
|
127
|
+
:preferredEvidence ?PreferredMessage;
|
|
128
|
+
:preferredSource ?PreferredSource;
|
|
129
|
+
:rejectsLowerPriority ?RejectedMessage;
|
|
130
|
+
:repairsInconsistency :doorOpenAndClosed;
|
|
131
|
+
:resolvedDoorState ?PreferredState.
|
|
132
|
+
}.
|
|
133
|
+
|
|
134
|
+
# Materialize the final operational conclusion from the repaired state rather
|
|
135
|
+
# than from the inconsistent raw window.
|
|
136
|
+
{
|
|
137
|
+
:currentWindow :preferredRepairState ?State.
|
|
138
|
+
?State :actionText ?ActionText.
|
|
139
|
+
} => {
|
|
140
|
+
:currentWindow :materializedActionText ?ActionText.
|
|
141
|
+
}.
|
|
142
|
+
|
|
143
|
+
# Emit a report only when the parser replay, sliding-window bookkeeping, raw
|
|
144
|
+
# conflict, preferred repair, and final materialization were all derived.
|
|
145
|
+
{
|
|
146
|
+
?Stream a eymsg:RDFMessageStream;
|
|
147
|
+
eymsg:orderedEnvelopes ?Messages.
|
|
148
|
+
?Messages list:length ?Count.
|
|
149
|
+
:doorA :windowSize ?WindowSize.
|
|
150
|
+
:previousWindow :contains ?PreviousOnly.
|
|
151
|
+
:windowSlide :expired ?PreviousOnly;
|
|
152
|
+
:retained ?Overlap1, ?Overlap2;
|
|
153
|
+
:entered ?Entered.
|
|
154
|
+
:currentWindow :rawConflict :doorOpenAndClosed;
|
|
155
|
+
:inconsistentDoor :doorA;
|
|
156
|
+
:preferredEvidence ?PreferredMessage;
|
|
157
|
+
:preferredSource ?PreferredSource;
|
|
158
|
+
:resolvedDoorState ?State;
|
|
159
|
+
:materializedActionText ?ActionText.
|
|
160
|
+
?State :stateText ?StateText.
|
|
161
|
+
?PreferredMessage :priority ?PreferredPriority.
|
|
162
|
+
("# rdf-message-window-repair\n\n## Source files\n\n- [N3 rules](../rdf-message-window-repair.n3)\n- [Input RDF Message Log](../input/rdf-message-window-repair.trig)\n- [Story deck](../deck/rdf-message-window-repair.md)\n\n## Answer\nSliding-window RDF Message repair accepted: %d parser-replayed messages produced two overlapping %d-message ABox windows. The current window retained two envelopes from the previous window, expired one old door reading, and added one new safety-controller reading. Its raw graph-level materialization was inconsistent because doorA was both open and closed. The preferred repair kept the priority-%d assertion from %s, resolved the door state to %s, and materialized the action: %s.\n\n## Explanation\nThe RDF Message Log uses VERSION \\\"1.2-messages\\\" and MESSAGE delimiters. Eyeling parses those boundaries into eymsg: envelopes and payload graphs; the rules inspect each payload with log:includes rather than merging message bodies by hand. The example mirrors a sliding-window stream-reasoning pattern: when the window advances, overlapping message evidence is retained, an expired assertion leaves the window, an entering assertion can change the materialization, and a preferred repair fixes a noisy open/closed contradiction before the final action is produced." ?Count ?WindowSize ?PreferredPriority ?PreferredSource ?StateText ?ActionText) string:format ?Block.
|
|
163
|
+
} => {
|
|
164
|
+
:rdfMessageWindowRepairExample log:outputString ?Block.
|
|
165
|
+
:rdfMessageWindowRepairExample :demonstrates :RDFMessageLogReplay, :SlidingWindow, :OverlappingWindows, :ABoxStream, :RawDoorInconsistency, :PreferredRepair, :IncrementalMaterialization.
|
|
166
|
+
}.
|
package/examples/rdf-messages.n3
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
#
|
|
7
7
|
# eyeling -r examples/rdf-messages.n3 examples/input/rdf-messages.trig
|
|
8
8
|
#
|
|
9
|
+
# See the handbook section on RDF Message Log replay under -r:
|
|
10
|
+
# https://eyereasoner.github.io/eyeling/HANDBOOK#rdf-message-log-replay-under--r
|
|
11
|
+
#
|
|
9
12
|
# Motivation
|
|
10
13
|
# ----------
|
|
11
14
|
# The RDF Messages draft defines an RDF Message as an RDF dataset interpreted
|