Compare commits
2 Commits
e6102ba433
...
scope
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3077e78ec | ||
|
|
a941563749 |
@@ -109,10 +109,15 @@ public:
|
||||
fprintf(stderr, ">> registering %s.%s [%p]\n", dsPath.Buffer(),
|
||||
signalName.Buffer(), mmb);
|
||||
|
||||
uint8 dims = 0;
|
||||
uint32 elems = 1;
|
||||
(void)dataSourceIn.GetSignalNumberOfDimensions(dsIdx, dims);
|
||||
(void)dataSourceIn.GetSignalNumberOfElements(dsIdx, elems);
|
||||
|
||||
// Register canonical name
|
||||
StreamString dsFullName;
|
||||
dsFullName.Printf("%s.%s", dsPath.Buffer(), signalName.Buffer());
|
||||
service->RegisterSignal(addr, type, dsFullName.Buffer());
|
||||
service->RegisterSignal(addr, type, dsFullName.Buffer(), dims, elems);
|
||||
|
||||
// Register alias
|
||||
if (functionName != NULL_PTR(const char8 *)) {
|
||||
@@ -140,29 +145,21 @@ public:
|
||||
if (gamRef.IsValid()) {
|
||||
StreamString absGamPath;
|
||||
DebugService::GetFullObjectName(*(gamRef.operator->()), absGamPath);
|
||||
// Register full path (InputSignals/OutputSignals)
|
||||
// gamFullName.fPrintf(stderr, "%s.%s.%s", absGamPath.Buffer(),
|
||||
// dirStr, signalName.Buffer()); signalInfoPointers[i] =
|
||||
// service->RegisterSignal(addr, type, gamFullName.Buffer()); Also
|
||||
// register short path (In/Out) for GUI compatibility
|
||||
// Register short path (In/Out) for GUI compatibility
|
||||
gamFullName.Printf("%s.%s.%s", absGamPath.Buffer(), dirStrShort,
|
||||
signalName.Buffer());
|
||||
signalInfoPointers[i] =
|
||||
service->RegisterSignal(addr, type, gamFullName.Buffer());
|
||||
service->RegisterSignal(addr, type, gamFullName.Buffer(), dims, elems);
|
||||
} else {
|
||||
// Fallback to short name
|
||||
// gamFullName.fPrintf(stderr, "%s.%s.%s", functionName, dirStr,
|
||||
// signalName.Buffer()); signalInfoPointers[i] =
|
||||
// service->RegisterSignal(addr, type, gamFullName.Buffer()); Also
|
||||
// register short form
|
||||
// Fallback to short form
|
||||
gamFullName.Printf("%s.%s.%s", functionName, dirStrShort,
|
||||
signalName.Buffer());
|
||||
signalInfoPointers[i] =
|
||||
service->RegisterSignal(addr, type, gamFullName.Buffer());
|
||||
service->RegisterSignal(addr, type, gamFullName.Buffer(), dims, elems);
|
||||
}
|
||||
} else {
|
||||
signalInfoPointers[i] =
|
||||
service->RegisterSignal(addr, type, dsFullName.Buffer());
|
||||
service->RegisterSignal(addr, type, dsFullName.Buffer(), dims, elems);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ struct DebugSignalInfo {
|
||||
void* memoryAddress;
|
||||
TypeDescriptor type;
|
||||
StreamString name;
|
||||
uint8 numberOfDimensions;
|
||||
uint32 numberOfElements;
|
||||
volatile bool isTracing;
|
||||
volatile bool isForcing;
|
||||
uint8 forcedValue[1024];
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
#include "BasicTCPSocket.h"
|
||||
#include "ClassRegistryItem.h"
|
||||
#include "ConfigurationDatabase.h"
|
||||
#include "DataSourceI.h"
|
||||
#include "DebugBrokerWrapper.h"
|
||||
#include "DebugService.h"
|
||||
#include "GAM.h"
|
||||
#include "GlobalObjectsDatabase.h"
|
||||
#include "HighResolutionTimer.h"
|
||||
#include "ObjectBuilder.h"
|
||||
#include "ObjectRegistryDatabase.h"
|
||||
#include "StreamString.h"
|
||||
#include "TimeoutType.h"
|
||||
#include "TypeConversion.h"
|
||||
#include "ReferenceT.h"
|
||||
|
||||
namespace MARTe {
|
||||
|
||||
@@ -84,6 +87,7 @@ DebugService::DebugService()
|
||||
threadService(binderServer), streamerService(binderStreamer) {
|
||||
controlPort = 0;
|
||||
streamPort = 8081;
|
||||
logPort = 8082;
|
||||
streamIP = "127.0.0.1";
|
||||
isServer = false;
|
||||
suppressTimeoutLogs = true;
|
||||
@@ -106,6 +110,7 @@ DebugService::~DebugService() {
|
||||
for (uint32 i = 0; i < signals.Size(); i++) {
|
||||
delete signals[i];
|
||||
}
|
||||
this->Purge();
|
||||
}
|
||||
|
||||
bool DebugService::Initialise(StructuredDataI &data) {
|
||||
@@ -132,6 +137,15 @@ bool DebugService::Initialise(StructuredDataI &data) {
|
||||
(void)data.Read("UdpPort", 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;
|
||||
if (data.Read("StreamIP", tempIP)) {
|
||||
streamIP = tempIP;
|
||||
@@ -175,6 +189,34 @@ bool DebugService::Initialise(StructuredDataI &data) {
|
||||
return false;
|
||||
if (streamerService.Start() != ErrorManagement::NoError)
|
||||
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;
|
||||
}
|
||||
@@ -230,7 +272,9 @@ void DebugService::PatchRegistry() {
|
||||
|
||||
DebugSignalInfo *DebugService::RegisterSignal(void *memoryAddress,
|
||||
TypeDescriptor type,
|
||||
const char8 *name) {
|
||||
const char8 *name,
|
||||
uint8 numberOfDimensions,
|
||||
uint32 numberOfElements) {
|
||||
printf("<debug> registering: %s\n", name);
|
||||
mutex.FastLock();
|
||||
DebugSignalInfo *res = NULL_PTR(DebugSignalInfo *);
|
||||
@@ -248,6 +292,8 @@ DebugSignalInfo *DebugService::RegisterSignal(void *memoryAddress,
|
||||
res->memoryAddress = memoryAddress;
|
||||
res->type = type;
|
||||
res->name = name;
|
||||
res->numberOfDimensions = numberOfDimensions;
|
||||
res->numberOfElements = numberOfElements;
|
||||
res->isTracing = false;
|
||||
res->isForcing = false;
|
||||
res->internalID = sigIdx;
|
||||
@@ -430,6 +476,24 @@ ErrorManagement::ErrorType DebugService::Streamer(ExecutionInfo &info) {
|
||||
uint32 packetOffset = 0;
|
||||
uint32 sequenceNumber = 0;
|
||||
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;
|
||||
uint64 ts;
|
||||
uint8 sampleData[1024];
|
||||
@@ -536,7 +600,47 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
|
||||
}
|
||||
} else if (token == "DISCOVER")
|
||||
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);
|
||||
else if (token == "PAUSE") {
|
||||
SetPaused(true);
|
||||
@@ -554,7 +658,7 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
|
||||
StreamString json;
|
||||
json = "{\"Name\": \"Root\", \"Class\": \"ObjectRegistryDatabase\", "
|
||||
"\"Children\": [\n";
|
||||
(void)ExportTree(ObjectRegistryDatabase::Instance(), json);
|
||||
(void)ExportTree(ObjectRegistryDatabase::Instance(), json, NULL_PTR(const char8 *));
|
||||
json += "\n]}\nOK TREE\n";
|
||||
uint32 s = json.Size();
|
||||
if (client)
|
||||
@@ -747,7 +851,7 @@ void DebugService::InfoNode(const char8 *path, BasicTCPSocket *client) {
|
||||
}
|
||||
|
||||
uint32 DebugService::ExportTree(ReferenceContainer *container,
|
||||
StreamString &json) {
|
||||
StreamString &json, const char8 *pathPrefix) {
|
||||
if (container == NULL_PTR(ReferenceContainer *))
|
||||
return 0;
|
||||
uint32 size = container->Size();
|
||||
@@ -761,6 +865,14 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
|
||||
const char8 *cname = child->GetName();
|
||||
if (cname == NULL_PTR(const char8 *))
|
||||
cname = "unnamed";
|
||||
|
||||
StreamString currentPath;
|
||||
if (pathPrefix != NULL_PTR(const char8 *)) {
|
||||
currentPath.Printf("%s.%s", pathPrefix, cname);
|
||||
} else {
|
||||
currentPath = cname;
|
||||
}
|
||||
|
||||
nodeJson += "{\"Name\": \"";
|
||||
EscapeJson(cname, nodeJson);
|
||||
nodeJson += "\", \"Class\": \"";
|
||||
@@ -775,7 +887,7 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
|
||||
nodeJson += ", \"Children\": [\n";
|
||||
uint32 subCount = 0u;
|
||||
if (inner != NULL_PTR(ReferenceContainer *))
|
||||
subCount += ExportTree(inner, nodeJson);
|
||||
subCount += ExportTree(inner, nodeJson, currentPath.Buffer());
|
||||
if (ds != NULL_PTR(DataSourceI *)) {
|
||||
uint32 nSignals = ds->GetNumberOfSignals();
|
||||
for (uint32 j = 0u; j < nSignals; j++) {
|
||||
@@ -790,12 +902,22 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
|
||||
(void)ds->GetSignalNumberOfDimensions(j, dims);
|
||||
uint32 elems = 0u;
|
||||
(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\": \"";
|
||||
EscapeJson(sname.Buffer(), nodeJson);
|
||||
nodeJson += "\", \"Class\": \"Signal\", \"Type\": \"";
|
||||
EscapeJson(stype ? stype : "Unknown", nodeJson);
|
||||
nodeJson.Printf("\", \"Dimensions\": %d, \"Elements\": %u}", dims,
|
||||
nodeJson.Printf("\", \"Dimensions\": %d, \"Elements\": %u", dims,
|
||||
elems);
|
||||
nodeJson.Printf(", \"IsTraceable\": %s, \"IsForcable\": %s}",
|
||||
traceable ? "true" : "false",
|
||||
forcable ? "true" : "false");
|
||||
}
|
||||
}
|
||||
if (gam != NULL_PTR(GAM *)) {
|
||||
@@ -812,12 +934,23 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
|
||||
(void)gam->GetSignalNumberOfDimensions(InputSignals, j, dims);
|
||||
uint32 elems = 0u;
|
||||
(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.";
|
||||
EscapeJson(sname.Buffer(), nodeJson);
|
||||
nodeJson += "\", \"Class\": \"InputSignal\", \"Type\": \"";
|
||||
EscapeJson(stype ? stype : "Unknown", nodeJson);
|
||||
nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u}", dims,
|
||||
nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u", dims,
|
||||
elems);
|
||||
nodeJson.Printf(", \"IsTraceable\": %s, \"IsForcable\": %s}",
|
||||
traceable ? "true" : "false",
|
||||
forcable ? "true" : "false");
|
||||
}
|
||||
uint32 nOut = gam->GetNumberOfOutputSignals();
|
||||
for (uint32 j = 0u; j < nOut; j++) {
|
||||
@@ -832,12 +965,23 @@ uint32 DebugService::ExportTree(ReferenceContainer *container,
|
||||
(void)gam->GetSignalNumberOfDimensions(OutputSignals, j, dims);
|
||||
uint32 elems = 0u;
|
||||
(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.";
|
||||
EscapeJson(sname.Buffer(), nodeJson);
|
||||
nodeJson += "\", \"Class\": \"OutputSignal\", \"Type\": \"";
|
||||
EscapeJson(stype ? stype : "Unknown", nodeJson);
|
||||
nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u}", dims,
|
||||
nodeJson.Printf("\", \"Dimensions\": %u, \"Elements\": %u", dims,
|
||||
elems);
|
||||
nodeJson.Printf(", \"IsTraceable\": %s, \"IsForcable\": %s}",
|
||||
traceable ? "true" : "false",
|
||||
forcable ? "true" : "false");
|
||||
}
|
||||
}
|
||||
nodeJson += "\n]";
|
||||
@@ -908,27 +1052,169 @@ uint32 DebugService::TraceSignal(const char8 *name, bool enable,
|
||||
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) {
|
||||
if (client) {
|
||||
StreamString header = "{\n \"Signals\": [\n";
|
||||
uint32 s = header.Size();
|
||||
(void)client->Write(header.Buffer(), s);
|
||||
mutex.FastLock();
|
||||
uint32 total = 0;
|
||||
for (uint32 i = 0; i < aliases.Size(); i++) {
|
||||
if (total > 0) {
|
||||
uint32 commaSize = 2;
|
||||
(void)client->Write(",\n", commaSize);
|
||||
}
|
||||
StreamString line;
|
||||
DebugSignalInfo *sig = signals[aliases[i].signalIndex];
|
||||
const char8 *typeName =
|
||||
TypeDescriptor::GetTypeNameFromTypeDescriptor(sig->type);
|
||||
line.Printf(" {\"name\": \"%s\", \"id\": %d, \"type\": \"%s\"",
|
||||
line.Printf(" {\"name\": \"%s\", \"id\": %d, \"type\": \"%s\", \"dimensions\": %u, \"elements\": %u",
|
||||
aliases[i].name.Buffer(), sig->internalID,
|
||||
typeName ? typeName : "Unknown");
|
||||
typeName ? typeName : "Unknown", sig->numberOfDimensions, sig->numberOfElements);
|
||||
EnrichWithConfig(aliases[i].name.Buffer(), line);
|
||||
line += "}";
|
||||
if (i < aliases.Size() - 1)
|
||||
line += ",";
|
||||
line += "\n";
|
||||
s = line.Size();
|
||||
(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));
|
||||
|
||||
uint8 dims = 0;
|
||||
uint32 elems = 1;
|
||||
(void)monitoredSignals[i].dataSource->GetSignalNumberOfDimensions(monitoredSignals[i].signalIdx, dims);
|
||||
(void)monitoredSignals[i].dataSource->GetSignalNumberOfElements(monitoredSignals[i].signalIdx, elems);
|
||||
|
||||
line.Printf(" {\"name\": \"%s\", \"id\": %u, \"type\": \"%s\", \"dimensions\": %u, \"elements\": %u",
|
||||
monitoredSignals[i].path.Buffer(),
|
||||
monitoredSignals[i].internalID,
|
||||
typeName ? typeName : "Unknown", dims, elems);
|
||||
EnrichWithConfig(monitoredSignals[i].path.Buffer(), line);
|
||||
line += "}";
|
||||
s = line.Size();
|
||||
(void)client->Write(line.Buffer(), s);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
mutex.FastUnLock();
|
||||
StreamString footer = " ]\n}\nOK DISCOVER\n";
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
namespace MARTe {
|
||||
|
||||
class MemoryMapBroker;
|
||||
class DataSourceI;
|
||||
|
||||
struct SignalAlias {
|
||||
StreamString name;
|
||||
@@ -45,7 +46,8 @@ public:
|
||||
virtual bool Initialise(StructuredDataI &data);
|
||||
|
||||
DebugSignalInfo *RegisterSignal(void *memoryAddress, TypeDescriptor type,
|
||||
const char8 *name);
|
||||
const char8 *name, uint8 numberOfDimensions = 0,
|
||||
uint32 numberOfElements = 1);
|
||||
void ProcessSignal(DebugSignalInfo *signalInfo, uint32 size,
|
||||
uint64 timestamp);
|
||||
|
||||
@@ -64,17 +66,31 @@ public:
|
||||
uint32 ForceSignal(const char8 *name, const char8 *valueStr);
|
||||
uint32 UnforceSignal(const char8 *name);
|
||||
uint32 TraceSignal(const char8 *name, bool enable, uint32 decimation = 1);
|
||||
bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable);
|
||||
void Discover(BasicTCPSocket *client);
|
||||
void InfoNode(const char8 *path, BasicTCPSocket *client);
|
||||
void ListNodes(const char8 *path, BasicTCPSocket *client);
|
||||
void ServeConfig(BasicTCPSocket *client);
|
||||
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:
|
||||
void HandleCommand(StreamString cmd, BasicTCPSocket *client);
|
||||
void UpdateBrokersActiveStatus();
|
||||
|
||||
uint32 ExportTree(ReferenceContainer *container, StreamString &json);
|
||||
uint32 ExportTree(ReferenceContainer *container, StreamString &json, const char8 *pathPrefix);
|
||||
void PatchRegistry();
|
||||
|
||||
void EnrichWithConfig(const char8 *path, StreamString &json);
|
||||
@@ -85,6 +101,7 @@ private:
|
||||
|
||||
uint16 controlPort;
|
||||
uint16 streamPort;
|
||||
uint16 logPort;
|
||||
StreamString streamIP;
|
||||
bool isServer;
|
||||
bool suppressTimeoutLogs;
|
||||
@@ -123,6 +140,7 @@ private:
|
||||
Vec<DebugSignalInfo *> signals;
|
||||
Vec<SignalAlias> aliases;
|
||||
Vec<BrokerInfo> brokers;
|
||||
Vec<MonitoredSignal> monitoredSignals;
|
||||
|
||||
FastPollingMutexSem mutex;
|
||||
TraceRingBuffer traceBuffer;
|
||||
|
||||
@@ -33,6 +33,7 @@ include $(MAKEDEFAULTDIR)/MakeStdLibDefs.$(TARGET)
|
||||
|
||||
INCLUDES += -I$(ROOT_DIR)/Source/Core/Types/Result
|
||||
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/L1Portability
|
||||
INCLUDES += -I$(MARTe2_DIR)/Source/Core/BareMetal/L2Objects
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
OutputSignals = {
|
||||
Counter = {
|
||||
DataSource = DDB
|
||||
DataSource = SyncDB
|
||||
Type = uint32
|
||||
}
|
||||
Time = {
|
||||
@@ -48,10 +48,38 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
+GAM3 = {
|
||||
Class = IOGAM
|
||||
InputSignals = {
|
||||
Counter = {
|
||||
Frequency = 1
|
||||
Samples = 100
|
||||
Type = uint32
|
||||
DataSource = SyncDB
|
||||
}
|
||||
}
|
||||
OutputSignals = {
|
||||
Counter = {
|
||||
DataSource = DDB3
|
||||
NumberOfElements = 100
|
||||
Type = uint32
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+Data = {
|
||||
Class = ReferenceContainer
|
||||
DefaultDataSource = DDB
|
||||
+SyncDB = {
|
||||
Class = RealTimeThreadSynchronisation
|
||||
Timeout = 200
|
||||
Signals = {
|
||||
Counter = {
|
||||
Type = uint32
|
||||
}
|
||||
}
|
||||
}
|
||||
+Timer = {
|
||||
Class = LinuxTimer
|
||||
Signals = {
|
||||
@@ -94,6 +122,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
+DDB3 = {
|
||||
AllowNoProducer = 1
|
||||
Class = GAMDataSource
|
||||
}
|
||||
+DAMS = {
|
||||
Class = TimingDataSource
|
||||
}
|
||||
@@ -112,6 +144,10 @@
|
||||
Class = RealTimeThread
|
||||
Functions = {GAM2}
|
||||
}
|
||||
+Thread3 = {
|
||||
Class = RealTimeThread
|
||||
Functions = {GAM3}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,14 +161,6 @@
|
||||
Class = DebugService
|
||||
ControlPort = 8080
|
||||
UdpPort = 8081
|
||||
LogPort = 8082
|
||||
StreamIP = "127.0.0.1"
|
||||
}
|
||||
|
||||
+LoggerService = {
|
||||
Class = LoggerService
|
||||
CPUs = 0x1
|
||||
+DebugConsumer = {
|
||||
Class = TcpLogger
|
||||
Port = 8082
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include "BasicUDPSocket.h"
|
||||
#include "RealTimeApplication.h"
|
||||
#include "StandardParser.h"
|
||||
#include "StreamString.h"
|
||||
#include "GlobalObjectsDatabase.h"
|
||||
#include "TestCommon.h"
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace MARTe;
|
||||
@@ -31,6 +30,7 @@ void TestFullTracePipeline();
|
||||
void RunValidationTest();
|
||||
void TestConfigCommands();
|
||||
void TestGAMSignalTracing();
|
||||
void TestTreeCommand();
|
||||
|
||||
int main() {
|
||||
signal(SIGALRM, timeout_handler);
|
||||
@@ -83,9 +83,9 @@ int main() {
|
||||
// TestConfigCommands(); // Skipping for now
|
||||
Sleep::MSec(1000);
|
||||
|
||||
// printf("\n--- Test 6: GAM Signal Tracing ---\n");
|
||||
// TestGAMSignalTracing();
|
||||
// Sleep::MSec(1000);
|
||||
printf("\n--- Test 6: TREE Command Enhancement ---\n");
|
||||
TestTreeCommand();
|
||||
Sleep::MSec(1000);
|
||||
|
||||
printf("\nAll Integration Tests Finished.\n");
|
||||
|
||||
@@ -94,72 +94,6 @@ int main() {
|
||||
|
||||
// --- 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() {
|
||||
printf("--- Test: GAM Signal Tracing Issue ---\n");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/FileSystem/L1Portability
|
||||
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_Components_DIR)/Build/$(TARGET)/Components/DataSources/LinuxTimer -lLinuxTimer
|
||||
|
||||
74
Test/Integration/TestCommon.cpp
Normal file
74
Test/Integration/TestCommon.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
12
Test/Integration/TestCommon.h
Normal file
12
Test/Integration/TestCommon.h
Normal 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
|
||||
@@ -27,7 +27,7 @@ void TestFullTracePipeline() {
|
||||
|
||||
// 2. Register a mock signal
|
||||
uint32 mockValue = 0;
|
||||
DebugSignalInfo* sig = service.RegisterSignal(&mockValue, UnsignedInteger32Bit, "TraceTest.Signal");
|
||||
DebugSignalInfo* sig = service.RegisterSignal(&mockValue, UnsignedInteger32Bit, "TraceTest.Signal", 0, 1);
|
||||
assert(sig != NULL_PTR(DebugSignalInfo*));
|
||||
printf("Signal registered with ID: %u\n", sig->internalID);
|
||||
|
||||
|
||||
176
Test/Integration/TreeCommandTest.cpp
Normal file
176
Test/Integration/TreeCommandTest.cpp
Normal 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();
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
|
||||
// 1. Signal logic
|
||||
uint32 val = 0;
|
||||
service.RegisterSignal(&val, UnsignedInteger32Bit, "X.Y.Z");
|
||||
service.RegisterSignal(&val, UnsignedInteger32Bit, "X.Y.Z", 0, 1);
|
||||
assert(service.TraceSignal("Z", true) == 1);
|
||||
assert(service.ForceSignal("Z", "123") == 1);
|
||||
|
||||
|
||||
@@ -29,8 +29,14 @@ struct Signal {
|
||||
id: u32,
|
||||
#[serde(rename = "type")]
|
||||
sig_type: String,
|
||||
#[serde(default)]
|
||||
dimensions: u8,
|
||||
#[serde(default = "default_elements")]
|
||||
elements: u32,
|
||||
}
|
||||
|
||||
fn default_elements() -> u32 { 1 }
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DiscoverResponse {
|
||||
#[serde(rename = "Signals")]
|
||||
@@ -51,6 +57,10 @@ struct TreeItem {
|
||||
dimensions: Option<u8>,
|
||||
#[serde(rename = "Elements")]
|
||||
elements: Option<u32>,
|
||||
#[serde(rename = "IsTraceable")]
|
||||
is_traceable: Option<bool>,
|
||||
#[serde(rename = "IsForcable")]
|
||||
is_forcable: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -65,11 +75,14 @@ struct TraceData {
|
||||
last_value: f64,
|
||||
recording_tx: Option<Sender<[f64; 2]>>,
|
||||
recording_path: Option<String>,
|
||||
is_monitored: bool,
|
||||
}
|
||||
|
||||
struct SignalMetadata {
|
||||
names: Vec<String>,
|
||||
sig_type: String,
|
||||
dimensions: u8,
|
||||
elements: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -151,13 +164,14 @@ enum InternalEvent {
|
||||
Connected,
|
||||
Disconnected,
|
||||
InternalLog(String),
|
||||
TraceRequested(String),
|
||||
TraceRequested(String, bool), // Name, IsMonitored
|
||||
ClearTrace(String),
|
||||
UdpStats(u64),
|
||||
UdpDropped(u32),
|
||||
RecordPathChosen(String, String), // SignalName, FilePath
|
||||
RecordingError(String, String), // SignalName, ErrorMessage
|
||||
TelemMatched(u32), // Signal ID
|
||||
ServiceConfig { udp_port: String, log_port: String },
|
||||
}
|
||||
|
||||
// --- App State ---
|
||||
@@ -167,6 +181,11 @@ struct ForcingDialog {
|
||||
value: String,
|
||||
}
|
||||
|
||||
struct MonitorDialog {
|
||||
signal_path: String,
|
||||
period_ms: String,
|
||||
}
|
||||
|
||||
struct LogFilters {
|
||||
show_debug: bool,
|
||||
show_info: bool,
|
||||
@@ -212,6 +231,7 @@ struct MarteDebugApp {
|
||||
udp_dropped: u64,
|
||||
telem_match_count: HashMap<u32, u64>,
|
||||
forcing_dialog: Option<ForcingDialog>,
|
||||
monitoring_dialog: Option<MonitorDialog>,
|
||||
style_editor: Option<(usize, usize)>,
|
||||
tx_cmd: Sender<String>,
|
||||
rx_events: Receiver<InternalEvent>,
|
||||
@@ -291,6 +311,7 @@ impl MarteDebugApp {
|
||||
udp_dropped: 0,
|
||||
telem_match_count: HashMap::new(),
|
||||
forcing_dialog: None,
|
||||
monitoring_dialog: None,
|
||||
style_editor: None,
|
||||
tx_cmd,
|
||||
rx_events,
|
||||
@@ -430,19 +451,58 @@ impl MarteDebugApp {
|
||||
let _ = self.tx_cmd.send(format!("INFO {}", current_path));
|
||||
}
|
||||
if item.class.contains("Signal") {
|
||||
let elements = item.elements.unwrap_or(1);
|
||||
if elements > 1 {
|
||||
let header = egui::CollapsingHeader::new(format!("{} [{}] ({} elems)", label, item.class, elements))
|
||||
.id_salt(¤t_path);
|
||||
header.show(ui, |ui| {
|
||||
for i in 0..elements {
|
||||
let elem_path = format!("{}[{}]", current_path, i);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("{}[{}]", item.name, i));
|
||||
if ui.button("Trace").clicked() {
|
||||
let _ = self.tx_cmd.send(format!("TRACE {} 1", current_path));
|
||||
let _ = self.internal_tx.send(InternalEvent::TraceRequested(elem_path.clone(), false));
|
||||
}
|
||||
if item.class == "Signal" {
|
||||
if ui.button("Monitor").clicked() {
|
||||
self.monitoring_dialog = Some(MonitorDialog {
|
||||
signal_path: current_path.clone(),
|
||||
period_ms: "100".to_string(),
|
||||
});
|
||||
// Note: internal monitoring logic will handle individual elements via naming convention
|
||||
let _ = self.internal_tx.send(InternalEvent::TraceRequested(elem_path.clone(), true));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
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
|
||||
.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 {
|
||||
signal_path: current_path.clone(),
|
||||
value: "".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -535,6 +595,26 @@ fn tcp_command_worker(
|
||||
json_acc.clear();
|
||||
}
|
||||
} 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
|
||||
.send(InternalEvent::CommandResponse(trimmed.to_string()));
|
||||
}
|
||||
@@ -797,16 +877,23 @@ fn udp_worker(
|
||||
if let Some(meta) = metas.get(&id) {
|
||||
let _ = tx_events.send(InternalEvent::TelemMatched(id));
|
||||
let t = meta.sig_type.as_str();
|
||||
let val = match size {
|
||||
let type_size = if meta.elements > 0 { size / meta.elements } else { size };
|
||||
|
||||
for i in 0..meta.elements {
|
||||
let elem_offset = (i * type_size) as usize;
|
||||
if elem_offset + type_size as usize > data_slice.len() { break; }
|
||||
let elem_data = &data_slice[elem_offset..elem_offset + type_size as usize];
|
||||
|
||||
let val = match type_size {
|
||||
1 => {
|
||||
if t.contains('u') {
|
||||
data_slice[0] as f64
|
||||
elem_data[0] as f64
|
||||
} else {
|
||||
(data_slice[0] as i8) as f64
|
||||
(elem_data[0] as i8) as f64
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let b = data_slice[0..2].try_into().unwrap();
|
||||
let b = elem_data[0..2].try_into().unwrap();
|
||||
if t.contains('u') {
|
||||
u16::from_le_bytes(b) as f64
|
||||
} else {
|
||||
@@ -814,7 +901,7 @@ fn udp_worker(
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
let b = data_slice[0..4].try_into().unwrap();
|
||||
let b = elem_data[0..4].try_into().unwrap();
|
||||
if t.contains("float") {
|
||||
f32::from_le_bytes(b) as f64
|
||||
} else if t.contains('u') {
|
||||
@@ -824,7 +911,7 @@ fn udp_worker(
|
||||
}
|
||||
}
|
||||
8 => {
|
||||
let b = data_slice[0..8].try_into().unwrap();
|
||||
let b = elem_data[0..8].try_into().unwrap();
|
||||
if t.contains("float") {
|
||||
f64::from_le_bytes(b)
|
||||
} else if t.contains('u') {
|
||||
@@ -835,12 +922,19 @@ fn udp_worker(
|
||||
}
|
||||
_ => 0.0,
|
||||
};
|
||||
|
||||
for name in &meta.names {
|
||||
let target_name = if meta.elements > 1 {
|
||||
format!("{}[{}]", name, i)
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
local_updates
|
||||
.entry(name.clone())
|
||||
.entry(target_name.clone())
|
||||
.or_default()
|
||||
.push([ts_s, val]);
|
||||
last_values.insert(name.clone(), val);
|
||||
last_values.insert(target_name, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += size as usize;
|
||||
@@ -889,6 +983,8 @@ impl eframe::App for MarteDebugApp {
|
||||
let meta = metas.entry(s.id).or_insert_with(|| SignalMetadata {
|
||||
names: Vec::new(),
|
||||
sig_type: s.sig_type.clone(),
|
||||
dimensions: s.dimensions,
|
||||
elements: s.elements,
|
||||
});
|
||||
if !meta.names.contains(&s.name) {
|
||||
meta.names.push(s.name.clone());
|
||||
@@ -906,14 +1002,16 @@ impl eframe::App for MarteDebugApp {
|
||||
InternalEvent::NodeInfo(info) => {
|
||||
self.node_info = info;
|
||||
}
|
||||
InternalEvent::TraceRequested(name) => {
|
||||
InternalEvent::TraceRequested(name, is_monitored) => {
|
||||
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),
|
||||
last_value: 0.0,
|
||||
recording_tx: None,
|
||||
recording_path: None,
|
||||
is_monitored,
|
||||
});
|
||||
entry.is_monitored = is_monitored;
|
||||
self.logs.push_back(LogEntry {
|
||||
time: Local::now().format("%H:%M:%S").to_string(),
|
||||
level: "GUI_INFO".to_string(),
|
||||
@@ -937,11 +1035,33 @@ impl eframe::App for MarteDebugApp {
|
||||
self.connected = true;
|
||||
// Wait for connection to stabilize before sending commands
|
||||
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());
|
||||
// Wait for TREE response before sending next command
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
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 => {
|
||||
self.connected = false;
|
||||
}
|
||||
@@ -1025,6 +1145,63 @@ 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());
|
||||
|
||||
// Check if it's an array signal to add all elements to view
|
||||
let mut elements = 1;
|
||||
if let Some(tree) = &self.app_tree {
|
||||
// Helper to find item in tree
|
||||
fn find_item<'a>(item: &'a TreeItem, target: &str, current: &str) -> Option<&'a TreeItem> {
|
||||
let path = if current.is_empty() { item.name.clone() } else { format!("{}.{}", current, item.name) };
|
||||
if path == target || (current.is_empty() && item.name == "Root" && target.is_empty()) { return Some(item); }
|
||||
if let Some(children) = &item.children {
|
||||
for child in children {
|
||||
if let Some(found) = find_item(child, target, &path) { return Some(found); }
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
if let Some(found) = find_item(tree, &dialog.signal_path, "") {
|
||||
elements = found.elements.unwrap_or(1);
|
||||
}
|
||||
}
|
||||
|
||||
if elements > 1 {
|
||||
for i in 0..elements {
|
||||
let elem_path = format!("{}[{}]", dialog.signal_path, i);
|
||||
let _ = self.internal_tx.send(InternalEvent::TraceRequested(elem_path, true));
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
let mut close = false;
|
||||
egui::Window::new("Signal Style").show(ctx, |ui| {
|
||||
@@ -1205,11 +1382,11 @@ impl eframe::App for MarteDebugApp {
|
||||
ui.label("Control:");
|
||||
ui.text_edit_singleline(&mut self.config.tcp_port);
|
||||
ui.end_row();
|
||||
ui.label("Telemetry:");
|
||||
ui.text_edit_singleline(&mut self.config.udp_port);
|
||||
ui.label("Telemetry (Auto):");
|
||||
ui.label(&self.config.udp_port);
|
||||
ui.end_row();
|
||||
ui.label("Logs:");
|
||||
ui.text_edit_singleline(&mut self.config.log_port);
|
||||
ui.label("Logs (Auto):");
|
||||
ui.label(&self.config.log_port);
|
||||
ui.end_row();
|
||||
});
|
||||
if ui.button("🔄 Apply").clicked() {
|
||||
@@ -1319,7 +1496,11 @@ impl eframe::App for MarteDebugApp {
|
||||
}
|
||||
});
|
||||
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
|
||||
.internal_tx
|
||||
.send(InternalEvent::ClearTrace(key.clone()));
|
||||
|
||||
Reference in New Issue
Block a user