Bundeswehr.Uniity.SEDAPExpress.Messages
0.1.0-preview.1
dotnet add package Bundeswehr.Uniity.SEDAPExpress.Messages --version 0.1.0-preview.1
NuGet\Install-Package Bundeswehr.Uniity.SEDAPExpress.Messages -Version 0.1.0-preview.1
<PackageReference Include="Bundeswehr.Uniity.SEDAPExpress.Messages" Version="0.1.0-preview.1" />
<PackageVersion Include="Bundeswehr.Uniity.SEDAPExpress.Messages" Version="0.1.0-preview.1" />
<PackageReference Include="Bundeswehr.Uniity.SEDAPExpress.Messages" />
paket add Bundeswehr.Uniity.SEDAPExpress.Messages --version 0.1.0-preview.1
#r "nuget: Bundeswehr.Uniity.SEDAPExpress.Messages, 0.1.0-preview.1"
#:package Bundeswehr.Uniity.SEDAPExpress.Messages@0.1.0-preview.1
#addin nuget:?package=Bundeswehr.Uniity.SEDAPExpress.Messages&version=0.1.0-preview.1&prerelease
#tool nuget:?package=Bundeswehr.Uniity.SEDAPExpress.Messages&version=0.1.0-preview.1&prerelease
SEDAP-Express-DOTNET
.NET port of SEDAP-Express — a library for parsing and producing SEDAP Express messages used in Bundeswehr/UNIITY systems.
Requirements
.NET 10.0 or later.
Usage
Installation
Install the NuGet package:
dotnet add package Bundeswehr.Uniity.SEDAPExpress.Messages
Or add it to your .csproj:
<PackageReference Include="Bundeswehr.Uniity.SEDAPExpress.Messages" Version="x.y.z" />
Setup
Create a MessageSerializer. It accepts an optional ILogger<MessageSerializer>; without one, log output goes to the console.
using Bundeswehr.Uniity.SEDAPExpress.Messages.Serializers;
// Console logging:
MessageSerializer serializer = new();
// With a logger:
MessageSerializer serializer = new(logger);
With dependency injection (IMessageSerializer is the interface):
using Bundeswehr.Uniity.SEDAPExpress.Messages.Abstractions;
using Bundeswehr.Uniity.SEDAPExpress.Messages.Serializers;
// MessageSerializer resolves ILogger<MessageSerializer> from the container automatically:
services.AddLogging();
services.AddSingleton<IMessageSerializer, MessageSerializer>();
Deserializing messages
using Bundeswehr.Uniity.SEDAPExpress.Messages;
using Bundeswehr.Uniity.SEDAPExpress.Messages.Abstractions;
using Bundeswehr.Uniity.SEDAPExpress.Messages.Serializers;
MessageSerializer serializer = new();
// Throws MessageParseException on failure:
ISedapExpressMessage message = serializer.Deserialize(
"CONTACT;01;661D64C0;UNIT1;U;;;TRACK01;FALSE;52.52;13.405");
// Non-throwing overload (accepts ReadOnlySpan<char> for zero-allocation hot paths):
if (serializer.TryDeserialize(rawSpan, out ISedapExpressMessage? message))
{
// use message
}
// Dispatch by concrete type:
switch (message)
{
case ContactMessage contact:
Console.WriteLine($"Contact '{contact.ContactId}' at {contact.Latitude}, {contact.Longitude}");
break;
case HeartbeatMessage heartbeat:
Console.WriteLine($"Heartbeat from {heartbeat.Sender}");
break;
case TextMessage text:
Console.WriteLine($"[{text.TextType}] {text.FreeText}");
break;
// ...
}
Serializing messages
All message types are immutable sealed record classes. Construct one and pass it to Serialize:
using Bundeswehr.Uniity.SEDAPExpress.Messages;
using Bundeswehr.Uniity.SEDAPExpress.Messages.Abstractions;
using Bundeswehr.Uniity.SEDAPExpress.Messages.Serializers;
MessageSerializer serializer = new();
ContactMessage contact = new(
Number: 0x01,
Time: 0x661D64C0,
Sender: "UNIT1",
Classification: Classification.Unclas,
Acknowledgement: Acknowledgement.False,
Mac: null,
ContactId: "TRACK01",
DeleteFlag: DeleteFlagType.False,
Latitude: 52.5200,
Longitude: 13.4050,
Altitude: null,
RelativeXDistance: null,
RelativeYDistance: null,
RelativeZDistance: null,
Speed: null,
Course: null,
Heading: null,
Roll: null,
Pitch: null,
Width: null,
Length: null,
Height: null,
Name: "Target Alpha",
Source: null,
Sidc: null,
Mmsi: null,
Icao: null,
MultimediaData: null,
Comment: null
);
string wire = serializer.Serialize(contact);
// -> "CONTACT;01;661D64C0;UNIT1;U;;;TRACK01;FALSE;52.52;13.405;;;;;;;;;;;;;;;Target Alpha"
Trailing empty fields are stripped automatically on serialization.
Wire-format extension helpers
Extension methods for converting enum values to and from their wire representations are available independently of the serializer, for custom parsing or formatting scenarios:
using Bundeswehr.Uniity.SEDAPExpress.Messages;
// Single-char enums:
char c = Classification.Secret.ToWireChar(); // -> 'S'
bool ok = ClassificationExtensions.TryFromWireChar('S', out Classification cls); // cls = Secret
// String-mapped enums:
string s = Acknowledgement.True.ToWireString(); // -> "TRUE"
string s = DeleteFlagType.False.ToWireString(); // -> "FALSE"
string s = ContentType.Json.ToWireString(); // -> "JSON"
// Byte-mapped (CommandType uses a non-sequential wire value for GenericAction = 0xFF):
byte b = CommandType.GenericAction.ToWireByte(); // -> 0xFF
bool ok = CommandTypeExtensions.TryFromWireByte(0xFF, out CommandType ct); // ct = GenericAction
// Int-mapped (all remaining enums map sequentially from 0):
int i = GraphicType.Circle.ToWireInt(); // -> 4
bool ok = IntEnumExtensions.TryFromWireInt(4, out GraphicType gt); // gt = Circle
Contact source values are char-mapped via ContactSourceExtensions.ToWireChar / FromWireChar.
Message type names are string-mapped via MessageTypeExtensions.ToWireString / TryFromWireString.
Supported message types
| Wire name | .NET type | Description |
|---|---|---|
| ACKNOWLEDGE | AcknowledgeMessage |
Acknowledges receipt of another message |
| COMMAND | CommandMessage |
Remote command (start, stop, configure, …) |
| CONTACT | ContactMessage |
Tracked contact with position and identity data |
| EMISSION | EmissionMessage |
Electromagnetic emission report |
| GENERIC | GenericMessage |
Freeform payload with encoding metadata |
| GRAPHIC | GraphicMessage |
Graphical overlay element |
| HEARTBEAT | HeartbeatMessage |
Liveness / keepalive signal |
| KEYEXCHANGE | KeyexchangeMessage |
Cryptographic key exchange |
| METEO | MeteoMessage |
Meteorological data |
| OWNUNIT | OwnUnitMessage |
Own-unit position and state report |
| POINT | PointMessage |
Named geographic point |
| RESEND | ResendMessage |
Request to re-transmit a prior message |
| STATUS | StatusMessage |
Platform technical and operational state |
| TEXT | TextMessage |
Free-text message (info, warning, chat, …) |
| TIMESYNC | TimesyncMessage |
Clock synchronisation |
Wire format
Messages are semicolon-delimited strings. The first seven fields form the common header:
<MessageType>;<Number(hex)>;<Time(hex)>;<Sender>;<Classification>;<Acknowledgement>;<MAC>[;<field>...]
| Field | Format |
|---|---|
MessageType |
Uppercase name, e.g. CONTACT |
Number |
Hex byte, 00–7F |
Time |
8–16 hex digits |
Classification |
Single char: P Public · U Unclas · R Restricted · C Confidential · S Secret · T TopSecret · - None |
Acknowledgement |
TRUE, FALSE, or empty (treated as FALSE) |
MAC |
Optional sender MAC address |
Development
A dev container is provided for VS Code / GitHub Codespaces. It installs the .NET 10 SDK and restores packages automatically.
See CONTRIBUTING.md for build, test, and release instructions.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-preview.1 | 97 | 3/19/2026 |