Compare commits
3 Commits
experimental
...
scope
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a1ecd3aba | |||
| 96d98dfc3d | |||
| b86ede99b9 |
@@ -9,6 +9,7 @@
|
|||||||
#include "MemoryMapBroker.h"
|
#include "MemoryMapBroker.h"
|
||||||
#include "ObjectBuilder.h"
|
#include "ObjectBuilder.h"
|
||||||
#include "ObjectRegistryDatabase.h"
|
#include "ObjectRegistryDatabase.h"
|
||||||
|
#include "Threads.h"
|
||||||
#include "Vec.h"
|
#include "Vec.h"
|
||||||
|
|
||||||
// Original broker headers
|
// Original broker headers
|
||||||
@@ -26,22 +27,91 @@
|
|||||||
|
|
||||||
namespace MARTe {
|
namespace MARTe {
|
||||||
|
|
||||||
|
// Recursive search for any object with a given name anywhere in the registry.
|
||||||
|
// Used to find GAMs regardless of nesting level.
|
||||||
|
static Reference FindByNameRecursive(ReferenceContainer *container,
|
||||||
|
const char8 *name) {
|
||||||
|
if (container == NULL_PTR(ReferenceContainer *))
|
||||||
|
return Reference();
|
||||||
|
uint32 n = container->Size();
|
||||||
|
for (uint32 i = 0; i < n; i++) {
|
||||||
|
Reference child = container->Get(i);
|
||||||
|
if (!child.IsValid())
|
||||||
|
continue;
|
||||||
|
if (StringHelper::Compare(child->GetName(), name) == 0)
|
||||||
|
return child;
|
||||||
|
ReferenceContainer *sub =
|
||||||
|
dynamic_cast<ReferenceContainer *>(child.operator->());
|
||||||
|
if (sub != NULL_PTR(ReferenceContainer *)) {
|
||||||
|
Reference found = FindByNameRecursive(sub, name);
|
||||||
|
if (found.IsValid())
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Reference();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Helper for optimized signal processing within brokers.
|
* @brief Helper for optimized signal processing within brokers.
|
||||||
*/
|
*/
|
||||||
class DebugBrokerHelper {
|
class DebugBrokerHelper {
|
||||||
public:
|
public:
|
||||||
|
// Evaluate a break condition against the current live value of a signal.
|
||||||
|
// Returns true if the condition is met and execution should be paused.
|
||||||
|
// Only called when signal->breakOp != BREAK_OFF; reads memoryAddress directly.
|
||||||
|
static bool EvaluateBreak(const DebugSignalInfo *s) {
|
||||||
|
if (s->memoryAddress == NULL_PTR(void *)) return false;
|
||||||
|
float64 val = 0.0;
|
||||||
|
const TypeDescriptor &t = s->type;
|
||||||
|
if (t == Float64Bit) val = *static_cast<const float64 *>(s->memoryAddress);
|
||||||
|
else if (t == Float32Bit) val = *static_cast<const float32 *>(s->memoryAddress);
|
||||||
|
else if (t == UnsignedInteger32Bit) val = *static_cast<const uint32 *>(s->memoryAddress);
|
||||||
|
else if (t == SignedInteger32Bit) val = *static_cast<const int32 *>(s->memoryAddress);
|
||||||
|
else if (t == UnsignedInteger64Bit) val = static_cast<float64>(*static_cast<const uint64 *>(s->memoryAddress));
|
||||||
|
else if (t == SignedInteger64Bit) val = static_cast<float64>(*static_cast<const int64 *>(s->memoryAddress));
|
||||||
|
else if (t == UnsignedInteger16Bit) val = *static_cast<const uint16 *>(s->memoryAddress);
|
||||||
|
else if (t == SignedInteger16Bit) val = *static_cast<const int16 *>(s->memoryAddress);
|
||||||
|
else if (t == UnsignedInteger8Bit) val = *static_cast<const uint8 *>(s->memoryAddress);
|
||||||
|
else if (t == SignedInteger8Bit) val = *static_cast<const int8 *>(s->memoryAddress);
|
||||||
|
else return false; // unsupported type — skip
|
||||||
|
const float64 thr = s->breakThreshold;
|
||||||
|
switch (s->breakOp) {
|
||||||
|
case BREAK_GT: return val > thr;
|
||||||
|
case BREAK_LT: return val < thr;
|
||||||
|
case BREAK_EQ: return val == thr;
|
||||||
|
case BREAK_GEQ: return val >= thr;
|
||||||
|
case BREAK_LEQ: return val <= thr;
|
||||||
|
case BREAK_NEQ: return val != thr;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spin-wait point for output brokers — called from Execute() AFTER Process().
|
||||||
|
// Spins while paused, then consumes one step counter tick (if stepping).
|
||||||
|
// Input brokers must NOT call this — they complete normally to avoid blocking
|
||||||
|
// cross-thread EventSem posts (RealTimeThreadSynchBroker would time out).
|
||||||
|
static void OutputPauseAndStep(DebugService *service, const char8 *gamName) {
|
||||||
|
if (service == NULL_PTR(DebugService *)) return;
|
||||||
|
// Wait if already paused (manual PAUSE or breakpoint from a previous cycle)
|
||||||
|
while (service->IsPaused()) Sleep::MSec(10);
|
||||||
|
// Pass the OS thread name so per-thread step filtering works.
|
||||||
|
const char8 *tName = Threads::Name(Threads::Id());
|
||||||
|
service->ConsumeStepIfNeeded(gamName, tName);
|
||||||
|
while (service->IsPaused()) Sleep::MSec(10);
|
||||||
|
}
|
||||||
|
|
||||||
static void Process(DebugService *service,
|
static void Process(DebugService *service,
|
||||||
DebugSignalInfo **signalInfoPointers,
|
DebugSignalInfo **signalInfoPointers,
|
||||||
Vec<uint32> &activeIndices, Vec<uint32> &activeSizes,
|
Vec<uint32> &activeIndices, Vec<uint32> &activeSizes,
|
||||||
FastPollingMutexSem &activeMutex) {
|
FastPollingMutexSem &activeMutex,
|
||||||
|
volatile bool *anyBreakFlag,
|
||||||
|
Vec<uint32> *breakIndices) {
|
||||||
if (service == NULL_PTR(DebugService *))
|
if (service == NULL_PTR(DebugService *))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Re-establish break logic
|
// NOTE: No spin here. Spinning for paused state is handled in Execute() of
|
||||||
while (service->IsPaused()) {
|
// OUTPUT brokers only (see OutputPauseAndStep). Input brokers must not block
|
||||||
Sleep::MSec(10);
|
// because that prevents cross-thread EventSem posts from completing.
|
||||||
}
|
|
||||||
|
|
||||||
activeMutex.FastLock();
|
activeMutex.FastLock();
|
||||||
uint32 n = activeIndices.Size();
|
uint32 n = activeIndices.Size();
|
||||||
@@ -59,6 +129,23 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Conditional break check — zero cost when anyBreakFlag is false
|
||||||
|
// (single volatile read; the RT loop never pays for this when no break is set).
|
||||||
|
if (*anyBreakFlag && !service->IsPaused() &&
|
||||||
|
breakIndices != NULL_PTR(Vec<uint32> *)) {
|
||||||
|
uint32 nb = breakIndices->Size();
|
||||||
|
for (uint32 i = 0; i < nb; i++) {
|
||||||
|
uint32 idx = (*breakIndices)[i];
|
||||||
|
DebugSignalInfo *s = signalInfoPointers[idx];
|
||||||
|
if (s != NULL_PTR(DebugSignalInfo *) && s->breakOp != BREAK_OFF &&
|
||||||
|
EvaluateBreak(s)) {
|
||||||
|
service->SetPaused(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
activeMutex.FastUnLock();
|
activeMutex.FastUnLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +156,8 @@ public:
|
|||||||
uint32 numCopies, MemoryMapBrokerCopyTableEntry *copyTable,
|
uint32 numCopies, MemoryMapBrokerCopyTableEntry *copyTable,
|
||||||
const char8 *functionName, SignalDirection direction,
|
const char8 *functionName, SignalDirection direction,
|
||||||
volatile bool *anyActiveFlag, Vec<uint32> *activeIndices,
|
volatile bool *anyActiveFlag, Vec<uint32> *activeIndices,
|
||||||
Vec<uint32> *activeSizes, FastPollingMutexSem *activeMutex) {
|
Vec<uint32> *activeSizes, FastPollingMutexSem *activeMutex,
|
||||||
|
volatile bool *anyBreakFlag, Vec<uint32> *breakIndices) {
|
||||||
if (numCopies > 0) {
|
if (numCopies > 0) {
|
||||||
signalInfoPointers = new DebugSignalInfo *[numCopies];
|
signalInfoPointers = new DebugSignalInfo *[numCopies];
|
||||||
for (uint32 i = 0; i < numCopies; i++)
|
for (uint32 i = 0; i < numCopies; i++)
|
||||||
@@ -126,21 +214,14 @@ public:
|
|||||||
(direction == InputSignals) ? "InputSignals" : "OutputSignals";
|
(direction == InputSignals) ? "InputSignals" : "OutputSignals";
|
||||||
const char8 *dirStrShort = (direction == InputSignals) ? "In" : "Out";
|
const char8 *dirStrShort = (direction == InputSignals) ? "In" : "Out";
|
||||||
|
|
||||||
// Try to find the GAM with different path variations
|
// Search recursively through the entire registry for the GAM by name.
|
||||||
|
// Direct Find("GAM1") only checks top-level; the GAM may be nested
|
||||||
|
// several levels deep inside a RealTimeApplication container.
|
||||||
Reference gamRef =
|
Reference gamRef =
|
||||||
ObjectRegistryDatabase::Instance()->Find(functionName);
|
FindByNameRecursive(ObjectRegistryDatabase::Instance(),
|
||||||
if (!gamRef.IsValid()) {
|
functionName);
|
||||||
// Try with "App.Functions." prefix
|
fprintf(stderr, ">> GAM lookup '%s': %s\n", functionName,
|
||||||
StreamString tryPath;
|
gamRef.IsValid() ? "FOUND" : "NOT FOUND");
|
||||||
tryPath.Printf("App.Functions.%s", functionName);
|
|
||||||
gamRef = ObjectRegistryDatabase::Instance()->Find(tryPath.Buffer());
|
|
||||||
}
|
|
||||||
if (!gamRef.IsValid()) {
|
|
||||||
// Try with "Functions." prefix
|
|
||||||
StreamString tryPath;
|
|
||||||
tryPath.Printf("Functions.%s", functionName);
|
|
||||||
gamRef = ObjectRegistryDatabase::Instance()->Find(tryPath.Buffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gamRef.IsValid()) {
|
if (gamRef.IsValid()) {
|
||||||
StreamString absGamPath;
|
StreamString absGamPath;
|
||||||
@@ -164,8 +245,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register broker in DebugService for optimized control
|
// Register broker in DebugService for optimized control
|
||||||
|
bool isOutputBroker = (direction == OutputSignals);
|
||||||
service->RegisterBroker(signalInfoPointers, numCopies, mmb, anyActiveFlag,
|
service->RegisterBroker(signalInfoPointers, numCopies, mmb, anyActiveFlag,
|
||||||
activeIndices, activeSizes, activeMutex);
|
activeIndices, activeSizes, activeMutex,
|
||||||
|
anyBreakFlag, breakIndices,
|
||||||
|
(functionName != NULL_PTR(const char8 *)) ? functionName : dsPath.Buffer(),
|
||||||
|
isOutputBroker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -180,6 +265,9 @@ public:
|
|||||||
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
||||||
numSignals = 0;
|
numSignals = 0;
|
||||||
anyActive = false;
|
anyActive = false;
|
||||||
|
anyBreakActive = false;
|
||||||
|
isOutput = false;
|
||||||
|
gamName[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~DebugBrokerWrapper() {
|
virtual ~DebugBrokerWrapper() {
|
||||||
@@ -189,9 +277,15 @@ public:
|
|||||||
|
|
||||||
virtual bool Execute() {
|
virtual bool Execute() {
|
||||||
bool ret = BaseClass::Execute();
|
bool ret = BaseClass::Execute();
|
||||||
if (ret && (anyActive || (service && service->IsPaused()))) {
|
if (ret && (anyActive || anyBreakActive)) {
|
||||||
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
||||||
activeSizes, activeMutex);
|
activeSizes, activeMutex,
|
||||||
|
&anyBreakActive, &breakIndices);
|
||||||
|
}
|
||||||
|
// Output brokers are the safe pause point: base Execute has already
|
||||||
|
// committed data / posted any cross-thread EventSems.
|
||||||
|
if (ret && isOutput) {
|
||||||
|
DebugBrokerHelper::OutputPauseAndStep(service, gamName);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -203,10 +297,13 @@ public:
|
|||||||
direction == InputSignals ? "In" : "Out");
|
direction == InputSignals ? "In" : "Out");
|
||||||
if (ret) {
|
if (ret) {
|
||||||
numSignals = this->GetNumberOfCopies();
|
numSignals = this->GetNumberOfCopies();
|
||||||
|
isOutput = (direction == OutputSignals);
|
||||||
|
StringHelper::CopyN(gamName, name, 255u);
|
||||||
DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers,
|
DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers,
|
||||||
numSignals, this->copyTable, name,
|
numSignals, this->copyTable, name,
|
||||||
direction, &anyActive, &activeIndices,
|
direction, &anyActive, &activeIndices,
|
||||||
&activeSizes, &activeMutex);
|
&activeSizes, &activeMutex,
|
||||||
|
&anyBreakActive, &breakIndices);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -218,10 +315,13 @@ public:
|
|||||||
direction == InputSignals ? "In" : "Out");
|
direction == InputSignals ? "In" : "Out");
|
||||||
if (ret) {
|
if (ret) {
|
||||||
numSignals = this->GetNumberOfCopies();
|
numSignals = this->GetNumberOfCopies();
|
||||||
|
isOutput = (direction == OutputSignals);
|
||||||
|
StringHelper::CopyN(gamName, name, 255u);
|
||||||
DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers,
|
DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers,
|
||||||
numSignals, this->copyTable, name,
|
numSignals, this->copyTable, name,
|
||||||
direction, &anyActive, &activeIndices,
|
direction, &anyActive, &activeIndices,
|
||||||
&activeSizes, &activeMutex);
|
&activeSizes, &activeMutex,
|
||||||
|
&anyBreakActive, &breakIndices);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -230,8 +330,12 @@ public:
|
|||||||
DebugSignalInfo **signalInfoPointers;
|
DebugSignalInfo **signalInfoPointers;
|
||||||
uint32 numSignals;
|
uint32 numSignals;
|
||||||
volatile bool anyActive;
|
volatile bool anyActive;
|
||||||
|
volatile bool anyBreakActive;
|
||||||
|
bool isOutput;
|
||||||
|
char8 gamName[256];
|
||||||
Vec<uint32> activeIndices;
|
Vec<uint32> activeIndices;
|
||||||
Vec<uint32> activeSizes;
|
Vec<uint32> activeSizes;
|
||||||
|
Vec<uint32> breakIndices;
|
||||||
FastPollingMutexSem activeMutex;
|
FastPollingMutexSem activeMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -243,6 +347,9 @@ public:
|
|||||||
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
||||||
numSignals = 0;
|
numSignals = 0;
|
||||||
anyActive = false;
|
anyActive = false;
|
||||||
|
anyBreakActive = false;
|
||||||
|
isOutput = false;
|
||||||
|
gamName[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~DebugBrokerWrapperNoOptim() {
|
virtual ~DebugBrokerWrapperNoOptim() {
|
||||||
@@ -252,9 +359,13 @@ public:
|
|||||||
|
|
||||||
virtual bool Execute() {
|
virtual bool Execute() {
|
||||||
bool ret = BaseClass::Execute();
|
bool ret = BaseClass::Execute();
|
||||||
if (ret && (anyActive || (service && service->IsPaused()))) {
|
if (ret && (anyActive || anyBreakActive)) {
|
||||||
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
||||||
activeSizes, activeMutex);
|
activeSizes, activeMutex,
|
||||||
|
&anyBreakActive, &breakIndices);
|
||||||
|
}
|
||||||
|
if (ret && isOutput) {
|
||||||
|
DebugBrokerHelper::OutputPauseAndStep(service, gamName);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -264,10 +375,13 @@ public:
|
|||||||
bool ret = BaseClass::Init(direction, ds, name, gamMem);
|
bool ret = BaseClass::Init(direction, ds, name, gamMem);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
numSignals = this->GetNumberOfCopies();
|
numSignals = this->GetNumberOfCopies();
|
||||||
|
isOutput = (direction == OutputSignals);
|
||||||
|
StringHelper::CopyN(gamName, name, 255u);
|
||||||
DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers,
|
DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers,
|
||||||
numSignals, this->copyTable, name,
|
numSignals, this->copyTable, name,
|
||||||
direction, &anyActive, &activeIndices,
|
direction, &anyActive, &activeIndices,
|
||||||
&activeSizes, &activeMutex);
|
&activeSizes, &activeMutex,
|
||||||
|
&anyBreakActive, &breakIndices);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -276,8 +390,12 @@ public:
|
|||||||
DebugSignalInfo **signalInfoPointers;
|
DebugSignalInfo **signalInfoPointers;
|
||||||
uint32 numSignals;
|
uint32 numSignals;
|
||||||
volatile bool anyActive;
|
volatile bool anyActive;
|
||||||
|
volatile bool anyBreakActive;
|
||||||
|
bool isOutput;
|
||||||
|
char8 gamName[256];
|
||||||
Vec<uint32> activeIndices;
|
Vec<uint32> activeIndices;
|
||||||
Vec<uint32> activeSizes;
|
Vec<uint32> activeSizes;
|
||||||
|
Vec<uint32> breakIndices;
|
||||||
FastPollingMutexSem activeMutex;
|
FastPollingMutexSem activeMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -288,6 +406,8 @@ public:
|
|||||||
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
||||||
numSignals = 0;
|
numSignals = 0;
|
||||||
anyActive = false;
|
anyActive = false;
|
||||||
|
anyBreakActive = false;
|
||||||
|
gamName[0] = '\0';
|
||||||
}
|
}
|
||||||
virtual ~DebugMemoryMapAsyncOutputBroker() {
|
virtual ~DebugMemoryMapAsyncOutputBroker() {
|
||||||
if (signalInfoPointers)
|
if (signalInfoPointers)
|
||||||
@@ -295,9 +415,14 @@ public:
|
|||||||
}
|
}
|
||||||
virtual bool Execute() {
|
virtual bool Execute() {
|
||||||
bool ret = MemoryMapAsyncOutputBroker::Execute();
|
bool ret = MemoryMapAsyncOutputBroker::Execute();
|
||||||
if (ret && (anyActive || (service && service->IsPaused()))) {
|
if (ret && (anyActive || anyBreakActive)) {
|
||||||
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
||||||
activeSizes, activeMutex);
|
activeSizes, activeMutex,
|
||||||
|
&anyBreakActive, &breakIndices);
|
||||||
|
}
|
||||||
|
// Async output brokers are always output direction
|
||||||
|
if (ret) {
|
||||||
|
DebugBrokerHelper::OutputPauseAndStep(service, gamName);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -313,10 +438,11 @@ public:
|
|||||||
numberOfBuffersIn, cpuMaskIn, stackSizeIn);
|
numberOfBuffersIn, cpuMaskIn, stackSizeIn);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
numSignals = this->GetNumberOfCopies();
|
numSignals = this->GetNumberOfCopies();
|
||||||
|
StringHelper::CopyN(gamName, (functionName != NULL_PTR(const char8 *)) ? functionName : "", 255u);
|
||||||
DebugBrokerHelper::InitSignals(
|
DebugBrokerHelper::InitSignals(
|
||||||
this, dataSourceIn, service, signalInfoPointers, numSignals,
|
this, dataSourceIn, service, signalInfoPointers, numSignals,
|
||||||
this->copyTable, functionName, direction, &anyActive, &activeIndices,
|
this->copyTable, functionName, direction, &anyActive, &activeIndices,
|
||||||
&activeSizes, &activeMutex);
|
&activeSizes, &activeMutex, &anyBreakActive, &breakIndices);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -324,8 +450,11 @@ public:
|
|||||||
DebugSignalInfo **signalInfoPointers;
|
DebugSignalInfo **signalInfoPointers;
|
||||||
uint32 numSignals;
|
uint32 numSignals;
|
||||||
volatile bool anyActive;
|
volatile bool anyActive;
|
||||||
|
volatile bool anyBreakActive;
|
||||||
|
char8 gamName[256];
|
||||||
Vec<uint32> activeIndices;
|
Vec<uint32> activeIndices;
|
||||||
Vec<uint32> activeSizes;
|
Vec<uint32> activeSizes;
|
||||||
|
Vec<uint32> breakIndices;
|
||||||
FastPollingMutexSem activeMutex;
|
FastPollingMutexSem activeMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -338,6 +467,8 @@ public:
|
|||||||
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
|
||||||
numSignals = 0;
|
numSignals = 0;
|
||||||
anyActive = false;
|
anyActive = false;
|
||||||
|
anyBreakActive = false;
|
||||||
|
gamName[0] = '\0';
|
||||||
}
|
}
|
||||||
virtual ~DebugMemoryMapAsyncTriggerOutputBroker() {
|
virtual ~DebugMemoryMapAsyncTriggerOutputBroker() {
|
||||||
if (signalInfoPointers)
|
if (signalInfoPointers)
|
||||||
@@ -345,9 +476,13 @@ public:
|
|||||||
}
|
}
|
||||||
virtual bool Execute() {
|
virtual bool Execute() {
|
||||||
bool ret = MemoryMapAsyncTriggerOutputBroker::Execute();
|
bool ret = MemoryMapAsyncTriggerOutputBroker::Execute();
|
||||||
if (ret && (anyActive || (service && service->IsPaused()))) {
|
if (ret && (anyActive || anyBreakActive)) {
|
||||||
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
DebugBrokerHelper::Process(service, signalInfoPointers, activeIndices,
|
||||||
activeSizes, activeMutex);
|
activeSizes, activeMutex,
|
||||||
|
&anyBreakActive, &breakIndices);
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
DebugBrokerHelper::OutputPauseAndStep(service, gamName);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -363,10 +498,11 @@ public:
|
|||||||
stackSizeIn);
|
stackSizeIn);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
numSignals = this->GetNumberOfCopies();
|
numSignals = this->GetNumberOfCopies();
|
||||||
|
StringHelper::CopyN(gamName, (functionName != NULL_PTR(const char8 *)) ? functionName : "", 255u);
|
||||||
DebugBrokerHelper::InitSignals(
|
DebugBrokerHelper::InitSignals(
|
||||||
this, dataSourceIn, service, signalInfoPointers, numSignals,
|
this, dataSourceIn, service, signalInfoPointers, numSignals,
|
||||||
this->copyTable, functionName, direction, &anyActive, &activeIndices,
|
this->copyTable, functionName, direction, &anyActive, &activeIndices,
|
||||||
&activeSizes, &activeMutex);
|
&activeSizes, &activeMutex, &anyBreakActive, &breakIndices);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -374,8 +510,11 @@ public:
|
|||||||
DebugSignalInfo **signalInfoPointers;
|
DebugSignalInfo **signalInfoPointers;
|
||||||
uint32 numSignals;
|
uint32 numSignals;
|
||||||
volatile bool anyActive;
|
volatile bool anyActive;
|
||||||
|
volatile bool anyBreakActive;
|
||||||
|
char8 gamName[256];
|
||||||
Vec<uint32> activeIndices;
|
Vec<uint32> activeIndices;
|
||||||
Vec<uint32> activeSizes;
|
Vec<uint32> activeSizes;
|
||||||
|
Vec<uint32> breakIndices;
|
||||||
FastPollingMutexSem activeMutex;
|
FastPollingMutexSem activeMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,17 @@
|
|||||||
|
|
||||||
namespace MARTe {
|
namespace MARTe {
|
||||||
|
|
||||||
|
// Break condition operators stored in DebugSignalInfo::breakOp
|
||||||
|
enum BreakOp {
|
||||||
|
BREAK_OFF = 0,
|
||||||
|
BREAK_GT = 1, // >
|
||||||
|
BREAK_LT = 2, // <
|
||||||
|
BREAK_EQ = 3, // ==
|
||||||
|
BREAK_GEQ = 4, // >=
|
||||||
|
BREAK_LEQ = 5, // <=
|
||||||
|
BREAK_NEQ = 6 // !=
|
||||||
|
};
|
||||||
|
|
||||||
struct DebugSignalInfo {
|
struct DebugSignalInfo {
|
||||||
void* memoryAddress;
|
void* memoryAddress;
|
||||||
TypeDescriptor type;
|
TypeDescriptor type;
|
||||||
@@ -16,10 +27,13 @@ struct DebugSignalInfo {
|
|||||||
uint32 numberOfElements;
|
uint32 numberOfElements;
|
||||||
volatile bool isTracing;
|
volatile bool isTracing;
|
||||||
volatile bool isForcing;
|
volatile bool isForcing;
|
||||||
uint8 forcedValue[1024];
|
uint8 forcedValue[1024];
|
||||||
uint32 internalID;
|
uint32 internalID;
|
||||||
volatile uint32 decimationFactor;
|
volatile uint32 decimationFactor;
|
||||||
volatile uint32 decimationCounter;
|
volatile uint32 decimationCounter;
|
||||||
|
// Conditional break fields (zero-cost when breakOp == BREAK_OFF)
|
||||||
|
volatile uint8 breakOp; // BreakOp enum value
|
||||||
|
float64 breakThreshold; // comparison threshold
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|||||||
@@ -7,11 +7,15 @@
|
|||||||
#include "GAM.h"
|
#include "GAM.h"
|
||||||
#include "GlobalObjectsDatabase.h"
|
#include "GlobalObjectsDatabase.h"
|
||||||
#include "HighResolutionTimer.h"
|
#include "HighResolutionTimer.h"
|
||||||
|
#include "LoggerService.h"
|
||||||
#include "Message.h"
|
#include "Message.h"
|
||||||
#include "ObjectBuilder.h"
|
#include "ObjectBuilder.h"
|
||||||
#include "ObjectRegistryDatabase.h"
|
#include "ObjectRegistryDatabase.h"
|
||||||
|
#include "Threads.h"
|
||||||
|
#include "Sleep.h"
|
||||||
#include "StreamString.h"
|
#include "StreamString.h"
|
||||||
#include "TimeoutType.h"
|
#include "TimeoutType.h"
|
||||||
|
#include "TcpLogger.h"
|
||||||
#include "TypeConversion.h"
|
#include "TypeConversion.h"
|
||||||
#include "ReferenceT.h"
|
#include "ReferenceT.h"
|
||||||
|
|
||||||
@@ -93,7 +97,11 @@ DebugService::DebugService()
|
|||||||
isServer = false;
|
isServer = false;
|
||||||
suppressTimeoutLogs = true;
|
suppressTimeoutLogs = true;
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
|
stepRemaining = 0u;
|
||||||
|
manualConfigSet = false;
|
||||||
activeClient = NULL_PTR(BasicTCPSocket *);
|
activeClient = NULL_PTR(BasicTCPSocket *);
|
||||||
|
streamerPacketOffset = 0u;
|
||||||
|
streamerSequenceNumber = 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugService::~DebugService() {
|
DebugService::~DebugService() {
|
||||||
@@ -157,18 +165,10 @@ bool DebugService::Initialise(StructuredDataI &data) {
|
|||||||
suppressTimeoutLogs = (suppress == 1);
|
suppressTimeoutLogs = (suppress == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to capture full configuration autonomously if data is a
|
// Do NOT call MoveToRoot() on the shared CDB here — that corrupts the
|
||||||
// ConfigurationDatabase
|
// ReferenceContainer::Initialise() traversal cursor for sibling objects.
|
||||||
ConfigurationDatabase *cdb = dynamic_cast<ConfigurationDatabase *>(&data);
|
// Just capture the local subtree; full config is rebuilt lazily from the
|
||||||
if (cdb != NULL_PTR(ConfigurationDatabase *)) {
|
// live ObjectRegistryDatabase when ServeConfig/EnrichWithConfig is called.
|
||||||
// Save current position
|
|
||||||
StreamString currentPath;
|
|
||||||
// In MARTe2 ConfigurationDatabase there isn't a direct GetCurrentPath,
|
|
||||||
// but we can at least try to copy from root if we are at root.
|
|
||||||
// For now, we rely on explicit SetFullConfig or documentary injection.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy local branch as fallback
|
|
||||||
(void)data.Copy(fullConfig);
|
(void)data.Copy(fullConfig);
|
||||||
|
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
@@ -193,9 +193,170 @@ bool DebugService::Initialise(StructuredDataI &data) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugService::InjectTcpLoggerIfNeeded() {
|
||||||
|
if (logPort == 0u)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check if the ORD already contains a LoggerService with at least one TcpLogger.
|
||||||
|
// If so, leave it untouched.
|
||||||
|
Reference existing = ObjectRegistryDatabase::Instance()->Find("LoggerService");
|
||||||
|
if (existing.IsValid()) {
|
||||||
|
ReferenceContainer *rc = dynamic_cast<ReferenceContainer *>(existing.operator->());
|
||||||
|
if (rc != NULL_PTR(ReferenceContainer *)) {
|
||||||
|
for (uint32 i = 0u; i < rc->Size(); i++) {
|
||||||
|
ReferenceT<TcpLogger> child = rc->Get(i);
|
||||||
|
if (child.IsValid()) {
|
||||||
|
printf("[DebugService] Found existing TcpLogger in LoggerService — skipping injection.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a CDB that mirrors the config-file declaration:
|
||||||
|
// LoggerService node (current position)
|
||||||
|
// Class = LoggerService
|
||||||
|
// CPUs = 1
|
||||||
|
// DebugConsumer (child)
|
||||||
|
// Class = TcpLogger
|
||||||
|
// Port = <logPort>
|
||||||
|
ConfigurationDatabase lsCdb;
|
||||||
|
(void)lsCdb.Write("Class", "LoggerService");
|
||||||
|
uint32 cpus = 1u;
|
||||||
|
(void)lsCdb.Write("CPUs", cpus);
|
||||||
|
// ReferenceContainer::Initialise only instantiates children whose names
|
||||||
|
// start with '+' (matching the StandardParser convention).
|
||||||
|
if (lsCdb.CreateRelative("+DebugConsumer")) {
|
||||||
|
(void)lsCdb.Write("Class", "TcpLogger");
|
||||||
|
uint32 p = static_cast<uint32>(logPort);
|
||||||
|
(void)lsCdb.Write("Port", p);
|
||||||
|
(void)lsCdb.MoveToAncestor(1u);
|
||||||
|
}
|
||||||
|
(void)lsCdb.MoveToRoot();
|
||||||
|
|
||||||
|
ReferenceT<LoggerService> ls(
|
||||||
|
"LoggerService", GlobalObjectsDatabase::Instance()->GetStandardHeap());
|
||||||
|
if (!ls.IsValid()) {
|
||||||
|
printf("[DebugService] Failed to create LoggerService object.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ls->SetName("LoggerService");
|
||||||
|
if (!ls->Initialise(lsCdb)) {
|
||||||
|
printf("[DebugService] LoggerService::Initialise() failed.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert into the ORD so it is findable by name and cleaned up on shutdown.
|
||||||
|
if (!ObjectRegistryDatabase::Instance()->Insert(ls)) {
|
||||||
|
printf("[DebugService] Failed to insert LoggerService into ORD.\n");
|
||||||
|
// Keep a local reference anyway so the logger thread stays alive.
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ORD now holds a reference to ls; the LoggerService stays alive until
|
||||||
|
// ObjectRegistryDatabase::Purge(). No need to store a local reference.
|
||||||
|
printf("[DebugService] Auto-injected LoggerService + TcpLogger on port %u.\n", logPort);
|
||||||
|
}
|
||||||
|
|
||||||
void DebugService::SetFullConfig(ConfigurationDatabase &config) {
|
void DebugService::SetFullConfig(ConfigurationDatabase &config) {
|
||||||
config.MoveToRoot();
|
config.MoveToRoot();
|
||||||
config.Copy(fullConfig);
|
config.Copy(fullConfig);
|
||||||
|
manualConfigSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BuildCDBFromContainer(ReferenceContainer *container,
|
||||||
|
ConfigurationDatabase &cdb) {
|
||||||
|
if (container == NULL_PTR(ReferenceContainer *))
|
||||||
|
return;
|
||||||
|
uint32 n = container->Size();
|
||||||
|
for (uint32 i = 0u; i < n; i++) {
|
||||||
|
Reference child = container->Get(i);
|
||||||
|
if (!child.IsValid())
|
||||||
|
continue;
|
||||||
|
const char8 *name = child->GetName();
|
||||||
|
if (name == NULL_PTR(const char8 *))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool created = cdb.CreateRelative(name);
|
||||||
|
if (!created) {
|
||||||
|
if (!cdb.MoveRelative(name))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char8 *className = child->GetClassProperties()->GetName();
|
||||||
|
if (className != NULL_PTR(const char8 *))
|
||||||
|
(void)cdb.Write("Class", className);
|
||||||
|
|
||||||
|
// Export scalar parameters via a SEPARATE CDB so the live cursor is
|
||||||
|
// never touched by ExportData. Then copy only top-level LEAF values
|
||||||
|
// (i.e. scalars where MoveRelative fails) and skip sub-nodes.
|
||||||
|
//
|
||||||
|
// This avoids two ExportData pitfalls:
|
||||||
|
// 1. ReferenceContainer::ExportData writes numeric-indexed child nodes
|
||||||
|
// (+0, +1, ...) — those are sub-nodes and get filtered out.
|
||||||
|
// 2. Some DataSource ExportData implementations follow internal
|
||||||
|
// references and write sibling objects as children — also sub-nodes,
|
||||||
|
// also filtered out.
|
||||||
|
//
|
||||||
|
// Scalar parameters (ControlPort, UdpPort, CPUs, Port, ...) pass through
|
||||||
|
// because they are leaf values, not sub-nodes.
|
||||||
|
{
|
||||||
|
ConfigurationDatabase exportCdb;
|
||||||
|
if (child->ExportData(exportCdb)) {
|
||||||
|
exportCdb.MoveToRoot();
|
||||||
|
uint32 nExport = exportCdb.GetNumberOfChildren();
|
||||||
|
for (uint32 j = 0u; j < nExport; j++) {
|
||||||
|
const char8 *ek = exportCdb.GetChildName(j);
|
||||||
|
if (StringHelper::Compare(ek, "Class") == 0 ||
|
||||||
|
StringHelper::Compare(ek, "Name") == 0 ||
|
||||||
|
StringHelper::Compare(ek, "IsContainer") == 0)
|
||||||
|
continue;
|
||||||
|
// Sub-node check: MoveRelative succeeds only for nodes, not scalars
|
||||||
|
if (exportCdb.MoveRelative(ek)) {
|
||||||
|
exportCdb.MoveToAncestor(1u);
|
||||||
|
continue; // skip sub-nodes entirely
|
||||||
|
}
|
||||||
|
// Leaf scalar — convert to string and write into the main CDB
|
||||||
|
AnyType at = exportCdb.GetType(ek);
|
||||||
|
if (at.GetDataPointer() != NULL_PTR(void *)) {
|
||||||
|
char8 buf[1024];
|
||||||
|
AnyType st(CharString, 0u, buf);
|
||||||
|
st.SetNumberOfElements(0, 1024);
|
||||||
|
if (TypeConvert(st, at)) {
|
||||||
|
(void)cdb.Write(ek, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceContainer *sub =
|
||||||
|
dynamic_cast<ReferenceContainer *>(child.operator->());
|
||||||
|
if (sub != NULL_PTR(ReferenceContainer *))
|
||||||
|
BuildCDBFromContainer(sub, cdb);
|
||||||
|
|
||||||
|
(void)cdb.MoveToAncestor(1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugService::RebuildConfigFromRegistry() {
|
||||||
|
fullConfig = ConfigurationDatabase();
|
||||||
|
BuildCDBFromContainer(ObjectRegistryDatabase::Instance(), fullConfig);
|
||||||
|
|
||||||
|
// ExportData on ReferenceContainer subclasses (including DebugService itself)
|
||||||
|
// only writes Name/IsContainer/indexed children — it never re-emits the
|
||||||
|
// config-file parameters that were read in Initialise(). Write them back
|
||||||
|
// explicitly from the member variables that Initialise() stored.
|
||||||
|
const char8 *myName = GetName();
|
||||||
|
if (myName != NULL_PTR(const char8 *)) {
|
||||||
|
if (fullConfig.MoveRelative(myName)) {
|
||||||
|
(void)fullConfig.Write("ControlPort", static_cast<uint32>(controlPort));
|
||||||
|
(void)fullConfig.Write("UdpPort", static_cast<uint32>(streamPort));
|
||||||
|
(void)fullConfig.Write("LogPort", static_cast<uint32>(logPort));
|
||||||
|
if (streamIP.Size() > 0u)
|
||||||
|
(void)fullConfig.Write("StreamIP", streamIP.Buffer());
|
||||||
|
(void)fullConfig.MoveToAncestor(1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PatchItemInternal(const char8 *originalName,
|
static void PatchItemInternal(const char8 *originalName,
|
||||||
@@ -247,7 +408,7 @@ DebugSignalInfo *DebugService::RegisterSignal(void *memoryAddress,
|
|||||||
const char8 *name,
|
const char8 *name,
|
||||||
uint8 numberOfDimensions,
|
uint8 numberOfDimensions,
|
||||||
uint32 numberOfElements) {
|
uint32 numberOfElements) {
|
||||||
printf("<debug> registering: %s\n", name);
|
fprintf(stderr, "<debug> RegisterSignal[%p]: %s\n", (void*)this, name);
|
||||||
mutex.FastLock();
|
mutex.FastLock();
|
||||||
DebugSignalInfo *res = NULL_PTR(DebugSignalInfo *);
|
DebugSignalInfo *res = NULL_PTR(DebugSignalInfo *);
|
||||||
uint32 sigIdx = 0xFFFFFFFF;
|
uint32 sigIdx = 0xFFFFFFFF;
|
||||||
@@ -271,6 +432,8 @@ DebugSignalInfo *DebugService::RegisterSignal(void *memoryAddress,
|
|||||||
res->internalID = sigIdx;
|
res->internalID = sigIdx;
|
||||||
res->decimationFactor = 1;
|
res->decimationFactor = 1;
|
||||||
res->decimationCounter = 0;
|
res->decimationCounter = 0;
|
||||||
|
res->breakOp = BREAK_OFF;
|
||||||
|
res->breakThreshold = 0.0;
|
||||||
signals.Push(res);
|
signals.Push(res);
|
||||||
}
|
}
|
||||||
if (sigIdx != 0xFFFFFFFF) {
|
if (sigIdx != 0xFFFFFFFF) {
|
||||||
@@ -314,7 +477,10 @@ void DebugService::RegisterBroker(DebugSignalInfo **signalPointers,
|
|||||||
volatile bool *anyActiveFlag,
|
volatile bool *anyActiveFlag,
|
||||||
Vec<uint32> *activeIndices,
|
Vec<uint32> *activeIndices,
|
||||||
Vec<uint32> *activeSizes,
|
Vec<uint32> *activeSizes,
|
||||||
FastPollingMutexSem *activeMutex) {
|
FastPollingMutexSem *activeMutex,
|
||||||
|
volatile bool *anyBreakFlag,
|
||||||
|
Vec<uint32> *breakIndices,
|
||||||
|
const char8 *gamName, bool isOutput) {
|
||||||
mutex.FastLock();
|
mutex.FastLock();
|
||||||
BrokerInfo b;
|
BrokerInfo b;
|
||||||
b.signalPointers = signalPointers;
|
b.signalPointers = signalPointers;
|
||||||
@@ -324,6 +490,11 @@ void DebugService::RegisterBroker(DebugSignalInfo **signalPointers,
|
|||||||
b.activeIndices = activeIndices;
|
b.activeIndices = activeIndices;
|
||||||
b.activeSizes = activeSizes;
|
b.activeSizes = activeSizes;
|
||||||
b.activeMutex = activeMutex;
|
b.activeMutex = activeMutex;
|
||||||
|
b.anyBreakFlag = anyBreakFlag;
|
||||||
|
b.breakIndices = breakIndices;
|
||||||
|
b.isOutput = isOutput;
|
||||||
|
if (gamName != NULL_PTR(const char8 *))
|
||||||
|
b.gamName = gamName;
|
||||||
brokers.Push(b);
|
brokers.Push(b);
|
||||||
mutex.FastUnLock();
|
mutex.FastUnLock();
|
||||||
}
|
}
|
||||||
@@ -365,6 +536,28 @@ void DebugService::UpdateBrokersActiveStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugService::UpdateBrokersBreakStatus() {
|
||||||
|
for (uint32 i = 0; i < brokers.Size(); i++) {
|
||||||
|
Vec<uint32> tempBreak;
|
||||||
|
uint32 count = 0;
|
||||||
|
for (uint32 j = 0; j < brokers[i].numSignals; j++) {
|
||||||
|
DebugSignalInfo *s = brokers[i].signalPointers[j];
|
||||||
|
if (s != NULL_PTR(DebugSignalInfo *) && s->breakOp != BREAK_OFF) {
|
||||||
|
tempBreak.Push(j);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (brokers[i].activeMutex)
|
||||||
|
brokers[i].activeMutex->FastLock();
|
||||||
|
if (brokers[i].breakIndices)
|
||||||
|
*(brokers[i].breakIndices) = tempBreak;
|
||||||
|
if (brokers[i].anyBreakFlag)
|
||||||
|
*(brokers[i].anyBreakFlag) = (count > 0);
|
||||||
|
if (brokers[i].activeMutex)
|
||||||
|
brokers[i].activeMutex->FastUnLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ErrorManagement::ErrorType DebugService::Execute(ExecutionInfo &info) {
|
ErrorManagement::ErrorType DebugService::Execute(ExecutionInfo &info) {
|
||||||
return ErrorManagement::FatalError;
|
return ErrorManagement::FatalError;
|
||||||
}
|
}
|
||||||
@@ -379,63 +572,62 @@ ErrorManagement::ErrorType DebugService::Server(ExecutionInfo &info) {
|
|||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
if (info.GetStage() == ExecutionInfo::StartupStage) {
|
if (info.GetStage() == ExecutionInfo::StartupStage) {
|
||||||
serverThreadId = Threads::Id();
|
serverThreadId = Threads::Id();
|
||||||
|
// Wait for ObjectRegistryDatabase::Initialise() to finish processing all
|
||||||
|
// sibling objects (LoggerService etc. come after DebugService in the config).
|
||||||
|
// 500 ms is well above any realistic initialisation time.
|
||||||
|
Sleep::MSec(500u);
|
||||||
|
InjectTcpLoggerIfNeeded();
|
||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
while (info.GetStage() == ExecutionInfo::MainStage) {
|
// The MARTe2 framework calls Execute() in a loop; each call should do
|
||||||
while (activeClient == NULL_PTR(BasicTCPSocket *)) {
|
// one unit of work and return so the framework can check for Stop().
|
||||||
BasicTCPSocket *newClient = tcpServer.WaitConnection(TTInfiniteWait);
|
// This replaces the old internal infinite-while pattern.
|
||||||
if (newClient != NULL_PTR(BasicTCPSocket *)) {
|
if (activeClient == NULL_PTR(BasicTCPSocket *)) {
|
||||||
// Single connection mode: disconnect any existing client first
|
// Wait briefly for a new connection; return so the framework loop can
|
||||||
activeClient = newClient;
|
// check if Stop() was requested between calls.
|
||||||
}
|
BasicTCPSocket *newClient = tcpServer.WaitConnection(TimeoutType(100));
|
||||||
|
if (newClient != NULL_PTR(BasicTCPSocket *)) {
|
||||||
|
activeClient = newClient;
|
||||||
}
|
}
|
||||||
// Single connection mode: only check client 0
|
} else {
|
||||||
{
|
// Check if client is still connected
|
||||||
if (activeClient != NULL_PTR(BasicTCPSocket *)) {
|
if (!activeClient->IsConnected()) {
|
||||||
// Check if client is still connected
|
activeClient->Close();
|
||||||
if (!activeClient->IsConnected()) {
|
delete activeClient;
|
||||||
activeClient->Close();
|
activeClient = NULL_PTR(BasicTCPSocket *);
|
||||||
delete activeClient;
|
} else {
|
||||||
activeClient = NULL_PTR(BasicTCPSocket *);
|
char buffer[1024];
|
||||||
|
uint32 size = 1024;
|
||||||
} else {
|
if (activeClient->Read(buffer, size)) {
|
||||||
char buffer[1024];
|
if (size > 0) {
|
||||||
uint32 size = 1024;
|
// Process each line separately
|
||||||
if (activeClient->Read(buffer, size)) {
|
char *ptr = buffer;
|
||||||
if (size > 0) {
|
char *end = buffer + size;
|
||||||
// Process each line separately
|
while (ptr < end) {
|
||||||
char *ptr = buffer;
|
char *newline = (char *)memchr(ptr, '\n', end - ptr);
|
||||||
char *end = buffer + size;
|
if (!newline) {
|
||||||
while (ptr < end) {
|
break;
|
||||||
char *newline = (char *)memchr(ptr, '\n', end - ptr);
|
|
||||||
if (!newline) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*newline = '\0';
|
|
||||||
// Skip carriage return if present
|
|
||||||
if (newline > ptr && *(newline - 1) == '\r')
|
|
||||||
*(newline - 1) = '\0';
|
|
||||||
StreamString command;
|
|
||||||
uint32 len = (uint32)(newline - ptr);
|
|
||||||
command.Write(ptr, len);
|
|
||||||
if (command.Size() > 0) {
|
|
||||||
HandleCommand(command, activeClient);
|
|
||||||
}
|
|
||||||
ptr = newline + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
*newline = '\0';
|
||||||
// // Read failed (client disconnected or error), clean up
|
// Skip carriage return if present
|
||||||
if (activeClient != NULL_PTR(BasicTCPSocket *)) {
|
if (newline > ptr && *(newline - 1) == '\r')
|
||||||
activeClient->Close();
|
*(newline - 1) = '\0';
|
||||||
delete activeClient;
|
StreamString command;
|
||||||
activeClient = NULL_PTR(BasicTCPSocket *);
|
uint32 len = (uint32)(newline - ptr);
|
||||||
|
command.Write(ptr, len);
|
||||||
|
if (command.Size() > 0) {
|
||||||
|
HandleCommand(command, activeClient);
|
||||||
}
|
}
|
||||||
|
ptr = newline + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Read failed (client disconnected or error), clean up
|
||||||
|
activeClient->Close();
|
||||||
|
delete activeClient;
|
||||||
|
activeClient = NULL_PTR(BasicTCPSocket *);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Sleep::MSec(10);
|
|
||||||
}
|
}
|
||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
@@ -447,71 +639,68 @@ ErrorManagement::ErrorType DebugService::Streamer(ExecutionInfo &info) {
|
|||||||
streamerThreadId = Threads::Id();
|
streamerThreadId = Threads::Id();
|
||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
|
// Set UDP destination (idempotent, called each Execute() invocation)
|
||||||
InternetHost dest(streamPort, streamIP.Buffer());
|
InternetHost dest(streamPort, streamIP.Buffer());
|
||||||
(void)udpSocket.SetDestination(dest);
|
(void)udpSocket.SetDestination(dest);
|
||||||
uint8 packetBuffer[4096];
|
|
||||||
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;
|
// Poll monitored signals
|
||||||
uint64 ts;
|
uint64 currentTimeMs = (uint64)((float64)HighResolutionTimer::Counter() *
|
||||||
uint8 sampleData[1024];
|
HighResolutionTimer::Period() * 1000.0);
|
||||||
bool hasData = false;
|
mutex.FastLock();
|
||||||
while ((info.GetStage() == ExecutionInfo::MainStage) &&
|
for (uint32 i = 0; i < monitoredSignals.Size(); i++) {
|
||||||
traceBuffer.Pop(id, ts, sampleData, size, 1024)) {
|
if (currentTimeMs >= (monitoredSignals[i].lastPollTime + monitoredSignals[i].periodMs)) {
|
||||||
hasData = true;
|
monitoredSignals[i].lastPollTime = currentTimeMs;
|
||||||
if (packetOffset == 0) {
|
uint64 ts = (uint64)((float64)HighResolutionTimer::Counter() *
|
||||||
TraceHeader header;
|
HighResolutionTimer::Period() * 1000000.0);
|
||||||
header.magic = 0xDA7A57AD;
|
void *address = NULL_PTR(void *);
|
||||||
header.seq = sequenceNumber++;
|
if (monitoredSignals[i].dataSource->GetSignalMemoryBuffer(monitoredSignals[i].signalIdx, 0, address)) {
|
||||||
header.timestamp = HighResolutionTimer::Counter();
|
traceBuffer.Push(monitoredSignals[i].internalID, ts, (uint8 *)address, monitoredSignals[i].size);
|
||||||
header.count = 0;
|
|
||||||
memcpy(packetBuffer, &header, sizeof(TraceHeader));
|
|
||||||
packetOffset = sizeof(TraceHeader);
|
|
||||||
}
|
}
|
||||||
if (packetOffset + 16 + size > 1400) {
|
|
||||||
uint32 toWrite = packetOffset;
|
|
||||||
(void)udpSocket.Write((char8 *)packetBuffer, toWrite);
|
|
||||||
TraceHeader header;
|
|
||||||
header.magic = 0xDA7A57AD;
|
|
||||||
header.seq = sequenceNumber++;
|
|
||||||
header.timestamp = HighResolutionTimer::Counter();
|
|
||||||
header.count = 0;
|
|
||||||
memcpy(packetBuffer, &header, sizeof(TraceHeader));
|
|
||||||
packetOffset = sizeof(TraceHeader);
|
|
||||||
}
|
|
||||||
memcpy(&packetBuffer[packetOffset], &id, 4);
|
|
||||||
memcpy(&packetBuffer[packetOffset + 4], &ts, 8);
|
|
||||||
memcpy(&packetBuffer[packetOffset + 12], &size, 4);
|
|
||||||
memcpy(&packetBuffer[packetOffset + 16], sampleData, size);
|
|
||||||
packetOffset += (16 + size);
|
|
||||||
((TraceHeader *)packetBuffer)->count++;
|
|
||||||
}
|
}
|
||||||
if (packetOffset > 0) {
|
}
|
||||||
uint32 toWrite = packetOffset;
|
mutex.FastUnLock();
|
||||||
(void)udpSocket.Write((char8 *)packetBuffer, toWrite);
|
|
||||||
packetOffset = 0;
|
// Drain ring buffer into UDP packet(s)
|
||||||
|
uint32 id, size;
|
||||||
|
uint64 ts;
|
||||||
|
uint8 sampleData[1024];
|
||||||
|
bool hasData = false;
|
||||||
|
while (traceBuffer.Pop(id, ts, sampleData, size, 1024)) {
|
||||||
|
hasData = true;
|
||||||
|
if (streamerPacketOffset == 0u) {
|
||||||
|
TraceHeader header;
|
||||||
|
header.magic = 0xDA7A57AD;
|
||||||
|
header.seq = streamerSequenceNumber++;
|
||||||
|
header.timestamp = HighResolutionTimer::Counter();
|
||||||
|
header.count = 0;
|
||||||
|
memcpy(streamerPacketBuffer, &header, sizeof(TraceHeader));
|
||||||
|
streamerPacketOffset = sizeof(TraceHeader);
|
||||||
}
|
}
|
||||||
if (!hasData)
|
if (streamerPacketOffset + 16u + size > 1400u) {
|
||||||
Sleep::MSec(1);
|
uint32 toWrite = streamerPacketOffset;
|
||||||
|
(void)udpSocket.Write((char8 *)streamerPacketBuffer, toWrite);
|
||||||
|
TraceHeader header;
|
||||||
|
header.magic = 0xDA7A57AD;
|
||||||
|
header.seq = streamerSequenceNumber++;
|
||||||
|
header.timestamp = HighResolutionTimer::Counter();
|
||||||
|
header.count = 0;
|
||||||
|
memcpy(streamerPacketBuffer, &header, sizeof(TraceHeader));
|
||||||
|
streamerPacketOffset = sizeof(TraceHeader);
|
||||||
|
}
|
||||||
|
memcpy(&streamerPacketBuffer[streamerPacketOffset], &id, 4);
|
||||||
|
memcpy(&streamerPacketBuffer[streamerPacketOffset + 4], &ts, 8);
|
||||||
|
memcpy(&streamerPacketBuffer[streamerPacketOffset + 12], &size, 4);
|
||||||
|
memcpy(&streamerPacketBuffer[streamerPacketOffset + 16], sampleData, size);
|
||||||
|
streamerPacketOffset += (16u + size);
|
||||||
|
((TraceHeader *)streamerPacketBuffer)->count++;
|
||||||
|
}
|
||||||
|
if (streamerPacketOffset > 0u) {
|
||||||
|
uint32 toWrite = streamerPacketOffset;
|
||||||
|
(void)udpSocket.Write((char8 *)streamerPacketBuffer, toWrite);
|
||||||
|
streamerPacketOffset = 0u;
|
||||||
|
}
|
||||||
|
if (!hasData) {
|
||||||
|
Sleep::MSec(1);
|
||||||
}
|
}
|
||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
@@ -575,6 +764,72 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
|
|||||||
(void)client->Write(resp.Buffer(), s);
|
(void)client->Write(resp.Buffer(), s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (token == "STEP") {
|
||||||
|
StreamString nStr;
|
||||||
|
uint32 n = 1u;
|
||||||
|
if (cmd.GetToken(nStr, delims, term)) {
|
||||||
|
AnyType nVal(UnsignedInteger32Bit, 0u, &n);
|
||||||
|
AnyType nS(CharString, 0u, nStr.Buffer());
|
||||||
|
(void)TypeConvert(nVal, nS);
|
||||||
|
}
|
||||||
|
// Optional thread name: STEP <n> [<threadName>]
|
||||||
|
StreamString threadStr;
|
||||||
|
const char8 *threadArg = NULL_PTR(const char8 *);
|
||||||
|
if (cmd.GetToken(threadStr, delims, term) && threadStr.Size() > 0u) {
|
||||||
|
threadArg = threadStr.Buffer();
|
||||||
|
}
|
||||||
|
Step(n, threadArg);
|
||||||
|
if (client) {
|
||||||
|
StreamString resp;
|
||||||
|
resp.Printf("OK STEP %u\n", n);
|
||||||
|
uint32 s = resp.Size();
|
||||||
|
(void)client->Write(resp.Buffer(), s);
|
||||||
|
}
|
||||||
|
} else if (token == "STEP_STATUS") {
|
||||||
|
GetStepStatus(client);
|
||||||
|
} else if (token == "VALUE") {
|
||||||
|
StreamString sigName;
|
||||||
|
if (cmd.GetToken(sigName, delims, term)) {
|
||||||
|
GetSignalValue(sigName.Buffer(), client);
|
||||||
|
} else if (client) {
|
||||||
|
const char8 *errResp = "{\"Error\": \"Missing signal name\"}\nOK VALUE\n";
|
||||||
|
uint32 s = StringHelper::Length(errResp);
|
||||||
|
(void)client->Write(errResp, s);
|
||||||
|
}
|
||||||
|
} else if (token == "BREAK") {
|
||||||
|
// BREAK <signal> <op> <threshold> — set break condition
|
||||||
|
// BREAK <signal> OFF — clear break condition
|
||||||
|
StreamString name, opStr;
|
||||||
|
if (cmd.GetToken(name, delims, term) && cmd.GetToken(opStr, delims, term)) {
|
||||||
|
uint32 count = 0;
|
||||||
|
if (opStr == "OFF") {
|
||||||
|
count = ClearBreak(name.Buffer());
|
||||||
|
} else {
|
||||||
|
StreamString threshStr;
|
||||||
|
if (cmd.GetToken(threshStr, delims, term)) {
|
||||||
|
uint8 op = BREAK_OFF;
|
||||||
|
if (opStr == ">") op = BREAK_GT;
|
||||||
|
else if (opStr == "<") op = BREAK_LT;
|
||||||
|
else if (opStr == "==") op = BREAK_EQ;
|
||||||
|
else if (opStr == ">=") op = BREAK_GEQ;
|
||||||
|
else if (opStr == "<=") op = BREAK_LEQ;
|
||||||
|
else if (opStr == "!=") op = BREAK_NEQ;
|
||||||
|
if (op != BREAK_OFF) {
|
||||||
|
float64 threshold = 0.0;
|
||||||
|
AnyType thrVal(Float64Bit, 0u, &threshold);
|
||||||
|
AnyType thrStr(CharString, 0u, threshStr.Buffer());
|
||||||
|
(void)TypeConvert(thrVal, thrStr);
|
||||||
|
count = SetBreak(name.Buffer(), op, threshold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (client) {
|
||||||
|
StreamString resp;
|
||||||
|
resp.Printf("OK BREAK %u\n", count);
|
||||||
|
uint32 s = resp.Size();
|
||||||
|
(void)client->Write(resp.Buffer(), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (token == "DISCOVER")
|
} else if (token == "DISCOVER")
|
||||||
Discover(client);
|
Discover(client);
|
||||||
else if (token == "MSG") {
|
else if (token == "MSG") {
|
||||||
@@ -622,67 +877,81 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
|
|||||||
msgConfig.Write("Mode", "ExpectsReply");
|
msgConfig.Write("Mode", "ExpectsReply");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.Size() > 0u) {
|
// Parse payload key=value lines into a ConfigurationDatabase.
|
||||||
|
// ConstantGAM::SetOutput (and similar handlers) expect a
|
||||||
|
// ReferenceT<StructuredDataI> inserted into the Message's
|
||||||
|
// reference container — NOT a sub-node of the message config.
|
||||||
|
ReferenceT<ConfigurationDatabase> paramCdb(
|
||||||
|
"ConfigurationDatabase",
|
||||||
|
GlobalObjectsDatabase::Instance()->GetStandardHeap());
|
||||||
|
|
||||||
|
if (payload.Size() > 0u && paramCdb.IsValid()) {
|
||||||
payload.Seek(0u);
|
payload.Seek(0u);
|
||||||
StreamString line;
|
StreamString line;
|
||||||
while (payload.GetToken(line, "\n", term)) {
|
while (payload.GetToken(line, "\n", term)) {
|
||||||
if (line.Size() > 0u) {
|
if (line.Size() > 0u) {
|
||||||
const char8 *eq = StringHelper::SearchChar(line.Buffer(), '=');
|
const char8 *eq = StringHelper::SearchChar(line.Buffer(), '=');
|
||||||
if (eq != NULL_PTR(const char8 *)) {
|
if (eq != NULL_PTR(const char8 *)) {
|
||||||
StreamString key, val;
|
|
||||||
uint32 eqPos = (uint32)(eq - line.Buffer());
|
uint32 eqPos = (uint32)(eq - line.Buffer());
|
||||||
(void)line.Seek(0u);
|
(void)line.Seek(0u);
|
||||||
|
|
||||||
char8* keyBuf = new char8[eqPos + 1];
|
char8 keyBuf[256] = {'\0'};
|
||||||
uint32 keyReadSize = eqPos;
|
uint32 keyReadSize = eqPos;
|
||||||
if (line.Read(keyBuf, keyReadSize)) {
|
(void)line.Read(keyBuf, keyReadSize);
|
||||||
keyBuf[eqPos] = '\0';
|
|
||||||
key = keyBuf;
|
|
||||||
}
|
|
||||||
delete[] keyBuf;
|
|
||||||
|
|
||||||
(void)line.Seek(eqPos + 1u);
|
(void)line.Seek(eqPos + 1u);
|
||||||
uint32 valLen = line.Size() - eqPos - 1u;
|
uint32 valLen = (uint32)(line.Size() - eqPos - 1u);
|
||||||
char8* valBuf = new char8[valLen + 1];
|
char8 valBuf[1024] = {'\0'};
|
||||||
uint32 valReadSize = valLen;
|
(void)line.Read(valBuf, valLen);
|
||||||
if (line.Read(valBuf, valReadSize)) {
|
|
||||||
valBuf[valLen] = '\0';
|
// Trim trailing whitespace from value
|
||||||
val = valBuf;
|
for (int32 ti = (int32)valLen - 1; ti >= 0; ti--) {
|
||||||
|
if (valBuf[ti] == ' ' || valBuf[ti] == '\r' || valBuf[ti] == '\t')
|
||||||
|
valBuf[ti] = '\0';
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamString key = keyBuf;
|
||||||
|
key = key.Buffer(); // trim happens via assignment
|
||||||
|
// Trim leading whitespace from key
|
||||||
|
const char8 *kp = keyBuf;
|
||||||
|
while (*kp == ' ' || *kp == '\t') kp++;
|
||||||
|
|
||||||
|
if (*kp != '\0') {
|
||||||
|
(void)paramCdb->Write(kp, valBuf);
|
||||||
}
|
}
|
||||||
delete[] valBuf;
|
|
||||||
|
|
||||||
if (key.Size() > 0u) {
|
|
||||||
if (msgConfig.CreateRelative("Payload")) {
|
|
||||||
(void)msgConfig.Write(key.Buffer(), val.Buffer());
|
|
||||||
(void)msgConfig.MoveToAncestor(1u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
line = "";
|
line = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorManagement::ErrorType err = ErrorManagement::ParametersError;
|
ErrorManagement::ErrorType err = ErrorManagement::ParametersError;
|
||||||
if (msg->Initialise(msgConfig)) {
|
if (msg->Initialise(msgConfig)) {
|
||||||
|
if (paramCdb.IsValid() && payload.Size() > 0u) {
|
||||||
|
// Insert the CDB as a ReferenceT<StructuredDataI> parameter
|
||||||
|
(void)msg->Insert(paramCdb);
|
||||||
|
}
|
||||||
// Find destination object in the global database
|
// Find destination object in the global database
|
||||||
Reference destObj = ObjectRegistryDatabase::Instance()->Find(dest.Buffer());
|
Reference destObj = ObjectRegistryDatabase::Instance()->Find(dest.Buffer());
|
||||||
if (destObj.IsValid()) {
|
if (destObj.IsValid()) {
|
||||||
Object* sender = this;
|
Object* sender = this;
|
||||||
// Double check if we are in the registry to be a valid sender
|
|
||||||
StreamString myPath;
|
StreamString myPath;
|
||||||
if (!GetFullObjectName(*this, myPath)) {
|
if (!GetFullObjectName(*this, myPath)) {
|
||||||
sender = NULL_PTR(Object*);
|
sender = NULL_PTR(Object*);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wait) {
|
if (wait) {
|
||||||
err = MessageI::WaitForReply(msg, TTInfiniteWait);
|
err = MessageI::WaitForReply(msg, TTInfiniteWait);
|
||||||
} else {
|
} else {
|
||||||
err = MessageI::SendMessage(msg, sender);
|
(void)MessageI::SendMessage(msg, sender);
|
||||||
|
// Fire-and-forget: destination found, message sent.
|
||||||
|
// Whether the recipient had a matching filter is not
|
||||||
|
// reported back to the caller — return OK.
|
||||||
|
err = ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
printf("<debug> MSG: Destination object %s not found in ORD\n", dest.Buffer());
|
printf("<debug> MSG: Destination object %s not found in ORD\n", dest.Buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,8 +1054,13 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
|
|||||||
void DebugService::EnrichWithConfig(const char8 *path, StreamString &json) {
|
void DebugService::EnrichWithConfig(const char8 *path, StreamString &json) {
|
||||||
if (path == NULL_PTR(const char8 *))
|
if (path == NULL_PTR(const char8 *))
|
||||||
return;
|
return;
|
||||||
|
if (!manualConfigSet) {
|
||||||
|
RebuildConfigFromRegistry();
|
||||||
|
}
|
||||||
fullConfig.MoveToRoot();
|
fullConfig.MoveToRoot();
|
||||||
|
|
||||||
|
fprintf(stderr, "[EnrichWithConfig] path=%s\n", path);
|
||||||
|
|
||||||
const char8 *current = path;
|
const char8 *current = path;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
while (ok) {
|
while (ok) {
|
||||||
@@ -801,29 +1075,49 @@ void DebugService::EnrichWithConfig(const char8 *path, StreamString &json) {
|
|||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalise short direction names to both forms so we search consistently.
|
||||||
|
// Paths use "In"/"Out" (alias convention); CDBs may store either form.
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
// 1. Try exact match (bare name - rebuilt-from-registry CDBs use this)
|
||||||
if (fullConfig.MoveRelative(part.Buffer())) {
|
if (fullConfig.MoveRelative(part.Buffer())) {
|
||||||
// Found exact
|
fprintf(stderr, "[EnrichWithConfig] nav exact '%s' OK\n", part.Buffer());
|
||||||
} else {
|
found = true;
|
||||||
bool found = false;
|
}
|
||||||
|
|
||||||
|
// 2. Try +name (raw config-file CDBs prefix nodes with '+')
|
||||||
|
if (!found) {
|
||||||
|
StreamString prefixed;
|
||||||
|
prefixed.Printf("+%s", part.Buffer());
|
||||||
|
if (fullConfig.MoveRelative(prefixed.Buffer())) {
|
||||||
|
fprintf(stderr, "[EnrichWithConfig] nav prefixed '%s' OK\n", prefixed.Buffer());
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Expand short direction aliases: In -> InputSignals / Out -> OutputSignals
|
||||||
|
if (!found) {
|
||||||
if (part == "In") {
|
if (part == "In") {
|
||||||
if (fullConfig.MoveRelative("InputSignals")) {
|
if (fullConfig.MoveRelative("InputSignals")) {
|
||||||
|
fprintf(stderr, "[EnrichWithConfig] nav 'In'->InputSignals OK\n");
|
||||||
|
found = true;
|
||||||
|
} else if (fullConfig.MoveRelative("+InputSignals")) {
|
||||||
|
fprintf(stderr, "[EnrichWithConfig] nav 'In'->+InputSignals OK\n");
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
} else if (part == "Out") {
|
} else if (part == "Out") {
|
||||||
if (fullConfig.MoveRelative("OutputSignals")) {
|
if (fullConfig.MoveRelative("OutputSignals")) {
|
||||||
found = true;
|
found = true;
|
||||||
|
} else if (fullConfig.MoveRelative("+OutputSignals")) {
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
StreamString prefixed;
|
fprintf(stderr, "[EnrichWithConfig] FAILED at part '%s'\n", part.Buffer());
|
||||||
prefixed.Printf("+%s", part.Buffer());
|
fullConfig.MoveToRoot();
|
||||||
if (fullConfig.MoveRelative(prefixed.Buffer())) {
|
return;
|
||||||
// Found prefixed
|
|
||||||
} else {
|
|
||||||
return; // Not found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -886,6 +1180,14 @@ void DebugService::JsonifyDatabase(ConfigurationDatabase &db,
|
|||||||
void DebugService::ServeConfig(BasicTCPSocket *client) {
|
void DebugService::ServeConfig(BasicTCPSocket *client) {
|
||||||
if (client == NULL_PTR(BasicTCPSocket *))
|
if (client == NULL_PTR(BasicTCPSocket *))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// If no manual config was injected (test case), rebuild from the live registry.
|
||||||
|
// In production, Initialise() only captures the DebugService subtree (to avoid
|
||||||
|
// corrupting the shared CDB cursor), so we always need to rebuild here.
|
||||||
|
if (!manualConfigSet) {
|
||||||
|
RebuildConfigFromRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
StreamString json;
|
StreamString json;
|
||||||
fullConfig.MoveToRoot();
|
fullConfig.MoveToRoot();
|
||||||
JsonifyDatabase(fullConfig, json);
|
JsonifyDatabase(fullConfig, json);
|
||||||
@@ -930,6 +1232,7 @@ void DebugService::InfoNode(const char8 *path, BasicTCPSocket *client) {
|
|||||||
}
|
}
|
||||||
EnrichWithConfig(path, json);
|
EnrichWithConfig(path, json);
|
||||||
} else {
|
} else {
|
||||||
|
StreamString enrichAlias;
|
||||||
mutex.FastLock();
|
mutex.FastLock();
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (uint32 i = 0; i < aliases.Size(); i++) {
|
for (uint32 i = 0; i < aliases.Size(); i++) {
|
||||||
@@ -941,13 +1244,21 @@ void DebugService::InfoNode(const char8 *path, BasicTCPSocket *client) {
|
|||||||
json.Printf("\"Name\": \"%s\", \"Class\": \"Signal\", \"Type\": "
|
json.Printf("\"Name\": \"%s\", \"Class\": \"Signal\", \"Type\": "
|
||||||
"\"%s\", \"ID\": %d",
|
"\"%s\", \"ID\": %d",
|
||||||
s->name.Buffer(), tname ? tname : "Unknown", s->internalID);
|
s->name.Buffer(), tname ? tname : "Unknown", s->internalID);
|
||||||
EnrichWithConfig(aliases[i].name.Buffer(), json);
|
enrichAlias = aliases[i].name;
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!found) {
|
||||||
|
fprintf(stderr, "[InfoNode][%p] signal '%s' NOT found in %u aliases:\n", (void*)this, path, (uint32)aliases.Size());
|
||||||
|
for (uint32 i = 0; i < aliases.Size(); i++) {
|
||||||
|
fprintf(stderr, " alias[%u] = '%s'\n", i, aliases[i].name.Buffer());
|
||||||
|
}
|
||||||
|
}
|
||||||
mutex.FastUnLock();
|
mutex.FastUnLock();
|
||||||
if (!found)
|
if (found)
|
||||||
|
EnrichWithConfig(enrichAlias.Buffer(), json);
|
||||||
|
else
|
||||||
json += "\"Error\": \"Object not found\"";
|
json += "\"Error\": \"Object not found\"";
|
||||||
}
|
}
|
||||||
json += "}\nOK INFO\n";
|
json += "}\nOK INFO\n";
|
||||||
@@ -1157,6 +1468,159 @@ uint32 DebugService::TraceSignal(const char8 *name, bool enable,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugService::ConsumeStepIfNeeded(const char8 *gamName,
|
||||||
|
const char8 *threadName) {
|
||||||
|
if (stepRemaining == 0u) return;
|
||||||
|
mutex.FastLock();
|
||||||
|
// If a thread filter is set, only the matching OS thread consumes step credits.
|
||||||
|
if (stepThreadFilter.Size() > 0u &&
|
||||||
|
(threadName == NULL_PTR(const char8 *) || stepThreadFilter != threadName)) {
|
||||||
|
mutex.FastUnLock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stepRemaining > 0u) {
|
||||||
|
stepRemaining--;
|
||||||
|
if (stepRemaining == 0u) {
|
||||||
|
isPaused = true;
|
||||||
|
pausedAtGam = (gamName != NULL_PTR(const char8 *)) ? gamName : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex.FastUnLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugService::Step(uint32 n, const char8 *threadName) {
|
||||||
|
mutex.FastLock();
|
||||||
|
stepRemaining = n;
|
||||||
|
isPaused = false;
|
||||||
|
stepThreadFilter = (threadName != NULL_PTR(const char8 *)) ? threadName : "";
|
||||||
|
mutex.FastUnLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugService::GetStepStatus(BasicTCPSocket *client) {
|
||||||
|
if (client == NULL_PTR(BasicTCPSocket *)) return;
|
||||||
|
mutex.FastLock();
|
||||||
|
bool paused = isPaused;
|
||||||
|
uint32 remaining = stepRemaining;
|
||||||
|
StreamString gam = pausedAtGam;
|
||||||
|
StreamString threadFilter = stepThreadFilter;
|
||||||
|
mutex.FastUnLock();
|
||||||
|
StreamString resp;
|
||||||
|
resp.Printf("{\"Paused\": %s, \"PausedAtGam\": \"%s\", \"StepRemaining\": %u, \"StepThread\": \"%s\"}\nOK STEP_STATUS\n",
|
||||||
|
paused ? "true" : "false",
|
||||||
|
gam.Buffer(),
|
||||||
|
remaining,
|
||||||
|
threadFilter.Buffer());
|
||||||
|
uint32 s = resp.Size();
|
||||||
|
(void)client->Write(resp.Buffer(), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugService::GetSignalValue(const char8 *name, BasicTCPSocket *client) {
|
||||||
|
if (client == NULL_PTR(BasicTCPSocket *)) return;
|
||||||
|
mutex.FastLock();
|
||||||
|
DebugSignalInfo *sig = NULL_PTR(DebugSignalInfo *);
|
||||||
|
for (uint32 i = 0u; i < aliases.Size(); i++) {
|
||||||
|
if (aliases[i].name == name ||
|
||||||
|
SuffixMatch(aliases[i].name.Buffer(), name)) {
|
||||||
|
sig = signals[aliases[i].signalIndex];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sig == NULL_PTR(DebugSignalInfo *)) {
|
||||||
|
mutex.FastUnLock();
|
||||||
|
StreamString resp;
|
||||||
|
resp.Printf("{\"Error\": \"Signal not found: %s\"}\nOK VALUE\n", name);
|
||||||
|
uint32 s = resp.Size();
|
||||||
|
(void)client->Write(resp.Buffer(), s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TypeDescriptor td = sig->type;
|
||||||
|
uint32 nElem = sig->numberOfElements;
|
||||||
|
uint32 byteSize = (td.numberOfBits > 0u) ? (td.numberOfBits / 8u) : 1u;
|
||||||
|
uint32 totalBytes = byteSize * nElem;
|
||||||
|
if (totalBytes > 1024u) { totalBytes = 1024u; nElem = totalBytes / byteSize; }
|
||||||
|
// Copy bytes while holding the mutex to avoid data races with the RT thread
|
||||||
|
uint8 localBuf[1024];
|
||||||
|
memset(localBuf, 0, sizeof(localBuf));
|
||||||
|
if (sig->memoryAddress != NULL_PTR(void *)) {
|
||||||
|
memcpy(localBuf, sig->memoryAddress, totalBytes);
|
||||||
|
}
|
||||||
|
mutex.FastUnLock();
|
||||||
|
|
||||||
|
// Build the value string via CharString TypeConvert — the same path used by
|
||||||
|
// EnrichWithConfig/JsonifyDatabase, which is known to work for all types.
|
||||||
|
StreamString valueStr;
|
||||||
|
if (nElem > 1u) {
|
||||||
|
// Array or matrix: produce comma-separated text
|
||||||
|
for (uint32 i = 0u; i < nElem; i++) {
|
||||||
|
char8 elemBuf[128] = {'\0'};
|
||||||
|
AnyType srcElem(td, 0u, (void *)(localBuf + i * byteSize));
|
||||||
|
AnyType dstStr(CharString, 0u, elemBuf);
|
||||||
|
dstStr.SetNumberOfElements(0u, 128u);
|
||||||
|
(void)TypeConvert(dstStr, srcElem);
|
||||||
|
if (i > 0u) valueStr += ", ";
|
||||||
|
valueStr += elemBuf;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char8 elemBuf[256] = {'\0'};
|
||||||
|
AnyType srcElem(td, 0u, (void *)localBuf);
|
||||||
|
AnyType dstStr(CharString, 0u, elemBuf);
|
||||||
|
dstStr.SetNumberOfElements(0u, 256u);
|
||||||
|
(void)TypeConvert(dstStr, srcElem);
|
||||||
|
valueStr = elemBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamString resp;
|
||||||
|
resp += "{\"Name\": \"";
|
||||||
|
resp += name;
|
||||||
|
resp += "\", \"Value\": \"";
|
||||||
|
// Escape the value text (quotes inside a string value)
|
||||||
|
const char8 *vp = valueStr.Buffer();
|
||||||
|
while (vp != NULL_PTR(const char8 *) && *vp != '\0') {
|
||||||
|
if (*vp == '"') resp += "\\\"";
|
||||||
|
else if (*vp == '\\') resp += "\\\\";
|
||||||
|
else { char8 tmp[2] = { *vp, '\0' }; resp += tmp; }
|
||||||
|
vp++;
|
||||||
|
}
|
||||||
|
resp += "\", \"Elements\": ";
|
||||||
|
resp.Printf("%u}\nOK VALUE\n", nElem);
|
||||||
|
uint32 s = resp.Size();
|
||||||
|
(void)client->Write(resp.Buffer(), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 DebugService::SetBreak(const char8 *name, uint8 op, float64 threshold) {
|
||||||
|
mutex.FastLock();
|
||||||
|
uint32 count = 0;
|
||||||
|
for (uint32 i = 0; i < aliases.Size(); i++) {
|
||||||
|
if (aliases[i].name == name ||
|
||||||
|
SuffixMatch(aliases[i].name.Buffer(), name)) {
|
||||||
|
DebugSignalInfo *s = signals[aliases[i].signalIndex];
|
||||||
|
s->breakThreshold = threshold;
|
||||||
|
s->breakOp = op;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0)
|
||||||
|
UpdateBrokersBreakStatus();
|
||||||
|
mutex.FastUnLock();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 DebugService::ClearBreak(const char8 *name) {
|
||||||
|
mutex.FastLock();
|
||||||
|
uint32 count = 0;
|
||||||
|
for (uint32 i = 0; i < aliases.Size(); i++) {
|
||||||
|
if (aliases[i].name == name ||
|
||||||
|
SuffixMatch(aliases[i].name.Buffer(), name)) {
|
||||||
|
signals[aliases[i].signalIndex]->breakOp = BREAK_OFF;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0)
|
||||||
|
UpdateBrokersBreakStatus();
|
||||||
|
mutex.FastUnLock();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
bool DebugService::IsInstrumented(const char8 *fullPath, bool &traceable,
|
bool DebugService::IsInstrumented(const char8 *fullPath, bool &traceable,
|
||||||
bool &forcable) {
|
bool &forcable) {
|
||||||
mutex.FastLock();
|
mutex.FastLock();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "MessageI.h"
|
#include "MessageI.h"
|
||||||
#include "Object.h"
|
#include "Object.h"
|
||||||
#include "ReferenceContainer.h"
|
#include "ReferenceContainer.h"
|
||||||
|
#include "ReferenceT.h"
|
||||||
#include "SingleThreadService.h"
|
#include "SingleThreadService.h"
|
||||||
#include "StreamString.h"
|
#include "StreamString.h"
|
||||||
#include "Vec.h"
|
#include "Vec.h"
|
||||||
@@ -31,6 +32,12 @@ struct BrokerInfo {
|
|||||||
Vec<uint32> *activeIndices;
|
Vec<uint32> *activeIndices;
|
||||||
Vec<uint32> *activeSizes;
|
Vec<uint32> *activeSizes;
|
||||||
FastPollingMutexSem *activeMutex;
|
FastPollingMutexSem *activeMutex;
|
||||||
|
// Conditional break — mirrors activeIndices but for break-enabled signals
|
||||||
|
volatile bool *anyBreakFlag;
|
||||||
|
Vec<uint32> *breakIndices;
|
||||||
|
// GAM association for step-by-GAM
|
||||||
|
StreamString gamName;
|
||||||
|
bool isOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DebugService : public ReferenceContainer,
|
class DebugService : public ReferenceContainer,
|
||||||
@@ -54,7 +61,10 @@ public:
|
|||||||
void RegisterBroker(DebugSignalInfo **signalPointers, uint32 numSignals,
|
void RegisterBroker(DebugSignalInfo **signalPointers, uint32 numSignals,
|
||||||
MemoryMapBroker *broker, volatile bool *anyActiveFlag,
|
MemoryMapBroker *broker, volatile bool *anyActiveFlag,
|
||||||
Vec<uint32> *activeIndices, Vec<uint32> *activeSizes,
|
Vec<uint32> *activeIndices, Vec<uint32> *activeSizes,
|
||||||
FastPollingMutexSem *activeMutex);
|
FastPollingMutexSem *activeMutex,
|
||||||
|
volatile bool *anyBreakFlag, Vec<uint32> *breakIndices,
|
||||||
|
const char8 *gamName = NULL_PTR(const char8 *),
|
||||||
|
bool isOutput = false);
|
||||||
|
|
||||||
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
|
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
|
||||||
|
|
||||||
@@ -63,17 +73,34 @@ public:
|
|||||||
bool IsPaused() const { return isPaused; }
|
bool IsPaused() const { return isPaused; }
|
||||||
void SetPaused(bool paused) { isPaused = paused; }
|
void SetPaused(bool paused) { isPaused = paused; }
|
||||||
|
|
||||||
|
// Step-by-GAM / per-thread: called by each output broker.
|
||||||
|
// threadName is the OS thread name (from Threads::Name(Threads::Id())).
|
||||||
|
// Decrements stepRemaining only when stepThreadFilter is empty or matches;
|
||||||
|
// when stepRemaining reaches 0, sets isPaused = true and records gamName.
|
||||||
|
void ConsumeStepIfNeeded(const char8 *gamName,
|
||||||
|
const char8 *threadName = NULL_PTR(const char8 *));
|
||||||
|
|
||||||
|
void GetStepStatus(BasicTCPSocket *client);
|
||||||
|
// Step n cycles; if threadName is non-null/non-empty, only that thread advances.
|
||||||
|
void Step(uint32 n, const char8 *threadName = NULL_PTR(const char8 *));
|
||||||
|
|
||||||
|
// Read the current raw value of a signal from its memory address.
|
||||||
|
void GetSignalValue(const char8 *name, BasicTCPSocket *client);
|
||||||
|
|
||||||
static bool GetFullObjectName(const Object &obj, StreamString &fullPath);
|
static bool GetFullObjectName(const Object &obj, StreamString &fullPath);
|
||||||
|
|
||||||
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);
|
||||||
|
uint32 SetBreak(const char8 *name, uint8 op, float64 threshold);
|
||||||
|
uint32 ClearBreak(const char8 *name);
|
||||||
bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable);
|
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);
|
||||||
|
void RebuildConfigFromRegistry();
|
||||||
|
|
||||||
struct MonitoredSignal {
|
struct MonitoredSignal {
|
||||||
ReferenceT<DataSourceI> dataSource;
|
ReferenceT<DataSourceI> dataSource;
|
||||||
@@ -91,6 +118,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
void HandleCommand(StreamString cmd, BasicTCPSocket *client);
|
void HandleCommand(StreamString cmd, BasicTCPSocket *client);
|
||||||
void UpdateBrokersActiveStatus();
|
void UpdateBrokersActiveStatus();
|
||||||
|
void UpdateBrokersBreakStatus();
|
||||||
|
void InjectTcpLoggerIfNeeded();
|
||||||
|
|
||||||
uint32 ExportTree(ReferenceContainer *container, StreamString &json, const char8 *pathPrefix);
|
uint32 ExportTree(ReferenceContainer *container, StreamString &json, const char8 *pathPrefix);
|
||||||
void PatchRegistry();
|
void PatchRegistry();
|
||||||
@@ -108,6 +137,9 @@ private:
|
|||||||
bool isServer;
|
bool isServer;
|
||||||
bool suppressTimeoutLogs;
|
bool suppressTimeoutLogs;
|
||||||
volatile bool isPaused;
|
volatile bool isPaused;
|
||||||
|
volatile uint32 stepRemaining;
|
||||||
|
StreamString pausedAtGam;
|
||||||
|
StreamString stepThreadFilter; // empty = all threads; non-empty = only this OS thread name
|
||||||
|
|
||||||
BasicTCPSocket tcpServer;
|
BasicTCPSocket tcpServer;
|
||||||
BasicUDPSocket udpSocket;
|
BasicUDPSocket udpSocket;
|
||||||
@@ -121,7 +153,6 @@ private:
|
|||||||
if (type == StreamerType) {
|
if (type == StreamerType) {
|
||||||
return parent->Streamer(info);
|
return parent->Streamer(info);
|
||||||
}
|
}
|
||||||
printf("serve TCP\n");
|
|
||||||
return parent->Server(info);
|
return parent->Server(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +180,13 @@ private:
|
|||||||
|
|
||||||
BasicTCPSocket *activeClient;
|
BasicTCPSocket *activeClient;
|
||||||
|
|
||||||
|
// Streamer state persisted across Execute() calls (framework loops Execute)
|
||||||
|
uint8 streamerPacketBuffer[4096];
|
||||||
|
uint32 streamerPacketOffset;
|
||||||
|
uint32 streamerSequenceNumber;
|
||||||
|
|
||||||
ConfigurationDatabase fullConfig;
|
ConfigurationDatabase fullConfig;
|
||||||
|
bool manualConfigSet;
|
||||||
|
|
||||||
static DebugService *instance;
|
static DebugService *instance;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ TcpLogger::~TcpLogger() {
|
|||||||
clientsMutex.FastUnLock();
|
clientsMutex.FastUnLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TcpLogger::ExportData(StructuredDataI & data) {
|
||||||
|
bool ok = data.Write("Port", static_cast<uint32>(port));
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
bool TcpLogger::Initialise(StructuredDataI & data) {
|
bool TcpLogger::Initialise(StructuredDataI & data) {
|
||||||
if (!ReferenceContainer::Initialise(data)) return false;
|
if (!ReferenceContainer::Initialise(data)) return false;
|
||||||
|
|
||||||
@@ -92,64 +97,66 @@ ErrorManagement::ErrorType TcpLogger::Execute(ExecutionInfo & info) {
|
|||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (info.GetStage() == ExecutionInfo::MainStage) {
|
// Each Execute() call does one cycle. The MARTe2 framework loops Execute()
|
||||||
// 1. Check for new connections
|
// so we must NOT spin in an infinite internal loop here — doing so prevents
|
||||||
BasicTCPSocket *newClient = server.WaitConnection(1);
|
// the framework from ever delivering the TerminationStage and causes
|
||||||
if (newClient != NULL_PTR(BasicTCPSocket *)) {
|
// Stop() to time out, leaving threads running after the destructor.
|
||||||
clientsMutex.FastLock();
|
|
||||||
bool added = false;
|
// 1. Check for new connections (1 ms timeout → returns promptly)
|
||||||
for (uint32 i=0; i<MAX_CLIENTS; i++) {
|
BasicTCPSocket *newClient = server.WaitConnection(1);
|
||||||
if (activeClients[i] == NULL_PTR(BasicTCPSocket*)) {
|
if (newClient != NULL_PTR(BasicTCPSocket *)) {
|
||||||
activeClients[i] = newClient;
|
clientsMutex.FastLock();
|
||||||
added = true;
|
bool added = false;
|
||||||
break;
|
for (uint32 i=0; i<MAX_CLIENTS; i++) {
|
||||||
}
|
if (activeClients[i] == NULL_PTR(BasicTCPSocket*)) {
|
||||||
}
|
activeClients[i] = newClient;
|
||||||
clientsMutex.FastUnLock();
|
added = true;
|
||||||
if (!added) {
|
break;
|
||||||
newClient->Close();
|
|
||||||
delete newClient;
|
|
||||||
} else {
|
|
||||||
(void)newClient->SetBlocking(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clientsMutex.FastUnLock();
|
||||||
// 2. Stream data to clients
|
if (!added) {
|
||||||
bool hadData = false;
|
newClient->Close();
|
||||||
while (readIdx != writeIdx) {
|
delete newClient;
|
||||||
hadData = true;
|
|
||||||
uint32 idx = readIdx % QUEUE_SIZE;
|
|
||||||
TcpLogEntry &entry = queue[idx];
|
|
||||||
|
|
||||||
StreamString level;
|
|
||||||
ErrorManagement::ErrorCodeToStream(entry.info.header.errorType, level);
|
|
||||||
|
|
||||||
StreamString packet;
|
|
||||||
packet.Printf("LOG %s %s\n", level.Buffer(), entry.description);
|
|
||||||
uint32 size = packet.Size();
|
|
||||||
|
|
||||||
clientsMutex.FastLock();
|
|
||||||
for (uint32 j=0; j<MAX_CLIENTS; j++) {
|
|
||||||
if (activeClients[j] != NULL_PTR(BasicTCPSocket*)) {
|
|
||||||
uint32 s = size;
|
|
||||||
if (!activeClients[j]->Write(packet.Buffer(), s)) {
|
|
||||||
activeClients[j]->Close();
|
|
||||||
delete activeClients[j];
|
|
||||||
activeClients[j] = NULL_PTR(BasicTCPSocket*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientsMutex.FastUnLock();
|
|
||||||
readIdx = (readIdx + 1) % QUEUE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hadData) {
|
|
||||||
(void)eventSem.Wait(TimeoutType(100));
|
|
||||||
eventSem.Reset();
|
|
||||||
} else {
|
} else {
|
||||||
Sleep::MSec(1);
|
(void)newClient->SetBlocking(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Stream queued entries to clients
|
||||||
|
bool hadData = false;
|
||||||
|
while (readIdx != writeIdx) {
|
||||||
|
hadData = true;
|
||||||
|
uint32 idx = readIdx % QUEUE_SIZE;
|
||||||
|
TcpLogEntry &entry = queue[idx];
|
||||||
|
|
||||||
|
StreamString level;
|
||||||
|
ErrorManagement::ErrorCodeToStream(entry.info.header.errorType, level);
|
||||||
|
|
||||||
|
StreamString packet;
|
||||||
|
packet.Printf("LOG %s %s\n", level.Buffer(), entry.description);
|
||||||
|
uint32 size = packet.Size();
|
||||||
|
|
||||||
|
clientsMutex.FastLock();
|
||||||
|
for (uint32 j=0; j<MAX_CLIENTS; j++) {
|
||||||
|
if (activeClients[j] != NULL_PTR(BasicTCPSocket*)) {
|
||||||
|
uint32 s = size;
|
||||||
|
if (!activeClients[j]->Write(packet.Buffer(), s)) {
|
||||||
|
activeClients[j]->Close();
|
||||||
|
delete activeClients[j];
|
||||||
|
activeClients[j] = NULL_PTR(BasicTCPSocket*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientsMutex.FastUnLock();
|
||||||
|
readIdx = (readIdx + 1) % QUEUE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hadData) {
|
||||||
|
// Brief wait so we don't busy-spin; return so Stop() can take effect
|
||||||
|
(void)eventSem.Wait(TimeoutType(10));
|
||||||
|
eventSem.Reset();
|
||||||
|
}
|
||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public:
|
|||||||
|
|
||||||
virtual bool Initialise(StructuredDataI & data);
|
virtual bool Initialise(StructuredDataI & data);
|
||||||
|
|
||||||
|
virtual bool ExportData(StructuredDataI & data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Implementation of LoggerConsumerI.
|
* @brief Implementation of LoggerConsumerI.
|
||||||
* Called by LoggerService.
|
* Called by LoggerService.
|
||||||
|
|||||||
@@ -26,6 +26,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
+CGAM = {
|
||||||
|
Class = ConstantGAM
|
||||||
|
OutputSignals = {
|
||||||
|
Test = {
|
||||||
|
DataSource = DDB2
|
||||||
|
Type = float32
|
||||||
|
Default = 0.123
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+GAM2 = {
|
+GAM2 = {
|
||||||
Class = IOGAM
|
Class = IOGAM
|
||||||
InputSignals = {
|
InputSignals = {
|
||||||
@@ -36,6 +46,10 @@
|
|||||||
Time = {
|
Time = {
|
||||||
DataSource = TimerSlow
|
DataSource = TimerSlow
|
||||||
}
|
}
|
||||||
|
Test = {
|
||||||
|
DataSource = DDB2
|
||||||
|
Type = float32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OutputSignals = {
|
OutputSignals = {
|
||||||
Counter = {
|
Counter = {
|
||||||
@@ -46,6 +60,10 @@
|
|||||||
Type = uint32
|
Type = uint32
|
||||||
DataSource = Logger
|
DataSource = Logger
|
||||||
}
|
}
|
||||||
|
ConstOut = {
|
||||||
|
DataSource = Logger
|
||||||
|
Type = float32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+GAM3 = {
|
+GAM3 = {
|
||||||
@@ -122,6 +140,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
+DDB2 = {
|
||||||
|
AllowNoProducer = 1
|
||||||
|
Class = GAMDataSource
|
||||||
|
}
|
||||||
+DDB3 = {
|
+DDB3 = {
|
||||||
AllowNoProducer = 1
|
AllowNoProducer = 1
|
||||||
Class = GAMDataSource
|
Class = GAMDataSource
|
||||||
@@ -142,7 +164,7 @@
|
|||||||
}
|
}
|
||||||
+Thread2 = {
|
+Thread2 = {
|
||||||
Class = RealTimeThread
|
Class = RealTimeThread
|
||||||
Functions = {GAM2}
|
Functions = {GAM2 CGAM}
|
||||||
}
|
}
|
||||||
+Thread3 = {
|
+Thread3 = {
|
||||||
Class = RealTimeThread
|
Class = RealTimeThread
|
||||||
@@ -165,11 +187,3 @@
|
|||||||
StreamIP = "127.0.0.1"
|
StreamIP = "127.0.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
+LoggerService = {
|
|
||||||
Class = LoggerService
|
|
||||||
CPUs = 0x1
|
|
||||||
+DebugConsumer = {
|
|
||||||
Class = TcpLogger
|
|
||||||
Port = 8082
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -27,16 +27,18 @@ const char8 * const config_command_text =
|
|||||||
" CustomGAMField = \"GAMValue\" "
|
" CustomGAMField = \"GAMValue\" "
|
||||||
" InputSignals = {"
|
" InputSignals = {"
|
||||||
" Counter = { DataSource = Timer Type = uint32 Frequency = 1000 PVName = \"PROC:VAR:1\" }"
|
" Counter = { DataSource = Timer Type = uint32 Frequency = 1000 PVName = \"PROC:VAR:1\" }"
|
||||||
|
" Time = { DataSource = Timer Type = uint32 }"
|
||||||
" }"
|
" }"
|
||||||
" OutputSignals = {"
|
" OutputSignals = {"
|
||||||
" Counter = { DataSource = DDB Type = uint32 }"
|
" Counter = { DataSource = DDB Type = uint32 }"
|
||||||
|
" Time = { DataSource = DDB Type = uint32 }"
|
||||||
" }"
|
" }"
|
||||||
" }"
|
" }"
|
||||||
" }"
|
" }"
|
||||||
" +Data = {"
|
" +Data = {"
|
||||||
" Class = ReferenceContainer "
|
" Class = ReferenceContainer "
|
||||||
" +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } } }"
|
" +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } Time = { Type = uint32 } } }"
|
||||||
" +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } } }"
|
" +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } Time = { Type = uint32 } } }"
|
||||||
" +DAMS = { Class = TimingDataSource }"
|
" +DAMS = { Class = TimingDataSource }"
|
||||||
" }"
|
" }"
|
||||||
" +States = {"
|
" +States = {"
|
||||||
@@ -97,16 +99,12 @@ void TestConfigCommands() {
|
|||||||
|
|
||||||
// Start the application to trigger broker execution and signal registration
|
// Start the application to trigger broker execution and signal registration
|
||||||
ReferenceT<RealTimeApplication> app = ObjectRegistryDatabase::Instance()->Find("App");
|
ReferenceT<RealTimeApplication> app = ObjectRegistryDatabase::Instance()->Find("App");
|
||||||
if (app.IsValid()) {
|
assert(app.IsValid());
|
||||||
if (app->ConfigureApplication()) {
|
assert(app->ConfigureApplication());
|
||||||
if (app->PrepareNextState("State1") == ErrorManagement::NoError) {
|
assert(app->PrepareNextState("State1") == ErrorManagement::NoError);
|
||||||
if (app->StartNextStateExecution() == ErrorManagement::NoError) {
|
assert(app->StartNextStateExecution() == ErrorManagement::NoError);
|
||||||
printf("Application started (for signal registration).\n");
|
printf("Application started (for signal registration).\n");
|
||||||
Sleep::MSec(500); // Wait for some cycles
|
Sleep::MSec(500); // Wait for some cycles
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReferenceT<DebugService> service = ObjectRegistryDatabase::Instance()->Find("DebugService");
|
ReferenceT<DebugService> service = ObjectRegistryDatabase::Instance()->Find("DebugService");
|
||||||
if (service.IsValid()) {
|
if (service.IsValid()) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ void timeout_handler(int sig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ErrorProcessFunction(const MARTe::ErrorManagement::ErrorInformation &errorInfo, const char8 * const errorDescription) {
|
void ErrorProcessFunction(const MARTe::ErrorManagement::ErrorInformation &errorInfo, const char8 * const errorDescription) {
|
||||||
// printf("[MARTe Error] %s: %s\n", errorInfo.className, errorDescription);
|
printf("[MARTe Error] %s: %s\n", errorInfo.className, errorDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward declarations of other tests
|
// Forward declarations of other tests
|
||||||
@@ -80,7 +80,7 @@ int main() {
|
|||||||
Sleep::MSec(1000);
|
Sleep::MSec(1000);
|
||||||
|
|
||||||
printf("\n--- Test 5: Config & Metadata Enrichment ---\n");
|
printf("\n--- Test 5: Config & Metadata Enrichment ---\n");
|
||||||
// TestConfigCommands(); // Skipping for now
|
TestConfigCommands();
|
||||||
Sleep::MSec(1000);
|
Sleep::MSec(1000);
|
||||||
|
|
||||||
printf("\n--- Test 6: TREE Command Enhancement ---\n");
|
printf("\n--- Test 6: TREE Command Enhancement ---\n");
|
||||||
|
|||||||
@@ -39,17 +39,19 @@ void TestTreeCommand() {
|
|||||||
" Class = IOGAM "
|
" Class = IOGAM "
|
||||||
" InputSignals = {"
|
" InputSignals = {"
|
||||||
" Counter = { DataSource = Timer Type = uint32 Frequency = 1000 }"
|
" Counter = { DataSource = Timer Type = uint32 Frequency = 1000 }"
|
||||||
|
" Time = { DataSource = Timer Type = uint32 }"
|
||||||
" }"
|
" }"
|
||||||
" OutputSignals = {"
|
" OutputSignals = {"
|
||||||
" Counter = { DataSource = DDB Type = uint32 }"
|
" Counter = { DataSource = DDB Type = uint32 }"
|
||||||
|
" Time = { DataSource = DDB Type = uint32 }"
|
||||||
" }"
|
" }"
|
||||||
" }"
|
" }"
|
||||||
" }"
|
" }"
|
||||||
" +Data = {"
|
" +Data = {"
|
||||||
" Class = ReferenceContainer "
|
" Class = ReferenceContainer "
|
||||||
" DefaultDataSource = DDB "
|
" DefaultDataSource = DDB "
|
||||||
" +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } } }"
|
" +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } Time = { Type = uint32 } } }"
|
||||||
" +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } } }"
|
" +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } Time = { Type = uint32 } } }"
|
||||||
" +DAMS = { Class = TimingDataSource }"
|
" +DAMS = { Class = TimingDataSource }"
|
||||||
" }"
|
" }"
|
||||||
" +States = {"
|
" +States = {"
|
||||||
|
|||||||
@@ -63,14 +63,61 @@ public:
|
|||||||
Vec<uint32> sizes;
|
Vec<uint32> sizes;
|
||||||
FastPollingMutexSem mutex;
|
FastPollingMutexSem mutex;
|
||||||
DebugSignalInfo* ptrs[1] = { service.signals[0] };
|
DebugSignalInfo* ptrs[1] = { service.signals[0] };
|
||||||
service.RegisterBroker(ptrs, 1, NULL_PTR(MemoryMapBroker*), &active, &indices, &sizes, &mutex);
|
volatile bool anyBreak = false;
|
||||||
|
Vec<uint32> breakIdx;
|
||||||
|
service.RegisterBroker(ptrs, 1, NULL_PTR(MemoryMapBroker*), &active, &indices, &sizes, &mutex, &anyBreak, &breakIdx);
|
||||||
service.UpdateBrokersActiveStatus();
|
service.UpdateBrokersActiveStatus();
|
||||||
assert(active == true);
|
assert(active == true);
|
||||||
assert(indices.Size() == 1);
|
assert(indices.Size() == 1);
|
||||||
assert(indices[0] == 0);
|
assert(indices[0] == 0);
|
||||||
|
|
||||||
// Helper Process
|
// Helper Process
|
||||||
DebugBrokerHelper::Process(&service, ptrs, indices, sizes, mutex);
|
DebugBrokerHelper::Process(&service, ptrs, indices, sizes, mutex, &anyBreak, &breakIdx);
|
||||||
|
|
||||||
|
// 4. Step / per-thread filter
|
||||||
|
// STEP with no thread filter — all threads can consume
|
||||||
|
service.Step(2u, NULL_PTR(const char8 *));
|
||||||
|
assert(!service.IsPaused());
|
||||||
|
assert(service.stepRemaining == 2u);
|
||||||
|
service.ConsumeStepIfNeeded("GAM1", "ThreadA");
|
||||||
|
assert(service.stepRemaining == 1u);
|
||||||
|
assert(!service.IsPaused());
|
||||||
|
service.ConsumeStepIfNeeded("GAM1", "ThreadB");
|
||||||
|
assert(service.stepRemaining == 0u);
|
||||||
|
assert(service.IsPaused());
|
||||||
|
assert(service.pausedAtGam == "GAM1");
|
||||||
|
|
||||||
|
// STEP with a thread filter — only matching thread consumes
|
||||||
|
service.Step(2u, "ThreadA");
|
||||||
|
assert(!service.IsPaused());
|
||||||
|
assert(service.stepThreadFilter == "ThreadA");
|
||||||
|
// ThreadB should be ignored
|
||||||
|
service.ConsumeStepIfNeeded("GAMB", "ThreadB");
|
||||||
|
assert(service.stepRemaining == 2u); // unchanged
|
||||||
|
assert(!service.IsPaused());
|
||||||
|
// ThreadA consumes both credits
|
||||||
|
service.ConsumeStepIfNeeded("GAMA1", "ThreadA");
|
||||||
|
assert(service.stepRemaining == 1u);
|
||||||
|
assert(!service.IsPaused());
|
||||||
|
service.ConsumeStepIfNeeded("GAMA2", "ThreadA");
|
||||||
|
assert(service.stepRemaining == 0u);
|
||||||
|
assert(service.IsPaused());
|
||||||
|
assert(service.pausedAtGam == "GAMA2");
|
||||||
|
|
||||||
|
// STEP with unknown thread (NULL threadName arg to ConsumeStep) is also filtered out
|
||||||
|
service.Step(1u, "ThreadA");
|
||||||
|
service.ConsumeStepIfNeeded("X", NULL_PTR(const char8 *));
|
||||||
|
assert(service.stepRemaining == 1u); // not consumed
|
||||||
|
service.SetPaused(false);
|
||||||
|
service.Step(0u, NULL_PTR(const char8 *));
|
||||||
|
|
||||||
|
// 5. VALUE command (NULL client — smoke test, no crash)
|
||||||
|
service.HandleCommand("VALUE X.Y.Z", NULL_PTR(BasicTCPSocket*));
|
||||||
|
service.HandleCommand("VALUE NoSuchSignal", NULL_PTR(BasicTCPSocket*));
|
||||||
|
service.HandleCommand("STEP 1 ThreadA", NULL_PTR(BasicTCPSocket*));
|
||||||
|
assert(service.stepThreadFilter == "ThreadA");
|
||||||
|
service.HandleCommand("STEP 3", NULL_PTR(BasicTCPSocket*));
|
||||||
|
assert(service.stepThreadFilter.Size() == 0u);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,9 +133,9 @@ void timeout_handler(int sig) {
|
|||||||
int main() {
|
int main() {
|
||||||
signal(SIGALRM, timeout_handler);
|
signal(SIGALRM, timeout_handler);
|
||||||
alarm(10);
|
alarm(10);
|
||||||
printf("--- MARTe2 Debug Suite COVERAGE V29 ---\n");
|
printf("--- MARTe2 Debug Suite COVERAGE V30 ---\n");
|
||||||
MARTe::TestTcpLogger();
|
MARTe::TestTcpLogger();
|
||||||
MARTe::DebugServiceTest::TestAll();
|
MARTe::DebugServiceTest::TestAll();
|
||||||
printf("\nCOVERAGE V29 PASSED!\n");
|
printf("\nCOVERAGE V30 PASSED!\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+1189
-100
File diff suppressed because it is too large
Load Diff
+34
-7
@@ -11,13 +11,16 @@ fi
|
|||||||
|
|
||||||
# 2. Paths
|
# 2. Paths
|
||||||
MARTE_EX="${MARTe2_DIR}/Build/${TARGET}/App/MARTeApp.ex"
|
MARTE_EX="${MARTe2_DIR}/Build/${TARGET}/App/MARTeApp.ex"
|
||||||
DEBUG_LIB="$(pwd)/Build/libmarte_dev.so"
|
BUILD_DIR="$(pwd)/Build/${TARGET}"
|
||||||
|
|
||||||
|
# Our plugin libraries
|
||||||
|
DEBUG_LIB="${BUILD_DIR}/Components/Interfaces/DebugService/libDebugService.so"
|
||||||
|
TCPLOGGER_LIB="${BUILD_DIR}/Components/Interfaces/TCPLogger/libTcpLogger.so"
|
||||||
|
|
||||||
# Component library base search path
|
# Component library base search path
|
||||||
COMPONENTS_BUILD_DIR="${MARTe2_Components_DIR}/Build/${TARGET}/Components"
|
COMPONENTS_BUILD_DIR="${MARTe2_Components_DIR}/Build/${TARGET}/Components"
|
||||||
|
|
||||||
# DYNAMICALLY FIND ALL COMPONENT DIRS
|
# DYNAMICALLY FIND ALL COMPONENT DIRS and add to LD_LIBRARY_PATH
|
||||||
# MARTe2 Loader needs the specific directories containing .so files in LD_LIBRARY_PATH
|
|
||||||
ALL_COMPONENT_DIRS=$(find "$COMPONENTS_BUILD_DIR" -type d)
|
ALL_COMPONENT_DIRS=$(find "$COMPONENTS_BUILD_DIR" -type d)
|
||||||
for dir in $ALL_COMPONENT_DIRS; do
|
for dir in $ALL_COMPONENT_DIRS; do
|
||||||
if ls "$dir"/*.so >/dev/null 2>&1; then
|
if ls "$dir"/*.so >/dev/null 2>&1; then
|
||||||
@@ -26,15 +29,39 @@ for dir in $ALL_COMPONENT_DIRS; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Ensure our build dir and core dir are included
|
# Ensure our build dir and core dir are included
|
||||||
export LD_LIBRARY_PATH="$(pwd)/Build:${MARTe2_DIR}/Build/${TARGET}/Core:${LD_LIBRARY_PATH}"
|
export LD_LIBRARY_PATH="${BUILD_DIR}/Components/Interfaces/DebugService:${BUILD_DIR}/Components/Interfaces/TCPLogger:${MARTe2_DIR}/Build/${TARGET}/Core:${LD_LIBRARY_PATH}"
|
||||||
|
|
||||||
# 3. Cleanup
|
# 3. Cleanup
|
||||||
echo "Cleaning up lingering processes..."
|
echo "Cleaning up lingering processes..."
|
||||||
pkill -9 MARTeApp.ex
|
pkill -9 MARTeApp.ex
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
# 4. Launch Application
|
# 4. Validate libraries exist
|
||||||
|
for lib in "$DEBUG_LIB" "$TCPLOGGER_LIB"; do
|
||||||
|
if [ ! -f "$lib" ]; then
|
||||||
|
echo "ERROR: Library not found: $lib"
|
||||||
|
echo "Run: make -f Makefile.gcc core"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 5. Launch Application
|
||||||
echo "Launching standard MARTeApp.ex with debug_test.cfg..."
|
echo "Launching standard MARTeApp.ex with debug_test.cfg..."
|
||||||
# PRELOAD ensures our DebugService class is available to the registry early
|
# LD_PRELOAD ensures our classes are registered before the config is parsed.
|
||||||
export LD_PRELOAD="${DEBUG_LIB}"
|
# MARTeApp.ex does not use dlopen for plugins — preloading is the standard approach.
|
||||||
|
# All required component .so files are collected via the find loop into LD_PRELOAD too.
|
||||||
|
PRELOAD_LIBS="${DEBUG_LIB}:${TCPLOGGER_LIB}"
|
||||||
|
|
||||||
|
# Collect any additional component .so files referenced by the config
|
||||||
|
for lib in \
|
||||||
|
"${COMPONENTS_BUILD_DIR}/DataSources/RealTimeThreadSynchronisation/RealTimeThreadSynchronisation.so" \
|
||||||
|
"${COMPONENTS_BUILD_DIR}/DataSources/LinuxTimer/LinuxTimer.so" \
|
||||||
|
"${COMPONENTS_BUILD_DIR}/DataSources/LoggerDataSource/LoggerDataSource.so" \
|
||||||
|
"${COMPONENTS_BUILD_DIR}/GAMs/IOGAM/IOGAM.so"; do
|
||||||
|
if [ -f "$lib" ]; then
|
||||||
|
PRELOAD_LIBS="${PRELOAD_LIBS}:${lib}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
export LD_PRELOAD="${PRELOAD_LIBS}"
|
||||||
"$MARTE_EX" -f Test/Configurations/debug_test.cfg -l RealTimeLoader -s State1
|
"$MARTE_EX" -f Test/Configurations/debug_test.cfg -l RealTimeLoader -s State1
|
||||||
|
|||||||
Reference in New Issue
Block a user