# DebugService

`DebugService` is a MARTe2 `Object` that instruments a running MARTe2 application for
real-time signal tracing, forcing, and breakpoints — **without modifying the application
source code**.

It lives at `Source/Components/Interfaces/DebugService/` in this repository and is built
alongside `TCPLogger` at `Source/Components/Interfaces/TCPLogger/`.

---

## Architecture

### Core Mechanism: Registry Patching

On `Initialise()`, `PatchRegistry()` replaces the `ObjectBuilder` for every standard
`MemoryMap*Broker` class in the `ClassRegistryDatabase`.  When
`RealTimeApplication::ConfigureApplication()` runs afterward it instantiates wrapped broker
objects instead of the originals.  The application sees no difference.

Patched broker types:

| Class | Wrapper |
|---|---|
| `MemoryMapInputBroker` | `DebugBrokerWrapper<MemoryMapInputBroker>` |
| `MemoryMapOutputBroker` | `DebugBrokerWrapper<MemoryMapOutputBroker>` |
| `MemoryMapSynchronisedInputBroker` | `DebugBrokerWrapper<MemoryMapSynchronisedInputBroker>` |
| `MemoryMapSynchronisedOutputBroker` | `DebugBrokerWrapper<MemoryMapSynchronisedOutputBroker>` |
| `MemoryMapMultiBufferBroker` | `DebugBrokerWrapper<MemoryMapMultiBufferBroker>` |
| `MemoryMapMultiBufferOutputBroker` | `DebugBrokerWrapper<MemoryMapMultiBufferOutputBroker>` |
| `MemoryMapAsynchronousInputBroker` | `DebugBrokerWrapperAsync<MemoryMapAsynchronousInputBroker>` |
| `MemoryMapAsynchronousOutputBroker` | `DebugBrokerWrapperAsync<MemoryMapAsynchronousOutputBroker>` |
| `MemoryMapInterpolatedInputBroker` | `DebugBrokerWrapper<MemoryMapInterpolatedInputBroker>` |
| `MemoryMapStatefulOutputBroker` | `DebugBrokerWrapper<MemoryMapStatefulOutputBroker>` |
| `MemoryMapStatefulInputBroker` | `DebugBrokerWrapper<MemoryMapStatefulInputBroker>` |

### DebugServiceI Abstraction

`DebugServiceI.h` defines an abstract singleton interface decoupling the broker injection
layer from the transport:

- **RT-path API** (called every RT cycle): `RegisterSignal`, `ProcessSignal`, `RegisterBroker`,
  `IsPaused`/`SetPaused`, `ConsumeStepIfNeeded`
- **Control-path API**: `ForceSignal`/`UnforceSignal`, `TraceSignal`, `SetBreak`/`ClearBreak`,
  `RegisterMonitorSignal`/`UnmonitorSignal`

### Signal Registration and Aliases

Each signal is registered with two names:
1. **Canonical**: `<DataSourcePath>.<SignalName>` (e.g. `App.Data.DDB.Counter`)
2. **GAM alias**: `<GAMPath>.In.<SignalName>` or `<GAMPath>.Out.<SignalName>`

Both map to the same `DebugSignalInfo*`.  Alias lookup is bidirectional — a short unqualified
name matches any longer canonical or alias path.

### Communication Channels

| Port | Protocol | Purpose |
|------|----------|---------|
| 8080 (default) | TCP text | Command and control |
| 8081 (default) | UDP binary | High-speed telemetry stream |
| 8082 (default) | TCP stream | Log forwarding via `TcpLogger` |

### TraceRingBuffer

Single-producer/single-consumer circular byte buffer (4 MB default) defined in `DebugCore.h`.
Buffer layout per entry: `[ID:4][Timestamp:8][Size:4][Data:N]`.

`Push()` is serialised by `tracePushMutex` (multiple RT threads write).
`Pop()` is called exclusively by the Streamer thread.

---

## Configuration

Add `DebugService` as a **sibling** of `+App` (not inside it):

```text
+DebugService = {
    Class       = DebugService
    ControlPort = 8080
    UdpPort     = 8081
    LogPort     = 8082
}

+Logger = {
    Class = TcpLogger
    Port  = 8082
}

+App = {
    Class = RealTimeApplication
    ...
}
```

Call `SetFullConfig(cdb)` after `ConfigureApplication()` to enable the `CONFIG` and `INFO`
commands.

---

## TCP Command Interface (port 8080)

Commands are newline-terminated (`\n`) UTF-8 text strings.  One client is served at a time.
Rate limit: 100 commands/second.  Idle timeout: 30 seconds.

### `DISCOVER`
List all registered signals.

```
Request:  DISCOVER\n
Response: {"Signals":[{"id":<n>,"name":"…","alias":"…","type":"…","elements":<n>},...]}
          OK DISCOVER
```

### `TREE`
Return the live `ObjectRegistryDatabase` hierarchy as JSON.

```
Request:  TREE\n
Response: {"name":"Root","children":[…]}
          OK TREE
```

### `INFO <path>`
Return metadata for a specific ORD node, enriched with config fields.

### `LS [path]`
List the immediate children of an ORD node.

### `TRACE <name> <0|1> [decimation]`
Enable or disable high-speed tracing. Optional decimation controls how many RT cycles are
skipped between samples (default 1 = every cycle).

```
Request:  TRACE App.Data.DDB.Counter 1\n
Response: OK TRACE <match_count>\n
```

### `FORCE <name> <value>`
Inject a persistent value into a signal's memory location every RT cycle.

```
Request:  FORCE App.Data.DDB.Counter 9999\n
Response: OK FORCE <match_count>\n
```

### `UNFORCE <name>`
Remove a forced value; the signal resumes normal data-flow.

### `BREAK <name> <op> <threshold>`
Set a conditional breakpoint. Supported operators: `>`, `<`, `==`, `>=`, `<=`, `!=`, `OFF`.

```
Request:  BREAK App.Data.DDB.Counter > 1000\n
Response: OK BREAK <match_count>\n
```

### `PAUSE` / `RESUME`
Halt or release all patched RT threads.

### `STEP <n> [thread]`
Resume for exactly `n` RT output-broker cycles, then pause again.

### `VALUE <name>`
Read the current raw value of a signal. Arrays capped at 256 elements.

### `CONFIG`
Return the full application configuration as JSON (requires `SetFullConfig()`).

### `MSG <object> <function> [key=value …]`
Send a MARTe2 `Message` to any object in the ORD.

### `SERVICE_INFO`
Return metadata about the debug service (ports, transport type).

---

## UDP Telemetry Format (port 8081)

Packets are little-endian and packed.

### `TraceHeader` (20 bytes)

| Offset | Type | Field | Description |
|---|---|---|---|
| 0 | uint32 | `magic` | Always `0xDA7A57AD` |
| 4 | uint32 | `seq` | Monotonically incrementing sequence number |
| 8 | uint64 | `timestamp` | High-resolution hardware timestamp |
| 16 | uint32 | `count` | Number of samples in this datagram |

### Sample Entry (per signal)

| Offset | Type | Field | Description |
|---|---|---|---|
| 0 | uint32 | `id` | Signal ID from `DISCOVER` |
| 4 | uint64 | `timestamp` | Per-sample RT timestamp |
| 12 | uint32 | `size` | Payload size in bytes |
| 16 | bytes | `data` | Raw signal memory |

Multiple samples are packed into one datagram up to `STREAMER_MTU = 1400` bytes.

---

## Log Forwarding (TcpLogger, port 8082)

`TcpLogger` connects to the MARTe2 global `LoggerI` and forwards every `REPORT_ERROR` call:

```
<level>|<object>|<function>|<message>\n
```

Up to 8 simultaneous log clients supported.

---

## Performance Hardening

| Fix | Change |
|---|---|
| Multi-producer ring | `tracePushMutex` serialises `TraceRingBuffer::Push()` |
| Break evaluation | Break indices copied to stack-local array before evaluating outside the lock |
| Decimation counter | `Atomic::Add` for lock-free per-signal decimation in multi-producer `ProcessSignal` |
| Ring corruption | `Pop()` discards all entries only when `size >= bufferSize` |
| TCP rate limit | Disconnect clients sending > 100 commands/second |
| Idle timeout | Disconnect active TCP client after 30 s of silence |
| TCP framing | `inputBuffer` accumulates partial commands across `Read()` calls; bounded at 8 KiB |
| VALUE output size | Caps output at 256 elements; includes `"Truncated"` flag |

---

## Key Source Files

| File | Purpose |
|---|---|
| `DebugCore.h` | `DebugSignalInfo` struct, `TraceRingBuffer`, `BreakOp` enum |
| `DebugServiceI.h` | Abstract singleton interface, `SignalAlias`, `BrokerInfo` |
| `DebugBrokerWrapper.h` | `DebugBrokerHelper`, wrapper templates, `DebugBrokerBuilder` |
| `DebugService.h/.cpp` | TCP/UDP transport implementation; `Server()` and `Streamer()` threads |
| `DebugServiceBase.h/.cpp` | Shared signal registry and alias matching logic |
| `TcpLogger.h/.cpp` | `LoggerConsumerI` forwarding `REPORT_ERROR` events to TCP clients |
