GCC Code Coverage Report


Directory: ./
File: Svc/WasmSequencer/WasmSequencer.cpp
Date: 2026-11-03 22:10:14
Exec Total Coverage
Lines: 96 305 31.5%
Functions: 8 25 32.0%
Branches: 57 219 26.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title WasmSequencer.cpp
3 // \author tumbar
4 // \brief cpp file for WasmSequencer component implementation class
5 // ======================================================================
6
7 #include "Svc/WasmSequencer/WasmSequencer.hpp"
8
9 #include "Fw/Cmd/CmdResponseEnumAc.hpp"
10 #include "Fw/Types/Assert.hpp"
11 #include "Fw/Types/LinearBufferTemplate.hpp"
12 #include "Fw/Types/Serializable.hpp"
13 #include "Fw/Types/SuccessEnumAc.hpp"
14 #include "Os/Mutex.hpp"
15 #include "Svc/Seq/BlockStateEnumAc.hpp"
16 #include "Svc/Seq/SeqArgsSerializableAc.hpp"
17 #include "Svc/WasmSequencer/WasmSequencer_CommandRequestSerializableAc.hpp"
18 #include "Svc/WasmSequencer/WasmSequencer_ControllerStateMachine_StateEnumAc.hpp"
19 #include "Svc/WasmSequencer/WasmSequencer_HostFunctionEnumAc.hpp"
20 #include "Svc/WasmSequencer/WasmSequencer_InvokeRequestSerializableAc.hpp"
21 #include "Svc/WasmSequencer/WasmSequencer_LoadRequestSerializableAc.hpp"
22 #include "Svc/WasmSequencer/WasmSequencer_SignalSourceEnumAc.hpp"
23 #include "Svc/WasmSequencer/fprime_spacewasm/include/fprime_spacewasm.h"
24 #include "Svc/WasmSequencer/spacewasm_include/spacewasm.h"
25 #include "config/CommandDispatcherImplCfg.hpp"
26 #include "config/FwAssertArgTypeAliasAc.h"
27 #include "config/FwIndexTypeAliasAc.h"
28 #include "config/FwSizeTypeAliasAc.h"
29
30 namespace Svc {
31
32 2 U8* WasmSequencer ::globalAllocCallback(void* userdata, size_t size, size_t align) {
33
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (userdata == nullptr) {
34 ✗ return nullptr;
35 }
36 2 return static_cast<WasmSequencer*>(userdata)->globalAlloc(static_cast<U32>(size), static_cast<U32>(align));
37 }
38
39 2 void WasmSequencer ::globalDeallocCallback(void* userdata, U8* ptr, size_t size, size_t align) {
40 (void)size;
41 (void)align;
42
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (userdata != nullptr) {
43 2 static_cast<WasmSequencer*>(userdata)->globalDealloc(ptr);
44 }
45 2 }
46
47 // ----------------------------------------------------------------------
48 // Component construction and destruction
49 // ----------------------------------------------------------------------
50
51 1 WasmSequencer ::WasmSequencer(const char* const compName)
52 : WasmSequencerComponentBase(compName),
53 1 m_heapPages(nullptr),
54 1 m_heapPagesUsed(0),
55 1 m_heapPoisoned(false),
56 1 m_guestPool(nullptr),
57 1 m_guestPoolOffset(0),
58 1 m_wasm(nullptr),
59 1 m_hasExecutingContext(false),
60
1/1
✓ Branch 1 taken 1 times.
1 m_pendingTimer(),
61 1 m_hasPendingTimer(false),
62
1/1
✓ Branch 1 taken 1 times.
1 m_hostFunctionStart(),
63 1 m_hasHostFunctionStart(false),
64 1 m_dequeueSucceeded(false),
65 1 m_invokeStatus(SPACEWASM_OK),
66 1 m_pendingPause(false),
67 1 m_cancelRequested(false),
68 1 m_writeTelemetryQueued(false),
69
11/11
✓ Branch 3 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 15 taken 1 times.
✓ Branch 18 taken 1 times.
✓ Branch 21 taken 5 times.
✓ Branch 23 taken 5 times.
✓ Branch 24 taken 1 times.
✓ Branch 26 taken 1 times.
✓ Branch 29 taken 1 times.
8 m_sequencesStarted(0) {}
70
71 1 void WasmSequencer ::configure(const Config& cfg, Fw::MemAllocator& mallocator) {
72 1 FW_ASSERT(this->m_wasm == nullptr);
73 1 FW_ASSERT(this->m_allocator == nullptr);
74
75 1 getGlobalAllocatorLock()->lock();
76 1 const auto status = spacewasm_fprime_register_global_allocator(&globalAllocCallback, &globalDeallocCallback, this);
77 1 getGlobalAllocatorLock()->unlock();
78
79 1 FW_ASSERT(status == SPACEWASM_OK, status);
80
81 1 this->m_config = cfg;
82
83 // Allocate the heap memory pool
84 {
85 1 FW_ASSERT(
86 this->m_config.heapPages > 0 && this->m_config.heapPages <= Svc::WasmSequencerConfig::SPACEWASM_MAX_PAGES,
87 static_cast<FwAssertArgType>(this->m_config.heapPages), Svc::WasmSequencerConfig::SPACEWASM_MAX_PAGES);
88
89 // Allocate the heap page list
90 {
91 1 FwSizeType actualSize = sizeof(U8*) * this->m_config.heapPages;
92
1/1
✓ Branch 1 taken 1 times.
1 auto ptr = mallocator.checkedAllocate(0, actualSize);
93 1 this->m_heapPages = reinterpret_cast<U8**>(ptr);
94 }
95
96 // Allocate each heap page
97
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (FwSizeType i = 0; i < this->m_config.heapPages; i++) {
98 8 auto actualSize = Svc::WasmSequencerConfig::SPACEWASM_PAGE_SIZE;
99 auto ptr =
100
1/1
✓ Branch 1 taken 8 times.
8 mallocator.checkedAllocate(static_cast<FwIndexType>(i) + 1, actualSize, SPACEWASM_MEMORY_ALIGNMENT);
101 8 this->m_heapPages[i] = reinterpret_cast<U8*>(ptr);
102 }
103 1 this->m_heapPagesUsed = 0;
104 }
105
106 // Allocate the guest memory pool
107 {
108 1 auto actualSize = this->m_config.guestMemorySize;
109
1/1
✓ Branch 1 taken 1 times.
1 auto ptr = mallocator.checkedAllocate(static_cast<FwIndexType>(this->m_config.heapPages) + 1, actualSize,
110 SPACEWASM_MEMORY_ALIGNMENT);
111 1 this->m_guestPoolOffset = 0;
112 1 this->m_guestPool = reinterpret_cast<U8*>(ptr);
113 }
114
115 // Allocate the serialOut buffer
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (this->m_config.serialOutMax > 0) {
117 ✗ auto actualSize = this->m_config.serialOutMax;
118 ✗ auto ptr = mallocator.checkedAllocate(static_cast<FwIndexType>(this->m_config.heapPages) + 2, actualSize);
119 ✗ this->m_serialOutBuffer.setExtBuffer(reinterpret_cast<U8*>(ptr), actualSize);
120 }
121
122 // Allocate the serialIn queues
123 {
124
1/1
✓ Branch 1 taken 1 times.
1 Os::ScopeLock scopeLock(this->m_serialInMutex);
125
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (FwIndexType i = 0; i < NUM_SERIALIN_INPUT_PORTS; i++) {
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (this->m_config.serialIn[i].size > 0) {
127 ✗ auto actualSize = this->m_config.serialIn[i].size;
128 auto ptr =
129 ✗ mallocator.checkedAllocate(static_cast<FwIndexType>(this->m_config.heapPages) + 3 + i, actualSize);
130 ✗ this->m_serialInQueue[i].setup(reinterpret_cast<U8*>(ptr), actualSize);
131 }
132 }
133 1 }
134
135 // Allocate the initial store
136 1 this->m_allocator = &mallocator;
137 1 this->createStore();
138 1 }
139
140 1 void WasmSequencer ::deinit() {
141
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (this->m_wasm != nullptr) {
142 1 this->destroyStore();
143 }
144
145
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (this->m_allocator != nullptr) {
146 // Deallocate each heap page
147
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (FwSizeType i = 0; i < this->m_config.heapPages; i++) {
148 8 this->m_allocator->deallocate(static_cast<FwIndexType>(i) + 1, this->m_heapPages[i]);
149 8 this->m_heapPages[i] = nullptr;
150 }
151
152 // Deallocate the heap page pool
153 1 this->m_allocator->deallocate(0, this->m_heapPages);
154
155 // Deallocate the guest memory pool
156
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (this->m_guestPool != nullptr) {
157 1 this->m_allocator->deallocate(static_cast<FwIndexType>(this->m_config.heapPages) + 1, this->m_guestPool);
158 }
159
160 // Deallocate the serialOut buffer
161
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (this->m_serialOutBuffer.getBuffAddr() != nullptr) {
162 ✗ this->m_allocator->deallocate(static_cast<FwIndexType>(this->m_config.heapPages) + 2,
163 ✗ this->m_serialOutBuffer.getBuffAddr());
164 }
165
166 // Deallocate the serialIn queues
167
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (FwIndexType i = 0; i < NUM_SERIALIN_INPUT_PORTS; i++) {
168
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (this->m_serialInQueue[i].get_capacity() > 0) {
169 ✗ this->m_allocator->deallocate(static_cast<FwIndexType>(this->m_config.heapPages) + 3 + i,
170 ✗ this->m_serialInQueue[i].get_buffer());
171 }
172 }
173 }
174
175 // Release our slot in the process-wide global-allocator registry so it can
176 // be reused by a later sequencer instance.
177 1 getGlobalAllocatorLock()->lock();
178 1 (void)spacewasm_fprime_deregister_global_allocator(this);
179 1 getGlobalAllocatorLock()->unlock();
180
181 // Clean up dangling pointers
182 1 this->m_allocator = nullptr;
183 1 this->m_wasm = nullptr;
184
185 1 WasmSequencerComponentBase::deinit();
186 1 }
187
188 // ----------------------------------------------------------------------
189 // Handler implementations for typed input ports
190 // ----------------------------------------------------------------------
191
192 131 void WasmSequencer ::checkTimers_handler(FwIndexType portNum, U32 context) {
193 // Drive the sleep-wake / host-function-timeout checks in the state machine.
194 131 this->interpreter_sendSignal_checkTimers();
195 131 }
196
197 ✗ void WasmSequencer ::cmdResponseIn_handler(FwIndexType portNum,
198 FwOpcodeType opCode,
199 U32 cmdSeq,
200 const Fw::CmdResponse& response) {
201 ✗ FW_ASSERT(this->m_wasm != nullptr);
202
203 // The CmdDisp echoes back the context we sent, not a real cmdSeq. We packed
204 // our cmdUid into that context (see makeCmdUid); rename for clarity.
205 ✗ const U32 cmdUid = cmdSeq;
206 ✗ const U16 sequenceIndex = static_cast<U16>((cmdUid & 0xFFFF0000) >> 16);
207 ✗ const U16 cmdIndex = static_cast<U16>(cmdUid & 0xFFFF);
208 ✗ const U16 currentSequenceIndex = static_cast<U16>(this->m_sequencesStarted & 0xFFFF);
209 ✗ const U16 currentCmdIndex = static_cast<U16>(this->m_tlm.commandsDispatched & 0xFFFF);
210
211 // If the response is from a previous execution window, treat it as a nominal
212 // late reply (e.g. a command that returned after a CANCEL) and just report it
213 // without failing the current sequence.
214 ✗ if (sequenceIndex != currentSequenceIndex) {
215 ✗ this->log_WARNING_LO_CmdResponseFromOldSequence(CmdDispatcherCfg::getEventOpcode(opCode), response,
216 sequenceIndex, currentSequenceIndex);
217 ✗ return;
218 }
219
220 // From here on the response claims to be from the current sequence, so any
221 // inconsistency is a genuine error that should fail the sequence.
222 ✗ if (this->interpreter_getState() !=
223 ✗ WasmSequencer_InterpreterStateMachine_State::RUNNING_AWAITING_RESPONSE_WAITING ||
224 ✗ this->m_pendingHostFunction.kind != WasmSequencer_HostFunction::COMMAND) {
225 ✗ this->interpreter_sendSignal_hostResponseUnexpected(WasmSequencer_HostFunction::COMMAND);
226 ✗ return;
227 }
228
229 // Awaiting a command response, but was it for this exact dispatch instance, or
230 // an earlier one in this sequence with the same opcode?
231 ✗ if (cmdIndex != currentCmdIndex) {
232 ✗ this->log_WARNING_HI_WrongCmdResponseIndex(CmdDispatcherCfg::getEventOpcode(opCode), response, cmdIndex,
233 currentCmdIndex);
234 ✗ this->interpreter_sendSignal_hostResponseUnexpected(WasmSequencer_HostFunction::COMMAND);
235 ✗ return;
236 }
237
238 ✗ this->m_pendingHostFunction.clear();
239
240 // Track commands that came back with a non-OK response.
241 ✗ if (response != Fw::CmdResponse::OK) {
242 ✗ this->m_tlm.commandsFailed++;
243 }
244
245 ✗ this->interpreter_sendSignal_hostResumeI32(static_cast<I32>(response.e));
246 }
247
248 131 void WasmSequencer ::writeTelemetry_handler(FwIndexType portNum, U32 context) {
249 131 bool expected = false;
250
1/2
✓ Branch 1 taken 131 times.
✗ Branch 2 not taken.
131 if (this->m_writeTelemetryQueued.compare_exchange_strong(expected, true)) {
251
1/1
✓ Branch 1 taken 131 times.
131 this->writeTelemetryInternal_internalInterfaceInvoke();
252 }
253 131 }
254
255 ✗ void WasmSequencer ::writeTelemetryInternal_overflowHook() {
256 ✗ this->m_writeTelemetryQueued = false;
257 ✗ }
258
259 131 void WasmSequencer ::writeTelemetryInternal_internalInterfaceHandler() {
260 131 this->m_writeTelemetryQueued = false;
261 131 FW_ASSERT(this->m_wasm != nullptr);
262
263
1/1
✓ Branch 1 taken 131 times.
131 auto now = this->getTime();
264
265
3/3
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
✓ Branch 7 taken 131 times.
131 this->tlmWrite_ControllerState(this->controller_getState(), now);
266
3/3
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
✓ Branch 7 taken 131 times.
131 this->tlmWrite_InterpreterState(this->interpreter_getState(), now);
267
2/2
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
131 this->tlmWrite_SequencesSucceeded(this->m_tlm.sequencesSucceeded, now);
268
2/2
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
131 this->tlmWrite_SequencesFailed(this->m_tlm.sequencesFailed, now);
269
2/2
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
131 this->tlmWrite_SequencesCancelled(this->m_tlm.sequencesCancelled, now);
270
2/2
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
131 this->tlmWrite_CommandsDispatched(this->m_tlm.commandsDispatched, now);
271
2/2
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
131 this->tlmWrite_CommandsFailed(this->m_tlm.commandsFailed, now);
272
2/2
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
131 this->tlmWrite_LastTrapReason(this->m_exit.lastTrapReason, now);
273
2/2
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
131 this->tlmWrite_SeqName(this->m_tlm.sequenceName, now);
274 131 }
275
276 ✗ void WasmSequencer ::seqRunIn_handler(FwIndexType portNum, const Fw::StringBase& filename, const Svc::SeqArgs& args) {
277 ✗ FW_ASSERT(this->m_wasm != nullptr);
278
279 ✗ Fw::String runModuleName = "";
280
281 ✗ this->controller_sendSignal_run(Svc::WasmSequencer_LoadRequest(
282 filename, runModuleName,
283 ✗ Svc::WasmSequencer_RequestContext(WasmSequencer_SignalSource::PORT_RUN, WasmSequencer_CommandRequest(0, 0),
284 BlockState::NO_BLOCK,
285 /* moduleIdx */ 0 // placeholder, gets filled in after load
286 )));
287 ✗ }
288
289 ✗ void WasmSequencer ::seqCancelIn_handler(FwIndexType portNum) {
290 ✗ this->controller_sendSignal_cancel();
291 ✗ this->interpreter_sendSignal_cancel();
292 ✗ }
293
294 // ----------------------------------------------------------------------
295 // Handler implementations for serial input ports
296 // ----------------------------------------------------------------------
297
298 ✗ void WasmSequencer ::serialIn_handler(FwIndexType portNum, Fw::LinearBufferBase& buffer) {
299 ✗ FW_ASSERT(portNum < NUM_SERIALIN_INPUT_PORTS, portNum, NUM_SERIALIN_INPUT_PORTS);
300 ✗ Os::ScopeLock scopeLock(this->m_serialInMutex);
301 ✗ auto& queue = this->m_serialInQueue[portNum];
302 ✗ auto fullFullBehavior = this->m_config.serialIn[portNum].fullBehavior;
303
304 // Each message is framed on the queue as [U32 length][payload]
305 ✗ const FwSizeType headerSize = sizeof(U32);
306 ✗ const FwSizeType payloadSize = buffer.getSize();
307 ✗ const FwSizeType capacity = queue.get_capacity();
308
309 // Make sure the queue is sized to hold this framed message at all. This does
310 // not check free space, only that the queue's capacity is large enough.
311 ✗ if (headerSize + payloadSize > capacity) {
312 // The largest payload this queue could ever hold is capacity - headerSize. A queue smaller
313 // than the header (including a port left un-configured with capacity 0) can hold nothing.
314 ✗ const FwSizeType maxPayload = (capacity > headerSize) ? (capacity - headerSize) : 0;
315 ✗ this->log_WARNING_HI_SerialInFrameTooLarge(static_cast<U32>(portNum), static_cast<U32>(payloadSize),
316 static_cast<U32>(maxPayload));
317 ✗ return;
318 }
319
320 // Total framed size; <= capacity by the assertions above, so it cannot overflow.
321 ✗ const FwSizeType frameSize = headerSize + payloadSize;
322
323 // Check if we _can_ push the data to the queue
324 ✗ if (frameSize > queue.get_free_size()) {
325 // The queue is full and cannot push this data
326 ✗ switch (fullFullBehavior) {
327 ✗ case SerialInQueueFullBehavior::DROP_OLDEST:
328 // Drop oldest messages until this one fits.
329 ✗ while (frameSize > queue.get_free_size()) {
330 U32 nextMsgSize;
331 ✗ auto status = queue.peek(nextMsgSize);
332 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, portNum, status);
333
334 ✗ const FwSizeType allocated = queue.get_allocated_size();
335 ✗ FW_ASSERT(allocated >= headerSize, portNum, static_cast<FwAssertArgType>(allocated));
336 ✗ FW_ASSERT(static_cast<FwSizeType>(nextMsgSize) <= allocated - headerSize, portNum,
337 static_cast<FwAssertArgType>(nextMsgSize), static_cast<FwAssertArgType>(allocated));
338
339 ✗ status = queue.rotate(headerSize + nextMsgSize);
340
341 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, portNum, status);
342 }
343
344 // Now it fits, fall through to push the data to the queue
345 ✗ break;
346 ✗ case SerialInQueueFullBehavior::DROP_NEWEST:
347 // Drop this message
348 ✗ return;
349
350 ✗ case SerialInQueueFullBehavior::ASSERT:
351 ✗ FW_ASSERT(false, portNum, static_cast<FwAssertArgType>(frameSize),
352 static_cast<FwAssertArgType>(queue.get_free_size()), static_cast<FwAssertArgType>(capacity));
353 ✗ break;
354 }
355 }
356
357 // The message should be able to be put into the queue.
358 // Enqueue it as a raw [U32 size][payload] frame. We use the raw (const U8*, size)
359 ✗ Fw::LinearBufferTemplate<sizeof(U32)> sizeSer;
360 ✗ auto status = sizeSer.serializeFrom(static_cast<U32>(payloadSize));
361 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
362
363 ✗ status = queue.serialize(sizeSer.getBuffAddr(), headerSize);
364 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
365
366 ✗ status = queue.serialize(buffer.getBuffAddr(), payloadSize);
367 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
368
369 // Wake up any blocking recv calls
370 ✗ this->interpreter_sendSignal_serialInMessage(portNum);
371 ✗ }
372
373 // ----------------------------------------------------------------------
374 // Handler implementations for commands
375 // ----------------------------------------------------------------------
376
377 ✗ void WasmSequencer ::RUN_cmdHandler(FwOpcodeType opCode,
378 U32 cmdSeq,
379 const Fw::CmdStringArg& fileName,
380 const Svc::BlockState& block) {
381 ✗ FW_ASSERT(this->m_wasm != nullptr);
382
383 ✗ Fw::String runModuleName = "";
384 ✗ this->controller_sendSignal_run(Svc::WasmSequencer_LoadRequest(
385 fileName, runModuleName,
386 ✗ Svc::WasmSequencer_RequestContext(WasmSequencer_SignalSource::COMMAND_RUN,
387 ✗ WasmSequencer_CommandRequest(opCode, cmdSeq), block,
388 /* moduleIdx */ 0 // placeholder, gets filled in after load
389 )));
390 ✗ }
391
392 ✗ void WasmSequencer ::WAIT_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
393 ✗ FW_ASSERT(this->m_wasm != nullptr);
394
395 ✗ switch (this->controller_getState()) {
396 ✗ case WasmSequencer_ControllerStateMachine_State::IDLE:
397 case WasmSequencer_ControllerStateMachine_State::READY:
398 // Nothing is executing, respond immediately
399 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
400 ✗ break;
401 ✗ default: {
402 ✗ const auto status = this->m_waiting.enqueue(WaitingCmd(opCode, cmdSeq));
403 ✗ if (status != Fw::Success::SUCCESS) {
404 ✗ this->log_WARNING_HI_TooManyBlockingCommands();
405 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
406 }
407 ✗ }
408 }
409 ✗ }
410
411 ✗ void WasmSequencer ::LOAD_cmdHandler(FwOpcodeType opCode,
412 U32 cmdSeq,
413 const Fw::CmdStringArg& fileName,
414 const Fw::CmdStringArg& name) {
415 ✗ FW_ASSERT(this->m_wasm != nullptr);
416
417 ✗ this->controller_sendSignal_load(Svc::WasmSequencer_LoadRequest(
418 fileName, name,
419 ✗ Svc::WasmSequencer_RequestContext(WasmSequencer_SignalSource::COMMAND_LOAD,
420 ✗ WasmSequencer_CommandRequest(opCode, cmdSeq), Svc::BlockState::BLOCK,
421 /* moduleIdx */ 0 // placeholder, gets filled in after load
422 )));
423 ✗ }
424
425 ✗ void WasmSequencer ::INVOKE_cmdHandler(FwOpcodeType opCode,
426 U32 cmdSeq,
427 const Fw::CmdStringArg& module,
428 const Svc::BlockState& block) {
429 ✗ FW_ASSERT(this->m_wasm != nullptr);
430
431 ✗ this->controller_sendSignal_invoke(Svc::WasmSequencer_InvokeRequest(
432 ✗ module, Svc::WasmSequencer_RequestContext(WasmSequencer_SignalSource::COMMAND_INVOKE,
433 ✗ WasmSequencer_CommandRequest(opCode, cmdSeq), block,
434 /* moduleIdx */ 0 // placeholder, gets filled in after invoke
435 )));
436 ✗ }
437
438 ✗ void WasmSequencer ::CANCEL_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
439 ✗ this->controller_sendSignal_cancel();
440 ✗ this->interpreter_sendSignal_cmdCancel(WasmSequencer_CommandRequest(opCode, cmdSeq));
441 ✗ }
442
443 ✗ void WasmSequencer ::PAUSE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
444 ✗ if (this->interpreter_getState() == WasmSequencer_InterpreterStateMachine_State::IDLE) {
445 ✗ this->log_WARNING_LO_SequenceNotRunning();
446 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
447 ✗ return;
448 }
449
450 ✗ this->m_pendingPause = true;
451 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
452 }
453
454 ✗ void WasmSequencer ::CONTINUE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
455 ✗ FW_ASSERT(this->m_wasm != nullptr);
456
457 ✗ switch (this->interpreter_getState()) {
458 ✗ case WasmSequencer_InterpreterStateMachine_State::RUNNING_AWAITING_RESPONSE_SLEEPING:
459 case WasmSequencer_InterpreterStateMachine_State::RUNNING_AWAITING_RESPONSE_WAITING:
460 case WasmSequencer_InterpreterStateMachine_State::RUNNING_SPINNING:
461 // Already running
462 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
463 ✗ break;
464 ✗ case WasmSequencer_InterpreterStateMachine_State::RUNNING_PAUSED:
465 ✗ this->interpreter_sendSignal_cmd_CONTINUE();
466 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
467 ✗ break;
468 ✗ case WasmSequencer_InterpreterStateMachine_State::IDLE:
469 ✗ this->log_WARNING_LO_SequenceNotRunning();
470 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
471 ✗ break;
472 ✗ default:
473 ✗ FW_ASSERT(false, this->interpreter_getState());
474 }
475 ✗ }
476
477 ✗ void WasmSequencer ::GLOBAL_SET_I32_cmdHandler(FwOpcodeType opCode,
478 U32 cmdSeq,
479 const Fw::CmdStringArg& moduleName,
480 const Fw::CmdStringArg& name,
481 I32 value) {
482 ✗ FW_ASSERT(this->m_wasm != nullptr);
483
484 spacewasm_value_t s_value;
485 ✗ s_value.tag = SPACEWASM_I32;
486 ✗ s_value.u.i32_ = value;
487
488 ✗ auto status = this->setGlobal(moduleName, name, s_value);
489 ✗ if (status == SPACEWASM_OK) {
490 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
491 } else {
492 ✗ this->log_WARNING_LO_GlobalSetFailed(moduleName, name, status);
493 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
494 }
495 ✗ }
496
497 ✗ void WasmSequencer ::GLOBAL_SET_I64_cmdHandler(FwOpcodeType opCode,
498 U32 cmdSeq,
499 const Fw::CmdStringArg& moduleName,
500 const Fw::CmdStringArg& name,
501 I64 value) {
502 ✗ FW_ASSERT(this->m_wasm != nullptr);
503
504 spacewasm_value_t s_value;
505 ✗ s_value.tag = SPACEWASM_I64;
506 ✗ s_value.u.i64_ = value;
507
508 ✗ auto status = this->setGlobal(moduleName, name, s_value);
509 ✗ if (status == SPACEWASM_OK) {
510 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
511 } else {
512 ✗ this->log_WARNING_LO_GlobalSetFailed(moduleName, name, status);
513 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
514 }
515 ✗ }
516
517 ✗ void WasmSequencer ::GLOBAL_SET_F32_cmdHandler(FwOpcodeType opCode,
518 U32 cmdSeq,
519 const Fw::CmdStringArg& moduleName,
520 const Fw::CmdStringArg& name,
521 F32 value) {
522 ✗ FW_ASSERT(this->m_wasm != nullptr);
523
524 spacewasm_value_t s_value;
525 ✗ s_value.tag = SPACEWASM_F32;
526 ✗ s_value.u.f32_ = value;
527
528 ✗ auto status = this->setGlobal(moduleName, name, s_value);
529 ✗ if (status == SPACEWASM_OK) {
530 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
531 } else {
532 ✗ this->log_WARNING_LO_GlobalSetFailed(moduleName, name, status);
533 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
534 }
535 ✗ }
536
537 ✗ void WasmSequencer ::GLOBAL_SET_F64_cmdHandler(FwOpcodeType opCode,
538 U32 cmdSeq,
539 const Fw::CmdStringArg& moduleName,
540 const Fw::CmdStringArg& name,
541 F64 value) {
542 ✗ FW_ASSERT(this->m_wasm != nullptr);
543
544 spacewasm_value_t g_value;
545 ✗ g_value.tag = SPACEWASM_F64;
546 ✗ g_value.u.f64_ = value;
547
548 ✗ auto status = this->setGlobal(moduleName, name, g_value);
549 ✗ if (status == SPACEWASM_OK) {
550 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
551 } else {
552 ✗ this->log_WARNING_LO_GlobalSetFailed(moduleName, name, status);
553 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
554 }
555 ✗ }
556
557 ✗ void WasmSequencer ::GLOBAL_GET_cmdHandler(FwOpcodeType opCode,
558 U32 cmdSeq,
559 const Fw::CmdStringArg& moduleName,
560 const Fw::CmdStringArg& name) {
561 ✗ FW_ASSERT(this->m_wasm != nullptr);
562
563 spacewasm_value_t g_value;
564 ✗ auto status = this->getGlobal(moduleName, name, g_value);
565 ✗ if (status == SPACEWASM_OK) {
566 ✗ switch (g_value.tag) {
567 ✗ case SPACEWASM_I32:
568 ✗ this->log_ACTIVITY_LO_GlobalValueI32(moduleName, name, g_value.u.i32_);
569 ✗ break;
570 ✗ case SPACEWASM_I64:
571 ✗ this->log_ACTIVITY_LO_GlobalValueI64(moduleName, name, g_value.u.i64_);
572 ✗ break;
573 ✗ case SPACEWASM_F32:
574 ✗ this->log_ACTIVITY_LO_GlobalValueF32(moduleName, name, g_value.u.f32_);
575 ✗ break;
576 ✗ case SPACEWASM_F64:
577 ✗ this->log_ACTIVITY_LO_GlobalValueF64(moduleName, name, g_value.u.f64_);
578 ✗ break;
579 ✗ default:
580 ✗ FW_ASSERT(false, g_value.tag);
581 }
582
583 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
584 } else {
585 ✗ this->log_WARNING_LO_GlobalGetFailed(moduleName, name, status);
586 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
587 }
588 ✗ }
589
590 } // namespace Svc
591