> ## Documentation Index
> Fetch the complete documentation index at: https://playbook.smedleygroup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Internal Signals

> The x prefixed signals the charging pipeline emits about itself, what each one means, how to unpack the packed ones, and what a healthy kart looks like.

The pipeline reports on itself using the same mechanism it reports kart data with. This page is the reference for those signals.

They come from the receiver rather than from a kart, so they exist whether or not a kart is answering, which is what makes them the first place to look when one is not. For how they are produced, see [The Charging Data Receiver](/software/developer/estate/systems/telemetry/receiver).

Every signal the pipeline emits about itself is prefixed `x`. They are written into InfluxDB exactly like real CAN channels, which is why they appear in the dashboard and in Data Explorer alongside battery data, and why they are worth knowing by name.

Counters described as **per burst** are counted within a single receive burst and start again on the next one. Counters described as **cumulative** run from the moment that connection opened.

| Signal | Emitted | Meaning |
| - | - | - |
| `xConnectionAttempt` | Every connect attempt | 1 per attempt |
| `xNewConnection` | Connect succeeded | 1 |
| `xTcpConnectionFailed` | Connect failed | 1 |
| `xPing` | Connect failed | ICMP result for the connection's IP |
| `xTcpConnectionKilled` | A loop threw | 1. The connection is being torn down and rebuilt |
| `xTotalReqsCnt` | Each dataset sent | Cumulative request frames sent |
| `xTotalDatasetsSentCnt` | Each dataset sent | Cumulative complete datasets sent |
| `xTotalDatasetsPerSecSentCnt` | Each dataset sent | Datasets per second, averaged since the connection opened |
| `xSendTmeMs` | Each dataset sent | Milliseconds spent writing the dataset |
| `xReqTimeDelayMs` | Each dataset sent | Milliseconds slept to hold the configured rate |
| `xTotalResCnt` | Each receive burst | Cumulative response frames received |
| `xReceiveTimeMs` | Each receive burst | Milliseconds spent in the burst |
| `xNodeResPerConsumeCnt_{id}` | Each receive burst | Per burst count of responses whose frame ID is one the receiver asks for |
| `xOtherResPerConsumeCnt_{id}` | Each receive burst | Per burst count of frames whose ID is not one it asks for, which is where the controller's automatic signals are counted |
| `xDataLoggerReqsPerConsumeCnt` | Each receive burst | Per burst count of frames treated as the data logger's own requests |
| `xFrameIdUnknown_{id}` | A frame ID is not in the DBC | 1 per frame |
| `xKartInfo` | Every internal message | Kart type, battery spec and category packed into one number |
| `xEndpoint` | Every internal message | The connection's IP address and port encoded as a number |

> [!WARNING]
>
> The `{id}` in the two response counters is the frame ID masked with `0x3FFFFFFF`, so the top two bits are cleared. The `{id}` in `xFrameIdUnknown_` is the raw, unmasked frame ID. The same physical frame can therefore appear under two different numbers depending on which signal you are reading.

## Decoding xKartInfo And xEndpoint

Two of the internal signals are packed values rather than readings, so they need unpacking before they mean anything.

`xKartInfo` carries what the kart is, in one number, as `category << 12 | type << 4 | batterySpec`:

| Bits | Field |
| - | - |
| 12 to 15 | Category |
| 4 to 11 | Kart type |
| 0 to 3 | Battery spec |

Kart type, category and battery spec are validated as a set rather than independently, so in practice the type tells you the rest, and the category tells you how many battery nodes to expect:

| Kart type | Type value | Category | Category value | Battery nodes |
| - | - | - | - | - |
| `EK1` | `0x02` | `BB` | `0` | 1 |
| `EK2` | `0x04` | `CAD` | `1` | 2 |
| `EK3` | `0x08` | `JNR` | `2` | 3 |

> [!NOTE]
>
> This is also the source of the `Invalid combination of KartType, BatterySpec and Category` validation error. The three fields are checked against a fixed list of permitted sets, so a kart with a plausible looking type and the wrong category will not load.

`xEndpoint` encodes the connection's address as a number. Each octet of the IP is padded to three digits and concatenated to make the integer part, and the port becomes the fractional part. So `172.25.160.1:1234` is written as `172025160001.1234`, read back as octets `172`, `025`, `160`, `001` and port `1234`.

> [!WARNING]
>
> The port only survives if it does not end in a zero. It is stored as a decimal fraction, so port `1240` comes back as `.124` and port `80` as `.8`. Use `xEndpoint` to identify the IP address, and take the port from `ip-config.json` rather than from this signal.

## Reading Them

Internal signals are the closest thing the track machine has to monitoring, so it is worth knowing what a healthy kart looks like.

| What you see | What it means |
| - | - |
| `xNodeResPerConsumeCnt_*` present, no `xFrameIdUnknown_*` | Healthy. The device is answering what it was asked, and the DBC recognises it |
| `xFrameIdUnknown_*` dominating | The byte order does not match the ECAN. See [Fixing The Byte Order For An ECAN](/software/developer/estate/systems/telemetry/troubleshooting#fixing-the-byte-order-for-an-ecan) |
| `xOtherResPerConsumeCnt_*` present | Expected. This is where the controller's automatic frames land, since they arrive without ever being requested |
| `xConnectionAttempt` climbing, no `xNewConnection` | Cannot establish TCP. Read `xPing` to separate a network problem from a refused port |
| `xTcpConnectionKilled` recurring | The link is flapping, or NATS is unavailable long enough to break the loops |
| `xReqTimeDelayMs` at or near zero | No slack left. The connection cannot hold `fullRequestDatasetsPerSecond` and is running flat out |
| `xDataLoggerReqsPerConsumeCnt` on a connection with no logger | `isDataLoggerAttached` disagrees with reality, since this signal is only ever emitted when it is set |

## Key Files

| File | Role |
| - | - |
| `Telemetry.Shared/Utils/InternalSignals.cs` | The signal names, and the labelling helper |
| `Container.FromTcp.Consumer.ToRaw.Producer/Services/TelemetryCollectionService.cs` | The counters emitted per request and per receive burst |
| `Container.FromTcp.Consumer.ToRaw.Producer/Services/WifiReceiver.cs` | The connection signals |
| `Container.FromTcp.Consumer.ToRaw.Producer/Models/KartSpecs.cs` | The `xKartInfo` packing |
| `Container.FromRaw.Consumer.ToTelemetry.Producer/Services/DbcService.cs` | `xFrameIdUnknown_` |

## Related Documentation

| Page | Relevance |
| - | - |
| [The Charging Data Receiver](/software/developer/estate/systems/telemetry/receiver) | What produces these signals, and why |
| [Troubleshooting](/software/developer/estate/systems/telemetry/troubleshooting) | The symptoms these signals point at |
| [Telemetry Dashboard](/software/developer/estate/systems/telemetry/dashboard) | Where some of these are surfaced without you having to query for them |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.