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