Improved overall features
This commit is contained in:
@@ -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";
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
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
|
||||||
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();
|
||||||
|
}
|
||||||
@@ -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() {
|
||||||
let _ = self.tx_cmd.send(format!("TRACE {} 0", key));
|
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
|
let _ = self
|
||||||
.internal_tx
|
.internal_tx
|
||||||
.send(InternalEvent::ClearTrace(key.clone()));
|
.send(InternalEvent::ClearTrace(key.clone()));
|
||||||
|
|||||||
Reference in New Issue
Block a user