Improved overall features

This commit is contained in:
Martino Ferrari
2026-03-03 21:41:59 +01:00
parent e6102ba433
commit a941563749
10 changed files with 689 additions and 103 deletions

View File

@@ -1,15 +1,18 @@
#include "BasicTCPSocket.h" #include "BasicTCPSocket.h"
#include "ClassRegistryItem.h" #include "ClassRegistryItem.h"
#include "ConfigurationDatabase.h" #include "ConfigurationDatabase.h"
#include "DataSourceI.h"
#include "DebugBrokerWrapper.h" #include "DebugBrokerWrapper.h"
#include "DebugService.h" #include "DebugService.h"
#include "GAM.h" #include "GAM.h"
#include "GlobalObjectsDatabase.h"
#include "HighResolutionTimer.h" #include "HighResolutionTimer.h"
#include "ObjectBuilder.h" #include "ObjectBuilder.h"
#include "ObjectRegistryDatabase.h" #include "ObjectRegistryDatabase.h"
#include "StreamString.h" #include "StreamString.h"
#include "TimeoutType.h" #include "TimeoutType.h"
#include "TypeConversion.h" #include "TypeConversion.h"
#include "ReferenceT.h"
namespace MARTe { namespace MARTe {
@@ -84,6 +87,7 @@ DebugService::DebugService()
threadService(binderServer), streamerService(binderStreamer) { threadService(binderServer), streamerService(binderStreamer) {
controlPort = 0; controlPort = 0;
streamPort = 8081; streamPort = 8081;
logPort = 8082;
streamIP = "127.0.0.1"; streamIP = "127.0.0.1";
isServer = false; isServer = false;
suppressTimeoutLogs = true; suppressTimeoutLogs = true;
@@ -106,6 +110,7 @@ DebugService::~DebugService() {
for (uint32 i = 0; i < signals.Size(); i++) { for (uint32 i = 0; i < signals.Size(); i++) {
delete signals[i]; delete signals[i];
} }
this->Purge();
} }
bool DebugService::Initialise(StructuredDataI &data) { bool DebugService::Initialise(StructuredDataI &data) {
@@ -132,6 +137,15 @@ bool DebugService::Initialise(StructuredDataI &data) {
(void)data.Read("UdpPort", port); (void)data.Read("UdpPort", port);
streamPort = (uint16)port; streamPort = (uint16)port;
} }
port = 8082;
if (data.Read("LogPort", port)) {
logPort = (uint16)port;
} else {
(void)data.Read("TcpLogPort", port);
logPort = (uint16)port;
}
StreamString tempIP; StreamString tempIP;
if (data.Read("StreamIP", tempIP)) { if (data.Read("StreamIP", tempIP)) {
streamIP = tempIP; streamIP = tempIP;
@@ -175,6 +189,34 @@ bool DebugService::Initialise(StructuredDataI &data) {
return false; return false;
if (streamerService.Start() != ErrorManagement::NoError) if (streamerService.Start() != ErrorManagement::NoError)
return false; return false;
if (logPort > 0) {
Reference tcpLogger(
"TcpLogger", GlobalObjectsDatabase::Instance()->GetStandardHeap());
if (tcpLogger.IsValid()) {
ConfigurationDatabase loggerConfig;
loggerConfig.Write("Port", (uint32)logPort);
if (tcpLogger->Initialise(loggerConfig)) {
this->Insert(tcpLogger);
Reference loggerService(
"LoggerService",
GlobalObjectsDatabase::Instance()->GetStandardHeap());
if (loggerService.IsValid()) {
ConfigurationDatabase serviceConfig;
serviceConfig.Write("CPUs", (uint32)1);
ReferenceContainer *lc =
dynamic_cast<ReferenceContainer *>(loggerService.operator->());
if (lc != NULL_PTR(ReferenceContainer *)) {
lc->Insert(tcpLogger);
}
if (loggerService->Initialise(serviceConfig)) {
this->Insert(loggerService);
}
}
}
}
}
} }
return true; return true;
} }
@@ -430,6 +472,24 @@ ErrorManagement::ErrorType DebugService::Streamer(ExecutionInfo &info) {
uint32 packetOffset = 0; uint32 packetOffset = 0;
uint32 sequenceNumber = 0; uint32 sequenceNumber = 0;
while (info.GetStage() == ExecutionInfo::MainStage) { while (info.GetStage() == ExecutionInfo::MainStage) {
// Poll monitored signals
uint64 currentTimeMs = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000.0);
mutex.FastLock();
for (uint32 i = 0; i < monitoredSignals.Size(); i++) {
if (currentTimeMs >= (monitoredSignals[i].lastPollTime + monitoredSignals[i].periodMs)) {
monitoredSignals[i].lastPollTime = currentTimeMs;
uint64 ts = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000000.0);
void *address = NULL_PTR(void *);
if (monitoredSignals[i].dataSource->GetSignalMemoryBuffer(monitoredSignals[i].signalIdx, 0, address)) {
traceBuffer.Push(monitoredSignals[i].internalID, ts, (uint8 *)address, monitoredSignals[i].size);
}
}
}
mutex.FastUnLock();
uint32 id, size; uint32 id, size;
uint64 ts; uint64 ts;
uint8 sampleData[1024]; uint8 sampleData[1024];
@@ -536,7 +596,47 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
} }
} else if (token == "DISCOVER") } else if (token == "DISCOVER")
Discover(client); Discover(client);
else if (token == "CONFIG") else if (token == "SERVICE_INFO") {
if (client) {
StreamString resp;
resp.Printf("OK SERVICE_INFO TCP_CTRL:%u UDP_STREAM:%u TCP_LOG:%u STATE:%s\n",
controlPort, streamPort, logPort, isPaused ? "PAUSED" : "RUNNING");
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
}
} else if (token == "MONITOR") {
StreamString subToken;
if (cmd.GetToken(subToken, delims, term) && subToken == "SIGNAL") {
StreamString name, period;
if (cmd.GetToken(name, delims, term) && cmd.GetToken(period, delims, term)) {
uint32 p = 100;
AnyType pVal(UnsignedInteger32Bit, 0u, &p);
AnyType pStr(CharString, 0u, period.Buffer());
(void)TypeConvert(pVal, pStr);
uint32 count = RegisterMonitorSignal(name.Buffer(), p);
if (client) {
StreamString resp;
resp.Printf("OK MONITOR %u\n", count);
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
}
}
}
} else if (token == "UNMONITOR") {
StreamString subToken;
if (cmd.GetToken(subToken, delims, term) && subToken == "SIGNAL") {
StreamString name;
if (cmd.GetToken(name, delims, term)) {
uint32 count = UnmonitorSignal(name.Buffer());
if (client) {
StreamString resp;
resp.Printf("OK UNMONITOR %u\n", count);
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
}
}
}
} else if (token == "CONFIG")
ServeConfig(client); ServeConfig(client);
else if (token == "PAUSE") { else if (token == "PAUSE") {
SetPaused(true); SetPaused(true);
@@ -554,7 +654,7 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
StreamString json; StreamString json;
json = "{\"Name\": \"Root\", \"Class\": \"ObjectRegistryDatabase\", " json = "{\"Name\": \"Root\", \"Class\": \"ObjectRegistryDatabase\", "
"\"Children\": [\n"; "\"Children\": [\n";
(void)ExportTree(ObjectRegistryDatabase::Instance(), json); (void)ExportTree(ObjectRegistryDatabase::Instance(), json, NULL_PTR(const char8 *));
json += "\n]}\nOK TREE\n"; json += "\n]}\nOK TREE\n";
uint32 s = json.Size(); uint32 s = json.Size();
if (client) if (client)
@@ -747,7 +847,7 @@ void DebugService::InfoNode(const char8 *path, BasicTCPSocket *client) {
} }
uint32 DebugService::ExportTree(ReferenceContainer *container, uint32 DebugService::ExportTree(ReferenceContainer *container,
StreamString &json) { StreamString &json, const char8 *pathPrefix) {
if (container == NULL_PTR(ReferenceContainer *)) if (container == NULL_PTR(ReferenceContainer *))
return 0; return 0;
uint32 size = container->Size(); uint32 size = container->Size();
@@ -761,6 +861,14 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
const char8 *cname = child->GetName(); const char8 *cname = child->GetName();
if (cname == NULL_PTR(const char8 *)) if (cname == NULL_PTR(const char8 *))
cname = "unnamed"; cname = "unnamed";
StreamString currentPath;
if (pathPrefix != NULL_PTR(const char8 *)) {
currentPath.Printf("%s.%s", pathPrefix, cname);
} else {
currentPath = cname;
}
nodeJson += "{\"Name\": \""; nodeJson += "{\"Name\": \"";
EscapeJson(cname, nodeJson); EscapeJson(cname, nodeJson);
nodeJson += "\", \"Class\": \""; nodeJson += "\", \"Class\": \"";
@@ -775,7 +883,7 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
nodeJson += ", \"Children\": [\n"; nodeJson += ", \"Children\": [\n";
uint32 subCount = 0u; uint32 subCount = 0u;
if (inner != NULL_PTR(ReferenceContainer *)) if (inner != NULL_PTR(ReferenceContainer *))
subCount += ExportTree(inner, nodeJson); subCount += ExportTree(inner, nodeJson, currentPath.Buffer());
if (ds != NULL_PTR(DataSourceI *)) { if (ds != NULL_PTR(DataSourceI *)) {
uint32 nSignals = ds->GetNumberOfSignals(); uint32 nSignals = ds->GetNumberOfSignals();
for (uint32 j = 0u; j < nSignals; j++) { for (uint32 j = 0u; j < nSignals; j++) {
@@ -790,12 +898,22 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
(void)ds->GetSignalNumberOfDimensions(j, dims); (void)ds->GetSignalNumberOfDimensions(j, dims);
uint32 elems = 0u; uint32 elems = 0u;
(void)ds->GetSignalNumberOfElements(j, elems); (void)ds->GetSignalNumberOfElements(j, elems);
StreamString signalFullPath;
signalFullPath.Printf("%s.%s", currentPath.Buffer(), sname.Buffer());
bool traceable = false;
bool forcable = false;
(void)IsInstrumented(signalFullPath.Buffer(), traceable, forcable);
nodeJson += "{\"Name\": \""; nodeJson += "{\"Name\": \"";
EscapeJson(sname.Buffer(), nodeJson); EscapeJson(sname.Buffer(), nodeJson);
nodeJson += "\", \"Class\": \"Signal\", \"Type\": \""; nodeJson += "\", \"Class\": \"Signal\", \"Type\": \"";
EscapeJson(stype ? stype : "Unknown", nodeJson); EscapeJson(stype ? stype : "Unknown", nodeJson);
nodeJson.Printf("\", \"Dimensions\": %d, \"Elements\": %u}", dims, nodeJson.Printf("\", \"Dimensions\": %d, \"Elements\": %u", dims,
elems); elems);
nodeJson.Printf(", \"IsTraceable\": %s, \"IsForcable\": %s}",
traceable ? "true" : "false",
forcable ? "true" : "false");
} }
} }
if (gam != NULL_PTR(GAM *)) { if (gam != NULL_PTR(GAM *)) {
@@ -812,12 +930,23 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
(void)gam->GetSignalNumberOfDimensions(InputSignals, j, dims); (void)gam->GetSignalNumberOfDimensions(InputSignals, j, dims);
uint32 elems = 0u; uint32 elems = 0u;
(void)gam->GetSignalNumberOfElements(InputSignals, j, elems); (void)gam->GetSignalNumberOfElements(InputSignals, j, elems);
StreamString signalFullPath;
signalFullPath.Printf("%s.In.%s", currentPath.Buffer(),
sname.Buffer());
bool traceable = false;
bool forcable = false;
(void)IsInstrumented(signalFullPath.Buffer(), traceable, forcable);
nodeJson += "{\"Name\": \"In."; nodeJson += "{\"Name\": \"In.";
EscapeJson(sname.Buffer(), nodeJson); EscapeJson(sname.Buffer(), nodeJson);
nodeJson += "\", \"Class\": \"InputSignal\", \"Type\": \""; nodeJson += "\", \"Class\": \"InputSignal\", \"Type\": \"";
EscapeJson(stype ? stype : "Unknown", nodeJson); EscapeJson(stype ? stype : "Unknown", nodeJson);
nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u}", dims, nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u", dims,
elems); elems);
nodeJson.Printf(", \"IsTraceable\": %s, \"IsForcable\": %s}",
traceable ? "true" : "false",
forcable ? "true" : "false");
} }
uint32 nOut = gam->GetNumberOfOutputSignals(); uint32 nOut = gam->GetNumberOfOutputSignals();
for (uint32 j = 0u; j < nOut; j++) { for (uint32 j = 0u; j < nOut; j++) {
@@ -832,12 +961,23 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
(void)gam->GetSignalNumberOfDimensions(OutputSignals, j, dims); (void)gam->GetSignalNumberOfDimensions(OutputSignals, j, dims);
uint32 elems = 0u; uint32 elems = 0u;
(void)gam->GetSignalNumberOfElements(OutputSignals, j, elems); (void)gam->GetSignalNumberOfElements(OutputSignals, j, elems);
StreamString signalFullPath;
signalFullPath.Printf("%s.Out.%s", currentPath.Buffer(),
sname.Buffer());
bool traceable = false;
bool forcable = false;
(void)IsInstrumented(signalFullPath.Buffer(), traceable, forcable);
nodeJson += "{\"Name\": \"Out."; nodeJson += "{\"Name\": \"Out.";
EscapeJson(sname.Buffer(), nodeJson); EscapeJson(sname.Buffer(), nodeJson);
nodeJson += "\", \"Class\": \"OutputSignal\", \"Type\": \""; nodeJson += "\", \"Class\": \"OutputSignal\", \"Type\": \"";
EscapeJson(stype ? stype : "Unknown", nodeJson); EscapeJson(stype ? stype : "Unknown", nodeJson);
nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u}", dims, nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u", dims,
elems); elems);
nodeJson.Printf(", \"IsTraceable\": %s, \"IsForcable\": %s}",
traceable ? "true" : "false",
forcable ? "true" : "false");
} }
} }
nodeJson += "\n]"; nodeJson += "\n]";
@@ -908,13 +1048,121 @@ uint32 DebugService::TraceSignal(const char8 *name, bool enable,
return count; return count;
} }
bool DebugService::IsInstrumented(const char8 *fullPath, bool &traceable,
bool &forcable) {
mutex.FastLock();
bool found = false;
for (uint32 i = 0; i < aliases.Size(); i++) {
if (aliases[i].name == fullPath ||
SuffixMatch(aliases[i].name.Buffer(), fullPath)) {
found = true;
break;
}
}
mutex.FastUnLock();
traceable = found;
forcable = found;
return found;
}
uint32 DebugService::RegisterMonitorSignal(const char8 *path, uint32 periodMs) {
mutex.FastLock();
uint32 count = 0;
// Check if already monitored
for (uint32 j = 0; j < monitoredSignals.Size(); j++) {
if (monitoredSignals[j].path == path) {
monitoredSignals[j].periodMs = periodMs;
mutex.FastUnLock();
return 1;
}
}
// Path resolution: find the DataSource object
StreamString fullPath = path;
fullPath.Seek(0);
char8 term;
Vec<StreamString> parts;
StreamString token;
while (fullPath.GetToken(token, ".", term)) {
parts.Push(token);
token = "";
}
if (parts.Size() >= 2) {
StreamString signalName = parts[parts.Size() - 1u];
StreamString dsPath;
for (uint32 i = 0; i < parts.Size() - 1u; i++) {
dsPath += parts[i];
if (i < parts.Size() - 2u)
dsPath += ".";
}
ReferenceT<DataSourceI> ds =
ObjectRegistryDatabase::Instance()->Find(dsPath.Buffer());
if (ds.IsValid()) {
uint32 idx = 0;
if (ds->GetSignalIndex(idx, signalName.Buffer())) {
MonitoredSignal m;
m.dataSource = ds;
m.signalIdx = idx;
m.path = path;
m.periodMs = periodMs;
m.lastPollTime = 0;
m.size = 0;
(void)ds->GetSignalByteSize(idx, m.size);
if (m.size == 0)
m.size = 4;
// Use high-bit for polled signals to avoid conflict with brokered ones
m.internalID = 0x80000000 | monitoredSignals.Size();
// Re-use existing ID if signal is also instrumented via broker
for (uint32 i = 0; i < aliases.Size(); i++) {
if (aliases[i].name == path ||
SuffixMatch(aliases[i].name.Buffer(), path)) {
m.internalID = signals[aliases[i].signalIndex]->internalID;
break;
}
}
monitoredSignals.Push(m);
count = 1;
}
}
}
mutex.FastUnLock();
return count;
}
uint32 DebugService::UnmonitorSignal(const char8 *path) {
mutex.FastLock();
uint32 count = 0;
for (uint32 i = 0; i < monitoredSignals.Size(); i++) {
if (monitoredSignals[i].path == path ||
SuffixMatch(monitoredSignals[i].path.Buffer(), path)) {
(void)monitoredSignals.Remove(i);
i--;
count++;
}
}
mutex.FastUnLock();
return count;
}
void DebugService::Discover(BasicTCPSocket *client) { void DebugService::Discover(BasicTCPSocket *client) {
if (client) { if (client) {
StreamString header = "{\n \"Signals\": [\n"; StreamString header = "{\n \"Signals\": [\n";
uint32 s = header.Size(); uint32 s = header.Size();
(void)client->Write(header.Buffer(), s); (void)client->Write(header.Buffer(), s);
mutex.FastLock(); mutex.FastLock();
uint32 total = 0;
for (uint32 i = 0; i < aliases.Size(); i++) { for (uint32 i = 0; i < aliases.Size(); i++) {
if (total > 0) {
uint32 commaSize = 2;
(void)client->Write(",\n", commaSize);
}
StreamString line; StreamString line;
DebugSignalInfo *sig = signals[aliases[i].signalIndex]; DebugSignalInfo *sig = signals[aliases[i].signalIndex];
const char8 *typeName = const char8 *typeName =
@@ -924,11 +1172,39 @@ void DebugService::Discover(BasicTCPSocket *client) {
typeName ? typeName : "Unknown"); typeName ? typeName : "Unknown");
EnrichWithConfig(aliases[i].name.Buffer(), line); EnrichWithConfig(aliases[i].name.Buffer(), line);
line += "}"; line += "}";
if (i < aliases.Size() - 1)
line += ",";
line += "\n";
s = line.Size(); s = line.Size();
(void)client->Write(line.Buffer(), s); (void)client->Write(line.Buffer(), s);
total++;
}
// Export monitored signals not already in aliases
for (uint32 i = 0; i < monitoredSignals.Size(); i++) {
bool found = false;
for (uint32 j = 0; j < aliases.Size(); j++) {
if (aliases[j].name == monitoredSignals[i].path) {
found = true;
break;
}
}
if (!found) {
if (total > 0) {
uint32 commaSize = 2;
(void)client->Write(",\n", commaSize);
}
StreamString line;
const char8 *typeName = TypeDescriptor::GetTypeNameFromTypeDescriptor(
monitoredSignals[i].dataSource->GetSignalType(
monitoredSignals[i].signalIdx));
line.Printf(" {\"name\": \"%s\", \"id\": %u, \"type\": \"%s\"",
monitoredSignals[i].path.Buffer(),
monitoredSignals[i].internalID,
typeName ? typeName : "Unknown");
EnrichWithConfig(monitoredSignals[i].path.Buffer(), line);
line += "}";
s = line.Size();
(void)client->Write(line.Buffer(), s);
total++;
}
} }
mutex.FastUnLock(); mutex.FastUnLock();
StreamString footer = " ]\n}\nOK DISCOVER\n"; StreamString footer = " ]\n}\nOK DISCOVER\n";

View File

@@ -16,6 +16,7 @@
namespace MARTe { namespace MARTe {
class MemoryMapBroker; class MemoryMapBroker;
class DataSourceI;
struct SignalAlias { struct SignalAlias {
StreamString name; StreamString name;
@@ -64,17 +65,31 @@ public:
uint32 ForceSignal(const char8 *name, const char8 *valueStr); uint32 ForceSignal(const char8 *name, const char8 *valueStr);
uint32 UnforceSignal(const char8 *name); uint32 UnforceSignal(const char8 *name);
uint32 TraceSignal(const char8 *name, bool enable, uint32 decimation = 1); uint32 TraceSignal(const char8 *name, bool enable, uint32 decimation = 1);
bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable);
void Discover(BasicTCPSocket *client); void Discover(BasicTCPSocket *client);
void InfoNode(const char8 *path, BasicTCPSocket *client); void InfoNode(const char8 *path, BasicTCPSocket *client);
void ListNodes(const char8 *path, BasicTCPSocket *client); void ListNodes(const char8 *path, BasicTCPSocket *client);
void ServeConfig(BasicTCPSocket *client); void ServeConfig(BasicTCPSocket *client);
void SetFullConfig(ConfigurationDatabase &config); void SetFullConfig(ConfigurationDatabase &config);
struct MonitoredSignal {
ReferenceT<DataSourceI> dataSource;
uint32 signalIdx;
uint32 internalID;
uint32 periodMs;
uint64 lastPollTime;
uint32 size;
StreamString path;
};
uint32 RegisterMonitorSignal(const char8 *path, uint32 periodMs);
uint32 UnmonitorSignal(const char8 *path);
private: private:
void HandleCommand(StreamString cmd, BasicTCPSocket *client); void HandleCommand(StreamString cmd, BasicTCPSocket *client);
void UpdateBrokersActiveStatus(); void UpdateBrokersActiveStatus();
uint32 ExportTree(ReferenceContainer *container, StreamString &json); uint32 ExportTree(ReferenceContainer *container, StreamString &json, const char8 *pathPrefix);
void PatchRegistry(); void PatchRegistry();
void EnrichWithConfig(const char8 *path, StreamString &json); void EnrichWithConfig(const char8 *path, StreamString &json);
@@ -85,6 +100,7 @@ private:
uint16 controlPort; uint16 controlPort;
uint16 streamPort; uint16 streamPort;
uint16 logPort;
StreamString streamIP; StreamString streamIP;
bool isServer; bool isServer;
bool suppressTimeoutLogs; bool suppressTimeoutLogs;
@@ -123,6 +139,7 @@ private:
Vec<DebugSignalInfo *> signals; Vec<DebugSignalInfo *> signals;
Vec<SignalAlias> aliases; Vec<SignalAlias> aliases;
Vec<BrokerInfo> brokers; Vec<BrokerInfo> brokers;
Vec<MonitoredSignal> monitoredSignals;
FastPollingMutexSem mutex; FastPollingMutexSem mutex;
TraceRingBuffer traceBuffer; TraceRingBuffer traceBuffer;

View File

@@ -33,6 +33,7 @@ include $(MAKEDEFAULTDIR)/MakeStdLibDefs.$(TARGET)
INCLUDES += -I$(ROOT_DIR)/Source/Core/Types/Result INCLUDES += -I$(ROOT_DIR)/Source/Core/Types/Result
INCLUDES += -I$(ROOT_DIR)/Source/Core/Types/Vec INCLUDES += -I$(ROOT_DIR)/Source/Core/Types/Vec
INCLUDES += -I$(ROOT_DIR)/Source/Components/Interfaces/TCPLogger
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L0Types INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L0Types
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L1Portability INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L1Portability
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L2Objects INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L2Objects

View File

@@ -125,14 +125,6 @@
Class = DebugService Class = DebugService
ControlPort = 8080 ControlPort = 8080
UdpPort = 8081 UdpPort = 8081
LogPort = 8082
StreamIP = "127.0.0.1" StreamIP = "127.0.0.1"
} }
+LoggerService = {
Class = LoggerService
CPUs = 0x1
+DebugConsumer = {
Class = TcpLogger
Port = 8082
}
}

View File

@@ -7,8 +7,7 @@
#include "BasicUDPSocket.h" #include "BasicUDPSocket.h"
#include "RealTimeApplication.h" #include "RealTimeApplication.h"
#include "StandardParser.h" #include "StandardParser.h"
#include "StreamString.h" #include "TestCommon.h"
#include "GlobalObjectsDatabase.h"
#include <stdio.h> #include <stdio.h>
using namespace MARTe; using namespace MARTe;
@@ -31,6 +30,7 @@ void TestFullTracePipeline();
void RunValidationTest(); void RunValidationTest();
void TestConfigCommands(); void TestConfigCommands();
void TestGAMSignalTracing(); void TestGAMSignalTracing();
void TestTreeCommand();
int main() { int main() {
signal(SIGALRM, timeout_handler); signal(SIGALRM, timeout_handler);
@@ -83,9 +83,9 @@ int main() {
// TestConfigCommands(); // Skipping for now // TestConfigCommands(); // Skipping for now
Sleep::MSec(1000); Sleep::MSec(1000);
// printf("\n--- Test 6: GAM Signal Tracing ---\n"); printf("\n--- Test 6: TREE Command Enhancement ---\n");
// TestGAMSignalTracing(); TestTreeCommand();
// Sleep::MSec(1000); Sleep::MSec(1000);
printf("\nAll Integration Tests Finished.\n"); printf("\nAll Integration Tests Finished.\n");
@@ -94,72 +94,6 @@ int main() {
// --- Test Implementation --- // --- Test Implementation ---
const char8 * const debug_test_config =
"DebugService = {"
" Class = DebugService "
" ControlPort = 8095 "
" UdpPort = 8096 "
" StreamIP = \"127.0.0.1\" "
"}"
"App = {"
" Class = RealTimeApplication "
" +Functions = {"
" Class = ReferenceContainer "
" +GAM1 = {"
" Class = IOGAM "
" InputSignals = {"
" Counter = { DataSource = Timer Type = uint32 Frequency = 1000 }"
" }"
" OutputSignals = {"
" Counter = { DataSource = DDB Type = uint32 }"
" }"
" }"
" +GAM2 = {"
" Class = IOGAM "
" InputSignals = {"
" Counter = { DataSource = TimerSlow Type = uint32 Frequency = 10 }"
" }"
" OutputSignals = {"
" Counter = { DataSource = Logger Type = uint32 }"
" }"
" }"
" }"
" +Data = {"
" Class = ReferenceContainer "
" DefaultDataSource = DDB "
" +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } } }"
" +TimerSlow = { Class = LinuxTimer SleepTime = 100000 Signals = { Counter = { Type = uint32 } } }"
" +Logger = { Class = LoggerDataSource Signals = { Counter = { Type = uint32 } } }"
" +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } } }"
" +DAMS = { Class = TimingDataSource }"
" }"
" +States = {"
" Class = ReferenceContainer "
" +State1 = { Class = RealTimeState +Threads = { Class = ReferenceContainer +Thread1 = { Class = RealTimeThread Functions = {GAM1 GAM2} } } }"
" }"
" +Scheduler = { Class = GAMScheduler TimingDataSource = DAMS }"
"}";
bool SendCommandGAM(uint16 port, const char8* cmd, StreamString &reply) {
BasicTCPSocket client;
if (!client.Open()) return false;
if (!client.Connect("127.0.0.1", port)) return false;
uint32 s = StringHelper::Length(cmd);
if (!client.Write(cmd, s)) return false;
char buffer[4096];
uint32 size = 4096;
TimeoutType timeout(2000);
if (client.Read(buffer, size, timeout)) {
reply.Write(buffer, size);
client.Close();
return true;
}
client.Close();
return false;
}
void TestGAMSignalTracing() { void TestGAMSignalTracing() {
printf("--- Test: GAM Signal Tracing Issue ---\n"); printf("--- Test: GAM Signal Tracing Issue ---\n");

View File

@@ -1,4 +1,4 @@
OBJSX = SchedulerTest.x TraceTest.x ValidationTest.x ConfigCommandTest.x OBJSX = SchedulerTest.x TraceTest.x ValidationTest.x ConfigCommandTest.x TreeCommandTest.x TestCommon.x
PACKAGE = Test/Integration PACKAGE = Test/Integration
@@ -29,6 +29,8 @@ INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L4LoggerService
INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L5GAMs INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L5GAMs
INCLUDES += -I$(MARTe2_DIR)/Source/Core/FileSystem/L1Portability INCLUDES += -I$(MARTe2_DIR)/Source/Core/FileSystem/L1Portability
INCLUDES += -I$(MARTe2_DIR)/Source/Core/FileSystem/L3Streams INCLUDES += -I$(MARTe2_DIR)/Source/Core/FileSystem/L3Streams
INCLUDES += -I$(MARTe2_Components_DIR)/Source/Components/GAMs/IOGAM
INCLUDES += -I$(MARTe2_Components_DIR)/Source/Components/DataSources/LinuxTimer
LIBRARIES += -L$(MARTe2_DIR)/Build/$(TARGET)/Core -lMARTe2 LIBRARIES += -L$(MARTe2_DIR)/Build/$(TARGET)/Core -lMARTe2
LIBRARIES += -L$(MARTe2_Components_DIR)/Build/$(TARGET)/Components/DataSources/LinuxTimer -lLinuxTimer LIBRARIES += -L$(MARTe2_Components_DIR)/Build/$(TARGET)/Components/DataSources/LinuxTimer -lLinuxTimer

View File

@@ -0,0 +1,74 @@
#include "TestCommon.h"
#include "BasicTCPSocket.h"
#include "StringHelper.h"
#include "TimeoutType.h"
namespace MARTe {
const char8 * const debug_test_config =
"DebugService = {"
" Class = DebugService "
" ControlPort = 8095 "
" UdpPort = 8096 "
" StreamIP = \"127.0.0.1\" "
"}"
"App = {"
" Class = RealTimeApplication "
" +Functions = {"
" Class = ReferenceContainer "
" +GAM1 = {"
" Class = IOGAM "
" InputSignals = {"
" Counter = { DataSource = Timer Type = uint32 Frequency = 1000 }"
" }"
" OutputSignals = {"
" Counter = { DataSource = DDB Type = uint32 }"
" }"
" }"
" +GAM2 = {"
" Class = IOGAM "
" InputSignals = {"
" Counter = { DataSource = TimerSlow Type = uint32 Frequency = 10 }"
" }"
" OutputSignals = {"
" Counter = { DataSource = Logger Type = uint32 }"
" }"
" }"
" }"
" +Data = {"
" Class = ReferenceContainer "
" DefaultDataSource = DDB "
" +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } } }"
" +TimerSlow = { Class = LinuxTimer SleepTime = 100000 Signals = { Counter = { Type = uint32 } } }"
" +Logger = { Class = LoggerDataSource Signals = { Counter = { Type = uint32 } } }"
" +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } } }"
" +DAMS = { Class = TimingDataSource }"
" }"
" +States = {"
" Class = ReferenceContainer "
" +State1 = { Class = RealTimeState +Threads = { Class = ReferenceContainer +Thread1 = { Class = RealTimeThread Functions = {GAM1 GAM2} } } }"
" }"
" +Scheduler = { Class = GAMScheduler TimingDataSource = DAMS }"
"}";
bool SendCommandGAM(uint16 port, const char8* cmd, StreamString &reply) {
BasicTCPSocket client;
if (!client.Open()) return false;
if (!client.Connect("127.0.0.1", port)) return false;
uint32 s = StringHelper::Length(cmd);
if (!client.Write(cmd, s)) return false;
char buffer[16384];
uint32 size = 16384;
TimeoutType timeout(5000);
if (client.Read(buffer, size, timeout)) {
reply.Write(buffer, size);
client.Close();
return true;
}
client.Close();
return false;
}
}

View File

@@ -0,0 +1,12 @@
#ifndef TESTCOMMON_H
#define TESTCOMMON_H
#include "CompilerTypes.h"
#include "StreamString.h"
namespace MARTe {
extern const char8 * const debug_test_config;
bool SendCommandGAM(uint16 port, const char8* cmd, StreamString &reply);
}
#endif

View File

@@ -0,0 +1,176 @@
#include "BasicTCPSocket.h"
#include "ConfigurationDatabase.h"
#include "DebugService.h"
#include "GlobalObjectsDatabase.h"
#include "ObjectRegistryDatabase.h"
#include "RealTimeApplication.h"
#include "StandardParser.h"
#include "StreamString.h"
#include "TestCommon.h"
#include "IOGAM.h"
#include "LinuxTimer.h"
#include "GAMDataSource.h"
#include "TimingDataSource.h"
#include "GAMScheduler.h"
#include <stdio.h>
using namespace MARTe;
void TestTreeCommand() {
printf("--- Test: TREE Command Enhancement ---\n");
ObjectRegistryDatabase::Instance()->Purge();
Sleep::MSec(2000); // Wait for sockets from previous tests to clear
ConfigurationDatabase cdb;
// Use unique ports to avoid conflict with other tests
const char8 * const tree_test_config =
"DebugService = {"
" Class = DebugService "
" ControlPort = 8110 "
" UdpPort = 8111 "
" StreamIP = \"127.0.0.1\" "
"}"
"App = {"
" Class = RealTimeApplication "
" +Functions = {"
" Class = ReferenceContainer "
" +GAM1 = {"
" Class = IOGAM "
" InputSignals = {"
" Counter = { DataSource = Timer Type = uint32 Frequency = 1000 }"
" }"
" OutputSignals = {"
" Counter = { DataSource = DDB Type = uint32 }"
" }"
" }"
" }"
" +Data = {"
" Class = ReferenceContainer "
" DefaultDataSource = DDB "
" +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } } }"
" +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } } }"
" +DAMS = { Class = TimingDataSource }"
" }"
" +States = {"
" Class = ReferenceContainer "
" +State1 = { Class = RealTimeState +Threads = { Class = ReferenceContainer +Thread1 = { Class = RealTimeThread Functions = {GAM1} } } }"
" }"
" +Scheduler = { Class = GAMScheduler TimingDataSource = DAMS }"
"}";
StreamString ss = tree_test_config;
ss.Seek(0);
StandardParser parser(ss, cdb);
if (!parser.Parse()) {
printf("ERROR: Failed to parse config\n");
return;
}
cdb.MoveToRoot();
uint32 n = cdb.GetNumberOfChildren();
for (uint32 i = 0; i < n; i++) {
const char8 *name = cdb.GetChildName(i);
ConfigurationDatabase child;
cdb.MoveRelative(name);
cdb.Copy(child);
cdb.MoveToAncestor(1u);
StreamString className;
child.Read("Class", className);
Reference ref(className.Buffer(),
GlobalObjectsDatabase::Instance()->GetStandardHeap());
if (!ref.IsValid()) {
printf("ERROR: Could not create object %s of class %s\n", name,
className.Buffer());
continue;
}
ref->SetName(name);
if (!ref->Initialise(child)) {
printf("ERROR: Failed to initialise object %s\n", name);
continue;
}
ObjectRegistryDatabase::Instance()->Insert(ref);
}
ReferenceT<DebugService> service =
ObjectRegistryDatabase::Instance()->Find("DebugService");
if (!service.IsValid()) {
printf("ERROR: DebugService not found\n");
return;
}
service->SetFullConfig(cdb);
ReferenceT<RealTimeApplication> app =
ObjectRegistryDatabase::Instance()->Find("App");
if (!app.IsValid()) {
printf("ERROR: App not found\n");
return;
}
if (!app->ConfigureApplication()) {
printf("ERROR: ConfigureApplication failed.\n");
return;
}
if (app->PrepareNextState("State1") != ErrorManagement::NoError) {
printf("ERROR: PrepareNextState failed.\n");
return;
}
if (app->StartNextStateExecution() != ErrorManagement::NoError) {
printf("ERROR: StartNextStateExecution failed.\n");
return;
}
printf("Application started.\n");
Sleep::MSec(1000);
// Step 1: Request TREE
StreamString reply;
if (SendCommandGAM(8110, "TREE\n", reply)) {
printf("TREE response received (len=%llu)\n", reply.Size());
// ...
}
// Step 2: SERVICE_INFO
printf("\n--- Step 2: SERVICE_INFO ---\n");
reply = "";
if (SendCommandGAM(8110, "SERVICE_INFO\n", reply)) {
printf("SERVICE_INFO response: %s", reply.Buffer());
if (StringHelper::SearchString(reply.Buffer(), "TCP_CTRL:8110") != NULL_PTR(const char8 *) &&
StringHelper::SearchString(reply.Buffer(), "UDP_STREAM:8111") != NULL_PTR(const char8 *)) {
printf("SUCCESS: SERVICE_INFO returned correct ports.\n");
} else {
printf("FAILURE: SERVICE_INFO returned incorrect data.\n");
}
}
// Step 3: MONITOR
printf("\n--- Step 3: MONITOR SIGNAL ---\n");
reply = "";
if (SendCommandGAM(8110, "MONITOR SIGNAL App.Data.Timer.Counter 10\n", reply)) {
printf("MONITOR response: %s", reply.Buffer());
if (StringHelper::SearchString(reply.Buffer(), "OK MONITOR 1") != NULL_PTR(const char8 *)) {
printf("SUCCESS: Signal monitored.\n");
} else {
printf("FAILURE: Could not monitor signal.\n");
}
}
// Step 4: UNMONITOR
printf("\n--- Step 4: UNMONITOR SIGNAL ---\n");
reply = "";
if (SendCommandGAM(8110, "UNMONITOR SIGNAL App.Data.Timer.Counter\n", reply)) {
printf("UNMONITOR response: %s", reply.Buffer());
if (StringHelper::SearchString(reply.Buffer(), "OK UNMONITOR 1") != NULL_PTR(const char8 *)) {
printf("SUCCESS: Signal unmonitored.\n");
} else {
printf("FAILURE: Could not unmonitor signal.\n");
}
}
app->StopCurrentStateExecution();
ObjectRegistryDatabase::Instance()->Purge();
}

View File

@@ -51,6 +51,10 @@ struct TreeItem {
dimensions: Option<u8>, dimensions: Option<u8>,
#[serde(rename = "Elements")] #[serde(rename = "Elements")]
elements: Option<u32>, elements: Option<u32>,
#[serde(rename = "IsTraceable")]
is_traceable: Option<bool>,
#[serde(rename = "IsForcable")]
is_forcable: Option<bool>,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -65,6 +69,7 @@ struct TraceData {
last_value: f64, last_value: f64,
recording_tx: Option<Sender<[f64; 2]>>, recording_tx: Option<Sender<[f64; 2]>>,
recording_path: Option<String>, recording_path: Option<String>,
is_monitored: bool,
} }
struct SignalMetadata { struct SignalMetadata {
@@ -151,13 +156,14 @@ enum InternalEvent {
Connected, Connected,
Disconnected, Disconnected,
InternalLog(String), InternalLog(String),
TraceRequested(String), TraceRequested(String, bool), // Name, IsMonitored
ClearTrace(String), ClearTrace(String),
UdpStats(u64), UdpStats(u64),
UdpDropped(u32), UdpDropped(u32),
RecordPathChosen(String, String), // SignalName, FilePath RecordPathChosen(String, String), // SignalName, FilePath
RecordingError(String, String), // SignalName, ErrorMessage RecordingError(String, String), // SignalName, ErrorMessage
TelemMatched(u32), // Signal ID TelemMatched(u32), // Signal ID
ServiceConfig { udp_port: String, log_port: String },
} }
// --- App State --- // --- App State ---
@@ -167,6 +173,11 @@ struct ForcingDialog {
value: String, value: String,
} }
struct MonitorDialog {
signal_path: String,
period_ms: String,
}
struct LogFilters { struct LogFilters {
show_debug: bool, show_debug: bool,
show_info: bool, show_info: bool,
@@ -212,6 +223,7 @@ struct MarteDebugApp {
udp_dropped: u64, udp_dropped: u64,
telem_match_count: HashMap<u32, u64>, telem_match_count: HashMap<u32, u64>,
forcing_dialog: Option<ForcingDialog>, forcing_dialog: Option<ForcingDialog>,
monitoring_dialog: Option<MonitorDialog>,
style_editor: Option<(usize, usize)>, style_editor: Option<(usize, usize)>,
tx_cmd: Sender<String>, tx_cmd: Sender<String>,
rx_events: Receiver<InternalEvent>, rx_events: Receiver<InternalEvent>,
@@ -291,6 +303,7 @@ impl MarteDebugApp {
udp_dropped: 0, udp_dropped: 0,
telem_match_count: HashMap::new(), telem_match_count: HashMap::new(),
forcing_dialog: None, forcing_dialog: None,
monitoring_dialog: None,
style_editor: None, style_editor: None,
tx_cmd, tx_cmd,
rx_events, rx_events,
@@ -430,13 +443,24 @@ impl MarteDebugApp {
let _ = self.tx_cmd.send(format!("INFO {}", current_path)); let _ = self.tx_cmd.send(format!("INFO {}", current_path));
} }
if item.class.contains("Signal") { if item.class.contains("Signal") {
if ui.button("Trace").clicked() { let traceable = item.is_traceable.unwrap_or(false);
let forcable = item.is_forcable.unwrap_or(false);
if traceable && ui.button("Trace").clicked() {
let _ = self.tx_cmd.send(format!("TRACE {} 1", current_path)); let _ = self.tx_cmd.send(format!("TRACE {} 1", current_path));
let _ = self let _ = self
.internal_tx .internal_tx
.send(InternalEvent::TraceRequested(current_path.clone())); .send(InternalEvent::TraceRequested(current_path.clone(), false));
} }
if ui.button("⚡ Force").clicked() { if item.class == "Signal" {
if ui.button("Monitor").clicked() {
self.monitoring_dialog = Some(MonitorDialog {
signal_path: current_path.clone(),
period_ms: "100".to_string(),
});
}
}
if forcable && ui.button("⚡ Force").clicked() {
self.forcing_dialog = Some(ForcingDialog { self.forcing_dialog = Some(ForcingDialog {
signal_path: current_path.clone(), signal_path: current_path.clone(),
value: "".to_string(), value: "".to_string(),
@@ -535,6 +559,26 @@ fn tcp_command_worker(
json_acc.clear(); json_acc.clear();
} }
} else { } else {
if trimmed.starts_with("OK SERVICE_INFO") {
// OK SERVICE_INFO TCP_CTRL:8110 UDP_STREAM:8111 TCP_LOG:8082 STATE:RUNNING
let parts: Vec<&str> = trimmed.split_whitespace().collect();
let mut udp = String::new();
let mut log = String::new();
for p in parts {
if p.starts_with("UDP_STREAM:") {
udp = p.split(':').nth(1).unwrap_or("").to_string();
}
if p.starts_with("TCP_LOG:") {
log = p.split(':').nth(1).unwrap_or("").to_string();
}
}
if !udp.is_empty() || !log.is_empty() {
let _ = tx_events_inner.send(InternalEvent::ServiceConfig {
udp_port: udp,
log_port: log,
});
}
}
let _ = tx_events_inner let _ = tx_events_inner
.send(InternalEvent::CommandResponse(trimmed.to_string())); .send(InternalEvent::CommandResponse(trimmed.to_string()));
} }
@@ -906,14 +950,16 @@ impl eframe::App for MarteDebugApp {
InternalEvent::NodeInfo(info) => { InternalEvent::NodeInfo(info) => {
self.node_info = info; self.node_info = info;
} }
InternalEvent::TraceRequested(name) => { InternalEvent::TraceRequested(name, is_monitored) => {
let mut data_map = self.traced_signals.lock().unwrap(); let mut data_map = self.traced_signals.lock().unwrap();
data_map.entry(name.clone()).or_insert_with(|| TraceData { let entry = data_map.entry(name.clone()).or_insert_with(|| TraceData {
values: VecDeque::with_capacity(10000), values: VecDeque::with_capacity(10000),
last_value: 0.0, last_value: 0.0,
recording_tx: None, recording_tx: None,
recording_path: None, recording_path: None,
is_monitored,
}); });
entry.is_monitored = is_monitored;
self.logs.push_back(LogEntry { self.logs.push_back(LogEntry {
time: Local::now().format("%H:%M:%S").to_string(), time: Local::now().format("%H:%M:%S").to_string(),
level: "GUI_INFO".to_string(), level: "GUI_INFO".to_string(),
@@ -937,11 +983,33 @@ impl eframe::App for MarteDebugApp {
self.connected = true; self.connected = true;
// Wait for connection to stabilize before sending commands // Wait for connection to stabilize before sending commands
std::thread::sleep(std::time::Duration::from_millis(200)); std::thread::sleep(std::time::Duration::from_millis(200));
let _ = self.tx_cmd.send("SERVICE_INFO".to_string());
std::thread::sleep(std::time::Duration::from_millis(100));
let _ = self.tx_cmd.send("TREE".to_string()); let _ = self.tx_cmd.send("TREE".to_string());
// Wait for TREE response before sending next command // Wait for TREE response before sending next command
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = self.tx_cmd.send("DISCOVER".to_string()); let _ = self.tx_cmd.send("DISCOVER".to_string());
} }
InternalEvent::ServiceConfig { udp_port, log_port } => {
let mut changed = false;
if !udp_port.is_empty() && self.config.udp_port != udp_port {
self.config.udp_port = udp_port;
changed = true;
}
if !log_port.is_empty() && self.config.log_port != log_port {
self.config.log_port = log_port;
changed = true;
}
if changed {
self.config.version += 1;
*self.shared_config.lock().unwrap() = self.config.clone();
self.logs.push_back(LogEntry {
time: Local::now().format("%H:%M:%S").to_string(),
level: "GUI_INFO".to_string(),
message: format!("Config updated from server: UDP={}, LOG={}", self.config.udp_port, self.config.log_port),
});
}
}
InternalEvent::Disconnected => { InternalEvent::Disconnected => {
self.connected = false; self.connected = false;
} }
@@ -1025,6 +1093,36 @@ impl eframe::App for MarteDebugApp {
} }
} }
if let Some(dialog) = &mut self.monitoring_dialog {
let mut close = false;
egui::Window::new("Monitor Signal").show(ctx, |ui| {
ui.label(&dialog.signal_path);
ui.horizontal(|ui| {
ui.label("Period (ms):");
ui.text_edit_singleline(&mut dialog.period_ms);
});
ui.horizontal(|ui| {
if ui.button("Apply").clicked() {
let period = dialog.period_ms.parse::<u32>().unwrap_or(100);
let _ = self
.tx_cmd
.send(format!("MONITOR SIGNAL {} {}", dialog.signal_path, period));
let _ = self.tx_cmd.send("DISCOVER".to_string());
let _ = self
.internal_tx
.send(InternalEvent::TraceRequested(dialog.signal_path.clone(), true));
close = true;
}
if ui.button("Cancel").clicked() {
close = true;
}
});
});
if close {
self.monitoring_dialog = None;
}
}
if let Some((p_idx, s_idx)) = self.style_editor { if let Some((p_idx, s_idx)) = self.style_editor {
let mut close = false; let mut close = false;
egui::Window::new("Signal Style").show(ctx, |ui| { egui::Window::new("Signal Style").show(ctx, |ui| {
@@ -1205,11 +1303,11 @@ impl eframe::App for MarteDebugApp {
ui.label("Control:"); ui.label("Control:");
ui.text_edit_singleline(&mut self.config.tcp_port); ui.text_edit_singleline(&mut self.config.tcp_port);
ui.end_row(); ui.end_row();
ui.label("Telemetry:"); ui.label("Telemetry (Auto):");
ui.text_edit_singleline(&mut self.config.udp_port); ui.label(&self.config.udp_port);
ui.end_row(); ui.end_row();
ui.label("Logs:"); ui.label("Logs (Auto):");
ui.text_edit_singleline(&mut self.config.log_port); ui.label(&self.config.log_port);
ui.end_row(); ui.end_row();
}); });
if ui.button("🔄 Apply").clicked() { if ui.button("🔄 Apply").clicked() {
@@ -1319,7 +1417,11 @@ impl eframe::App for MarteDebugApp {
} }
}); });
if ui.button("").clicked() { if ui.button("").clicked() {
if entry.is_monitored {
let _ = self.tx_cmd.send(format!("UNMONITOR SIGNAL {}", key));
} else {
let _ = self.tx_cmd.send(format!("TRACE {} 0", key)); let _ = self.tx_cmd.send(format!("TRACE {} 0", key));
}
let _ = self let _ = self
.internal_tx .internal_tx
.send(InternalEvent::ClearTrace(key.clone())); .send(InternalEvent::ClearTrace(key.clone()));