110 lines
3.2 KiB
Zig
110 lines
3.2 KiB
Zig
//! IPC Client - L0 -> L2 Event Bridge
|
|
//!
|
|
//! Sends transport events to the L2 Membrane Agent via Unix Domain Sockets.
|
|
|
|
const std = @import("std");
|
|
const net = std.net;
|
|
const os = std.os;
|
|
const mem = std.mem;
|
|
|
|
pub const IpcClient = struct {
|
|
allocator: mem.Allocator,
|
|
socket_path: []const u8,
|
|
stream: ?net.Stream,
|
|
connected: bool,
|
|
|
|
// Constants
|
|
const MAGIC: u16 = 0x55AA;
|
|
|
|
// Event Types
|
|
const EVENT_PACKET_RECEIVED: u8 = 0x01;
|
|
const EVENT_CONNECTION_ESTABLISHED: u8 = 0x02;
|
|
const EVENT_CONNECTION_DROPPED: u8 = 0x03;
|
|
|
|
pub fn init(allocator: mem.Allocator, socket_path: []const u8) IpcClient {
|
|
return IpcClient{
|
|
.allocator = allocator,
|
|
.socket_path = socket_path,
|
|
.stream = null,
|
|
.connected = false,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *IpcClient) void {
|
|
if (self.stream) |s| {
|
|
s.close();
|
|
}
|
|
}
|
|
|
|
/// Try to connect if not connected
|
|
pub fn connect(self: *IpcClient) !void {
|
|
if (self.connected) return;
|
|
|
|
// Non-blocking connect attempt
|
|
const stream = net.connectUnixSocket(self.socket_path) catch |err| {
|
|
// Connection failed (agent not running?)
|
|
// Just return, don't crash. We'll try again next time.
|
|
// Log debug?
|
|
return err;
|
|
};
|
|
|
|
self.stream = stream;
|
|
self.connected = true;
|
|
}
|
|
|
|
/// Send 'PacketReceived' event
|
|
pub fn sendPacketReceived(self: *IpcClient, sender_did: [32]u8, packet_type: u8, payload_size: u32) !void {
|
|
if (!self.connected) {
|
|
self.connect() catch return; // Retry connect
|
|
}
|
|
|
|
// Payload size: DID(32) + Type(1) + Size(4) = 37 bytes
|
|
const payload_len: u32 = 37;
|
|
|
|
// Prepare Header (8 bytes)
|
|
// Magic(2) + Type(1) + Flags(1) + Len(4)
|
|
var buffer: [8 + 37]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
const writer = fbs.writer();
|
|
|
|
// Header
|
|
try writer.writeInt(u16, MAGIC, .little);
|
|
try writer.writeInt(u8, EVENT_PACKET_RECEIVED, .little);
|
|
try writer.writeInt(u8, 0, .little); // Flags
|
|
try writer.writeInt(u32, payload_len, .little);
|
|
|
|
// Payload
|
|
try writer.writeAll(&sender_did);
|
|
try writer.writeInt(u8, packet_type, .little);
|
|
try writer.writeInt(u32, payload_size, .little);
|
|
|
|
// Send
|
|
if (self.stream) |s| {
|
|
s.writeAll(&buffer) catch |err| {
|
|
// Write failed, assume disconnected
|
|
self.connected = false;
|
|
s.close();
|
|
self.stream = null;
|
|
return err;
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
test "ipc packet serialization" {
|
|
// Just verify bytes match expected format
|
|
var buffer: [8 + 37]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
const writer = fbs.writer();
|
|
|
|
// Manual write
|
|
try writer.writeInt(u16, 0x55AA, .little);
|
|
try writer.writeInt(u8, 0x01, .little);
|
|
try writer.writeInt(u8, 0, .little);
|
|
try writer.writeInt(u32, 37, .little);
|
|
|
|
// Offset 8: Payload starts
|
|
try std.testing.expectEqual(buffer[0], 0xAA);
|
|
try std.testing.expectEqual(buffer[1], 0x55);
|
|
}
|