feat: WASM modules based simulation in Companion (#6435)
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "simulatorinterface.h"
|
||||
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
#include <atomic>
|
||||
|
||||
#include "wasm_export.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
class WasmSimulatorInterface : public SimulatorInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WasmSimulatorInterface(const QString & wasmPath, const QString & boardName,
|
||||
Board::Type boardType);
|
||||
virtual ~WasmSimulatorInterface();
|
||||
|
||||
QString name() override;
|
||||
bool isRunning() override;
|
||||
void readRadioData(QByteArray & dest) override {}
|
||||
uint8_t * getLcd() override;
|
||||
uint8_t getSensorInstance(uint16_t id, uint8_t defaultValue = 0) override;
|
||||
uint16_t getSensorRatio(uint16_t id) override;
|
||||
const int getCapability(Capability cap) override;
|
||||
|
||||
public slots:
|
||||
void init() override;
|
||||
void start(const char * filename = nullptr, bool tests = true) override;
|
||||
void stop() override;
|
||||
void setSdPath(const QString & sdPath = "",
|
||||
const QString & settingsPath = "") override;
|
||||
void setVolumeGain(const int value) override;
|
||||
void setRadioData(const QByteArray & data) override {}
|
||||
void setAnalogValue(uint8_t index, int16_t value) override;
|
||||
void setKey(uint8_t key, bool state) override;
|
||||
void setSwitch(uint8_t swtch, int8_t state) override;
|
||||
void setTrim(unsigned int idx, int value) override;
|
||||
void setTrimSwitch(uint8_t trim, bool state) override;
|
||||
void setTrainerInput(unsigned int inputNumber, int16_t value) override;
|
||||
void setInputValue(int type, uint8_t index, int16_t value) override;
|
||||
void rotaryEncoderEvent(int steps) override;
|
||||
void touchEvent(int type, int x, int y) override;
|
||||
void lcdFlushed() override;
|
||||
void setTrainerTimeout(uint16_t ms) override;
|
||||
void sendInternalModuleTelemetry(const quint8 protocol,
|
||||
const QByteArray data) override;
|
||||
void sendExternalModuleTelemetry(const quint8 protocol,
|
||||
const QByteArray data) override;
|
||||
void setLuaStateReloadPermanentScripts() override;
|
||||
void addTracebackDevice(QIODevice * device) override;
|
||||
void removeTracebackDevice(QIODevice * device) override;
|
||||
void receiveAuxSerialData(const quint8 port_num,
|
||||
const QByteArray & data) override;
|
||||
|
||||
// Called by WASM import simuGetAnalog
|
||||
int16_t getAnalogValue(uint8_t index);
|
||||
|
||||
// Called by WASM import simuTrace
|
||||
void writeTrace(const char * text);
|
||||
|
||||
// Called by WASM import simuQueueAudio
|
||||
void queueAudio(const uint8_t * buf, uint32_t len);
|
||||
|
||||
// Called by WASM import simuLcdNotify (from WAMR thread)
|
||||
void notifyLcdReady();
|
||||
|
||||
protected slots:
|
||||
void run();
|
||||
void onLcdNotify();
|
||||
|
||||
protected:
|
||||
bool loadModule();
|
||||
void unloadModule();
|
||||
bool resolveExports();
|
||||
void refreshLcd();
|
||||
void checkOutputsChanged();
|
||||
void initAudio();
|
||||
void deinitAudio();
|
||||
|
||||
QString m_wasmPath;
|
||||
QString m_boardName;
|
||||
Board::Type m_boardType;
|
||||
QString m_sdPath;
|
||||
QString m_settingsPath;
|
||||
|
||||
QTimer * m_timer10ms = nullptr;
|
||||
std::atomic<bool> m_lcdNotified{false};
|
||||
QMutex m_mutex;
|
||||
QMutex m_mtxTbDevices;
|
||||
QVector<QIODevice *> m_tracebackDevices;
|
||||
bool m_stopRequested = false;
|
||||
|
||||
// Audio
|
||||
SDL_AudioDeviceID m_audioDevice = 0;
|
||||
int m_volumeGain = SDL_MIX_MAXVOLUME;
|
||||
|
||||
// Host-side analog values (polled by WASM via simuGetAnalog)
|
||||
static constexpr int MAX_ANALOGS = 32;
|
||||
int16_t m_analogValues[MAX_ANALOGS] = {};
|
||||
|
||||
// Cached output values for change detection
|
||||
TxOutputs m_lastOutputs;
|
||||
bool m_resetOutputsData = true;
|
||||
|
||||
// Persistent WASM buffer for bulk copies (allocated once in init)
|
||||
uint32_t m_wasmScratchBuf = 0;
|
||||
uint32_t m_wasmScratchSize = 0;
|
||||
|
||||
// Host-side LCD buffer + persistent WASM-side copy buffer
|
||||
uint8_t * m_lcdBuffer = nullptr;
|
||||
uint32_t m_lcdBufferSize = 0;
|
||||
uint32_t m_wasmLcdBuf = 0;
|
||||
uint32_t m_lcdWidth = 0;
|
||||
uint32_t m_lcdHeight = 0;
|
||||
uint32_t m_lcdDepth = 0;
|
||||
|
||||
// WAMR handles
|
||||
wasm_module_t m_module = nullptr;
|
||||
wasm_module_inst_t m_moduleInst = nullptr;
|
||||
wasm_exec_env_t m_execEnv = nullptr;
|
||||
QByteArray m_wasmBinary;
|
||||
|
||||
// Cached WASM function references
|
||||
wasm_function_inst_t m_fnInit = nullptr;
|
||||
wasm_function_inst_t m_fnStart = nullptr;
|
||||
wasm_function_inst_t m_fnStop = nullptr;
|
||||
wasm_function_inst_t m_fnIsRunning = nullptr;
|
||||
wasm_function_inst_t m_fnSetKey = nullptr;
|
||||
wasm_function_inst_t m_fnSetTrim = nullptr;
|
||||
wasm_function_inst_t m_fnSetSwitch = nullptr;
|
||||
wasm_function_inst_t m_fnLcdChanged = nullptr;
|
||||
wasm_function_inst_t m_fnLcdCopy = nullptr;
|
||||
wasm_function_inst_t m_fnLcdGetWidth = nullptr;
|
||||
wasm_function_inst_t m_fnLcdGetHeight = nullptr;
|
||||
wasm_function_inst_t m_fnLcdGetDepth = nullptr;
|
||||
wasm_function_inst_t m_fnTouchDown = nullptr;
|
||||
wasm_function_inst_t m_fnTouchUp = nullptr;
|
||||
wasm_function_inst_t m_fnFatfsSetPaths = nullptr;
|
||||
wasm_function_inst_t m_fnRotaryEncoderEvent = nullptr;
|
||||
wasm_function_inst_t m_fnGetCapability = nullptr;
|
||||
|
||||
// Output value exports (bulk copy)
|
||||
wasm_function_inst_t m_fnGetNumChannels = nullptr;
|
||||
wasm_function_inst_t m_fnCopyChannelOutputs = nullptr;
|
||||
wasm_function_inst_t m_fnCopyMixOutputs = nullptr;
|
||||
wasm_function_inst_t m_fnGetNumLogicalSwitches = nullptr;
|
||||
wasm_function_inst_t m_fnCopyLogicalSwitches = nullptr;
|
||||
wasm_function_inst_t m_fnGetTrimValue = nullptr;
|
||||
wasm_function_inst_t m_fnGetTrimRange = nullptr;
|
||||
wasm_function_inst_t m_fnGetFlightMode = nullptr;
|
||||
wasm_function_inst_t m_fnGetNumGVars = nullptr;
|
||||
wasm_function_inst_t m_fnGetNumFlightModes = nullptr;
|
||||
wasm_function_inst_t m_fnGetGVar = nullptr;
|
||||
|
||||
// Phase 4: telemetry, trim, lua, lcd, trainer
|
||||
wasm_function_inst_t m_fnSetTrimValue = nullptr;
|
||||
wasm_function_inst_t m_fnSendTelemetry = nullptr;
|
||||
wasm_function_inst_t m_fnLuaReloadPermanentScripts = nullptr;
|
||||
wasm_function_inst_t m_fnLcdFlushed = nullptr;
|
||||
wasm_function_inst_t m_fnGetMaxTrainerChannels = nullptr;
|
||||
wasm_function_inst_t m_fnCopyTrainerInput = nullptr;
|
||||
wasm_function_inst_t m_fnSetTrainerTimeout = nullptr;
|
||||
|
||||
// Backlight and function switch LEDs
|
||||
wasm_function_inst_t m_fnGetBacklightState = nullptr;
|
||||
wasm_function_inst_t m_fnGetNumCustomSwitches = nullptr;
|
||||
wasm_function_inst_t m_fnGetCustomSwitchColor = nullptr;
|
||||
wasm_function_inst_t m_fnGetCustomSwitchIndex = nullptr;
|
||||
|
||||
// Trainer input buffer (flushed in bulk via run())
|
||||
static constexpr int MAX_TRAINER_CH = 16;
|
||||
int16_t m_trainerValues[MAX_TRAINER_CH] = {};
|
||||
bool m_trainerDirty = false;
|
||||
|
||||
// Cached function switch LED colors for change detection
|
||||
static constexpr int MAX_FS_LEDS = 8;
|
||||
uint32_t m_lastFSLedColors[MAX_FS_LEDS] = {};
|
||||
|
||||
wasm_function_inst_t m_fnMalloc = nullptr;
|
||||
wasm_function_inst_t m_fnFree = nullptr;
|
||||
};
|
||||
|
||||
class WasmSimulatorFactory : public SimulatorFactory
|
||||
{
|
||||
public:
|
||||
WasmSimulatorFactory(const QString & wasmPath, const QString & boardName,
|
||||
Board::Type boardType);
|
||||
|
||||
QString name() override;
|
||||
Board::Type type() override;
|
||||
SimulatorInterface * create() override;
|
||||
|
||||
private:
|
||||
QString m_wasmPath;
|
||||
QString m_boardName;
|
||||
Board::Type m_boardType;
|
||||
};
|
||||
Reference in New Issue
Block a user