ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
packml.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file packml.cpp
6 * @brief PackML / OMAC state model (ISA-TR88.00.02) - state engine + owned PackTags service. See packml.h.
7 */
8
10
11#if PC_ENABLE_PACKML
12
13#include "services/system/clock.h" // pc_millis - the monotonic source
14
15// ---------------------------------------------------------------------------
16// Pure state engine
17// ---------------------------------------------------------------------------
18
19PackMlState pc_packml_command(PackMlState s, PackMlCommand c)
20{
21 // Abort is legal from every state except the abort branch itself -> Aborting.
22 if (c == PackMlCommand::ABORT && s != PackMlState::ABORTING && s != PackMlState::ABORTED)
23 {
24 return PackMlState::ABORTING;
25 }
26 // Stop is legal from every state except the abort branch and the already-stopping/stopped/clearing
27 // states -> Stopping.
28 if (c == PackMlCommand::STOP && s != PackMlState::ABORTING && s != PackMlState::ABORTED &&
29 s != PackMlState::STOPPING && s != PackMlState::STOPPED && s != PackMlState::CLEARING)
30 {
31 return PackMlState::STOPPING;
32 }
33
34 switch (s)
35 {
36 case PackMlState::STOPPED:
37 if (c == PackMlCommand::RESET)
38 {
39 return PackMlState::RESETTING;
40 }
41 break;
42 case PackMlState::IDLE:
43 if (c == PackMlCommand::START)
44 {
45 return PackMlState::STARTING;
46 }
47 break;
48 case PackMlState::EXECUTE:
49 if (c == PackMlCommand::HOLD)
50 {
51 return PackMlState::HOLDING;
52 }
53 if (c == PackMlCommand::SUSPEND)
54 {
55 return PackMlState::SUSPENDING;
56 }
57 break;
58 case PackMlState::HELD:
59 if (c == PackMlCommand::UNHOLD)
60 {
61 return PackMlState::UNHOLDING;
62 }
63 break;
64 case PackMlState::SUSPENDED:
65 if (c == PackMlCommand::UNSUSPEND)
66 {
67 return PackMlState::UNSUSPENDING;
68 }
69 break;
70 case PackMlState::COMPLETE:
71 if (c == PackMlCommand::RESET)
72 {
73 return PackMlState::RESETTING;
74 }
75 break;
76 case PackMlState::ABORTED:
77 if (c == PackMlCommand::CLEAR)
78 {
79 return PackMlState::CLEARING;
80 }
81 break;
82 default:
83 break; // acting states accept only the Stop/Abort handled above
84 }
85 return s; // command not legal in this state: no transition
86}
87
88PackMlState pc_packml_state_complete(PackMlState s)
89{
90 switch (s)
91 {
92 case PackMlState::RESETTING:
93 return PackMlState::IDLE;
94 case PackMlState::STARTING:
95 return PackMlState::EXECUTE;
96 case PackMlState::HOLDING:
97 return PackMlState::HELD;
98 case PackMlState::UNHOLDING:
99 return PackMlState::EXECUTE;
100 case PackMlState::SUSPENDING:
101 return PackMlState::SUSPENDED;
102 case PackMlState::UNSUSPENDING:
103 return PackMlState::EXECUTE;
104 case PackMlState::COMPLETING:
105 return PackMlState::COMPLETE;
106 case PackMlState::STOPPING:
107 return PackMlState::STOPPED;
108 case PackMlState::ABORTING:
109 return PackMlState::ABORTED;
110 case PackMlState::CLEARING:
111 return PackMlState::STOPPED;
112 default:
113 return s; // wait states have no State-Complete transition
114 }
115}
116
117PackMlState pc_packml_execute_complete(PackMlState s)
118{
119 return (s == PackMlState::EXECUTE) ? PackMlState::COMPLETING : s;
120}
121
122bool pc_packml_is_acting(PackMlState s)
123{
124 switch (s)
125 {
126 case PackMlState::CLEARING:
127 case PackMlState::STARTING:
128 case PackMlState::STOPPING:
129 case PackMlState::ABORTING:
130 case PackMlState::HOLDING:
131 case PackMlState::UNHOLDING:
132 case PackMlState::SUSPENDING:
133 case PackMlState::UNSUSPENDING:
134 case PackMlState::RESETTING:
135 case PackMlState::COMPLETING:
136 return true;
137 default:
138 return false;
139 }
140}
141
142bool pc_packml_command_valid(PackMlState s, PackMlCommand c)
143{
144 return pc_packml_command(s, c) != s;
145}
146
147const char *pc_packml_state_name(PackMlState s)
148{
149 switch (s)
150 {
151 case PackMlState::CLEARING:
152 return "Clearing";
153 case PackMlState::STOPPED:
154 return "Stopped";
155 case PackMlState::STARTING:
156 return "Starting";
157 case PackMlState::IDLE:
158 return "Idle";
159 case PackMlState::SUSPENDED:
160 return "Suspended";
161 case PackMlState::EXECUTE:
162 return "Execute";
163 case PackMlState::STOPPING:
164 return "Stopping";
165 case PackMlState::ABORTING:
166 return "Aborting";
167 case PackMlState::ABORTED:
168 return "Aborted";
169 case PackMlState::HOLDING:
170 return "Holding";
171 case PackMlState::HELD:
172 return "Held";
173 case PackMlState::UNHOLDING:
174 return "Unholding";
175 case PackMlState::SUSPENDING:
176 return "Suspending";
177 case PackMlState::UNSUSPENDING:
178 return "Unsuspending";
179 case PackMlState::RESETTING:
180 return "Resetting";
181 case PackMlState::COMPLETING:
182 return "Completing";
183 case PackMlState::COMPLETE:
184 return "Complete";
185 default:
186 return "Undefined";
187 }
188}
189
190const char *pc_packml_command_name(PackMlCommand c)
191{
192 switch (c)
193 {
194 case PackMlCommand::RESET:
195 return "Reset";
196 case PackMlCommand::START:
197 return "Start";
198 case PackMlCommand::STOP:
199 return "Stop";
200 case PackMlCommand::HOLD:
201 return "Hold";
202 case PackMlCommand::UNHOLD:
203 return "Unhold";
204 case PackMlCommand::SUSPEND:
205 return "Suspend";
206 case PackMlCommand::UNSUSPEND:
207 return "Unsuspend";
208 case PackMlCommand::ABORT:
209 return "Abort";
210 case PackMlCommand::CLEAR:
211 return "Clear";
212 default:
213 return "None";
214 }
215}
216
217// ---------------------------------------------------------------------------
218// Owned service (one named owner, internal linkage)
219// ---------------------------------------------------------------------------
220
221// All PackML service state - the current state, unit mode, commanded speed, the production counters, and the
222// state/reset timestamps - grouped in one owner (BSS, no heap), unreachable from any other translation unit.
223struct PackMlSvcCtx
224{
225 PackMlState state = PackMlState::UNDEFINED;
226 PackMlMode mode = PackMlMode::PRODUCING;
227 float mach_speed_cmd = 0.0f;
228 uint32_t prod_processed = 0;
229 uint32_t prod_defective = 0;
230 uint32_t reset_ms = 0; // pc_millis at the last Reset -> AccTimeSinceReset base
231 uint32_t state_entry_ms = 0; // pc_millis at the last state change -> StateCurrentTime base
232};
233static PackMlSvcCtx s_pml;
234
235static void enter_state(PackMlState s)
236{
237 if (s == s_pml.state)
238 {
239 return;
240 }
241 s_pml.state = s;
242 s_pml.state_entry_ms = pc_millis();
243}
244
245void pc_packml_svc_init(PackMlMode mode)
246{
247 s_pml.mode = mode;
248 s_pml.mach_speed_cmd = 0.0f;
249 s_pml.prod_processed = 0;
250 s_pml.prod_defective = 0;
251 s_pml.reset_ms = pc_millis();
252 s_pml.state = PackMlState::UNDEFINED; // force enter_state to stamp the entry time
253 enter_state(PackMlState::STOPPED);
254}
255
256bool pc_packml_svc_command(PackMlCommand c)
257{
258 PackMlState next = pc_packml_command(s_pml.state, c);
259 if (next == s_pml.state)
260 {
261 return false;
262 }
263 if (c == PackMlCommand::RESET) // a fresh run: restart the accumulated-time clock
264 {
265 s_pml.reset_ms = pc_millis();
266 }
267 enter_state(next);
268 return true;
269}
270
271PackMlState pc_packml_svc_state_complete(void)
272{
273 enter_state(pc_packml_state_complete(s_pml.state));
274 return s_pml.state;
275}
276
277void pc_packml_svc_count(bool defective)
278{
279 if (s_pml.state != PackMlState::EXECUTE)
280 {
281 return; // units are only produced while executing
282 }
283 s_pml.prod_processed++;
284 if (defective)
285 {
286 s_pml.prod_defective++;
287 }
288}
289
290bool pc_packml_svc_complete_run(void)
291{
292 PackMlState next = pc_packml_execute_complete(s_pml.state);
293 if (next == s_pml.state)
294 {
295 return false;
296 }
297 enter_state(next);
298 return true;
299}
300
301bool pc_packml_svc_set_mode(PackMlMode mode)
302{
303 // A unit-mode change is only allowed in a stable, non-producing state (ISA-TR88.00.02 mode-change rules).
304 if (s_pml.state != PackMlState::STOPPED && s_pml.state != PackMlState::IDLE && s_pml.state != PackMlState::ABORTED)
305 {
306 return false;
307 }
308 s_pml.mode = mode;
309 return true;
310}
311
312void pc_packml_svc_set_speed(float mach_speed)
313{
314 s_pml.mach_speed_cmd = mach_speed;
315}
316
317PackMlState pc_packml_svc_state(void)
318{
319 return s_pml.state;
320}
321
322void pc_packml_svc_status(PackMlStatus *out)
323{
324 if (!out)
325 {
326 return;
327 }
328 uint32_t now = pc_millis();
329 out->state_current = s_pml.state;
330 out->unit_mode_current = s_pml.mode;
331 // MachSpeedActual is the commanded speed while producing, otherwise zero.
332 out->mach_speed_actual = (s_pml.state == PackMlState::EXECUTE) ? s_pml.mach_speed_cmd : 0.0f;
333 out->state_current_ms = now - s_pml.state_entry_ms;
334 out->acc_time_since_reset_ms = now - s_pml.reset_ms;
335 out->prod_processed = s_pml.prod_processed;
336 out->prod_defective = s_pml.prod_defective;
337}
338
339#endif // PC_ENABLE_PACKML
Pluggable monotonic clock for all library timing.
uint32_t pc_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
Definition clock.h:71
PackML / OMAC packaging-machine state model (ISA-TR88.00.02) - the state engine + PackTags.