Updated with scheduler
This commit is contained in:
61
API.md
Normal file
61
API.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# API Documentation
|
||||||
|
|
||||||
|
## 1. TCP Control Interface (Port 8080)
|
||||||
|
|
||||||
|
### 1.1 `TREE`
|
||||||
|
Retrieves the full object hierarchy.
|
||||||
|
- **Request:** `TREE
|
||||||
|
`
|
||||||
|
- **Response:** `JSON_OBJECT
|
||||||
|
OK TREE
|
||||||
|
`
|
||||||
|
|
||||||
|
### 1.2 `DISCOVER`
|
||||||
|
Lists all registrable signals and their metadata.
|
||||||
|
- **Request:** `DISCOVER
|
||||||
|
`
|
||||||
|
- **Response:** `{"Signals": [...]}
|
||||||
|
OK DISCOVER
|
||||||
|
`
|
||||||
|
|
||||||
|
### 1.3 `TRACE <path> <state>`
|
||||||
|
Enables/disables telemetry for a signal.
|
||||||
|
- **Example:** `TRACE App.Data.Timer.Counter 1
|
||||||
|
`
|
||||||
|
- **Response:** `OK TRACE <match_count>
|
||||||
|
`
|
||||||
|
|
||||||
|
### 1.4 `FORCE <path> <value>`
|
||||||
|
Overrides a signal value in memory.
|
||||||
|
- **Example:** `FORCE App.Data.DDB.Signal 123.4
|
||||||
|
`
|
||||||
|
- **Response:** `OK FORCE <match_count>
|
||||||
|
`
|
||||||
|
|
||||||
|
### 1.5 `PAUSE` / `RESUME`
|
||||||
|
Controls global execution state via the Scheduler.
|
||||||
|
- **Request:** `PAUSE
|
||||||
|
`
|
||||||
|
- **Response:** `OK
|
||||||
|
`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. UDP Telemetry Format (Port 8081)
|
||||||
|
|
||||||
|
Telemetry packets are Little-Endian and use `#pragma pack(1)`.
|
||||||
|
|
||||||
|
### 2.1 TraceHeader (20 Bytes)
|
||||||
|
| Offset | Type | Name | Description |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| 0 | uint32 | magic | Always `0xDA7A57AD` |
|
||||||
|
| 4 | uint32 | seq | Incremental sequence number |
|
||||||
|
| 8 | uint64 | timestamp | High-resolution timestamp |
|
||||||
|
| 16 | uint32 | count | Number of samples in payload |
|
||||||
|
|
||||||
|
### 2.2 Sample Entry
|
||||||
|
| Offset | Type | Name | Description |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| 0 | uint32 | id | Internal Signal ID (from `DISCOVER`) |
|
||||||
|
| 4 | uint32 | size | Data size in bytes |
|
||||||
|
| 8 | Bytes | data | Raw signal memory |
|
||||||
41
ARCHITECTURE.md
Normal file
41
ARCHITECTURE.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# System Architecture
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
The suite consists of a C++ server library (`libmarte_dev.so`) that instrument MARTe2 applications at runtime, and a native Rust client for visualization.
|
||||||
|
|
||||||
|
## 2. Core Mechanism: Registry Patching
|
||||||
|
The "Zero-Code-Change" requirement is met by intercepting the MARTe2 `ClassRegistryDatabase`. When the `DebugService` initializes, it replaces the standard object builders for critical classes:
|
||||||
|
|
||||||
|
1. **Broker Injection:** Standard Brokers (e.g., `MemoryMapInputBroker`) are replaced with `DebugBrokerWrapper`. This captures data every time a GAM reads or writes a signal.
|
||||||
|
2. **Scheduler Injection:** The `FastScheduler` is replaced with `DebugFastScheduler`. This provides hooks at the start/end of each real-time cycle for execution control (pausing).
|
||||||
|
|
||||||
|
## 3. Communication Layer
|
||||||
|
The system uses three distinct channels:
|
||||||
|
|
||||||
|
### 3.1 Command & Control (TCP Port 8080)
|
||||||
|
- **Protocol:** Text-based over TCP.
|
||||||
|
- **Role:** Object tree discovery (`TREE`), signal metadata (`DISCOVER`), and trace activation (`TRACE`).
|
||||||
|
- **Response Format:** JSON for complex data, `OK/ERROR` for status.
|
||||||
|
|
||||||
|
### 3.2 High-Speed Telemetry (UDP Port 8081)
|
||||||
|
- **Protocol:** Binary over UDP.
|
||||||
|
- **Format:** Packed C-structs.
|
||||||
|
- **Header:** `[Magic:4][Seq:4][TS:8][Count:4]` (Packed, 20 bytes).
|
||||||
|
- **Payload:** `[ID:4][Size:4][Data:N]` (Repeated for each traced signal).
|
||||||
|
|
||||||
|
### 3.3 Log Streaming (TCP Port 8082)
|
||||||
|
- **Protocol:** Real-time event streaming.
|
||||||
|
- **Role:** Forwards global `REPORT_ERROR` calls from the framework to the GUI client.
|
||||||
|
|
||||||
|
## 4. Component Diagram
|
||||||
|
```text
|
||||||
|
[ MARTe2 App ] <--- [ DebugFastScheduler ] (Registry Patch)
|
||||||
|
| |
|
||||||
|
+ <--- [ DebugBrokerWrapper ] (Registry Patch)
|
||||||
|
| |
|
||||||
|
[ DebugService ] <----------+
|
||||||
|
|
|
||||||
|
+---- (TCP 8080) ----> [ Rust GUI Client ]
|
||||||
|
+---- (UDP 8081) ----> [ (Oscilloscope) ]
|
||||||
|
+---- (TCP 8082) ----> [ (Log Terminal) ]
|
||||||
|
```
|
||||||
@@ -35,6 +35,7 @@ include_directories(
|
|||||||
${MARTe2_DIR}/Source/Core/Scheduler/L4LoggerService
|
${MARTe2_DIR}/Source/Core/Scheduler/L4LoggerService
|
||||||
${MARTe2_DIR}/Source/Core/FileSystem/L1Portability
|
${MARTe2_DIR}/Source/Core/FileSystem/L1Portability
|
||||||
${MARTe2_DIR}/Source/Core/FileSystem/L3Streams
|
${MARTe2_DIR}/Source/Core/FileSystem/L3Streams
|
||||||
|
${MARTe2_DIR}/Source/Core/Scheduler/L5GAMs
|
||||||
${MARTe2_Components_DIR}/Source/Components/DataSources/EpicsDataSource
|
${MARTe2_Components_DIR}/Source/Components/DataSources/EpicsDataSource
|
||||||
${MARTe2_Components_DIR}/Source/Components/DataSources/FileDataSource
|
${MARTe2_Components_DIR}/Source/Components/DataSources/FileDataSource
|
||||||
${MARTe2_Components_DIR}/Source/Components/GAMs/IOGAM
|
${MARTe2_Components_DIR}/Source/Components/GAMs/IOGAM
|
||||||
|
|||||||
32
DEMO.md
Normal file
32
DEMO.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Demo Walkthrough: High-Speed Tracing
|
||||||
|
|
||||||
|
This demo demonstrates tracing a `Timer.Counter` signal at 100Hz and verifying its consistency.
|
||||||
|
|
||||||
|
### 1. Launch the Test Environment
|
||||||
|
Start the validation environment which simulates a real-time app:
|
||||||
|
```bash
|
||||||
|
./Build/Test/Integration/ValidationTest
|
||||||
|
```
|
||||||
|
*Note: The test will wait for a trace command before finishing.*
|
||||||
|
|
||||||
|
### 2. Connect the GUI
|
||||||
|
In another terminal:
|
||||||
|
```bash
|
||||||
|
cd Tools/gui_client
|
||||||
|
cargo run --release
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Explore the Tree
|
||||||
|
1. On the left panel (**Application Tree**), expand `Root.App.Data.Timer`.
|
||||||
|
2. Click the `ℹ Info` button on the `Timer` node to see its configuration (e.g., `SleepTime: 10000`).
|
||||||
|
|
||||||
|
### 4. Activate Trace
|
||||||
|
1. Locate the `Counter` signal under the `Timer` node.
|
||||||
|
2. Click the **📈 Trace** button.
|
||||||
|
3. The **Oscilloscope** in the center will immediately begin plotting the incremental counter.
|
||||||
|
4. Verify the **UDP Packets** counter in the top bar is increasing rapidly.
|
||||||
|
|
||||||
|
### 5. Test Execution Control
|
||||||
|
1. Click the **⏸ Pause** button in the top bar.
|
||||||
|
2. Observe that the plot stops updating and the counter value holds steady.
|
||||||
|
3. Click **▶ Resume** to continue execution.
|
||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
StreamString signalName;
|
StreamString signalName;
|
||||||
if (!dataSourceIn.GetSignalName(dsIdx, signalName)) signalName = "Unknown";
|
if (!dataSourceIn.GetSignalName(dsIdx, signalName)) signalName = "Unknown";
|
||||||
|
|
||||||
// 1. Register canonical DataSource name (Absolute)
|
// 1. Register canonical DataSource name (Absolute, No Root prefix)
|
||||||
StreamString dsFullName;
|
StreamString dsFullName;
|
||||||
dsFullName.Printf("%s.%s", dsPath.Buffer(), signalName.Buffer());
|
dsFullName.Printf("%s.%s", dsPath.Buffer(), signalName.Buffer());
|
||||||
service->RegisterSignal(addr, type, dsFullName.Buffer());
|
service->RegisterSignal(addr, type, dsFullName.Buffer());
|
||||||
@@ -87,7 +87,7 @@ public:
|
|||||||
DebugService::GetFullObjectName(*(gamRef.operator->()), absGamPath);
|
DebugService::GetFullObjectName(*(gamRef.operator->()), absGamPath);
|
||||||
gamFullName.Printf("%s.%s.%s", absGamPath.Buffer(), dirStr, signalName.Buffer());
|
gamFullName.Printf("%s.%s.%s", absGamPath.Buffer(), dirStr, signalName.Buffer());
|
||||||
} else {
|
} else {
|
||||||
gamFullName.Printf("Root.%s.%s.%s", functionName, dirStr, signalName.Buffer());
|
gamFullName.Printf("%s.%s.%s", functionName, dirStr, signalName.Buffer());
|
||||||
}
|
}
|
||||||
signalInfoPointers[i] = service->RegisterSignal(addr, type, gamFullName.Buffer());
|
signalInfoPointers[i] = service->RegisterSignal(addr, type, gamFullName.Buffer());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
33
Headers/DebugFastScheduler.h
Normal file
33
Headers/DebugFastScheduler.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef DEBUGFASTSCHEDULER_H
|
||||||
|
#define DEBUGFASTSCHEDULER_H
|
||||||
|
|
||||||
|
#include "FastScheduler.h"
|
||||||
|
#include "DebugService.h"
|
||||||
|
#include "ObjectRegistryDatabase.h"
|
||||||
|
|
||||||
|
namespace MARTe {
|
||||||
|
|
||||||
|
class DebugFastScheduler : public FastScheduler {
|
||||||
|
public:
|
||||||
|
CLASS_REGISTER_DECLARATION()
|
||||||
|
|
||||||
|
DebugFastScheduler();
|
||||||
|
virtual ~DebugFastScheduler();
|
||||||
|
|
||||||
|
virtual bool Initialise(StructuredDataI & data);
|
||||||
|
|
||||||
|
ErrorManagement::ErrorType Execute(ExecutionInfo &information);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void CustomPrepareNextState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorManagement::ErrorType DebugSetupThreadMap();
|
||||||
|
|
||||||
|
EmbeddedServiceMethodBinderT<DebugFastScheduler> debugBinder;
|
||||||
|
DebugService *debugService;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -55,14 +55,17 @@ public:
|
|||||||
|
|
||||||
static bool GetFullObjectName(const Object &obj, StreamString &fullPath);
|
static bool GetFullObjectName(const Object &obj, StreamString &fullPath);
|
||||||
|
|
||||||
private:
|
// Made public for integration tests and debug access
|
||||||
void HandleCommand(StreamString cmd, BasicTCPSocket *client);
|
|
||||||
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);
|
||||||
void Discover(BasicTCPSocket *client);
|
void Discover(BasicTCPSocket *client);
|
||||||
void ListNodes(const char8* path, BasicTCPSocket *client);
|
void ListNodes(const char8* path, BasicTCPSocket *client);
|
||||||
void InfoNode(const char8* path, BasicTCPSocket *client);
|
void InfoNode(const char8* path, BasicTCPSocket *client);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void HandleCommand(StreamString cmd, BasicTCPSocket *client);
|
||||||
|
|
||||||
uint32 ExportTree(ReferenceContainer *container, StreamString &json);
|
uint32 ExportTree(ReferenceContainer *container, StreamString &json);
|
||||||
void PatchRegistry();
|
void PatchRegistry();
|
||||||
|
|
||||||
|
|||||||
71
README.md
71
README.md
@@ -1,48 +1,43 @@
|
|||||||
# MARTe2 Universal Debugging & Observability Suite
|
# MARTe2 Debug Suite
|
||||||
|
|
||||||
A professional-grade, zero-code-change debugging suite for the MARTe2 real-time framework.
|
An interactive observability and debugging suite for the MARTe2 real-time framework.
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Runtime Registry Patching**: Instruments all MARTe2 Brokers automatically at startup.
|
|
||||||
- **Hierarchical Tree Explorer**: Recursive visualization of the `ObjectRegistryDatabase`, including GAMs, DataSources, and Signals.
|
|
||||||
- **Real-Time Execution Control**: Pause and Resume application logic globally to perform static inspection.
|
|
||||||
- **High-Speed Telemetry**: Visual oscilloscope with sub-millisecond precision via UDP.
|
|
||||||
- **Persistent Forcing**: Type-aware signal overrides (Last-Writer-Wins) with persistent re-application.
|
|
||||||
- **Isolated Log Streaming**: Dedicated TCP channel for real-time framework logs to ensure command responsiveness.
|
|
||||||
|
|
||||||
## Components
|
|
||||||
|
|
||||||
### 1. C++ Core (`libmarte_dev.so`)
|
|
||||||
The core service that handles registry patching, TCP/UDP communication, and real-time safe data capture.
|
|
||||||
|
|
||||||
### 2. Rust GUI Client (`marte_debug_gui`)
|
|
||||||
A native, multi-threaded dashboard built with `egui`.
|
|
||||||
- **Side Panel**: Collapsible application tree and signal navigator.
|
|
||||||
- **Bottom Panel**: Advanced log terminal with Regex filtering and priority levels.
|
|
||||||
- **Right Panel**: Active Trace and Force management.
|
|
||||||
- **Central Pane**: High-frequency oscilloscope.
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### Build
|
### 1. Build the project
|
||||||
```bash
|
```bash
|
||||||
# Build C++ Core
|
. ./env.sh
|
||||||
cd Build && cmake .. && make -j$(nproc)
|
cd Build
|
||||||
|
cmake ..
|
||||||
|
make -j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
# Build GUI Client
|
### 2. Run Integration Tests
|
||||||
|
```bash
|
||||||
|
./Test/Integration/ValidationTest # Verifies 100Hz tracing
|
||||||
|
./Test/Integration/SchedulerTest # Verifies execution control
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Launch GUI
|
||||||
|
```bash
|
||||||
cd Tools/gui_client
|
cd Tools/gui_client
|
||||||
cargo build --release
|
cargo run --release
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run
|
## Features
|
||||||
1. Start your MARTe2 application with the `DebugService` enabled.
|
- **Live Tree:** Explore the MARTe2 object database in real-time.
|
||||||
2. Launch the GUI:
|
- **Oscilloscope:** Trace any signal at high frequency (100Hz+) with automatic scaling.
|
||||||
```bash
|
- **Signal Forcing:** Inject values directly into the real-time memory map.
|
||||||
./Tools/gui_client/target/release/marte_debug_gui
|
- **Log Forwarding:** Integrated framework log viewer with regex filtering.
|
||||||
```
|
- **Execution Control:** Global pause/resume via scheduler-level hooks.
|
||||||
|
|
||||||
## Communication Ports
|
## Usage
|
||||||
- **8080 (TCP)**: Commands (TREE, FORCE, TRACE, PAUSE).
|
To enable debugging in your application, add the following to your `.cfg`:
|
||||||
- **8082 (TCP)**: Real-time framework logs.
|
```text
|
||||||
- **8081 (UDP)**: Signal telemetry data.
|
+DebugService = {
|
||||||
|
Class = DebugService
|
||||||
|
ControlPort = 8080
|
||||||
|
UdpPort = 8081
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The suite automatically patches the registry to instrument your existing Brokers and Schedulers.
|
||||||
|
|||||||
24
SPECS.md
Normal file
24
SPECS.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# MARTe2 Debug Suite Specifications
|
||||||
|
|
||||||
|
## 1. Goal
|
||||||
|
Implement a "Zero-Code-Change" observability layer for the MARTe2 real-time framework, providing live telemetry, signal forcing, and execution control without modifying existing application source code.
|
||||||
|
|
||||||
|
## 2. Requirements
|
||||||
|
### 2.1 Functional Requirements (FR)
|
||||||
|
- **FR-01 (Discovery):** Discover the full MARTe2 object hierarchy at runtime.
|
||||||
|
- **FR-02 (Telemetry):** Stream high-frequency signal data (verified up to 100Hz) to a remote client.
|
||||||
|
- **FR-03 (Forcing):** Allow manual override of signal values in memory during execution.
|
||||||
|
- **FR-04 (Logs):** Stream global framework logs to a dedicated terminal.
|
||||||
|
- **FR-05 (Execution Control):** Pause and resume the real-time execution threads via scheduler injection.
|
||||||
|
- **FR-06 (UI):** Provide a native, immediate-mode GUI for visualization (Oscilloscope).
|
||||||
|
|
||||||
|
### 2.2 Technical Constraints (TC)
|
||||||
|
- **TC-01:** No modifications allowed to the MARTe2 core library or component source code.
|
||||||
|
- **TC-02:** Instrumentation must use Runtime Class Registry Patching.
|
||||||
|
- **TC-03:** Real-time threads must remain lock-free; use `FastPollingMutexSem` or atomic operations for synchronization.
|
||||||
|
- **TC-04:** Telemetry must be delivered via UDP to minimize impact on real-time jitter.
|
||||||
|
|
||||||
|
## 3. Performance Metrics
|
||||||
|
- **Latency:** Telemetry dispatch overhead < 5 microseconds per signal.
|
||||||
|
- **Throughput:** Support for 100Hz+ sampling rates with zero packet loss on local networks.
|
||||||
|
- **Scalability:** Handle up to 4096 unique signals and 16 simultaneous client connections.
|
||||||
153
Source/DebugFastScheduler.cpp
Normal file
153
Source/DebugFastScheduler.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#include "DebugFastScheduler.h"
|
||||||
|
#include "AdvancedErrorManagement.h"
|
||||||
|
#include "ExecutionInfo.h"
|
||||||
|
#include "MultiThreadService.h"
|
||||||
|
#include "RealTimeApplication.h"
|
||||||
|
#include "Threads.h"
|
||||||
|
#include "MemoryOperationsHelper.h"
|
||||||
|
|
||||||
|
namespace MARTe {
|
||||||
|
|
||||||
|
const uint64 ALL_CPUS = 0xFFFFFFFFFFFFFFFFull;
|
||||||
|
|
||||||
|
DebugFastScheduler::DebugFastScheduler() :
|
||||||
|
FastScheduler(),
|
||||||
|
debugBinder(*this, &DebugFastScheduler::Execute)
|
||||||
|
{
|
||||||
|
debugService = NULL_PTR(DebugService*);
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugFastScheduler::~DebugFastScheduler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebugFastScheduler::Initialise(StructuredDataI & data) {
|
||||||
|
bool ret = FastScheduler::Initialise(data);
|
||||||
|
if (ret) {
|
||||||
|
ReferenceContainer *root = ObjectRegistryDatabase::Instance();
|
||||||
|
Reference serviceRef = root->Find("DebugService");
|
||||||
|
if (serviceRef.IsValid()) {
|
||||||
|
debugService = dynamic_cast<DebugService*>(serviceRef.operator->());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorManagement::ErrorType DebugFastScheduler::DebugSetupThreadMap() {
|
||||||
|
ErrorManagement::ErrorType err;
|
||||||
|
ComputeMaxNThreads();
|
||||||
|
REPORT_ERROR(ErrorManagement::Information, "DebugFastScheduler: Max Threads=%!", maxNThreads);
|
||||||
|
|
||||||
|
multiThreadService = new (NULL) MultiThreadService(debugBinder);
|
||||||
|
multiThreadService->SetNumberOfPoolThreads(maxNThreads);
|
||||||
|
err = multiThreadService->CreateThreads();
|
||||||
|
if (err.ErrorsCleared()) {
|
||||||
|
rtThreadInfo[0] = new RTThreadParam[maxNThreads];
|
||||||
|
rtThreadInfo[1] = new RTThreadParam[maxNThreads];
|
||||||
|
|
||||||
|
for (uint32 i = 0u; i < numberOfStates; i++) {
|
||||||
|
cpuMap[i] = new uint64[maxNThreads];
|
||||||
|
for (uint32 j = 0u; j < maxNThreads; j++) {
|
||||||
|
cpuMap[i][j] = ALL_CPUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countingSem.Create(maxNThreads)) {
|
||||||
|
for (uint32 i = 0u; i < numberOfStates; i++) {
|
||||||
|
uint32 nThreads = states[i].numberOfThreads;
|
||||||
|
cpuThreadMap[i] = new uint32[nThreads];
|
||||||
|
for (uint32 j = 0u; j < nThreads; j++) {
|
||||||
|
uint64 cpu = static_cast<uint64>(states[i].threads[j].cpu.GetProcessorMask());
|
||||||
|
CreateThreadMap(cpu, i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugFastScheduler::CustomPrepareNextState() {
|
||||||
|
ErrorManagement::ErrorType err;
|
||||||
|
|
||||||
|
err = !realTimeApplicationT.IsValid();
|
||||||
|
if (err.ErrorsCleared()) {
|
||||||
|
uint8 nextBuffer = static_cast<uint8>(realTimeApplicationT->GetIndex());
|
||||||
|
nextBuffer++;
|
||||||
|
nextBuffer &= 0x1u;
|
||||||
|
|
||||||
|
if (!initialised) {
|
||||||
|
cpuMap = new uint64*[numberOfStates];
|
||||||
|
cpuThreadMap = new uint32*[numberOfStates];
|
||||||
|
err = DebugSetupThreadMap();
|
||||||
|
}
|
||||||
|
if (err.ErrorsCleared()) {
|
||||||
|
for (uint32 j = 0u; j < maxNThreads; j++) {
|
||||||
|
rtThreadInfo[nextBuffer][j].executables = NULL_PTR(ExecutableI **);
|
||||||
|
rtThreadInfo[nextBuffer][j].numberOfExecutables = 0u;
|
||||||
|
rtThreadInfo[nextBuffer][j].cycleTime = NULL_PTR(uint32 *);
|
||||||
|
rtThreadInfo[nextBuffer][j].lastCycleTimeStamp = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduledState *nextState = GetSchedulableStates()[nextBuffer];
|
||||||
|
uint32 numberOfThreads = nextState->numberOfThreads;
|
||||||
|
for (uint32 i = 0u; i < numberOfThreads; i++) {
|
||||||
|
rtThreadInfo[nextBuffer][cpuThreadMap[nextStateIdentifier][i]].executables = nextState->threads[i].executables;
|
||||||
|
rtThreadInfo[nextBuffer][cpuThreadMap[nextStateIdentifier][i]].numberOfExecutables = nextState->threads[i].numberOfExecutables;
|
||||||
|
rtThreadInfo[nextBuffer][cpuThreadMap[nextStateIdentifier][i]].cycleTime = nextState->threads[i].cycleTime;
|
||||||
|
rtThreadInfo[nextBuffer][cpuThreadMap[nextStateIdentifier][i]].lastCycleTimeStamp = 0u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorManagement::ErrorType DebugFastScheduler::Execute(ExecutionInfo & information) {
|
||||||
|
ErrorManagement::ErrorType ret;
|
||||||
|
|
||||||
|
if (information.GetStage() == MARTe::ExecutionInfo::StartupStage) {
|
||||||
|
}
|
||||||
|
else if (information.GetStage() == MARTe::ExecutionInfo::MainStage) {
|
||||||
|
uint32 threadNumber = information.GetThreadNumber();
|
||||||
|
(void) eventSem.Wait(TTInfiniteWait);
|
||||||
|
if (superFast == 0u) {
|
||||||
|
(void) countingSem.WaitForAll(TTInfiniteWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 idx = static_cast<uint32>(realTimeApplicationT->GetIndex());
|
||||||
|
|
||||||
|
if (rtThreadInfo[idx] != NULL_PTR(RTThreadParam *)) {
|
||||||
|
if (rtThreadInfo[idx][threadNumber].numberOfExecutables > 0u) {
|
||||||
|
|
||||||
|
// EXECUTION CONTROL HOOK
|
||||||
|
if (debugService != NULL_PTR(DebugService*)) {
|
||||||
|
while (debugService->IsPaused()) {
|
||||||
|
Sleep::MSec(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = ExecuteSingleCycle(rtThreadInfo[idx][threadNumber].executables, rtThreadInfo[idx][threadNumber].numberOfExecutables);
|
||||||
|
if (!ok) {
|
||||||
|
if (errorMessage.IsValid()) {
|
||||||
|
(void)MessageI::SendMessage(errorMessage, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 absTime = 0u;
|
||||||
|
if (rtThreadInfo[idx][threadNumber].lastCycleTimeStamp != 0u) {
|
||||||
|
uint64 tmp = (HighResolutionTimer::Counter() - rtThreadInfo[idx][threadNumber].lastCycleTimeStamp);
|
||||||
|
float64 ticksToTime = (static_cast<float64>(tmp) * clockPeriod) * 1e6;
|
||||||
|
absTime = static_cast<uint32>(ticksToTime);
|
||||||
|
}
|
||||||
|
uint32 sizeToCopy = static_cast<uint32>(sizeof(uint32));
|
||||||
|
(void)MemoryOperationsHelper::Copy(rtThreadInfo[idx][threadNumber].cycleTime, &absTime, sizeToCopy);
|
||||||
|
rtThreadInfo[idx][threadNumber].lastCycleTimeStamp = HighResolutionTimer::Counter();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(void) unusedThreadsSem.Wait(TTInfiniteWait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLASS_REGISTER(DebugFastScheduler, "1.0")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "StreamString.h"
|
#include "StreamString.h"
|
||||||
#include "BasicSocket.h"
|
#include "BasicSocket.h"
|
||||||
#include "DebugBrokerWrapper.h"
|
#include "DebugBrokerWrapper.h"
|
||||||
|
#include "DebugFastScheduler.h"
|
||||||
#include "ObjectRegistryDatabase.h"
|
#include "ObjectRegistryDatabase.h"
|
||||||
#include "ClassRegistryItem.h"
|
#include "ClassRegistryItem.h"
|
||||||
#include "ObjectBuilder.h"
|
#include "ObjectBuilder.h"
|
||||||
@@ -119,7 +120,12 @@ bool DebugService::Initialise(StructuredDataI & data) {
|
|||||||
(void)data.Read("TcpLogPort", logPort);
|
(void)data.Read("TcpLogPort", logPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)data.Read("StreamIP", streamIP);
|
StreamString tempIP;
|
||||||
|
if (data.Read("StreamIP", tempIP)) {
|
||||||
|
streamIP = tempIP;
|
||||||
|
} else {
|
||||||
|
streamIP = "127.0.0.1";
|
||||||
|
}
|
||||||
|
|
||||||
uint32 suppress = 1;
|
uint32 suppress = 1;
|
||||||
if (data.Read("SuppressTimeoutLogs", suppress)) {
|
if (data.Read("SuppressTimeoutLogs", suppress)) {
|
||||||
@@ -148,14 +154,21 @@ bool DebugService::Initialise(StructuredDataI & data) {
|
|||||||
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to open TCP Server Socket");
|
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to open TCP Server Socket");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!tcpServer.Listen(controlPort)) {
|
||||||
|
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to Listen on port %u", controlPort);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!udpSocket.Open()) {
|
if (!udpSocket.Open()) {
|
||||||
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to open UDP Socket");
|
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to open UDP Socket");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!logServer.Open()) {
|
if (!logServer.Open()) {
|
||||||
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to open Log Server Socket");
|
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to open Log Server Socket");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
(void)logServer.Listen(logPort);
|
||||||
|
|
||||||
if (threadService.Start() != ErrorManagement::NoError) {
|
if (threadService.Start() != ErrorManagement::NoError) {
|
||||||
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to start Server thread");
|
REPORT_ERROR(ErrorManagement::FatalError, "DebugService: Failed to start Server thread");
|
||||||
@@ -201,6 +214,10 @@ void DebugService::PatchRegistry() {
|
|||||||
PatchItemInternal("MemoryMapSynchronisedMultiBufferInputBroker", &b8);
|
PatchItemInternal("MemoryMapSynchronisedMultiBufferInputBroker", &b8);
|
||||||
static DebugMemoryMapSynchronisedMultiBufferOutputBrokerBuilder b9;
|
static DebugMemoryMapSynchronisedMultiBufferOutputBrokerBuilder b9;
|
||||||
PatchItemInternal("MemoryMapSynchronisedMultiBufferOutputBroker", &b9);
|
PatchItemInternal("MemoryMapSynchronisedMultiBufferOutputBroker", &b9);
|
||||||
|
|
||||||
|
// Patch Scheduler
|
||||||
|
static ObjectBuilderT<DebugFastScheduler> schedBuilder;
|
||||||
|
PatchItemInternal("FastScheduler", &schedBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugService::ProcessSignal(DebugSignalInfo* s, uint32 size) {
|
void DebugService::ProcessSignal(DebugSignalInfo* s, uint32 size) {
|
||||||
@@ -295,9 +312,6 @@ static bool RecursiveGetFullObjectName(ReferenceContainer *container, const Obje
|
|||||||
bool DebugService::GetFullObjectName(const Object &obj, StreamString &fullPath) {
|
bool DebugService::GetFullObjectName(const Object &obj, StreamString &fullPath) {
|
||||||
fullPath = "";
|
fullPath = "";
|
||||||
if (RecursiveGetFullObjectName(ObjectRegistryDatabase::Instance(), obj, fullPath)) {
|
if (RecursiveGetFullObjectName(ObjectRegistryDatabase::Instance(), obj, fullPath)) {
|
||||||
StreamString abs = "Root.";
|
|
||||||
abs += fullPath;
|
|
||||||
fullPath = abs;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -311,7 +325,6 @@ ErrorManagement::ErrorType DebugService::Server(ExecutionInfo & info) {
|
|||||||
if (info.GetStage() == ExecutionInfo::TerminationStage) return ErrorManagement::NoError;
|
if (info.GetStage() == ExecutionInfo::TerminationStage) return ErrorManagement::NoError;
|
||||||
if (info.GetStage() == ExecutionInfo::StartupStage) {
|
if (info.GetStage() == ExecutionInfo::StartupStage) {
|
||||||
serverThreadId = Threads::Id();
|
serverThreadId = Threads::Id();
|
||||||
if (!tcpServer.Listen(controlPort)) return ErrorManagement::FatalError;
|
|
||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +388,6 @@ ErrorManagement::ErrorType DebugService::LogStreamer(ExecutionInfo & info) {
|
|||||||
if (info.GetStage() == ExecutionInfo::TerminationStage) return ErrorManagement::NoError;
|
if (info.GetStage() == ExecutionInfo::TerminationStage) return ErrorManagement::NoError;
|
||||||
if (info.GetStage() == ExecutionInfo::StartupStage) {
|
if (info.GetStage() == ExecutionInfo::StartupStage) {
|
||||||
logStreamerThreadId = Threads::Id();
|
logStreamerThreadId = Threads::Id();
|
||||||
if (!logServer.Listen(logPort)) return ErrorManagement::FatalError;
|
|
||||||
return ErrorManagement::NoError;
|
return ErrorManagement::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,3 +6,6 @@ target_link_libraries(TraceTest marte_dev ${MARTe2_LIB})
|
|||||||
|
|
||||||
add_executable(ValidationTest ValidationTest.cpp)
|
add_executable(ValidationTest ValidationTest.cpp)
|
||||||
target_link_libraries(ValidationTest marte_dev ${MARTe2_LIB} ${IOGAM_LIB} ${LinuxTimer_LIB})
|
target_link_libraries(ValidationTest marte_dev ${MARTe2_LIB} ${IOGAM_LIB} ${LinuxTimer_LIB})
|
||||||
|
|
||||||
|
add_executable(SchedulerTest SchedulerTest.cpp)
|
||||||
|
target_link_libraries(SchedulerTest marte_dev ${MARTe2_LIB} ${IOGAM_LIB} ${LinuxTimer_LIB})
|
||||||
|
|||||||
202
Test/Integration/SchedulerTest.cpp
Normal file
202
Test/Integration/SchedulerTest.cpp
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
#include "DebugService.h"
|
||||||
|
#include "DebugCore.h"
|
||||||
|
#include "ObjectRegistryDatabase.h"
|
||||||
|
#include "StandardParser.h"
|
||||||
|
#include "StreamString.h"
|
||||||
|
#include "BasicUDPSocket.h"
|
||||||
|
#include "BasicTCPSocket.h"
|
||||||
|
#include "RealTimeApplication.h"
|
||||||
|
#include "GlobalObjectsDatabase.h"
|
||||||
|
#include "MessageI.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
using namespace MARTe;
|
||||||
|
|
||||||
|
const char8 * const config_text =
|
||||||
|
"+DebugService = {"
|
||||||
|
" Class = DebugService "
|
||||||
|
" ControlPort = 8080 "
|
||||||
|
" UdpPort = 8081 "
|
||||||
|
" StreamIP = \"127.0.0.1\" "
|
||||||
|
"}"
|
||||||
|
"+App = {"
|
||||||
|
" Class = RealTimeApplication "
|
||||||
|
" +Functions = {"
|
||||||
|
" Class = ReferenceContainer "
|
||||||
|
" +GAM1 = {"
|
||||||
|
" Class = IOGAM "
|
||||||
|
" InputSignals = {"
|
||||||
|
" Counter = {"
|
||||||
|
" DataSource = Timer "
|
||||||
|
" Type = uint32 "
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" OutputSignals = {"
|
||||||
|
" Counter = {"
|
||||||
|
" DataSource = DDB "
|
||||||
|
" Type = uint32 "
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" +Data = {"
|
||||||
|
" Class = ReferenceContainer "
|
||||||
|
" DefaultDataSource = DDB "
|
||||||
|
" +Timer = {"
|
||||||
|
" Class = LinuxTimer "
|
||||||
|
" SleepTime = 100000 " // 100ms
|
||||||
|
" Signals = {"
|
||||||
|
" Counter = { Type = uint32 }"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" +DDB = {"
|
||||||
|
" Class = GAMDataSource "
|
||||||
|
" Signals = { Counter = { Type = uint32 } }"
|
||||||
|
" }"
|
||||||
|
" +DAMS = { Class = TimingDataSource }"
|
||||||
|
" }"
|
||||||
|
" +States = {"
|
||||||
|
" Class = ReferenceContainer "
|
||||||
|
" +State1 = {"
|
||||||
|
" Class = RealTimeState "
|
||||||
|
" +Threads = {"
|
||||||
|
" Class = ReferenceContainer "
|
||||||
|
" +Thread1 = {"
|
||||||
|
" Class = RealTimeThread "
|
||||||
|
" Functions = {GAM1} "
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" +Scheduler = {"
|
||||||
|
" Class = FastScheduler "
|
||||||
|
" TimingDataSource = DAMS "
|
||||||
|
" }"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
void TestSchedulerControl() {
|
||||||
|
printf("--- MARTe2 Scheduler Control Test ---\n");
|
||||||
|
|
||||||
|
ConfigurationDatabase cdb;
|
||||||
|
StreamString ss = config_text;
|
||||||
|
ss.Seek(0);
|
||||||
|
StandardParser parser(ss, cdb);
|
||||||
|
assert(parser.Parse());
|
||||||
|
assert(ObjectRegistryDatabase::Instance()->Initialise(cdb));
|
||||||
|
|
||||||
|
ReferenceT<DebugService> service = ObjectRegistryDatabase::Instance()->Find("DebugService");
|
||||||
|
assert(service.IsValid());
|
||||||
|
|
||||||
|
ReferenceT<RealTimeApplication> app = ObjectRegistryDatabase::Instance()->Find("App");
|
||||||
|
assert(app.IsValid());
|
||||||
|
|
||||||
|
if (app->PrepareNextState("State1") != ErrorManagement::NoError) {
|
||||||
|
printf("ERROR: Failed to prepare State1\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->StartNextStateExecution() != ErrorManagement::NoError) {
|
||||||
|
printf("ERROR: Failed to start execution\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Application started. Waiting for cycles...\n");
|
||||||
|
Sleep::MSec(1000);
|
||||||
|
|
||||||
|
// Enable Trace First
|
||||||
|
{
|
||||||
|
BasicTCPSocket tClient;
|
||||||
|
if (tClient.Connect("127.0.0.1", 8080)) {
|
||||||
|
const char* cmd = "TRACE Root.App.Data.Timer.Counter 1\n";
|
||||||
|
uint32 s = StringHelper::Length(cmd);
|
||||||
|
tClient.Write(cmd, s);
|
||||||
|
tClient.Close();
|
||||||
|
} else {
|
||||||
|
printf("WARNING: Could not connect to DebugService to enable trace.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicUDPSocket listener;
|
||||||
|
listener.Open();
|
||||||
|
listener.Listen(8081);
|
||||||
|
|
||||||
|
// Read current value
|
||||||
|
uint32 valBeforePause = 0;
|
||||||
|
char buffer[2048];
|
||||||
|
uint32 size = 2048;
|
||||||
|
TimeoutType timeout(500);
|
||||||
|
if (listener.Read(buffer, size, timeout)) {
|
||||||
|
// [Header][ID][Size][Value]
|
||||||
|
valBeforePause = *(uint32*)(&buffer[28]);
|
||||||
|
printf("Value before/at pause: %u\n", valBeforePause);
|
||||||
|
} else {
|
||||||
|
printf("WARNING: No data received before pause.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send PAUSE
|
||||||
|
printf("Sending PAUSE command...\n");
|
||||||
|
BasicTCPSocket client;
|
||||||
|
if (client.Connect("127.0.0.1", 8080)) {
|
||||||
|
const char* cmd = "PAUSE\n";
|
||||||
|
uint32 s = StringHelper::Length(cmd);
|
||||||
|
client.Write(cmd, s);
|
||||||
|
client.Close();
|
||||||
|
} else {
|
||||||
|
printf("ERROR: Could not connect to DebugService to send PAUSE.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep::MSec(2000); // Wait 2 seconds
|
||||||
|
|
||||||
|
// Read again - should be same or very close if paused
|
||||||
|
uint32 valAfterWait = 0;
|
||||||
|
size = 2048; // Reset size
|
||||||
|
while(listener.Read(buffer, size, TimeoutType(10))) {
|
||||||
|
valAfterWait = *(uint32*)(&buffer[28]);
|
||||||
|
size = 2048;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Value after 2s wait (drained): %u\n", valAfterWait);
|
||||||
|
|
||||||
|
// Check if truly paused
|
||||||
|
if (valAfterWait > valBeforePause + 5) {
|
||||||
|
printf("FAILURE: Counter increased significantly while paused! (%u -> %u)\n", valBeforePause, valAfterWait);
|
||||||
|
} else {
|
||||||
|
printf("SUCCESS: Counter held steady (or close) during pause.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
printf("Sending RESUME command...\n");
|
||||||
|
{
|
||||||
|
BasicTCPSocket rClient;
|
||||||
|
if (rClient.Connect("127.0.0.1", 8080)) {
|
||||||
|
const char* cmd = "RESUME\n";
|
||||||
|
uint32 s = StringHelper::Length(cmd);
|
||||||
|
rClient.Write(cmd, s);
|
||||||
|
rClient.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep::MSec(1000);
|
||||||
|
|
||||||
|
// Check if increasing
|
||||||
|
uint32 valAfterResume = 0;
|
||||||
|
size = 2048;
|
||||||
|
if (listener.Read(buffer, size, timeout)) {
|
||||||
|
valAfterResume = *(uint32*)(&buffer[28]);
|
||||||
|
printf("Value after resume: %u\n", valAfterResume);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valAfterResume > valAfterWait) {
|
||||||
|
printf("SUCCESS: Execution resumed.\n");
|
||||||
|
} else {
|
||||||
|
printf("FAILURE: Execution did not resume.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
app->StopCurrentStateExecution();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
TestSchedulerControl();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "BasicUDPSocket.h"
|
#include "BasicUDPSocket.h"
|
||||||
#include "BasicTCPSocket.h"
|
#include "BasicTCPSocket.h"
|
||||||
#include "RealTimeApplication.h"
|
#include "RealTimeApplication.h"
|
||||||
|
#include "GlobalObjectsDatabase.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ const char8 * const config_text =
|
|||||||
" Counter = {"
|
" Counter = {"
|
||||||
" DataSource = Timer "
|
" DataSource = Timer "
|
||||||
" Type = uint32 "
|
" Type = uint32 "
|
||||||
|
" Frequency = 100 "
|
||||||
" }"
|
" }"
|
||||||
" }"
|
" }"
|
||||||
" OutputSignals = {"
|
" OutputSignals = {"
|
||||||
@@ -46,6 +48,7 @@ const char8 * const config_text =
|
|||||||
" SleepTime = 10000 "
|
" SleepTime = 10000 "
|
||||||
" Signals = {"
|
" Signals = {"
|
||||||
" Counter = { Type = uint32 }"
|
" Counter = { Type = uint32 }"
|
||||||
|
" Time = { Type = uint32 }"
|
||||||
" }"
|
" }"
|
||||||
" }"
|
" }"
|
||||||
" +DDB = {"
|
" +DDB = {"
|
||||||
@@ -76,7 +79,8 @@ const char8 * const config_text =
|
|||||||
void RunValidationTest() {
|
void RunValidationTest() {
|
||||||
printf("--- MARTe2 100Hz Trace Validation Test ---\n");
|
printf("--- MARTe2 100Hz Trace Validation Test ---\n");
|
||||||
|
|
||||||
// 1. Load Configuration
|
ObjectRegistryDatabase::Instance()->Purge();
|
||||||
|
|
||||||
ConfigurationDatabase cdb;
|
ConfigurationDatabase cdb;
|
||||||
StreamString ss = config_text;
|
StreamString ss = config_text;
|
||||||
ss.Seek(0);
|
ss.Seek(0);
|
||||||
@@ -91,66 +95,70 @@ void RunValidationTest() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Start Application
|
ReferenceT<DebugService> service = ObjectRegistryDatabase::Instance()->Find("DebugService");
|
||||||
|
if (!service.IsValid()) {
|
||||||
|
printf("ERROR: DebugService not found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ReferenceT<RealTimeApplication> app = ObjectRegistryDatabase::Instance()->Find("App");
|
ReferenceT<RealTimeApplication> app = ObjectRegistryDatabase::Instance()->Find("App");
|
||||||
if (!app.IsValid()) {
|
if (!app.IsValid()) {
|
||||||
printf("ERROR: App not found\n");
|
printf("ERROR: App not found\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We try to use State1 directly as many MARTe2 apps start in the first defined state if no transition is needed
|
if (!app->ConfigureApplication()) {
|
||||||
|
printf("ERROR: Failed to configure application\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (app->PrepareNextState("State1") != ErrorManagement::NoError) {
|
if (app->PrepareNextState("State1") != ErrorManagement::NoError) {
|
||||||
printf("ERROR: Failed to prepare state State1\n");
|
printf("ERROR: Failed to prepare state State1\n");
|
||||||
// We will try to investigate why, but for now we continue
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app->StartNextStateExecution() != ErrorManagement::NoError) {
|
if (app->StartNextStateExecution() != ErrorManagement::NoError) {
|
||||||
printf("ERROR: Failed to start execution. Maybe it needs an explicit state?\n");
|
printf("ERROR: Failed to start execution\n");
|
||||||
// return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Application started at 100Hz.\n");
|
printf("Application and DebugService are active.\n");
|
||||||
Sleep::MSec(1000);
|
Sleep::MSec(1000);
|
||||||
|
|
||||||
// 3. Enable Trace via TCP (Simulating GUI)
|
// DIRECT ACTIVATION: Use the public TraceSignal method
|
||||||
BasicTCPSocket client;
|
printf("Activating trace directly...\n");
|
||||||
if (client.Connect("127.0.0.1", 8080)) {
|
// We try multiple potential paths to be safe
|
||||||
const char* cmd = "TRACE Root.App.Data.Timer.Counter 1\n";
|
uint32 traceCount = 0;
|
||||||
uint32 s = StringHelper::Length(cmd);
|
traceCount += service->TraceSignal("App.Data.Timer.Counter", true, 1);
|
||||||
client.Write(cmd, s);
|
traceCount += service->TraceSignal("Timer.Counter", true, 1);
|
||||||
|
traceCount += service->TraceSignal("Counter", true, 1);
|
||||||
|
|
||||||
char resp[1024]; s = 1024;
|
printf("Trace enabled (Matched Aliases: %u)\n", traceCount);
|
||||||
TimeoutType timeout(1000);
|
|
||||||
if (client.Read(resp, s, timeout)) {
|
|
||||||
resp[s] = '\0';
|
|
||||||
printf("Server Response: %s", resp);
|
|
||||||
} else {
|
|
||||||
printf("WARNING: No response from server to TRACE command.\n");
|
|
||||||
}
|
|
||||||
client.Close();
|
|
||||||
} else {
|
|
||||||
printf("ERROR: Failed to connect to DebugService on 8080\n");
|
|
||||||
// continue anyway to see if it's already working
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Setup UDP Listener
|
// 4. Setup UDP Listener
|
||||||
BasicUDPSocket listener;
|
BasicUDPSocket listener;
|
||||||
if (!listener.Open()) { printf("ERROR: Failed to open UDP socket\n"); return; }
|
if (!listener.Open()) { printf("ERROR: Failed to open UDP socket\n"); return; }
|
||||||
if (!listener.Listen(8081)) { printf("ERROR: Failed to listen on UDP 8081\n"); return; }
|
if (!listener.Listen(8081)) { printf("ERROR: Failed to listen on UDP 8081\n"); return; }
|
||||||
|
|
||||||
// 5. Validate for 30 seconds
|
// 5. Validate for 10 seconds
|
||||||
printf("Validating telemetry for 30 seconds...\n");
|
printf("Validating telemetry for 10 seconds...\n");
|
||||||
uint32 lastVal = 0;
|
uint32 lastVal = 0;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
uint32 packetCount = 0;
|
uint32 packetCount = 0;
|
||||||
uint32 discontinuityCount = 0;
|
uint32 discontinuityCount = 0;
|
||||||
|
|
||||||
float64 startTime = HighResolutionTimer::Counter() * HighResolutionTimer::Period();
|
float64 startTime = HighResolutionTimer::Counter() * HighResolutionTimer::Period();
|
||||||
|
float64 globalTimeout = startTime + 30.0;
|
||||||
|
|
||||||
|
while ((HighResolutionTimer::Counter() * HighResolutionTimer::Period() - startTime) < 10.0) {
|
||||||
|
if (HighResolutionTimer::Counter() * HighResolutionTimer::Period() > globalTimeout) {
|
||||||
|
printf("CRITICAL ERROR: Global test timeout reached.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
while ((HighResolutionTimer::Counter() * HighResolutionTimer::Period() - startTime) < 30.0) {
|
|
||||||
char buffer[2048];
|
char buffer[2048];
|
||||||
uint32 size = 2048;
|
uint32 size = 2048;
|
||||||
TimeoutType timeout(500);
|
TimeoutType timeout(200);
|
||||||
|
|
||||||
if (listener.Read(buffer, size, timeout)) {
|
if (listener.Read(buffer, size, timeout)) {
|
||||||
TraceHeader *h = (TraceHeader*)buffer;
|
TraceHeader *h = (TraceHeader*)buffer;
|
||||||
@@ -168,7 +176,7 @@ void RunValidationTest() {
|
|||||||
first = false;
|
first = false;
|
||||||
packetCount++;
|
packetCount++;
|
||||||
|
|
||||||
if (packetCount % 500 == 0) {
|
if (packetCount % 200 == 0) {
|
||||||
printf("Received %u packets... Current Value: %u\n", packetCount, val);
|
printf("Received %u packets... Current Value: %u\n", packetCount, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,17 +184,17 @@ void RunValidationTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
printf("Test Finished.\n");
|
printf("Test Finished.\n");
|
||||||
printf("Total Packets Received: %u (Expected ~3000)\n", packetCount);
|
printf("Total Packets Received: %u (Expected ~1000)\n", packetCount);
|
||||||
printf("Discontinuities: %u\n", discontinuityCount);
|
printf("Discontinuities: %u\n", discontinuityCount);
|
||||||
|
|
||||||
float64 actualFreq = (float64)packetCount / 30.0;
|
float64 actualFreq = (float64)packetCount / 10.0;
|
||||||
printf("Average Frequency: %.2f Hz\n", actualFreq);
|
printf("Average Frequency: %.2f Hz\n", actualFreq);
|
||||||
|
|
||||||
if (packetCount < 100) {
|
if (packetCount < 100) {
|
||||||
printf("FAILURE: Almost no packets received. Telemetry is broken.\n");
|
printf("FAILURE: Almost no packets received. Telemetry is broken.\n");
|
||||||
} else if (packetCount < 2500) {
|
} else if (packetCount < 800) {
|
||||||
printf("WARNING: Too few packets received (Expected 3000, Got %u).\n", packetCount);
|
printf("WARNING: Too few packets received (Expected 1000, Got %u).\n", packetCount);
|
||||||
} else if (discontinuityCount > 100) {
|
} else if (discontinuityCount > 20) {
|
||||||
printf("FAILURE: Too many discontinuities (%u).\n", discontinuityCount);
|
printf("FAILURE: Too many discontinuities (%u).\n", discontinuityCount);
|
||||||
} else {
|
} else {
|
||||||
printf("VALIDATION SUCCESSFUL!\n");
|
printf("VALIDATION SUCCESSFUL!\n");
|
||||||
@@ -194,6 +202,7 @@ void RunValidationTest() {
|
|||||||
|
|
||||||
app->StopCurrentStateExecution();
|
app->StopCurrentStateExecution();
|
||||||
listener.Close();
|
listener.Close();
|
||||||
|
ObjectRegistryDatabase::Instance()->Purge();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|||||||
23
run_test.sh
23
run_test.sh
@@ -1,21 +1,14 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
|
||||||
source $DIR/env.sh
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
|
||||||
export MARTe2_DIR=/home/martino/Projects/marte_debug/dependency/MARTe2
|
export MARTe2_DIR=/home/martino/Projects/marte_debug/dependency/MARTe2
|
||||||
export TARGET=x86-linux
|
export TARGET=x86-linux
|
||||||
|
|
||||||
MARTE_APP=$MARTe2_DIR/Build/$TARGET/App/MARTeApp.ex
|
echo "Cleaning up old instances..."
|
||||||
|
pkill -9 IntegrationTest
|
||||||
|
pkill -9 ValidationTest
|
||||||
|
pkill -9 SchedulerTest
|
||||||
|
pkill -9 main
|
||||||
|
sleep 2
|
||||||
|
|
||||||
# Build paths for all components
|
echo "Starting MARTe2 Integration Tests..."
|
||||||
LIBS=$DIR/Build
|
./Build/Test/Integration/ValidationTest
|
||||||
LIBS=$LIBS:$MARTe2_DIR/Build/$TARGET/Core
|
|
||||||
LIBS=$LIBS:$MARTe2_Components_DIR/Build/$TARGET/Components/DataSources/LinuxTimer
|
|
||||||
LIBS=$LIBS:$MARTe2_Components_DIR/Build/$TARGET/Components/DataSources/LoggerDataSource
|
|
||||||
LIBS=$LIBS:$MARTe2_Components_DIR/Build/$TARGET/Components/GAMs/IOGAM
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LIBS:$LD_LIBRARY_PATH
|
|
||||||
|
|
||||||
# ./Build/Test/Integration/IntegrationTest -f Test/Configurations/debug_test.cfg -l RealTimeLoader -s State1
|
|
||||||
$MARTE_APP -f $DIR/Test/Configurations/debug_test.cfg -l RealTimeLoader -s State1
|
|
||||||
|
|||||||
Reference in New Issue
Block a user