#ifndef WEBDEBUGSERVICE_H #define WEBDEBUGSERVICE_H #include "BasicTCPSocket.h" #include "ConfigurationDatabase.h" #include "DataSourceI.h" #include "DebugServiceI.h" #include "EmbeddedServiceMethodBinderI.h" #include "ReferenceContainer.h" #include "ReferenceT.h" #include "SingleThreadService.h" namespace MARTe { /** * @brief HTTP/SSE implementation of DebugServiceI. * * Serves a single-page web UI on httpPort (default 8090). * Endpoints: * GET / — embedded HTML application * POST /api/command — execute a debug command; returns text response * GET /api/events — Server-Sent Events stream for real-time telemetry * * SSE event types (JSON payload on each "data:" line): * {"type":"trace", "name":"…","ts":,"value":} * {"type":"monitor", "name":"…","ts":,"value":} * {"type":"log", "level":"…","msg":"…"} * {"type":"status", "paused":,"gam":"…","remaining":} * {"type":"discover","signals":[…]} * {"type":"tree", "tree":{…}} * * This is an alternative to DebugService (TCP/UDP) and registers itself as the * global DebugServiceI singleton on Initialise(). */ class WebDebugService : public ReferenceContainer, public EmbeddedServiceMethodBinderI, public DebugServiceI { public: friend class WebDebugServiceTest; CLASS_REGISTER_DECLARATION() WebDebugService(); virtual ~WebDebugService(); virtual bool Initialise(StructuredDataI &data); // ------------------------------------------------------------------------- // DebugServiceI RT-path overrides // ------------------------------------------------------------------------- virtual DebugSignalInfo *RegisterSignal(void *memoryAddress, TypeDescriptor type, const char8 *name, uint8 numberOfDimensions = 0, uint32 numberOfElements = 1); virtual void ProcessSignal(DebugSignalInfo *signalInfo, uint32 size, uint64 timestamp); virtual void RegisterBroker(DebugSignalInfo **signalPointers, uint32 numSignals, MemoryMapBroker *broker, volatile bool *anyActiveFlag, Vec *activeIndices, Vec *activeSizes, FastPollingMutexSem *activeMutex, volatile bool *anyBreakFlag, Vec *breakIndices, const char8 *gamName = NULL_PTR(const char8 *), bool isOutput = false); virtual bool IsPaused() const { return isPaused; } virtual void SetPaused(bool paused) { isPaused = paused; } virtual bool IsStepPending() const { return stepRemaining > 0u; } virtual void ConsumeStepIfNeeded(const char8 *gamName, const char8 *threadName = NULL_PTR(const char8 *)); // ------------------------------------------------------------------------- // DebugServiceI control-path overrides // ------------------------------------------------------------------------- virtual uint32 ForceSignal (const char8 *name, const char8 *valueStr); virtual uint32 UnforceSignal (const char8 *name); virtual uint32 TraceSignal (const char8 *name, bool enable, uint32 decimation = 1); virtual uint32 SetBreak (const char8 *name, uint8 op, float64 threshold); virtual uint32 ClearBreak (const char8 *name); virtual bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable); virtual uint32 RegisterMonitorSignal(const char8 *path, uint32 periodMs); virtual uint32 UnmonitorSignal (const char8 *path); // EmbeddedServiceMethodBinderI (framework dispatches to sub-binders) virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info); // Inject the full config CDB (call after ConfigureApplication) void SetFullConfig(ConfigurationDatabase &config); void RebuildConfigFromRegistry(); struct MonitoredSignal { ReferenceT dataSource; uint32 signalIdx; uint32 internalID; uint32 periodMs; uint64 lastPollTime; uint32 size; StreamString path; }; private: // HTTP server helpers bool ParseRequest(BasicTCPSocket *client, StreamString &method, StreamString &path, StreamString &body); void SendHttp(BasicTCPSocket *client, uint32 code, const char8 *ct, StreamString &body); void HandleRequest(BasicTCPSocket *client, const StreamString &method, const StreamString &path, const StreamString &body); void UpgradeToSse(BasicTCPSocket *client); // Command handler — writes response to StreamString (not a socket) void HandleCommand(const StreamString &cmd, StreamString &out); void Discover (StreamString &out); void InfoNode (const char8 *path, StreamString &out); void ListNodes (const char8 *path, StreamString &out); void GetSignalValue(const char8 *name, StreamString &out); void ServeConfig (StreamString &out); void GetStepStatus(StreamString &out); void Step(uint32 n, const char8 *threadName = NULL_PTR(const char8 *)); // Tree / config helpers uint32 ExportTree(ReferenceContainer *container, StreamString &json, const char8 *pathPrefix); void EnrichWithConfig(const char8 *path, StreamString &json); static void JsonifyDatabase(ConfigurationDatabase &db, StreamString &json); // Registry patching (same as DebugService) void PatchRegistry(); // Broker index maintenance void UpdateBrokersActiveStatus(); void UpdateBrokersBreakStatus(); // SSE broadcast void BroadcastSse(const char8 *eventType, const StreamString &data); void RemoveDeadSseClients(); // Service thread entry points ErrorManagement::ErrorType HttpServer(ExecutionInfo &info); ErrorManagement::ErrorType Streamer (ExecutionInfo &info); // Sub-binder so the framework can dispatch to two separate threads class WdsBinder : public EmbeddedServiceMethodBinderI { public: enum ServiceType { Http, Stream }; WdsBinder(WebDebugService *p, ServiceType t) : parent(p), stype(t) {} virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info) { return (stype == Stream) ? parent->Streamer(info) : parent->HttpServer(info); } private: WebDebugService *parent; ServiceType stype; }; uint16 httpPort; volatile bool isPaused; volatile uint32 stepRemaining; StreamString pausedAtGam; StreamString stepThreadFilter; BasicTCPSocket tcpServer; WdsBinder binderHttp; WdsBinder binderStream; SingleThreadService httpService; SingleThreadService streamerService; Vec signals; Vec aliases; Vec brokers; Vec monitoredSignals; FastPollingMutexSem mutex; FastPollingMutexSem tracePushMutex; TraceRingBuffer traceBuffer; // SSE client list static const uint32 MAX_SSE_CLIENTS = 8u; BasicTCPSocket *sseClients[MAX_SSE_CLIENTS]; FastPollingMutexSem sseMutex; // Streamer packet state uint32 streamerSeq; uint64 streamerT0Ns; // nanosecond origin for relative timestamps sent over SSE ConfigurationDatabase fullConfig; bool manualConfigSet; static const uint32 GET_VALUE_MAX_ELEMENTS = 256u; // Log history buffer — replayed to newly connecting SSE clients static const uint32 LOG_HISTORY_SIZE = 64u; static const uint32 LOG_ENTRY_MAX_SIZE = 512u; char8 logHistory[64][512]; uint32 logHistoryWriteIdx; uint32 logHistoryFill; FastPollingMutexSem logHistoryMutex; }; } // namespace MARTe #endif // WEBDEBUGSERVICE_H