REST API und WebSocket für Kommunikation zwischen PC-Hauptprogramm und CM5.
PC-Hauptprogramm ◄──── HTTP/WebSocket ────► CM5 (ufi.local:5000)
Basis-URL: http://ufi.local:5000/api/
WebSocket: ws://ufi.local:5000/ws
{
"success": true,
"data": { ... },
"error": null
}{
"version": "1.0.0",
"stm32_connected": true,
"active_drive": 1,
"motor_on": false,
"buffer_tracks": 0
}{
"device": "UFI",
"hardware_version": "2.1",
"supported_drives": [
{"id": 1, "name": "Shugart A"},
{"id": 2, "name": "Shugart B"},
{"id": 3, "name": "Apple Disk II"},
{"id": 4, "name": "Amiga External"},
{"id": 5, "name": "IEC Bus"}
]
}// Request
{"drive": 1}
// Response
{"drive": 1, "name": "Shugart A", "ready": true}| drive | Beschreibung |
|---|---|
| 1 | FDD1 (Shugart A) |
| 2 | FDD2 (Shugart B) |
| 3 | Apple Disk II |
| 4 | Amiga External |
| 5 | IEC Bus (1541) |
// Request
{"on": true}
// Response
{"motor": true, "spin_up_time_ms": 500}{
"drive": 1,
"motor_on": true,
"current_track": 35,
"current_side": 0,
"track0": false,
"write_protected": true,
"disk_changed": false,
"rpm": 300.5
}// Request
{"track": 35}
// Response
{"track": 35, "seek_time_ms": 105}// Response
{"track": 0, "steps": 42}// Request
{"side": 0}Einzelnen Track lesen.
// Request
{
"track": 35,
"side": 0,
"revolutions": 3
}
// Response
{
"track": 35,
"side": 0,
"format": "C64_GCR",
"quality": 94.5,
"rpm": 300.2,
"flux_count": 49823,
"index_time_ns": 199850000,
"sectors": [
{"number": 0, "size": 256, "crc_ok": true, "quality": 98.0},
{"number": 1, "size": 256, "crc_ok": true, "quality": 95.5}
],
"warnings": [],
"protection": {"type": "none"}
}Komplette Disk lesen (asynchron).
// Request
{
"tracks": 80,
"sides": 2,
"revolutions": 3,
"retry_threshold": 80,
"max_retries": 3
}
// Response
{
"job_id": "disk_20240117_143052",
"status": "started"
}Job-Status abrufen.
{
"job_id": "disk_20240117_143052",
"status": "running",
"progress": 45.5,
"current_track": 36,
"current_side": 0,
"quality_avg": 93.2,
"elapsed_seconds": 142,
"eta_seconds": 174
}Job abbrechen.
Komplette Disk-Daten.
{
"disk_info": {
"format": "C64_GCR",
"total_tracks": 35,
"sides": 1,
"quality_avg": 94.2,
"total_sectors": 683,
"good_sectors": 680,
"bad_sectors": 3
},
"tracks": {
"1/0": {"quality": 98.5, "sectors": 21},
"2/0": {"quality": 95.2, "sectors": 21}
}
}Track-Details.
{
"track": 18,
"side": 0,
"format": "C64_GCR",
"quality": 96.5,
"sectors": [
{
"number": 0,
"data": "base64...",
"checksum_ok": true,
"weak_bits": []
}
]
}Raw Flux-Daten.
Query-Parameter:
revolution: 0-4 (optional)format: "json" oder "binary"
{
"track": 18,
"side": 0,
"sample_count": 49823,
"samples": [
{"delta_ns": 3245.5},
{"delta_ns": 4012.3}
]
}Binary Format:
Header (16 bytes):
uint8: track
uint8: side
uint8: revolution
uint8: flags
uint32: index_time_ns
uint32: sample_count
uint32: reserved
Data:
uint16[n]: delta_ticks (× 3.636 = ns)
{"devices_found": [8, 9]}// Request
{
"device": 8,
"command": "M-R",
"data": [0x00, 0x03, 0x02]
}
// Response
{"response": [0x41, 0x42, 0x43], "status": 0}// Request
{"device": 8, "track": 18, "sector": 0}
// Response
{
"track": 18,
"sector": 0,
"data": "base64_256_bytes...",
"error": 0
}ws://ufi.local:5000/ws
// Subscribe
{"type": "subscribe", "data": {"events": ["progress", "status"]}}
// Disk lesen
{"type": "read_disk", "data": {"tracks": 80, "sides": 2}}
// Abbrechen
{"type": "abort", "data": {}}// Progress
{"type": "progress", "data": {"track": 35, "side": 0, "progress": 43.75, "quality": 94.5}}
// Track fertig
{"type": "track_complete", "data": {"track": 35, "side": 0, "quality": 94.5}}
// Disk fertig
{"type": "disk_complete", "data": {"tracks_total": 160, "quality_avg": 93.8}}
// Fehler
{"type": "error", "data": {"code": 201, "message": "Track read failed"}}| Code | Beschreibung |
|---|---|
| 100 | Allgemeiner Fehler |
| 101 | STM32 nicht verbunden |
| 102 | Ungültige Parameter |
| 200 | Laufwerk-Fehler |
| 201 | Track lesen fehlgeschlagen |
| 202 | Kein Laufwerk ausgewählt |
| 203 | Motor nicht an |
| 300 | IEC Bus Fehler |
| 301 | IEC Gerät nicht gefunden |
UNKNOWN=0, PC_MFM_DD=1, PC_MFM_HD=2, AMIGA_DD=3, AMIGA_HD=4,
C64_GCR=5, C64_GCR_40=6, APPLE_II_GCR=7, ATARI_FM=8
none, weak_bits, timing, variable_density, custom
var client = new HttpClient();
client.BaseAddress = new Uri("http://ufi.local:5000/api/");
// Laufwerk wählen
await client.PostAsJsonAsync("drive/select", new { drive = 5 });
// Motor an
await client.PostAsJsonAsync("drive/motor", new { on = true });
// Track lesen
var resp = await client.PostAsJsonAsync("read/track", new {
track = 18, side = 0, revolutions = 3
});
var result = await resp.Content.ReadFromJsonAsync<TrackResult>();
// Motor aus
await client.PostAsJsonAsync("drive/motor", new { on = false });var
Client: THTTPClient;
Response: IHTTPResponse;
begin
Client := THTTPClient.Create;
// Track lesen
Response := Client.Post(
'http://ufi.local:5000/api/read/track',
TStringStream.Create('{"track":18,"side":0,"revolutions":3}')
);
ShowMessage(Response.ContentAsString);
end;