From 7ea34f9f95e030af1dc05218632c810c366d0d74 Mon Sep 17 00:00:00 2001 From: kf Date: Mon, 30 Mar 2026 14:15:09 +0900 Subject: [PATCH 01/12] refacator: move from globals to struct --- src/Config.zig | 2 +- src/command.zig | 4 +- src/modules/{mmc_client.zig => MmcClient.zig} | 100 +++++++++++++----- src/modules/mmc_client/Parameter.zig | 4 +- .../mmc_client/commands/assert_hall.zig | 12 +-- .../mmc_client/commands/assert_location.zig | 12 +-- .../mmc_client/commands/auto_initialize.zig | 14 +-- .../mmc_client/commands/axis_carrier.zig | 12 +-- src/modules/mmc_client/commands/calibrate.zig | 10 +- .../mmc_client/commands/carrier_axis.zig | 12 +-- .../mmc_client/commands/carrier_id.zig | 20 ++-- .../mmc_client/commands/carrier_location.zig | 12 +-- .../commands/clear_carrier_info.zig | 10 +- .../mmc_client/commands/clear_errors.zig | 10 +- src/modules/mmc_client/commands/connect.zig | 76 ++++++------- .../mmc_client/commands/disconnect.zig | 20 ++-- .../mmc_client/commands/get_acceleration.zig | 4 +- src/modules/mmc_client/commands/get_speed.zig | 4 +- .../mmc_client/commands/hall_status.zig | 12 +-- src/modules/mmc_client/commands/isolate.zig | 10 +- src/modules/mmc_client/commands/log.zig | 36 +++---- src/modules/mmc_client/commands/move.zig | 10 +- src/modules/mmc_client/commands/pause.zig | 8 +- .../mmc_client/commands/print_axis_info.zig | 12 +-- .../commands/print_carrier_info.zig | 12 +-- .../mmc_client/commands/print_driver_info.zig | 12 +-- src/modules/mmc_client/commands/pull.zig | 10 +- src/modules/mmc_client/commands/push.zig | 20 ++-- .../mmc_client/commands/release_carrier.zig | 10 +- .../mmc_client/commands/reset_system.zig | 22 ++-- src/modules/mmc_client/commands/resume.zig | 8 +- .../mmc_client/commands/server_version.zig | 10 +- .../mmc_client/commands/set_acceleration.zig | 6 +- .../mmc_client/commands/set_carrier_id.zig | 10 +- .../mmc_client/commands/set_line_zero.zig | 10 +- src/modules/mmc_client/commands/set_speed.zig | 6 +- .../mmc_client/commands/show_errors.zig | 12 +-- src/modules/mmc_client/commands/stop.zig | 8 +- src/modules/mmc_client/commands/stop_pull.zig | 10 +- src/modules/mmc_client/commands/stop_push.zig | 10 +- src/modules/mmc_client/commands/wait.zig | 26 ++--- src/modules/mmc_client/log.zig | 34 +++--- 42 files changed, 352 insertions(+), 310 deletions(-) rename src/modules/{mmc_client.zig => MmcClient.zig} (96%) diff --git a/src/Config.zig b/src/Config.zig index 64cd755e..9779914d 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -10,7 +10,7 @@ const ReturnDemo2Config = if (config.return_demo2) else void; const ClientCliConfig = - if (config.mmc_client) @import("modules/mmc_client.zig").Config else void; + if (config.mmc_client) @import("modules/MmcClient.zig").Config else void; const Mes07Config = if (config.mes07) @import("modules/mes07.zig").Config else void; diff --git a/src/command.zig b/src/command.zig index 66bda462..e806d11b 100644 --- a/src/command.zig +++ b/src/command.zig @@ -16,7 +16,7 @@ const config = @import("config"); const return_demo2 = if (config.return_demo2) @import("modules/return_demo2.zig") else void; const mmc_client = - if (config.mmc_client) @import("modules/mmc_client.zig") else void; + if (config.mmc_client) @import("modules/MmcClient.zig") else void; const mes07 = if (config.mes07) @import("modules/mes07.zig") else void; const Config = @import("Config.zig"); @@ -287,7 +287,7 @@ pub const Command = union(enum) { const module = @field(This, module_name); const kind_name = field.name[field_name_idx + 1 ..]; - return module.parameter.isValid( + return module.get().parameter.isValid( @field(module.Parameter.Kind, kind_name), input, ); diff --git a/src/modules/mmc_client.zig b/src/modules/MmcClient.zig similarity index 96% rename from src/modules/mmc_client.zig rename to src/modules/MmcClient.zig index 6555166a..a26df2e3 100644 --- a/src/modules/mmc_client.zig +++ b/src/modules/MmcClient.zig @@ -1,3 +1,4 @@ +const MmcClient = @This(); const std = @import("std"); const builtin = @import("builtin"); @@ -156,38 +157,74 @@ pub const error_response = struct { }; } }; -pub var parameter: Parameter = undefined; +parameter: Parameter, /// `lines` is initialized once the client is connected to a server. /// Deinitialized once disconnected from a server. -pub var lines: []Line = &.{}; +lines: []Line, /// The logging configuration is initialized once the client is connected, and /// deinitialized if the client is disconnected. -pub var log_config: log.Config = undefined; +log_config: log.Config, /// Currently connected socket. Nulled when disconnect. -pub var sock: ?zignet.Socket = null; +sock: ?zignet.Socket, /// Currently saved endpoint. The endpoint will be overwritten if the client /// is connected to a different server. Stays null before connected to a socket. -pub var endpoint: ?zignet.Endpoint = null; -pub var allocator: std.mem.Allocator = undefined; - +endpoint: ?zignet.Endpoint, +allocator: std.mem.Allocator, /// Store the configuration. -pub var config: Config = undefined; +config: Config, -var debug_allocator = std.heap.DebugAllocator(.{}){}; +pub fn create(c: Config) !MmcClient { + var self: MmcClient = .{ + .allocator = if (builtin.mode == .Debug) + debug_allocator.allocator() + else + std.heap.smp_allocator, -pub fn init(c: Config) !void { - allocator = if (builtin.mode == .Debug) - debug_allocator.allocator() - else - std.heap.smp_allocator; - config = .{ - .host = try allocator.dupe(u8, c.host), + // Store the configuration. + .config = undefined, + .parameter = undefined, + // `lines` is initialized once the client is connected to a server. + // Deinitialized once disconnected from a server. + .lines = &.{}, + // The logging configuration is initialized once the client is connected, and + // deinitialized if the client is disconnected. + .log_config = undefined, + // Currently connected socket. Nulled when disconnect. + .sock = null, + // Currently saved endpoint. The endpoint will be overwritten if the client + // is connected to a different server. Stays null before connected to a socket. + .endpoint = null, + }; + + self.config = .{ + .host = try self.allocator.dupe(u8, c.host), .port = c.port, }; - errdefer allocator.free(config.host); - parameter = .init(allocator); - errdefer parameter.deinit(); + errdefer self.allocator.free(self.config.host); + self.parameter = .init(self.allocator); + errdefer self.parameter.deinit(); + return self; +} + +var debug_allocator = std.heap.DebugAllocator(.{}){}; + +var current: ?MmcClient = null; + +pub fn get() *MmcClient { + // scaffold: + // throw error here + // e.g. ModuleNotInitialized + return &(current orelse std.debug.panic("Mmc_client module is not initialized", .{})); + // if (current) |*c| { + // return c; + // } + // return error.ModuleNotInitialized; +} + +pub fn init(c: Config) !void { + current = try MmcClient.create(c); + errdefer current = null; try command.registry.put(.{ .executable = .{ .name = "SERVER_VERSION", @@ -1295,9 +1332,11 @@ pub fn init(c: Config) !void { } pub fn deinit() void { + if (current == null) return; commands.disconnect.impl(&.{}) catch {}; - parameter.deinit(); - allocator.free(config.host); + get().parameter.deinit(); + get().allocator.free(get().config.host); + current = null; if (debug_allocator.detectLeaks()) { std.log.debug("Leaks detected", .{}); } @@ -1305,7 +1344,7 @@ pub fn deinit() void { } pub fn matchLine(name: []const u8) !usize { - for (lines) |line| { + for (get().lines) |line| { if (std.mem.eql(u8, line.name, name)) return line.index; } else return error.LineNameNotFound; } @@ -1374,9 +1413,12 @@ pub fn waitCommandCompleted(gpa: std.mem.Allocator, net: zignet.Socket) !void { }, }, }; - try sendRequest(allocator, net, request); - var decoded = try getResponse(allocator, net); - defer decoded.deinit(allocator); + // try sendRequest(allocator, net, request); + // var decoded = try getResponse(allocator, net); + // defer decoded.deinit(allocator); + try sendRequest(get().allocator, net, request); + var decoded = try getResponse(get().allocator, net); + defer decoded.deinit(get().allocator); var commands_resp = switch (decoded.body orelse return error.InvalidResponse) { .request_error => |req_err| { @@ -1465,7 +1507,7 @@ pub fn getResponse( } fn removeCommand(id: u32) !void { - const net = if (sock) |net| net else return error.ServerNotConnected; + const net = if (get().sock) |net| net else return error.ServerNotConnected; const request: api.protobuf.mmc.Request = .{ .body = .{ .command = .{ @@ -1475,9 +1517,9 @@ fn removeCommand(id: u32) !void { }, }, }; - try sendRequest(allocator, net, request); - var decoded = try getResponse(allocator, net); - defer decoded.deinit(allocator); + try sendRequest(get().allocator, net, request); + var decoded = try getResponse(get().allocator, net); + defer decoded.deinit(get().allocator); const removed_id = switch (decoded.body orelse return error.InvalidResponse) { .command => |command_resp| switch (command_resp.body orelse diff --git a/src/modules/mmc_client/Parameter.zig b/src/modules/mmc_client/Parameter.zig index 16d07f16..77a04e8c 100644 --- a/src/modules/mmc_client/Parameter.zig +++ b/src/modules/mmc_client/Parameter.zig @@ -2,7 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const Line = @import("Line.zig"); -const client = @import("../mmc_client.zig"); +const client = @import("../MmcClient.zig"); const Parameter = @This(); // TODO: Support auto completion @@ -161,7 +161,7 @@ value: struct { /// Assert the parameter is a valid line name fn isValid(self: *@This(), input: []const u8) bool { // Invalidate if not connected to server. - if (builtin.is_test == false and client.sock == null) return false; + if (builtin.is_test == false and client.get().sock == null) return false; var it = std.mem.tokenizeSequence(u8, input, ","); while (it.next()) |item| { if (self.items.contains(item) == false) return false; diff --git a/src/modules/mmc_client/commands/assert_hall.zig b/src/modules/mmc_client/commands/assert_hall.zig index ae7fb350..3096379d 100644 --- a/src/modules/mmc_client/commands/assert_hall.zig +++ b/src/modules/mmc_client/commands/assert_hall.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,7 +8,7 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "assert_hall"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const axis_id = try std.fmt.parseInt(u32, buf: { const input = params[1]; @@ -34,7 +34,7 @@ pub fn impl(params: [][]const u8) !void { else return error.InvalidHallAlarmSide; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var alarm_on: bool = true; if (params[3].len > 0) { @@ -66,9 +66,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/assert_location.zig b/src/modules/mmc_client/commands/assert_location.zig index ad25da64..be1e9dae 100644 --- a/src/modules/mmc_client/commands/assert_location.zig +++ b/src/modules/mmc_client/commands/assert_location.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,7 +8,7 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "assert_location"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; var ids = [1]u32{try std.fmt.parseInt(u32, b: { const input = params[1]; @@ -32,7 +32,7 @@ pub fn impl(params: [][]const u8) !void { else 1; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); const request: api.protobuf.mmc.Request = .{ @@ -50,9 +50,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/auto_initialize.zig b/src/modules/mmc_client/commands/auto_initialize.zig index a32ebbf6..a74dd973 100644 --- a/src/modules/mmc_client/commands/auto_initialize.zig +++ b/src/modules/mmc_client/commands/auto_initialize.zig @@ -1,6 +1,6 @@ //! This file contains callbacks for managing the server-side state. const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -9,11 +9,11 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "auto_initialize"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; var init_lines: std.ArrayList( api.protobuf.mmc.command.Request.AutoInitialize.Line, ) = .empty; - defer init_lines.deinit(client.allocator); + defer init_lines.deinit(client.get().allocator); if (params[0].len != 0) { var iterator = std.mem.tokenizeSequence( u8, @@ -22,11 +22,11 @@ pub fn impl(params: [][]const u8) !void { ); while (iterator.next()) |line_name| { const line_idx = try client.matchLine(line_name); - const _line = client.lines[line_idx]; + const _line = client.get().lines[line_idx]; const line: api.protobuf.mmc.command.Request.AutoInitialize.Line = .{ .line = _line.id, }; - try init_lines.append(client.allocator, line); + try init_lines.append(client.get().allocator, line); } } const request: api.protobuf.mmc.Request = .{ @@ -40,6 +40,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/axis_carrier.zig b/src/modules/mmc_client/commands/axis_carrier.zig index eac7ff59..b0425340 100644 --- a/src/modules/mmc_client/commands/axis_carrier.zig +++ b/src/modules/mmc_client/commands/axis_carrier.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,7 +8,7 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "axis_carrier"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const axis_id = try std.fmt.parseInt(u32, buf: { const input = params[1]; @@ -28,7 +28,7 @@ pub fn impl(params: [][]const u8) !void { if (save_var.len > 0 and std.ascii.isDigit(save_var[0])) return error.InvalidParameter; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); const request: api.protobuf.mmc.Request = .{ @@ -46,9 +46,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/calibrate.zig b/src/modules/mmc_client/commands/calibrate.zig index 12d25541..1941a856 100644 --- a/src/modules/mmc_client/commands/calibrate.zig +++ b/src/modules/mmc_client/commands/calibrate.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,10 +8,10 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "calibrate"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const request: api.protobuf.mmc.Request = .{ .body = .{ .command = .{ @@ -21,6 +21,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/carrier_axis.zig b/src/modules/mmc_client/commands/carrier_axis.zig index 7ca98773..c04ef3af 100644 --- a/src/modules/mmc_client/commands/carrier_axis.zig +++ b/src/modules/mmc_client/commands/carrier_axis.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,7 +7,7 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "carrier_axis"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; var ids = [1]u32{try std.fmt.parseInt(u32, b: { const input = params[1]; @@ -24,7 +24,7 @@ pub fn impl(params: [][]const u8) !void { } else break :b input; }, 0)}; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); const request: api.protobuf.mmc.Request = .{ @@ -42,9 +42,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/carrier_id.zig b/src/modules/mmc_client/commands/carrier_id.zig index 8ef5ba5f..e7238728 100644 --- a/src/modules/mmc_client/commands/carrier_id.zig +++ b/src/modules/mmc_client/commands/carrier_id.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,7 +8,7 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "carrier_id"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; var line_name_iterator = std.mem.tokenizeSequence( u8, params[0], @@ -30,19 +30,19 @@ pub fn impl(params: [][]const u8) !void { } } var lines: std.ArrayList(u32) = - try .initCapacity(client.allocator, line_counter); - defer lines.deinit(client.allocator); + try .initCapacity(client.get().allocator, line_counter); + defer lines.deinit(client.get().allocator); line_name_iterator.reset(); while (line_name_iterator.next()) |line_name| { try lines.append( - client.allocator, + client.get().allocator, @intCast(try client.matchLine(line_name)), ); } for (lines.items, 0..) |line_idx, i| { - const line = client.lines[@as(usize, @intCast(line_idx))]; + const line = client.get().lines[@as(usize, @intCast(line_idx))]; lines.items[i] = @as(u32, @intCast(line.id)); } @@ -59,9 +59,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { @@ -79,7 +79,7 @@ pub fn impl(params: [][]const u8) !void { var count: usize = 1; for (track.lines.items) |track_line| { const line_idx: usize = @intCast(track_line.id - 1); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const axis_state = track_line.axis_state; if (axis_state.items.len != line.axes) return error.InvalidResponse; var last_carrier: u32 = 0; diff --git a/src/modules/mmc_client/commands/carrier_location.zig b/src/modules/mmc_client/commands/carrier_location.zig index 9d80d4d8..6b0a74cf 100644 --- a/src/modules/mmc_client/commands/carrier_location.zig +++ b/src/modules/mmc_client/commands/carrier_location.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,7 +8,7 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "carrier_location"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; var ids = [1]u32{try std.fmt.parseInt(u32, b: { const input = params[1]; @@ -28,7 +28,7 @@ pub fn impl(params: [][]const u8) !void { if (save_var.len > 0 and std.ascii.isDigit(save_var[0])) return error.InvalidParameter; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); const request: api.protobuf.mmc.Request = .{ @@ -46,9 +46,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/clear_carrier_info.zig b/src/modules/mmc_client/commands/clear_carrier_info.zig index d63b9c5e..7edd4432 100644 --- a/src/modules/mmc_client/commands/clear_carrier_info.zig +++ b/src/modules/mmc_client/commands/clear_carrier_info.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,10 +8,10 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "clear_carrier_info"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var filter: ?client.Filter = null; if (params[1].len > 0) { filter = try .parse(params[1]); @@ -38,6 +38,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/clear_errors.zig b/src/modules/mmc_client/commands/clear_errors.zig index 3c122760..c3a58af1 100644 --- a/src/modules/mmc_client/commands/clear_errors.zig +++ b/src/modules/mmc_client/commands/clear_errors.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,10 +8,10 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "clear_errors"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var filter: ?client.Filter = null; if (params[1].len > 0) { filter = try .parse(params[1]); @@ -38,6 +38,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/connect.zig b/src/modules/mmc_client/commands/connect.zig index 04b81b1e..36cc15aa 100644 --- a/src/modules/mmc_client/commands/connect.zig +++ b/src/modules/mmc_client/commands/connect.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const disconnect = @import("disconnect.zig"); const tracy = @import("tracy"); @@ -8,7 +8,7 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "connect"); defer tracy_zone.end(); - if (client.sock) |_| disconnect.impl(&.{}) catch unreachable; + if (client.get().sock) |_| disconnect.impl(&.{}) catch unreachable; const endpoint: client.Config = if (params[0].len != 0) endpoint: { const last_delimiter_idx = @@ -35,7 +35,7 @@ pub fn impl(params: [][]const u8) !void { params[0][last_delimiter_idx + 1 ..], 0, ) catch return error.InvalidEndpoint, - .host = try client.allocator.dupe( + .host = try client.get().allocator.dupe( u8, params[0][1 .. last_delimiter_idx - 1], ), @@ -48,58 +48,58 @@ pub fn impl(params: [][]const u8) !void { params[0][last_delimiter_idx + 1 ..], 0, ) catch return error.InvalidEndpoint, - .host = try client.allocator.dupe( + .host = try client.get().allocator.dupe( u8, params[0][0..last_delimiter_idx], ), }; - } else if (client.endpoint == null) .{ - .host = try client.allocator.dupe(u8, client.config.host), - .port = client.config.port, + } else if (client.get().endpoint == null) .{ + .host = try client.get().allocator.dupe(u8, client.get().config.host), + .port = client.get().config.port, } else .{ - .host = switch (client.endpoint.?.addr) { + .host = switch (client.get().endpoint.?.addr) { .ipv4 => |ipv4| try std.fmt.allocPrint( - client.allocator, + client.get().allocator, "{f}", .{ipv4}, ), .ipv6 => |ipv6| ipv6: { const format = try std.fmt.allocPrint( - client.allocator, + client.get().allocator, "{f}", .{ipv6}, ); - defer client.allocator.free(format); + defer client.get().allocator.free(format); // Remove the square bracket from ipv6 break :ipv6 try std.fmt.allocPrint( - client.allocator, + client.get().allocator, "{s}", .{format[1 .. format.len - 1]}, ); }, }, - .port = client.endpoint.?.port, + .port = client.get().endpoint.?.port, }; - defer client.allocator.free(endpoint.host); + defer client.get().allocator.free(endpoint.host); std.log.info( "Trying to connect to {s}:{d}", .{ endpoint.host, endpoint.port }, ); const net = try client.zignet.Socket.connectToHost( - client.allocator, + client.get().allocator, endpoint.host, endpoint.port, &command.checkCommandInterrupt, 3000, ); - client.endpoint = try net.getRemoteEndPoint(); - client.sock = net; + client.get().endpoint = try net.getRemoteEndPoint(); + client.get().sock = net; errdefer { - for (client.lines) |*line| { - line.deinit(client.allocator); + for (client.get().lines) |*line| { + line.deinit(client.get().allocator); } - client.allocator.free(client.lines); - client.sock = null; + client.get().allocator.free(client.get().lines); + client.get().sock = null; net.close(); } // Request server information, for matching API and getting server name. @@ -108,9 +108,9 @@ pub fn impl(params: [][]const u8) !void { .core = .{ .kind = .CORE_REQUEST_KIND_SERVER_INFO }, }, }; - try client.sendRequest(client.allocator, net, server_request); - var server_decoded = try client.getResponse(client.allocator, net); - defer server_decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, server_request); + var server_decoded = try client.getResponse(client.get().allocator, net); + defer server_decoded.deinit(client.get().allocator); const server = switch (server_decoded.body orelse return error.InvalidResponse) { .core => |core_resp| switch (core_resp.body orelse @@ -149,9 +149,9 @@ pub fn impl(params: [][]const u8) !void { .core = .{ .kind = .CORE_REQUEST_KIND_TRACK_CONFIG }, }, }; - try client.sendRequest(client.allocator, net, track_request); - var track_decoded = try client.getResponse(client.allocator, net); - defer track_decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, track_request); + var track_decoded = try client.getResponse(client.get().allocator, net); + defer track_decoded.deinit(client.get().allocator); const track_config = switch (track_decoded.body orelse return error.InvalidResponse) { .core => |core_resp| switch (core_resp.body orelse @@ -167,36 +167,36 @@ pub fn impl(params: [][]const u8) !void { }, else => return error.InvalidResponse, }; - client.lines = try client.allocator.alloc( + client.get().lines = try client.get().allocator.alloc( client.Line, track_config.lines.items.len, ); errdefer { - for (client.lines) |*line| { - line.deinit(client.allocator); + for (client.get().lines) |*line| { + line.deinit(client.get().allocator); } - client.allocator.free(client.lines); + client.get().allocator.free(client.get().lines); } for ( track_config.lines.items, - client.lines, + client.get().lines, 0.., ) |config, *line, idx| { line.* = try client.Line.init( - client.allocator, + client.get().allocator, @intCast(idx), config, ); - try client.parameter.value.line.items.insert(config.name); + try client.get().parameter.value.line.items.insert(config.name); } // Initialize memory for logging configuration - client.log_config = - try client.log.Config.init(client.allocator, client.lines); - errdefer client.log_config.deinit(client.allocator); + client.get().log_config = + try client.log.Config.init(client.get().allocator, client.get().lines); + errdefer client.get().log_config.deinit(client.get().allocator); // Displaying track configuration std.log.info("Track configuration for {s}:", .{server.name}); var stdout = std.fs.File.stdout().writer(&.{}); - for (client.lines) |line| { + for (client.get().lines) |line| { try stdout.interface.print( "\t {s} ({}) - {} {s} | {} {s}\n", .{ diff --git a/src/modules/mmc_client/commands/disconnect.zig b/src/modules/mmc_client/commands/disconnect.zig index c8251806..f7061738 100644 --- a/src/modules/mmc_client/commands/disconnect.zig +++ b/src/modules/mmc_client/commands/disconnect.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); @@ -8,21 +8,21 @@ const tracy = @import("tracy"); pub fn impl(_: [][]const u8) error{ServerNotConnected}!void { const tracy_zone = tracy.traceNamed(@src(), "disconnect"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; client.log.stop.store(true, .monotonic); // Wait until the log finish storing log data and cleanup while (client.log.executing.load(.monotonic)) {} - client.parameter.reset(); - client.log_config.deinit(client.allocator); + client.get().parameter.reset(); + client.get().log_config.deinit(client.get().allocator); net.close(); - client.sock = null; - for (client.lines) |*line| { - line.deinit(client.allocator); + client.get().sock = null; + for (client.get().lines) |*line| { + line.deinit(client.get().allocator); } - client.allocator.free(client.lines); - client.lines = &.{}; + client.get().allocator.free(client.get().lines); + client.get().lines = &.{}; std.log.info( "Disconnected from {f}:{}", - .{ client.endpoint.?.addr, client.endpoint.?.port }, + .{ client.get().endpoint.?.addr, client.get().endpoint.?.port }, ); } diff --git a/src/modules/mmc_client/commands/get_acceleration.zig b/src/modules/mmc_client/commands/get_acceleration.zig index 7b799c11..26335ba0 100644 --- a/src/modules/mmc_client/commands/get_acceleration.zig +++ b/src/modules/mmc_client/commands/get_acceleration.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); @@ -13,7 +13,7 @@ pub fn impl(params: [][]const u8) !void { "Line {s} acceleration: {d} {s}", .{ line_name, - client.lines[line_idx].acceleration, + client.get().lines[line_idx].acceleration, client.standard.acceleration.unit, }, ); diff --git a/src/modules/mmc_client/commands/get_speed.zig b/src/modules/mmc_client/commands/get_speed.zig index a306727a..43e5350f 100644 --- a/src/modules/mmc_client/commands/get_speed.zig +++ b/src/modules/mmc_client/commands/get_speed.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); @@ -8,7 +8,7 @@ pub fn impl(params: [][]const u8) !void { defer tracy_zone.end(); const line_name: []const u8 = params[0]; const line_idx = try client.matchLine(line_name); - const velocity = client.lines[line_idx].velocity; + const velocity = client.get().lines[line_idx].velocity; std.log.info( "Line {s} speed: {d} {s}", .{ diff --git a/src/modules/mmc_client/commands/hall_status.zig b/src/modules/mmc_client/commands/hall_status.zig index 168a260b..4d022145 100644 --- a/src/modules/mmc_client/commands/hall_status.zig +++ b/src/modules/mmc_client/commands/hall_status.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,11 +7,11 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "hall_status"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; var filter: ?client.Filter = null; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); if (params[1].len > 0) { @@ -33,9 +33,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/isolate.zig b/src/modules/mmc_client/commands/isolate.zig index 880fc040..17e05669 100644 --- a/src/modules/mmc_client/commands/isolate.zig +++ b/src/modules/mmc_client/commands/isolate.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,7 +8,7 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "isolate"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const axis_id = try std.fmt.parseInt(u32, buf: { const input = params[1]; @@ -26,7 +26,7 @@ pub fn impl(params: [][]const u8) !void { }, 0); const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const dir: api.protobuf.mmc.command.Request.Direction = dir_parse: { if (std.ascii.eqlIgnoreCase("forward", params[2])) { @@ -80,6 +80,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/log.zig b/src/modules/mmc_client/commands/log.zig index 3068f852..52e36208 100644 --- a/src/modules/mmc_client/commands/log.zig +++ b/src/modules/mmc_client/commands/log.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const chrono = @import("chrono"); const tracy = @import("tracy"); @@ -10,11 +10,11 @@ const Kind = enum { all, axis, driver }; pub fn add(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "add_log"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; // Parsing line name const line_name = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; // Parsing logging kind const kind: Kind = kind: { if (params[1].len == 0) @@ -55,7 +55,7 @@ pub fn add(params: [][]const u8) !void { // the only thing that can be done from this point is to always show the // logging configuration even if there is an error when trying to toggle // the driver flag for logging. - defer client.log_config.status() catch {}; + defer client.get().log_config.status() catch {}; try modify(net, line, kind, range, true); } @@ -69,8 +69,8 @@ pub fn start(params: [][]const u8) !void { const file_path = if (path.len > 0) p: { // Check if the specified path is ended in csv. if (std.mem.eql(u8, path[path.len - 4 .. path.len], ".csv")) - break :p try client.allocator.dupe(u8, path); - break :p try std.fmt.allocPrint(client.allocator, "{s}.csv", .{path}); + break :p try client.get().allocator.dupe(u8, path); + break :p try std.fmt.allocPrint(client.get().allocator, "{s}.csv", .{path}); } else p: { var timestamp: u64 = @intCast(std.time.timestamp()); timestamp += std.time.s_per_hour * 9; @@ -83,7 +83,7 @@ pub fn start(params: [][]const u8) !void { 0, ); break :p try std.fmt.allocPrint( - client.allocator, + client.get().allocator, "mmc-logging-{}.{:0>2}.{:0>2}-{:0>2}.{:0>2}.{:0>2}.csv", .{ ymd.year, @@ -95,11 +95,11 @@ pub fn start(params: [][]const u8) !void { }, ); }; - defer client.allocator.free(file_path); + defer client.get().allocator.free(file_path); const log_thread = try std.Thread.spawn( .{}, client.log.runner, - .{ duration, try client.allocator.dupe(u8, file_path) }, + .{ duration, try client.get().allocator.dupe(u8, file_path) }, ); log_thread.detach(); } @@ -107,17 +107,17 @@ pub fn start(params: [][]const u8) !void { pub fn status(_: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "status_log"); defer tracy_zone.end(); - try client.log_config.status(); + try client.get().log_config.status(); } pub fn remove(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "remove_log"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; // Parsing line name const line_name = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; // Parsing logging kind const kind: Kind = kind: { if (params[1].len == 0) @@ -158,7 +158,7 @@ pub fn remove(params: [][]const u8) !void { // the only thing that can be shown from this point is to always show the // logging configuration even if there is an error when trying to toggle // the driver flag for logging. - defer client.log_config.status() catch {}; + defer client.get().log_config.status() catch {}; try modify(net, line, kind, range, false); } @@ -189,7 +189,7 @@ fn modify( ) !void { for (range.start..range.end + 1) |axis_id| { if (kind == .all or kind == .axis) - client.log_config.lines[line.index].axes[axis_id - 1] = flag; + client.get().log_config.lines[line.index].axes[axis_id - 1] = flag; if (kind == .all or kind == .driver) { var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); @@ -213,9 +213,9 @@ fn modify( }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { @@ -240,7 +240,7 @@ fn modify( const driver = track_line.driver_state.pop() orelse return error.InvalidResponse; - client.log_config.lines[line.index].drivers[driver.id - 1] = flag; + client.get().log_config.lines[line.index].drivers[driver.id - 1] = flag; } } } diff --git a/src/modules/mmc_client/commands/move.zig b/src/modules/mmc_client/commands/move.zig index 7c7a625b..27971001 100644 --- a/src/modules/mmc_client/commands/move.zig +++ b/src/modules/mmc_client/commands/move.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,10 +7,10 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "move_carrier"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const carrier_id: u10 = try std.fmt.parseInt(u10, b: { const input = params[1]; var suffix: ?usize = null; @@ -58,8 +58,8 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } fn parseTarget( diff --git a/src/modules/mmc_client/commands/pause.zig b/src/modules/mmc_client/commands/pause.zig index f5d52614..79442df3 100644 --- a/src/modules/mmc_client/commands/pause.zig +++ b/src/modules/mmc_client/commands/pause.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,7 +7,7 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "pause"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; var ids: [1]u32 = .{0}; if (params[0].len > 0) { const line_name = params[0]; @@ -25,6 +25,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/print_axis_info.zig b/src/modules/mmc_client/commands/print_axis_info.zig index 5c581d37..9f93547d 100644 --- a/src/modules/mmc_client/commands/print_axis_info.zig +++ b/src/modules/mmc_client/commands/print_axis_info.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,11 +7,11 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "print_axis_info"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; var filter: client.Filter = try .parse(params[1]); const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); const request: api.protobuf.mmc.Request = .{ @@ -28,9 +28,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/print_carrier_info.zig b/src/modules/mmc_client/commands/print_carrier_info.zig index 2e141d00..fb9f9075 100644 --- a/src/modules/mmc_client/commands/print_carrier_info.zig +++ b/src/modules/mmc_client/commands/print_carrier_info.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,7 +7,7 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "print_carrier_info"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; var filter: ?client.Filter = if (params[1].len > 0) try .parse(params[1]) @@ -15,7 +15,7 @@ pub fn impl(params: [][]const u8) !void { null; const pb_filter = if (filter) |*f| f.toProtobuf() else null; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); const request: api.protobuf.mmc.Request = .{ @@ -31,9 +31,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/print_driver_info.zig b/src/modules/mmc_client/commands/print_driver_info.zig index 958f28ec..a5dcbdbe 100644 --- a/src/modules/mmc_client/commands/print_driver_info.zig +++ b/src/modules/mmc_client/commands/print_driver_info.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,11 +7,11 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "print_driver_info"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; var filter: client.Filter = try .parse(params[1]); const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); const request: api.protobuf.mmc.Request = .{ @@ -28,9 +28,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/pull.zig b/src/modules/mmc_client/commands/pull.zig index 637abfb7..e3c00901 100644 --- a/src/modules/mmc_client/commands/pull.zig +++ b/src/modules/mmc_client/commands/pull.zig @@ -1,11 +1,11 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name = params[0]; const axis_id = try std.fmt.parseInt(u32, buf: { const input = params[1]; @@ -53,7 +53,7 @@ pub fn impl(params: [][]const u8) !void { false; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const disable_cas = if (params[5].len == 0) false else if (std.ascii.eqlIgnoreCase("on", params[5])) @@ -91,6 +91,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/push.zig b/src/modules/mmc_client/commands/push.zig index 66fccac0..021ba764 100644 --- a/src/modules/mmc_client/commands/push.zig +++ b/src/modules/mmc_client/commands/push.zig @@ -1,14 +1,14 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const axis_id: u32 = try std.fmt.parseInt(u32, buf: { const input = params[1]; var suffix: ?usize = null; @@ -80,9 +80,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { @@ -142,8 +142,8 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } // Push command request { @@ -163,7 +163,7 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } } diff --git a/src/modules/mmc_client/commands/release_carrier.zig b/src/modules/mmc_client/commands/release_carrier.zig index 7e9c7cb9..0e54874f 100644 --- a/src/modules/mmc_client/commands/release_carrier.zig +++ b/src/modules/mmc_client/commands/release_carrier.zig @@ -1,6 +1,6 @@ //! This file contains client for managing the server-side state. const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -9,10 +9,10 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "release_carrier"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var filter: ?client.Filter = null; if (params[1].len > 0) { filter = try .parse(params[1]); @@ -39,6 +39,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/reset_system.zig b/src/modules/mmc_client/commands/reset_system.zig index 7053ac87..accbd5fa 100644 --- a/src/modules/mmc_client/commands/reset_system.zig +++ b/src/modules/mmc_client/commands/reset_system.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,8 +8,8 @@ pub fn impl(_: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "reset_system"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; - for (client.lines) |line| { + const net = client.get().sock orelse return error.ServerNotConnected; + for (client.get().lines) |line| { // Send deinitialize command { const request: api.protobuf.mmc.Request = .{ @@ -21,8 +21,8 @@ pub fn impl(_: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } // Send clear errors command { @@ -35,8 +35,8 @@ pub fn impl(_: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } // Send stop push command { @@ -49,8 +49,8 @@ pub fn impl(_: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } // Send stop pull command { @@ -63,8 +63,8 @@ pub fn impl(_: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } } } diff --git a/src/modules/mmc_client/commands/resume.zig b/src/modules/mmc_client/commands/resume.zig index 5cf919b9..48555715 100644 --- a/src/modules/mmc_client/commands/resume.zig +++ b/src/modules/mmc_client/commands/resume.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,7 +7,7 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "resume"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; var ids: [1]u32 = .{0}; if (params[0].len > 0) { const line_name = params[0]; @@ -25,6 +25,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/server_version.zig b/src/modules/mmc_client/commands/server_version.zig index 7f11a2a8..bedd968a 100644 --- a/src/modules/mmc_client/commands/server_version.zig +++ b/src/modules/mmc_client/commands/server_version.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,15 +7,15 @@ const api = @import("mmc-api"); pub fn impl(_: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "server_version"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const request: api.protobuf.mmc.Request = .{ .body = .{ .core = .{ .kind = .CORE_REQUEST_KIND_SERVER_INFO }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const server = switch (decoded.body orelse return error.InvalidResponse) { .core => |core_resp| switch (core_resp.body orelse diff --git a/src/modules/mmc_client/commands/set_acceleration.zig b/src/modules/mmc_client/commands/set_acceleration.zig index 03086531..da34530c 100644 --- a/src/modules/mmc_client/commands/set_acceleration.zig +++ b/src/modules/mmc_client/commands/set_acceleration.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); @@ -12,10 +12,10 @@ pub fn impl(params: [][]const u8) !void { return error.InvalidAcceleration; const line_idx = try client.matchLine(line_name); - client.lines[line_idx].acceleration = carrier_acceleration; + client.get().lines[line_idx].acceleration = carrier_acceleration; std.log.info("Set acceleration to {d} {s}.", .{ - client.lines[line_idx].acceleration, + client.get().lines[line_idx].acceleration, client.standard.acceleration.unit, }); } diff --git a/src/modules/mmc_client/commands/set_carrier_id.zig b/src/modules/mmc_client/commands/set_carrier_id.zig index 1f8fc40b..bb23eca4 100644 --- a/src/modules/mmc_client/commands/set_carrier_id.zig +++ b/src/modules/mmc_client/commands/set_carrier_id.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,10 +7,10 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "set_carrier_id"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const carrier = try std.fmt.parseUnsigned(u32, b: { const input = params[1]; var suffix: ?usize = null; @@ -52,6 +52,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/set_line_zero.zig b/src/modules/mmc_client/commands/set_line_zero.zig index de9642e6..2a27b395 100644 --- a/src/modules/mmc_client/commands/set_line_zero.zig +++ b/src/modules/mmc_client/commands/set_line_zero.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,10 +8,10 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "set_line_zero"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; const request: api.protobuf.mmc.Request = .{ .body = .{ .command = .{ @@ -21,6 +21,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/set_speed.zig b/src/modules/mmc_client/commands/set_speed.zig index 50c37d8c..eb278c1a 100644 --- a/src/modules/mmc_client/commands/set_speed.zig +++ b/src/modules/mmc_client/commands/set_speed.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); @@ -12,12 +12,12 @@ pub fn impl(params: [][]const u8) !void { // from 0.1 to 6000.0 mm/s const line_idx = try client.matchLine(line_name); - client.lines[line_idx].velocity = carrier_speed; + client.get().lines[line_idx].velocity = carrier_speed; std.log.info( "Set speed to {d} {s}", .{ - client.lines[line_idx].velocity, + client.get().lines[line_idx].velocity, client.standard.speed.unit, }, ); diff --git a/src/modules/mmc_client/commands/show_errors.zig b/src/modules/mmc_client/commands/show_errors.zig index 07f631d4..554cc34d 100644 --- a/src/modules/mmc_client/commands/show_errors.zig +++ b/src/modules/mmc_client/commands/show_errors.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,10 +7,10 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "show_errors"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); var filter: ?client.Filter = null; @@ -34,9 +34,9 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/commands/stop.zig b/src/modules/mmc_client/commands/stop.zig index d50d042d..d22ecb26 100644 --- a/src/modules/mmc_client/commands/stop.zig +++ b/src/modules/mmc_client/commands/stop.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -7,7 +7,7 @@ const api = @import("mmc-api"); pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "stop"); defer tracy_zone.end(); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; var ids: [1]u32 = .{0}; if (params[0].len > 0) { const line_name = params[0]; @@ -25,6 +25,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/stop_pull.zig b/src/modules/mmc_client/commands/stop_pull.zig index 593f2afe..5dd3194d 100644 --- a/src/modules/mmc_client/commands/stop_pull.zig +++ b/src/modules/mmc_client/commands/stop_pull.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,10 +8,10 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "stop_pull"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var filter: ?client.Filter = null; if (params[1].len > 0) { filter = try .parse(params[1]); @@ -41,6 +41,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/stop_push.zig b/src/modules/mmc_client/commands/stop_push.zig index e4a1a80f..33807faf 100644 --- a/src/modules/mmc_client/commands/stop_push.zig +++ b/src/modules/mmc_client/commands/stop_push.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,10 +8,10 @@ pub fn impl(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "stop_push"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name = params[0]; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var filter: ?client.Filter = null; if (params[1].len > 0) { filter = try .parse(params[1]); @@ -41,6 +41,6 @@ pub fn impl(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - try client.waitCommandCompleted(client.allocator, net); + try client.sendRequest(client.get().allocator, net, request); + try client.waitCommandCompleted(client.get().allocator, net); } diff --git a/src/modules/mmc_client/commands/wait.zig b/src/modules/mmc_client/commands/wait.zig index 29983d95..33d3d9d2 100644 --- a/src/modules/mmc_client/commands/wait.zig +++ b/src/modules/mmc_client/commands/wait.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const client = @import("../../mmc_client.zig"); +const client = @import("../../MmcClient.zig"); const command = @import("../../../command.zig"); const tracy = @import("tracy"); const api = @import("mmc-api"); @@ -8,7 +8,7 @@ pub fn isolate(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "wait_isolate"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const carrier_id = try std.fmt.parseInt(u10, b: { const input = params[1]; @@ -29,7 +29,7 @@ pub fn isolate(params: [][]const u8) !void { else 0; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; try waitCarrierState( net, line.id, @@ -43,7 +43,7 @@ pub fn moveCarrier(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "wait_move_carrier"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name: []const u8 = params[0]; const carrier_id = try std.fmt.parseInt(u10, b: { const input = params[1]; @@ -65,7 +65,7 @@ pub fn moveCarrier(params: [][]const u8) !void { 0; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; try waitCarrierState( net, line.id, @@ -79,7 +79,7 @@ pub fn axisEmpty(params: [][]const u8) !void { const tracy_zone = tracy.traceNamed(@src(), "wait_axis_empty"); defer tracy_zone.end(); errdefer client.log.stop.store(true, .monotonic); - const net = client.sock orelse return error.ServerNotConnected; + const net = client.get().sock orelse return error.ServerNotConnected; const line_name = params[0]; const axis_id = try std.fmt.parseInt(u32, buf: { const input = params[1]; @@ -100,7 +100,7 @@ pub fn axisEmpty(params: [][]const u8) !void { else 0; const line_idx = try client.matchLine(line_name); - const line = client.lines[line_idx]; + const line = client.get().lines[line_idx]; var line_array: [1]u32 = .{line.id}; const lines: std.ArrayList(u32) = .fromOwnedSlice(&line_array); var wait_timer = try std.time.Timer.start(); @@ -127,9 +127,9 @@ pub fn axisEmpty(params: [][]const u8) !void { }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { @@ -195,9 +195,9 @@ fn waitCarrierState( }, }, }; - try client.sendRequest(client.allocator, net, request); - var decoded = try client.getResponse(client.allocator, net); - defer decoded.deinit(client.allocator); + try client.sendRequest(client.get().allocator, net, request); + var decoded = try client.getResponse(client.get().allocator, net); + defer decoded.deinit(client.get().allocator); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse return error.InvalidResponse) { diff --git a/src/modules/mmc_client/log.zig b/src/modules/mmc_client/log.zig index 2b01a397..50021188 100644 --- a/src/modules/mmc_client/log.zig +++ b/src/modules/mmc_client/log.zig @@ -4,7 +4,7 @@ const std = @import("std"); const api = @import("mmc-api"); const zignet = @import("zignet"); -const client = @import("../mmc_client.zig"); +const client = @import("../MmcClient.zig"); const command = @import("../../command.zig"); pub const Range = struct { start: u32 = 0, end: u32 = 0 }; @@ -74,7 +74,7 @@ pub const Config = struct { for (self.lines) |line| { if (line.isInitialized() == false) continue; try stdout.interface - .print("Line {s}: ", .{client.lines[line.id - 1].name}); + .print("Line {s}: ", .{client.get().lines[line.id - 1].name}); var axis_range: Range = .{}; var driver_range: Range = .{}; var first_axis_entry = true; @@ -346,7 +346,7 @@ const Stream = struct { }; try client.sendRequest(gpa, stream.socket, request); var decoded = try client.getResponse(gpa, stream.socket); - defer decoded.deinit(client.allocator); + defer decoded.deinit(gpa); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse @@ -477,7 +477,7 @@ const Stream = struct { }; try client.sendRequest(gpa, stream.socket, request); var decoded = try client.getResponse(gpa, stream.socket); - defer decoded.deinit(client.allocator); + defer decoded.deinit(gpa); const track = switch (decoded.body orelse return error.InvalidResponse) { .info => |info_resp| switch (info_resp.body orelse @@ -595,9 +595,9 @@ var file_reader_buf: [4096]u8 = undefined; var file_writer_buf: [4096]u8 = undefined; pub fn runner(duration: f64, file_path: []const u8) !void { - defer client.allocator.free(file_path); + defer client.get().allocator.free(file_path); // Validation steps - if (client.log_config.isInitialized() == false) + if (client.get().log_config.isInitialized() == false) return error.LoggingNotConfigured; // Assumption: The register is updated every 3 ms. const update_rate = 3; @@ -610,15 +610,15 @@ pub fn runner(duration: f64, file_path: []const u8) !void { executing.store(true, .monotonic); defer executing.store(false, .monotonic); // Stream setup. - if (client.sock == null) return error.SocketNotConnected; + if (client.get().sock == null) return error.SocketNotConnected; var stream: Stream = try .init( - client.allocator, + client.get().allocator, @as(usize, @intFromFloat(logging_size_float)), - client.log_config, - client.lines, - client.endpoint orelse return error.MissingEndpoint, + client.get().log_config, + client.get().lines, + client.get().endpoint orelse return error.MissingEndpoint, ); - defer stream.deinit(client.allocator); + defer stream.deinit(client.get().allocator); // Logging file setup. const log_file = try std.fs.cwd().createFile(file_path, .{}); defer { @@ -642,7 +642,7 @@ pub fn runner(duration: f64, file_path: []const u8) !void { f64, @floatFromInt(std.time.microTimestamp() - log_time_start), ) / std.time.us_per_s; - stream.get(client.allocator, timestamp) catch |e| { + stream.get(client.get().allocator, timestamp) catch |e| { std.log.err("{t}", .{e}); std.log.debug("{?f}", .{@errorReturnTrace()}); break; @@ -656,7 +656,7 @@ pub fn runner(duration: f64, file_path: []const u8) !void { var log_writer = log_file.writer(&.{}); // Write the headers of the data to the logging file. try log_writer.interface.print("timestamp,", .{}); - for (client.log_config.lines) |line_config| { + for (client.get().log_config.lines) |line_config| { var buf: [64]u8 = undefined; for (line_config.drivers, 1..) |log_driver, driver_id| { if (log_driver == false) continue; @@ -665,7 +665,7 @@ pub fn runner(duration: f64, file_path: []const u8) !void { try std.fmt.bufPrint( &buf, "{s}_driver{d}", - .{ client.lines[line_config.id - 1].name, driver_id }, + .{ client.get().lines[line_config.id - 1].name, driver_id }, ), "", Stream.Data.Line.Driver, @@ -678,7 +678,7 @@ pub fn runner(duration: f64, file_path: []const u8) !void { try std.fmt.bufPrint( &buf, "{s}_axis{d}", - .{ client.lines[line_config.id - 1].name, axis_id }, + .{ client.get().lines[line_config.id - 1].name, axis_id }, ), "", Stream.Data.Line.Axis, @@ -693,7 +693,7 @@ pub fn runner(duration: f64, file_path: []const u8) !void { if (timestamp - log_data.timestamp > duration) continue; try log_writer.interface.writeByte('\n'); try log_writer.interface.print("{},", .{log_data.timestamp}); - for (client.log_config.lines) |line_config| { + for (client.get().log_config.lines) |line_config| { const line_data = log_data.lines[line_config.id - 1]; for ( line_data.drivers, From 117302712adc8d4b98ea1afa67ae675c418761fc Mon Sep 17 00:00:00 2001 From: kf Date: Tue, 31 Mar 2026 16:18:32 +0900 Subject: [PATCH 02/12] refactor: switchable configuration --- src/command.zig | 289 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 275 insertions(+), 14 deletions(-) diff --git a/src/command.zig b/src/command.zig index e806d11b..81e989a2 100644 --- a/src/command.zig +++ b/src/command.zig @@ -62,6 +62,23 @@ pub const Registry = struct { } }; +/// Registers commands into the global command registry. +fn registerCommands(commands: []const Command) !void { + for (commands) |cmd| { + try registry.put(cmd); + } +} + +/// Unregisters commands from the global command registry. +fn unregisterCommands(commands: []const Command) void { + for (commands) |cmd| { + switch (cmd) { + .executable => registry.orderedRemove(cmd.executable.name), + .alias => registry.orderedRemove(cmd.alias.name), + } + } +} + pub const Table = struct { gpa: std.mem.Allocator, @@ -81,7 +98,7 @@ pub const Table = struct { pub fn deinit(self: *Table) void { if (self.header.len > 0) { for (self.header) |*header| { - self.allocator.free(header.*); + self.gpa.free(header.*); } self.gpa.free(self.header); } @@ -181,6 +198,24 @@ pub var variables: std.BufMap = undefined; pub var table: Table = undefined; +const LoadedConfig = struct { + id: []const u8, + source_path: []const u8, + arena: *std.heap.ArenaAllocator, + config: Config, + + pub fn deinit(self: *LoadedConfig) void { + self.config.deinit(); + self.arena.deinit(); + std.heap.smp_allocator.destroy(self.arena); + std.heap.smp_allocator.free(self.id); + std.heap.smp_allocator.free(self.source_path); + } +}; + +var loaded_configs: std.StringArrayHashMap(LoadedConfig) = undefined; +var active_config_id: ?[]const u8 = null; + // Flags to keep track of currently initialized modules, so that only // initialized will be deinitialized. var initialized_modules: std.EnumArray(Config.Module, bool) = undefined; @@ -345,6 +380,8 @@ pub fn init() !void { registry = Registry.init(allocator); variables = std.BufMap.init(allocator); table = Table.init(std.heap.smp_allocator); + loaded_configs = std.StringArrayHashMap(LoadedConfig).init(std.heap.smp_allocator); + active_config_id = null; command_queue = .{ .first = null, .last = null }; command_queue_lock = .{}; stop.store(false, .monotonic); @@ -376,15 +413,53 @@ pub fn init() !void { .name = "LOAD_CONFIG", .parameters = &[_]Command.Executable.Parameter{ .{ .name = "file path", .optional = true }, + .{ .name = "config id", .optional = true, .resolve = false }, }, .short_description = "Load CLI configuration file.", .long_description = \\Read given configuration file to dynamically load specified command \\modules. This configuration file must be in valid JSON5 format, with \\configuration parameters according to provided documentation. + \\Optional: Provide a config ID. If none is provided, a default ID will + \\be assigned. The config ID is used to switch between configurations. , .execute = &loadConfig, } }); + try registry.put(.{ .executable = .{ + .name = "USE_CONFIG", + .parameters = &[_]Command.Executable.Parameter{ + .{ .name = "config id", .resolve = false }, + }, + .short_description = "Activate a loaded configuration via its ID.", + .long_description = + \\Deactivate the currently active configuration and activate another + \\previously loaded configuration. + , + .execute = &useConfig, + } }); + try registry.put(.{ .executable = .{ + .name = "UNLOAD_CONFIG", + .parameters = &[_]Command.Executable.Parameter{ + .{ .name = "config id", .optional = true, .resolve = false }, + }, + .short_description = "Unload a previously loaded configuration.", + .long_description = + \\Unload the currently active configuration. Optionally, a config ID can be + \\provided to unload a specific configuration. If an active configuration + \\is unloaded, the first loaded configuration will be activated. + , + .execute = &unloadConfig, + } }); + try registry.put(.{ .executable = .{ + .name = "LIST_LOADED_CONFIGS", + .short_description = "List all loaded configurations.", + .long_description = + \\Display all currently loaded configurations with config ID and their + \\source path and whether they are active. The active configuration is + \\marked with '*'. + , + .execute = &listLoadedConfigs, + } }); try registry.put(.{ .executable = .{ .name = "WAIT", .parameters = &[_]Command.Executable.Parameter{ @@ -566,11 +641,14 @@ pub fn init() !void { pub fn deinit() void { deinitModules(); + deinitLoadedConfigs(); stop.store(true, .monotonic); defer stop.store(false, .monotonic); + table.deinit(); variables.deinit(); queueClear(); command_queue_lock = undefined; + loaded_configs.deinit(); registry.deinit(); arena.deinit(); } @@ -914,16 +992,22 @@ fn deinitModules() void { } } } + active_config_id = null; } -fn loadConfig(params: [][]const u8) !void { - // De-initialize any previously initialized modules. - deinitModules(); +fn deinitLoadedConfigs() void { + while (loaded_configs.count() > 0) { + const key = loaded_configs.keys()[0]; + var loaded = loaded_configs.get(key).?; + _ = loaded_configs.orderedRemove(key); + loaded.deinit(); + } + active_config_id = null; +} - // Load config file. - const config_file = if (params[0].len > 0) - std.fs.cwd().openFile(params[0], .{}) catch - try std.fs.openFileAbsolute(params[0], .{}) +fn openConfigFile(path: []const u8) !std.fs.File { + return if (path.len > 0) + std.fs.cwd().openFile(path, .{}) catch error.InvalidParameter else std.fs.cwd().openFile("config.json5", .{}) catch exe_local: { var exe_dir_buf: [std.fs.max_path_bytes]u8 = undefined; @@ -979,12 +1063,45 @@ fn loadConfig(params: [][]const u8) !void { .{}, ); }; - var m_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - const m_allocator = m_arena.allocator(); - var conf = try Config.parse(m_allocator, config_file); +} + +/// Generates a unique config ID. +fn genLoadedConfigId(file_path: []const u8, config_id: []const u8) ![]u8 { + var requested_id: []const u8 = config_id; + + if (requested_id.len == 0) { + requested_id = if (file_path.len > 0) + std.fs.path.stem(file_path) + else + "config"; + } + + if (!loaded_configs.contains(requested_id)) { + return try std.heap.smp_allocator.dupe(u8, requested_id); + } + + const base: []const u8 = requested_id; + var suffix: usize = 2; - // Initialize only the modules specified in config file. + while (true) : (suffix += 1) { + const candidate = try std.fmt.allocPrint( + std.heap.smp_allocator, + "{s}_{d}", + .{ base, suffix }, + ); + + if (!loaded_configs.contains(candidate)) return candidate; + std.heap.smp_allocator.free(candidate); + + // if (suffix == std.math.maxInt(@TypeOf(suffix))) return error.InvalidConfigID; + // std.log.debug("maxInt: {d}\n", .{std.math.maxInt(@TypeOf(suffix))}); + } +} + +fn initModulesFromConfig(conf: *Config) !void { const fields = @typeInfo(Config.Module).@"enum".fields; + errdefer deinitModules(); + for (conf.modules()) |module| { switch (@intFromEnum(module)) { inline 0...fields.len - 1 => |i| { @@ -1002,8 +1119,152 @@ fn loadConfig(params: [][]const u8) !void { else => unreachable, } } - conf.deinit(); - m_arena.deinit(); +} + +fn activateLoadedConfig(id: []const u8) !void { + const loaded = loaded_configs.getPtr(id) orelse + return error.InvalidParameter; + deinitModules(); + try initModulesFromConfig(&loaded.config); + active_config_id = loaded.id; +} + +fn loadConfig(params: [][]const u8) !void { + const file_path = if (params.len > 0) params[0] else ""; + const config_id = if (params.len > 1) params[1] else ""; + const resolved_source_path = + if (file_path.len > 0) file_path else "config.json5"; + + var config_file = try openConfigFile(file_path); + defer config_file.close(); + + const config_arena = try std.heap.smp_allocator.create(std.heap.ArenaAllocator); + errdefer std.heap.smp_allocator.destroy(config_arena); + + config_arena.* = std.heap.ArenaAllocator.init(std.heap.page_allocator); + errdefer config_arena.deinit(); + + const config_allocator = config_arena.allocator(); + + var conf = try Config.parse(config_allocator, config_file); + errdefer conf.deinit(); + + // Check if Config already loaded + var it_loaded = loaded_configs.iterator(); + while (it_loaded.next()) |entry| { + if (configEql(&conf, &entry.value_ptr.config)) + return error.ConfigAlreadyLoaded; + } + + const id = try genLoadedConfigId(file_path, config_id); + errdefer std.heap.smp_allocator.free(id); + + const source_path = try std.heap.smp_allocator.dupe( + u8, + resolved_source_path, + ); + errdefer std.heap.smp_allocator.free(source_path); + + try loaded_configs.put(id, .{ + .id = id, + .source_path = source_path, + .arena = config_arena, + .config = conf, + }); + + try activateLoadedConfig(id); + std.log.info("Loaded config as '{s}' from {s}\n", .{ id, source_path }); +} + +fn useConfig(params: [][]const u8) !void { + if (params.len == 0) return error.InvalidParameter; + try activateLoadedConfig(params[0]); + std.log.info("Using config '{s}'\n", .{params[0]}); +} + +fn unloadConfig(params: [][]const u8) !void { + const id = if (params[0].len > 0) + params[0] + else + active_config_id orelse return error.InvalidParameter; + + const idx = loaded_configs.getIndex(id) orelse + return error.InvalidParameter; + + const was_active = if (active_config_id) |active_id| + std.mem.eql(u8, active_id, id) + else + false; + + // Choose fallback config before removing + var fallback_id: ?[]const u8 = null; + if (was_active) { + for (loaded_configs.keys(), 0..) |loaded_id, i| { + if (i != idx and !std.mem.eql(u8, loaded_id, id)) { + fallback_id = loaded_id; + break; + } + } + } + + std.log.debug( + "unload config: id={s}, was active={}, fallback={s}", + .{ + id, + was_active, + fallback_id orelse "nope", + }, + ); + + const removed_entry = loaded_configs.fetchOrderedRemove(id) orelse + return error.InvalidParameter; + + if (was_active) { + deinitModules(); + } + + var removed = removed_entry.value; + errdefer removed.deinit(); + + if (was_active) { + if (fallback_id) |next_id| { + try activateLoadedConfig(next_id); + std.log.info("Unloaded config '{s}'. Active config now '{s}'\n", .{ id, next_id }); + } else { + std.log.info("Unloaded config '{s}'. No loaded config to activate available.", .{id}); + } + } else { + std.log.info("Unloaded config '{s}'\n", .{id}); + } + + removed.deinit(); +} + +fn listLoadedConfigs(_: [][]const u8) !void { + if (loaded_configs.count() == 0) { + std.log.info("No configs loaded.\n", .{}); + return; + } + + std.log.info( + \\Config ID: Config path + \\------------------------------------ + , .{}); + for (loaded_configs.keys(), loaded_configs.values()) |id, *loaded| { + const active = if (active_config_id) |active_id| + std.mem.eql(u8, active_id, id) + else + false; + + std.log.info( + "{s}{s}: {s}\n", + .{ + if (active) "* " else " ", + id, + loaded.source_path, + }, + ); + } } fn wait(params: [][]const u8) !void { From 039fe756dbed022b0af53322acf3e87b1dbc305e Mon Sep 17 00:00:00 2001 From: kf Date: Thu, 2 Apr 2026 16:42:09 +0900 Subject: [PATCH 03/12] refactor: change new commands long_description --- src/command.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/command.zig b/src/command.zig index 81e989a2..6d4fa2a7 100644 --- a/src/command.zig +++ b/src/command.zig @@ -444,8 +444,8 @@ pub fn init() !void { }, .short_description = "Unload a previously loaded configuration.", .long_description = - \\Unload the currently active configuration. Optionally, a config ID can be - \\provided to unload a specific configuration. If an active configuration + \\Unload the currently active configuration. Optionally, provided a + \\config ID to unload a specific configuration. If an active configuration \\is unloaded, the first loaded configuration will be activated. , .execute = &unloadConfig, @@ -455,8 +455,7 @@ pub fn init() !void { .short_description = "List all loaded configurations.", .long_description = \\Display all currently loaded configurations with config ID and their - \\source path and whether they are active. The active configuration is - \\marked with '*'. + \\source path. The active configuration is marked with '*'. , .execute = &listLoadedConfigs, } }); From 0e044c3eedb455bbeb098a8f4e2fd6e54d6df337 Mon Sep 17 00:00:00 2001 From: kf Date: Wed, 1 Apr 2026 18:02:58 +0900 Subject: [PATCH 04/12] refactor: register and remove commands --- src/Config.zig | 2 +- src/command.zig | 117 ++- src/modules/MmcClient.zig | 1876 ++++++++++++++++++------------------- src/modules/mes07.zig | 53 +- 4 files changed, 1002 insertions(+), 1046 deletions(-) diff --git a/src/Config.zig b/src/Config.zig index 9779914d..22e8f2ef 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -22,7 +22,7 @@ pub const Module = enum { mes07, }; -const ModuleConfig = union(Module) { +pub const ModuleConfig = union(Module) { return_demo2: ReturnDemo2Config, mmc_client: ClientCliConfig, mes07: Mes07Config, diff --git a/src/command.zig b/src/command.zig index 6d4fa2a7..f6bed391 100644 --- a/src/command.zig +++ b/src/command.zig @@ -21,6 +21,72 @@ const mes07 = if (config.mes07) @import("modules/mes07.zig") else void; const Config = @import("Config.zig"); +const ModuleSpec = struct { + init: *const fn (Config.ModuleConfig) anyerror!void, + deinit: *const fn () void, + commands: []const Command, +}; + +fn initModuleDisabled(_: Config.ModuleConfig) !void { + return error.ModuleDisabledAtBuildTime; +} + +fn deinitModuleDisabled() void {} + +fn initReturnDemo2(module_config: Config.ModuleConfig) !void { + const cfg = switch (module_config) { + .return_demo2 => |cfg| cfg, + else => unreachable, + }; + try return_demo2.init(cfg); +} + +fn initMmcClient(module_config: Config.ModuleConfig) !void { + const cfg = switch (module_config) { + .mmc_client => |cfg| cfg, + else => unreachable, + }; + try mmc_client.init(cfg); +} + +fn initMes07(module_config: Config.ModuleConfig) !void { + const cfg = switch (module_config) { + .mes07 => |cfg| cfg, + else => unreachable, + }; + try mes07.init(cfg); +} + +const module_specs = std.EnumArray(Config.Module, ModuleSpec).init(.{ + .return_demo2 = if (config.return_demo2) .{ + .init = initReturnDemo2, + .deinit = return_demo2.deinit, + .commands = return_demo2.module_commands[0..], + } else .{ + .init = initModuleDisabled, + .deinit = deinitModuleDisabled, + .commands = &.{}, + }, + .mmc_client = if (config.mmc_client) .{ + .init = initMmcClient, + .deinit = mmc_client.deinit, + .commands = mmc_client.module_commands[0..], + } else .{ + .init = initModuleDisabled, + .deinit = deinitModuleDisabled, + .commands = &.{}, + }, + .mes07 = if (config.mes07) .{ + .init = initMes07, + .deinit = mes07.deinit, + .commands = mes07.module_commands[0..], + } else .{ + .init = initModuleDisabled, + .deinit = deinitModuleDisabled, + .commands = &.{}, + }, +}); + pub const Registry = struct { mapping: std.StringArrayHashMap(Command.Executable), @@ -975,21 +1041,13 @@ fn file(params: [][]const u8) !void { fn deinitModules() void { var mod_it = initialized_modules.iterator(); - const fields = @typeInfo(Config.Module).@"enum".fields; - while (mod_it.next()) |e| { - if (e.value.*) { - switch (@intFromEnum(e.key)) { - inline 0...fields.len - 1 => |i| { - const f_type = @typeInfo(@field(@This(), fields[i].name)); - if (comptime f_type != .void) { - @field(@This(), fields[i].name).deinit(); - const module = @field(Config.Module, fields[i].name); - initialized_modules.set(module, false); - } - }, - else => unreachable, - } - } + while (mod_it.next()) |entry| { + if (!entry.value.*) continue; + + const spec = module_specs.get(entry.key); + unregisterCommands(spec.commands); + spec.deinit(); + initialized_modules.set(entry.key, false); } active_config_id = null; } @@ -1098,25 +1156,19 @@ fn genLoadedConfigId(file_path: []const u8, config_id: []const u8) ![]u8 { } fn initModulesFromConfig(conf: *Config) !void { - const fields = @typeInfo(Config.Module).@"enum".fields; errdefer deinitModules(); for (conf.modules()) |module| { - switch (@intFromEnum(module)) { - inline 0...fields.len - 1 => |i| { - const f_type = @typeInfo(@field(@This(), fields[i].name)); - if (comptime f_type != .void) { - try @field(@This(), fields[i].name).init( - @field(module, fields[i].name), - ); - initialized_modules.set( - @field(Config.Module, fields[i].name), - true, - ); - } - }, - else => unreachable, - } + const module_id = std.meta.activeTag(module); + const spec = module_specs.get(module_id); + + try spec.init(module); + errdefer spec.deinit(); + + try registerCommands(spec.commands); + errdefer unregisterCommands(spec.commands); + + initialized_modules.set(module_id, true); } } @@ -1207,8 +1259,9 @@ fn unloadConfig(params: [][]const u8) !void { } std.log.debug( - "unload config: id={s}, was active={}, fallback={s}", + "Unload config: idx={d}, id={s}, was active={}, fallback={s}", .{ + idx, id, was_active, fallback_id orelse "nope", diff --git a/src/modules/MmcClient.zig b/src/modules/MmcClient.zig index a26df2e3..12549c8c 100644 --- a/src/modules/MmcClient.zig +++ b/src/modules/MmcClient.zig @@ -17,216 +17,8 @@ pub const Config = struct { port: u16, }; -pub const Filter = union(enum) { - carrier: [1]u32, - driver: u32, - axis: u32, - - pub fn parse(filter: []const u8) (error{InvalidParameter} || std.fmt.ParseIntError)!Filter { - var suffix_idx: usize = 0; - for (filter) |c| { - if (std.ascii.isDigit(c)) suffix_idx += 1 else break; - } - // No digit is recognized. - if (suffix_idx == 0) return error.InvalidParameter; - const id = try std.fmt.parseUnsigned(u32, filter[0..suffix_idx], 0); - - // Check for single character suffix. - if (filter.len - suffix_idx == 1) { - if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "a")) - return Filter{ .axis = id } - else if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "c")) - return Filter{ .carrier = [1]u32{id} } - else if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "d")) - return Filter{ .driver = id }; - } - // Check for `axis` suffix - else if (filter.len - suffix_idx == 4 and - std.ascii.eqlIgnoreCase(filter[suffix_idx..], "axis")) - return Filter{ .axis = id } - // Check for `driver` suffix - else if (filter.len - suffix_idx == 6 and - std.ascii.eqlIgnoreCase(filter[suffix_idx..], "driver")) - return Filter{ .driver = id } - // Check for `carrier` suffix - else if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "carrier")) - return Filter{ .carrier = [1]u32{id} }; - return error.InvalidParameter; - } - - /// Invalidates original filter's allocated memory ownership. - pub fn toProtobuf(filter: *Filter) api.protobuf.mmc.info.Request.Track.filter_union { - return switch (filter.*) { - .axis => |axis_id| .{ - .axes = .{ - .start = axis_id, - .end = axis_id, - }, - }, - .driver => |driver_id| .{ - .drivers = .{ - .start = driver_id, - .end = driver_id, - }, - }, - .carrier => .{ - .carriers = .{ .ids = .fromOwnedSlice(&filter.carrier) }, - }, - }; - } -}; - -pub const standard = struct { - pub const time = struct { - pub const unit_short: []const u8 = "ms"; - pub const unit_long: []const u8 = "millisecond"; - }; - pub const length = struct { - pub const unit_short: []const u8 = "mm"; - pub const unit_long: []const u8 = "millimeter"; - }; - pub const speed = struct { - pub const range = struct { - pub const min: u16 = 0; - pub const max: u16 = 6000; - }; - pub const unit: []const u8 = "mm/s"; - }; - pub const acceleration = struct { - pub const range = struct { - pub const min: f32 = 0; - pub const max: f32 = 24_500; - }; - pub const unit: []const u8 = "mm/s²"; - }; -}; - -pub const error_response = struct { - /// Throw an error if receive response of core request error - pub fn throwCoreError(err: api.protobuf.mmc.core.Request.Error) anyerror { - return switch (err) { - .CORE_REQUEST_ERROR_UNSPECIFIED => error.InvalidResponse, - .CORE_REQUEST_ERROR_REQUEST_UNKNOWN => error.RequestUnknown, - _ => return error.UnexpectedResponse, - }; - } - - pub fn throwCommandError(err: api.protobuf.mmc.command.Request.Error) anyerror { - return switch (err) { - .COMMAND_REQUEST_ERROR_UNSPECIFIED, - => error.InvalidResponse, - .COMMAND_REQUEST_ERROR_INVALID_LOCATION => error.InvalidLocation, - .COMMAND_REQUEST_ERROR_INVALID_DISTANCE => error.InvalidDistance, - .COMMAND_REQUEST_ERROR_INVALID_LINE => error.InvalidLine, - .COMMAND_REQUEST_ERROR_INVALID_AXIS => error.InvalidAxis, - .COMMAND_REQUEST_ERROR_INVALID_DRIVER => error.InvalidDriver, - .COMMAND_REQUEST_ERROR_INVALID_ACCELERATION => error.InvalidAcceleration, - .COMMAND_REQUEST_ERROR_INVALID_VELOCITY => error.InvalidSpeed, - .COMMAND_REQUEST_ERROR_INVALID_DIRECTION => error.InvalidDirection, - .COMMAND_REQUEST_ERROR_INVALID_CARRIER => error.InvalidCarrier, - .COMMAND_REQUEST_ERROR_MISSING_PARAMETER => error.MissingParameter, - .COMMAND_REQUEST_ERROR_COMMAND_NOT_FOUND => error.CommandNotFound, - .COMMAND_REQUEST_ERROR_CARRIER_NOT_FOUND => error.CarrierNotFound, - .COMMAND_REQUEST_ERROR_OUT_OF_MEMORY => error.ServerRunningOutOfMemory, - .COMMAND_REQUEST_ERROR_MAXIMUM_AUTO_INITIALIZE_EXCEEDED => error.MaximumAutoInitializeExceeded, - .COMMAND_REQUEST_ERROR_CONFLICTING_CARRIER_ID => error.ConflictingCarrierId, - .COMMAND_REQUEST_ERROR_INVALID_COMMAND => error.InvalidCommand, - _ => return error.UnexpectedResponse, - }; - } - - pub fn throwInfoError(err: api.protobuf.mmc.info.Request.Error) anyerror { - return switch (err) { - .INFO_REQUEST_ERROR_UNSPECIFIED => error.InvalidResponse, - .INFO_REQUEST_ERROR_INVALID_LINE => error.InvalidLine, - .INFO_REQUEST_ERROR_INVALID_AXIS => error.InvalidAxis, - .INFO_REQUEST_ERROR_INVALID_DRIVER => error.InvalidDriver, - .INFO_REQUEST_ERROR_MISSING_PARAMETER => error.MissingParameter, - .INFO_REQUEST_ERROR_COMMAND_NOT_FOUND => error.CommandNotFound, - .INFO_REQUEST_ERROR_INVALID_COMMAND => error.InvalidCommand, - .INFO_REQUEST_ERROR_INVALID_CARRIER => error.InvalidCarrier, - _ => return error.UnexpectedResponse, - }; - } - - pub fn throwMmcError(err: api.protobuf.mmc.Request.Error) anyerror { - return switch (err) { - .MMC_REQUEST_ERROR_UNSPECIFIED => error.InvalidResponse, - .MMC_REQUEST_ERROR_INVALID_MESSAGE => error.InvalidMessage, - _ => return error.UnexpectedResponse, - }; - } -}; - -parameter: Parameter, -/// `lines` is initialized once the client is connected to a server. -/// Deinitialized once disconnected from a server. -lines: []Line, -/// The logging configuration is initialized once the client is connected, and -/// deinitialized if the client is disconnected. -log_config: log.Config, -/// Currently connected socket. Nulled when disconnect. -sock: ?zignet.Socket, -/// Currently saved endpoint. The endpoint will be overwritten if the client -/// is connected to a different server. Stays null before connected to a socket. -endpoint: ?zignet.Endpoint, -allocator: std.mem.Allocator, -/// Store the configuration. -config: Config, - -pub fn create(c: Config) !MmcClient { - var self: MmcClient = .{ - .allocator = if (builtin.mode == .Debug) - debug_allocator.allocator() - else - std.heap.smp_allocator, - - // Store the configuration. - .config = undefined, - .parameter = undefined, - // `lines` is initialized once the client is connected to a server. - // Deinitialized once disconnected from a server. - .lines = &.{}, - // The logging configuration is initialized once the client is connected, and - // deinitialized if the client is disconnected. - .log_config = undefined, - // Currently connected socket. Nulled when disconnect. - .sock = null, - // Currently saved endpoint. The endpoint will be overwritten if the client - // is connected to a different server. Stays null before connected to a socket. - .endpoint = null, - }; - - self.config = .{ - .host = try self.allocator.dupe(u8, c.host), - .port = c.port, - }; - errdefer self.allocator.free(self.config.host); - self.parameter = .init(self.allocator); - errdefer self.parameter.deinit(); - return self; -} - -var debug_allocator = std.heap.DebugAllocator(.{}){}; - -var current: ?MmcClient = null; - -pub fn get() *MmcClient { - // scaffold: - // throw error here - // e.g. ModuleNotInitialized - return &(current orelse std.debug.panic("Mmc_client module is not initialized", .{})); - // if (current) |*c| { - // return c; - // } - // return error.ModuleNotInitialized; -} - -pub fn init(c: Config) !void { - current = try MmcClient.create(c); - errdefer current = null; - - try command.registry.put(.{ .executable = .{ +pub const module_commands = [_]command.Command{ + .{ .executable = .{ .name = "SERVER_VERSION", .short_description = "Display the connected MMC server version.", .long_description = @@ -234,9 +26,8 @@ pub fn init(c: Config) !void { \\Version format ({major}.{minor}.{patch}). , .execute = &commands.server_version.impl, - } }); - errdefer command.registry.orderedRemove("SERVER_VERSION"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "CONNECT", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "endpoint", .optional = true }, @@ -255,452 +46,402 @@ pub fn init(c: Config) !void { \\provided in the configuration file. , .{}), .execute = &commands.connect.impl, - } }); - errdefer command.registry.orderedRemove("CONNECT"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "DISCONNECT", .short_description = "End connection with MMC server.", .long_description = std.fmt.comptimePrint( \\Disconnect from currently connected MMC server. , .{}), .execute = &commands.disconnect.impl, - } }); - errdefer command.registry.orderedRemove("DISCONNECT"); - try command.registry.put(.{ - .executable = .{ - .name = "SET_SPEED", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "speed" }, - }, - .short_description = "Set Carrier speed of specified Line", - .long_description = std.fmt.comptimePrint( - \\Set Carrier speed of specified Line. The speed value must be - \\greater than {d} and less than or equal to {d} {s}. - \\ - \\Example: Set speed of Line "line1" to 300 {s}. - \\SET_SPEED line1 300 - , .{ - standard.speed.range.min, - standard.speed.range.max, - standard.speed.unit, - standard.speed.unit, - }), - .execute = &commands.set_speed.impl, + } }, + .{ .executable = .{ + .name = "SET_SPEED", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "speed" }, }, - }); - errdefer command.registry.orderedRemove("SET_SPEED"); - try command.registry.put(.{ - .executable = .{ - .name = "SET_ACCELERATION", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "acceleration" }, - }, - .short_description = "Set Carrier acceleration of specified Line", - .long_description = std.fmt.comptimePrint( - \\Set Carrier acceleration of specified Line. The acceleration value - \\must be greater than {d} and less than or equal to {d} {s}. - \\ - \\Example: Set acceleration of Line "line1" to 200 {s}. - \\SET_ACCELERATION line1 200 - , .{ - standard.acceleration.range.min, - standard.acceleration.range.max, - standard.acceleration.unit, - standard.acceleration.unit, - }), - .execute = &commands.set_acceleration.impl, + .short_description = "Set Carrier speed of specified Line", + .long_description = std.fmt.comptimePrint( + \\Set Carrier speed of specified Line. The speed value must be + \\greater than {d} and less than or equal to {d} {s}. + \\ + \\Example: Set speed of Line "line1" to 300 {s}. + \\SET_SPEED line1 300 + , .{ + standard.speed.range.min, + standard.speed.range.max, + standard.speed.unit, + standard.speed.unit, + }), + .execute = &commands.set_speed.impl, + } }, + .{ .executable = .{ + .name = "SET_ACCELERATION", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "acceleration" }, }, - }); - errdefer command.registry.orderedRemove("SET_ACCELERATION"); - try command.registry.put(.{ - .executable = .{ - .name = "GET_SPEED", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - }, - .short_description = "Print Carrier speed of the specified Line", - .long_description = std.fmt.comptimePrint( - \\Print Carrier speed of specified Line. - \\ - \\Example: Print speed of Line "line1". - \\GET_SPEED line1 - , .{}), - .execute = &commands.get_speed.impl, + .short_description = "Set Carrier acceleration of specified Line", + .long_description = std.fmt.comptimePrint( + \\Set Carrier acceleration of specified Line. The acceleration value + \\must be greater than {d} and less than or equal to {d} {s}. + \\ + \\Example: Set acceleration of Line "line1" to 200 {s}. + \\SET_ACCELERATION line1 200 + , .{ + standard.acceleration.range.min, + standard.acceleration.range.max, + standard.acceleration.unit, + standard.acceleration.unit, + }), + .execute = &commands.set_acceleration.impl, + } }, + .{ .executable = .{ + .name = "GET_SPEED", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, }, - }); - errdefer command.registry.orderedRemove("GET_SPEED"); - try command.registry.put(.{ - .executable = .{ - .name = "GET_ACCELERATION", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - }, - .short_description = "Print Carrier acceleration of the specified Line", - .long_description = std.fmt.comptimePrint( - \\Print Carrier acceleration of the specified Line. - \\ - \\Example: Print acceleration of Line "line1". - \\GET_ACCELERATION line1 - , .{}), - .execute = &commands.get_acceleration.impl, + .short_description = "Print Carrier speed of the specified Line", + .long_description = std.fmt.comptimePrint( + \\Print Carrier speed of specified Line. + \\ + \\Example: Print speed of Line "line1". + \\GET_SPEED line1 + , .{}), + .execute = &commands.get_speed.impl, + } }, + .{ .executable = .{ + .name = "GET_ACCELERATION", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, }, - }); - errdefer command.registry.orderedRemove("GET_ACCELERATION"); - try command.registry.put(.{ - .executable = .{ - .name = "PRINT_AXIS_INFO", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "filter", .kind = .mmc_client_filter }, - }, - .short_description = "Print Axis information.", - .long_description = std.fmt.comptimePrint( - \\Print Axis information of specified Line. Specify which Axis or Axes - \\to print info via filter. To apply filter, provide ID with filter - \\suffix (e.g., 1a). Supported suffixes are: - \\ - "a" or "axis" to filter by Axis - \\ - "c" or "carrier" to filter by Carrier - \\ - "d" or "driver" to filter by Driver - \\ - \\Example: Print Axis information of Axis "1" on Line "line1". - \\PRINT_AXIS_INFO line1 1a - , .{}), - .execute = &commands.print_axis_info.impl, + .short_description = "Print Carrier acceleration of the specified Line", + .long_description = std.fmt.comptimePrint( + \\Print Carrier acceleration of the specified Line. + \\ + \\Example: Print acceleration of Line "line1". + \\GET_ACCELERATION line1 + , .{}), + .execute = &commands.get_acceleration.impl, + } }, + .{ .executable = .{ + .name = "PRINT_AXIS_INFO", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "filter", .kind = .mmc_client_filter }, }, - }); - errdefer command.registry.orderedRemove("PRINT_AXIS_INFO"); - try command.registry.put(.{ - .executable = .{ - .name = "PRINT_DRIVER_INFO", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "filter", .kind = .mmc_client_filter }, - }, - .short_description = "Print Driver information.", - .long_description = std.fmt.comptimePrint( - \\Print Driver information of specified Line. Specify which Driver to - \\print info via filter. To apply filter, provide ID with filter suffix - \\(e.g., 1d). Supported suffixes are: - \\ - "a" or "axis" to filter by Axis - \\ - "c" or "carrier" to filter by Carrier - \\ - "d" or "driver" to filter by Driver - \\ - \\Example: Get Driver information of Driver "2" on Line "line1". - \\PRINT_DRIVER_INFO line1 2d - , .{}), - .execute = &commands.print_driver_info.impl, + .short_description = "Print Axis information.", + .long_description = std.fmt.comptimePrint( + \\Print Axis information of specified Line. Specify which Axis or Axes + \\to print info via filter. To apply filter, provide ID with filter + \\suffix (e.g., 1a). Supported suffixes are: + \\ - "a" or "axis" to filter by Axis + \\ - "c" or "carrier" to filter by Carrier + \\ - "d" or "driver" to filter by Driver + \\ + \\Example: Print Axis information of Axis "1" on Line "line1". + \\PRINT_AXIS_INFO line1 1a + , .{}), + .execute = &commands.print_axis_info.impl, + } }, + .{ .executable = .{ + .name = "PRINT_DRIVER_INFO", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "filter", .kind = .mmc_client_filter }, }, - }); - errdefer command.registry.orderedRemove("PRINT_DRIVER_INFO"); - try command.registry.put(.{ - .executable = .{ - .name = "PRINT_CARRIER_INFO", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ - .name = "filter", - .optional = true, - .kind = .mmc_client_filter, - }, + .short_description = "Print Driver information.", + .long_description = std.fmt.comptimePrint( + \\Print Driver information of specified Line. Specify which Driver to + \\print info via filter. To apply filter, provide ID with filter suffix + \\(e.g., 1d). Supported suffixes are: + \\ - "a" or "axis" to filter by Axis + \\ - "c" or "carrier" to filter by Carrier + \\ - "d" or "driver" to filter by Driver + \\ + \\Example: Get Driver information of Driver "2" on Line "line1". + \\PRINT_DRIVER_INFO line1 2d + , .{}), + .execute = &commands.print_driver_info.impl, + } }, + .{ .executable = .{ + .name = "PRINT_CARRIER_INFO", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ + .name = "filter", + .optional = true, + .kind = .mmc_client_filter, }, - .short_description = "Print Carrier information.", - .long_description = std.fmt.comptimePrint( - \\Print Carrier information of specified Line. - \\Optional: Provide filter to specify selection of Carrier(s). To apply - \\filter, provide ID with filter suffix (e.g., 1c). Supported suffixes - \\are: - \\ - "a" or "axis" to filter by Axis - \\ - "c" or "carrier" to filter by Carrier - \\ - "d" or "driver" to filter by Driver - \\ - \\Example: Get Carrier information of Line "line1". - \\PRINT_CARRIER_INFO line1 - \\ - \\Example: Get Carrier information on Axis "2" on Line "line1". - \\PRINT_CARRIER_INFO line1 2a - , .{}), - .execute = &commands.print_carrier_info.impl, }, - }); - errdefer command.registry.orderedRemove("PRINT_CARRIER_INFO"); - try command.registry.put(.{ - .executable = .{ - .name = "AXIS_CARRIER", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Axis", .kind = .mmc_client_axis }, - .{ - .name = "result variable", - .optional = true, - .resolve = false, - .kind = .mmc_client_variable, - }, + .short_description = "Print Carrier information.", + .long_description = std.fmt.comptimePrint( + \\Print Carrier information of specified Line. + \\Optional: Provide filter to specify selection of Carrier(s). To apply + \\filter, provide ID with filter suffix (e.g., 1c). Supported suffixes + \\are: + \\ - "a" or "axis" to filter by Axis + \\ - "c" or "carrier" to filter by Carrier + \\ - "d" or "driver" to filter by Driver + \\ + \\Example: Get Carrier information of Line "line1". + \\PRINT_CARRIER_INFO line1 + \\ + \\Example: Get Carrier information on Axis "2" on Line "line1". + \\PRINT_CARRIER_INFO line1 2a + , .{}), + .execute = &commands.print_carrier_info.impl, + } }, + .{ .executable = .{ + .name = "AXIS_CARRIER", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Axis", .kind = .mmc_client_axis }, + .{ + .name = "result variable", + .optional = true, + .resolve = false, + .kind = .mmc_client_variable, }, - .short_description = "Print Carrier ID on Axis, if exists.", - .long_description = std.fmt.comptimePrint( - \\Print Carrier ID on Axis, if exists. Information only provided - \\when: - \\ - Carrier is on specified Axis. - \\ - Carrier is initialized - \\ - \\Optional: Store Carrier ID in provided variable. Variable cannot start - \\with a number and is case sensitive. - \\ - \\Example: Get Carrier ID on Axis "3" on Line "line1". - \\AXIS_CARRIER line1 3 - \\ - \\Example: Get Carrier ID on Axis "3" on Line "line1" and store - \\Carrier ID in variable "var". - \\AXIS_CARRIER line1 3 var - , .{}), - .execute = &commands.axis_carrier.impl, }, - }); - errdefer _ = command.registry.orderedRemove("AXIS_CARRIER"); - try command.registry.put(.{ - .executable = .{ - .name = "CARRIER_ID", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line(s)", .kind = .mmc_client_line }, - .{ - .name = "result variable prefix", - .optional = true, - .resolve = false, - .kind = .mmc_client_variable, - }, + .short_description = "Print Carrier ID on Axis, if exists.", + .long_description = std.fmt.comptimePrint( + \\Print Carrier ID on Axis, if exists. Information only provided + \\when: + \\ - Carrier is on specified Axis. + \\ - Carrier is initialized + \\ + \\Optional: Store Carrier ID in provided variable. Variable cannot start + \\with a number and is case sensitive. + \\ + \\Example: Get Carrier ID on Axis "3" on Line "line1". + \\AXIS_CARRIER line1 3 + \\ + \\Example: Get Carrier ID on Axis "3" on Line "line1" and store + \\Carrier ID in variable "var". + \\AXIS_CARRIER line1 3 var + , .{}), + .execute = &commands.axis_carrier.impl, + } }, + .{ .executable = .{ + .name = "CARRIER_ID", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line(s)", .kind = .mmc_client_line }, + .{ + .name = "result variable prefix", + .optional = true, + .resolve = false, + .kind = .mmc_client_variable, }, - .short_description = "Display Carrier IDs on Line.", - .long_description = std.fmt.comptimePrint( - \\Display Carrier IDs on Line. Scans specified Line(s), starting from - \\first Axis. Carrier IDs are provided in order of occurrence. Multi Line - \\input possible (e.g., line1,line2,line3). - \\Optional: Stores Carrier IDs in provided variable as indexed entries - \\(e.g., var1, var2, ...). Variable cannot start with a number and is - \\case sensitive. - \\ - \\Example: Get Carrier IDs on Line "line1". - \\CARRIER_ID line1 - \\ - \\Example: Get Carrier IDs on Line "line1" and "line2". - \\CARRIER_ID line1,line2 - , .{}), - .execute = &commands.carrier_id.impl, }, - }); - errdefer command.registry.orderedRemove("CARRIER_ID"); - try command.registry.put(.{ - .executable = .{ - .name = "ASSERT_CARRIER_LOCATION", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - .{ .name = "location" }, - .{ .name = "threshold", .optional = true }, - }, - .short_description = "Assert Carrier location.", - .long_description = std.fmt.comptimePrint( - \\Assert Carrier location. Error if Carrier is not at specified location. - \\Optional: Provide threshold to change default location threshold value. - \\Default location threshold value is 1 {s}. Location and threshold must - \\be provided in {s}. - \\ - \\Example: Check Carrier location of Carrier "1" on Line "line1" at - \\location 500 {s}. - \\ASSERT_CARRIER_LOCATION line1 1 500 - \\ - \\Example: Check Carrier location of Carrier "1" on Line "line1" at - \\location 500 {s} with threshold 20 {s}. - \\ASSERT_CARRIER_LOCATION line1 1 500 20 - , .{ - standard.length.unit_short, - standard.length.unit_long, - standard.length.unit_short, - standard.length.unit_short, - standard.time.unit_long, - }), - .execute = &commands.assert_location.impl, + .short_description = "Display Carrier IDs on Line.", + .long_description = std.fmt.comptimePrint( + \\Display Carrier IDs on Line. Scans specified Line(s), starting from + \\first Axis. Carrier IDs are provided in order of occurrence. Multi Line + \\input possible (e.g., line1,line2,line3). + \\Optional: Stores Carrier IDs in provided variable as indexed entries + \\(e.g., var1, var2, ...). Variable cannot start with a number and is + \\case sensitive. + \\ + \\Example: Get Carrier IDs on Line "line1". + \\CARRIER_ID line1 + \\ + \\Example: Get Carrier IDs on Line "line1" and "line2". + \\CARRIER_ID line1,line2 + , .{}), + .execute = &commands.carrier_id.impl, + } }, + .{ .executable = .{ + .name = "ASSERT_CARRIER_LOCATION", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, + .{ .name = "location" }, + .{ .name = "threshold", .optional = true }, }, - }); - errdefer command.registry.orderedRemove("ASSERT_CARRIER_LOCATION"); - try command.registry.put(.{ - .executable = .{ - .name = "CARRIER_LOCATION", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - .{ - .name = "result variable", - .resolve = false, - .optional = true, - .kind = .mmc_client_variable, - }, + .short_description = "Assert Carrier location.", + .long_description = std.fmt.comptimePrint( + \\Assert Carrier location. Error if Carrier is not at specified location. + \\Optional: Provide threshold to change default location threshold value. + \\Default location threshold value is 1 {s}. Location and threshold must + \\be provided in {s}. + \\ + \\Example: Check Carrier location of Carrier "1" on Line "line1" at + \\location 500 {s}. + \\ASSERT_CARRIER_LOCATION line1 1 500 + \\ + \\Example: Check Carrier location of Carrier "1" on Line "line1" at + \\location 500 {s} with threshold 20 {s}. + \\ASSERT_CARRIER_LOCATION line1 1 500 20 + , .{ + standard.length.unit_short, + standard.length.unit_long, + standard.length.unit_short, + standard.length.unit_short, + standard.time.unit_long, + }), + .execute = &commands.assert_location.impl, + } }, + .{ .executable = .{ + .name = "CARRIER_LOCATION", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, + .{ + .name = "result variable", + .resolve = false, + .optional = true, + .kind = .mmc_client_variable, }, - .short_description = "Display Carrier location, if exists.", - .long_description = std.fmt.comptimePrint( - \\Display location of Carrier on specified Line, if exists. - \\Optional: Store Carrier location in variable. Variable cannot start - \\with a number and is case sensitive. - \\ - \\Example: Display Carrier location of Carrier "2" on Line "line1". - \\CARRIER_LOCATION line1 2 - , .{}), - .execute = &commands.carrier_location.impl, }, - }); - errdefer command.registry.orderedRemove("CARRIER_LOCATION"); - try command.registry.put(.{ - .executable = .{ - .name = "CARRIER_AXIS", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - }, - .short_description = "Display Carrier Axis", - .long_description = std.fmt.comptimePrint( - \\Display Axis on which Carrier is currently. - \\ - \\Example: Display Carrier Axis of Carrier "2" on Line "line1". - \\CARRIER_AXIS line1 2 - , .{}), - .execute = &commands.carrier_axis.impl, + .short_description = "Display Carrier location, if exists.", + .long_description = std.fmt.comptimePrint( + \\Display location of Carrier on specified Line, if exists. + \\Optional: Store Carrier location in variable. Variable cannot start + \\with a number and is case sensitive. + \\ + \\Example: Display Carrier location of Carrier "2" on Line "line1". + \\CARRIER_LOCATION line1 2 + , .{}), + .execute = &commands.carrier_location.impl, + } }, + .{ .executable = .{ + .name = "CARRIER_AXIS", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, }, - }); - errdefer command.registry.orderedRemove("CARRIER_AXIS"); - try command.registry.put(.{ - .executable = .{ - .name = "HALL_STATUS", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ - .name = "filter", - .optional = true, - .kind = .mmc_client_filter, - }, + .short_description = "Display Carrier Axis", + .long_description = std.fmt.comptimePrint( + \\Display Axis on which Carrier is currently. + \\ + \\Example: Display Carrier Axis of Carrier "2" on Line "line1". + \\CARRIER_AXIS line1 2 + , .{}), + .execute = &commands.carrier_axis.impl, + } }, + .{ .executable = .{ + .name = "HALL_STATUS", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ + .name = "filter", + .optional = true, + .kind = .mmc_client_filter, }, - .short_description = "Display Hall Sensor state.", - .long_description = std.fmt.comptimePrint( - \\Display Hall Sensor status. - \\Optional: Provide filter to specify selection of Hall Sensor(s). To - \\apply filter, provide ID with filter suffix (e.g., 1a).Supported - \\suffixes are: - \\ - "a" or "axis" to filter by Axis - \\ - "c" or "carrier" to filter by Carrier - \\ - "d" or "driver" to filter by Driver - \\ - \\Example: Display all Hall Sensor states on Line "line1". - \\HALL_STATUS line1 - \\ - \\Example: Display Hall Sensors states on Axis "2" on Line "line1". - \\HALL_STATUS line1 2a - , .{}), - .execute = &commands.hall_status.impl, }, - }); - errdefer command.registry.orderedRemove("HALL_STATUS"); - try command.registry.put(.{ - .executable = .{ - .name = "ASSERT_HALL", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Axis", .kind = .mmc_client_axis }, - .{ .name = "side", .kind = .mmc_client_hall_side }, - .{ - .name = "on/off", - .optional = true, - .kind = .mmc_client_hall_state, - }, + .short_description = "Display Hall Sensor state.", + .long_description = std.fmt.comptimePrint( + \\Display Hall Sensor status. + \\Optional: Provide filter to specify selection of Hall Sensor(s). To + \\apply filter, provide ID with filter suffix (e.g., 1a).Supported + \\suffixes are: + \\ - "a" or "axis" to filter by Axis + \\ - "c" or "carrier" to filter by Carrier + \\ - "d" or "driver" to filter by Driver + \\ + \\Example: Display all Hall Sensor states on Line "line1". + \\HALL_STATUS line1 + \\ + \\Example: Display Hall Sensors states on Axis "2" on Line "line1". + \\HALL_STATUS line1 2a + , .{}), + .execute = &commands.hall_status.impl, + } }, + .{ .executable = .{ + .name = "ASSERT_HALL", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Axis", .kind = .mmc_client_axis }, + .{ .name = "side", .kind = .mmc_client_hall_side }, + .{ + .name = "on/off", + .optional = true, + .kind = .mmc_client_hall_state, }, - .short_description = "Assert Hall Sensor state.", - .long_description = std.fmt.comptimePrint( - \\Assert if Hall Sensor is in expected state. Hall Sensor location must - \\be provided, as: - \\ - front (direction of increasing Axis number) - \\ - back (direction of decreasing Axis number) - \\ - \\Hall Sensor state provided, as: - \\ - on (Hall Sensor indicator "on") - \\ - off (Hall Sensor indicator "off") - \\ - default state if not provided as "on" - \\ - \\Example: Assert Hall Sensor "on" state of Axis "3" at side "front" on - \\Line "line1". - \\ASSERT_HALL line1 3 front - \\ - \\Example: Assert Hall Sensor "off" state of Axis "3" at side "front" on - \\Line "line1". - \\ASSERT_HALL line1 3 front off - , .{}), - .execute = &commands.assert_hall.impl, }, - }); - errdefer command.registry.orderedRemove("ASSERT_HALL"); - try command.registry.put(.{ - .executable = .{ - .name = "CLEAR_ERRORS", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ - .name = "filter", - .optional = true, - .kind = .mmc_client_filter, - }, + .short_description = "Assert Hall Sensor state.", + .long_description = std.fmt.comptimePrint( + \\Assert if Hall Sensor is in expected state. Hall Sensor location must + \\be provided, as: + \\ - front (direction of increasing Axis number) + \\ - back (direction of decreasing Axis number) + \\ + \\Hall Sensor state provided, as: + \\ - on (Hall Sensor indicator "on") + \\ - off (Hall Sensor indicator "off") + \\ - default state if not provided as "on" + \\ + \\Example: Assert Hall Sensor "on" state of Axis "3" at side "front" on + \\Line "line1". + \\ASSERT_HALL line1 3 front + \\ + \\Example: Assert Hall Sensor "off" state of Axis "3" at side "front" on + \\Line "line1". + \\ASSERT_HALL line1 3 front off + , .{}), + .execute = &commands.assert_hall.impl, + } }, + .{ .executable = .{ + .name = "CLEAR_ERRORS", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ + .name = "filter", + .optional = true, + .kind = .mmc_client_filter, }, - .short_description = "Clear error states.", - .long_description = std.fmt.comptimePrint( - \\Clear error states on all Driver(s) of specified Line. - \\Optional: Provide filter to specify Driver. To apply filter, provide - \\ID with filter suffix (e.g., 1d). Supported suffixes are: - \\ - "a" or "axis" to filter by Axis - \\ - "c" or "carrier" to filter by Carrier - \\ - "d" or "driver" to filter by Driver - \\ - \\Example: Clear error states on all Driver(s) of Line "line1". - \\CLEAR_ERRORS line1 - \\ - \\Example: Clear error states on Driver "2" of Line "line1". - \\CLEAR_ERRORS line1 2d - , .{}), - .execute = &commands.clear_errors.impl, }, - }); - errdefer command.registry.orderedRemove("CLEAR_ERRORS"); - try command.registry.put(.{ - .executable = .{ - .name = "DEINITIALIZE", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ - .name = "filter", - .optional = true, - .kind = .mmc_client_filter, - }, + .short_description = "Clear error states.", + .long_description = std.fmt.comptimePrint( + \\Clear error states on all Driver(s) of specified Line. + \\Optional: Provide filter to specify Driver. To apply filter, provide + \\ID with filter suffix (e.g., 1d). Supported suffixes are: + \\ - "a" or "axis" to filter by Axis + \\ - "c" or "carrier" to filter by Carrier + \\ - "d" or "driver" to filter by Driver + \\ + \\Example: Clear error states on all Driver(s) of Line "line1". + \\CLEAR_ERRORS line1 + \\ + \\Example: Clear error states on Driver "2" of Line "line1". + \\CLEAR_ERRORS line1 2d + , .{}), + .execute = &commands.clear_errors.impl, + } }, + .{ .executable = .{ + .name = "DEINITIALIZE", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ + .name = "filter", + .optional = true, + .kind = .mmc_client_filter, }, - .short_description = "Deinitialize Carrier.", - .long_description = std.fmt.comptimePrint( - \\Deinitialize Carrier on specified Line. - \\Optional: Provide filter to specify selection of Carrier(s). To apply - \\filter, provide ID with filter suffix (e.g., 1c). Supported suffixes - \\are: - \\ - "a" or "axis" to filter by Axis - \\ - "c" or "carrier" to filter by Carrier - \\ - "d" or "driver" to filter by Driver - \\ - \\Example: Deinitialize Carrier(s) on Line "line1". - \\DEINITIALIZE line1 - \\ - \\Example: Deinitialize Carrier(s) on Driver "2" on Line "line1". - \\DEINITIALIZE line1 2d - \\ - \\Example: Deinitialize Carrier "3" on Line "line1". - \\DEINITIALIZE line1 3c - , .{}), - .execute = &commands.clear_carrier_info.impl, }, - }); - errdefer command.registry.orderedRemove("DEINITIALIZE"); - try command.registry.put(.{ .executable = .{ + .short_description = "Deinitialize Carrier.", + .long_description = std.fmt.comptimePrint( + \\Deinitialize Carrier on specified Line. + \\Optional: Provide filter to specify selection of Carrier(s). To apply + \\filter, provide ID with filter suffix (e.g., 1c). Supported suffixes + \\are: + \\ - "a" or "axis" to filter by Axis + \\ - "c" or "carrier" to filter by Carrier + \\ - "d" or "driver" to filter by Driver + \\ + \\Example: Deinitialize Carrier(s) on Line "line1". + \\DEINITIALIZE line1 + \\ + \\Example: Deinitialize Carrier(s) on Driver "2" on Line "line1". + \\DEINITIALIZE line1 2d + \\ + \\Example: Deinitialize Carrier "3" on Line "line1". + \\DEINITIALIZE line1 3c + , .{}), + .execute = &commands.clear_carrier_info.impl, + } }, + .{ .executable = .{ .name = "RESET_SYSTEM", .short_description = "Reset system state.", .long_description = std.fmt.comptimePrint( @@ -710,154 +451,138 @@ pub fn init(c: Config) !void { \\ - Reset all push and pull states. , .{}), .execute = &commands.reset_system.impl, - } }); - errdefer command.registry.orderedRemove("RESET_SYSTEM"); - try command.registry.put(.{ - .executable = .{ - .name = "RELEASE_CARRIER", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ - .name = "filter", - .optional = true, - .kind = .mmc_client_filter, - }, + } }, + .{ .executable = .{ + .name = "RELEASE_CARRIER", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ + .name = "filter", + .optional = true, + .kind = .mmc_client_filter, }, - .short_description = "Release Carrier", - .long_description = std.fmt.comptimePrint( - \\Release Carrier, allows to move Carrier through external force. Carrier - \\stays initialized. - \\Optional: Provide filter to specify selection of Carrier(s). To apply - \\filter, provide ID with filter suffix (e.g., 1c). Supported suffixes - \\are: - \\ - "a" or "axis" to filter by Axis - \\ - "c" or "carrier" to filter by Carrier - \\ - "d" or "driver" to filter by Driver - \\ - \\Example: Release Carrier(s) on Line "line1". - \\RELEASE_CARRIER line1 - \\ - \\Example: Release Carrier(s) on Driver "2" on Line "line1". - \\RELEASE_CARRIER line1 2d - \\ - \\Example: Release Carrier "3" on Line "line1". - \\RELEASE_CARRIER line1 3c - , .{}), - .execute = &commands.release_carrier.impl, }, - }); - errdefer command.registry.orderedRemove("RELEASE_CARRIER"); - try command.registry.put(.{ - .executable = .{ - .name = "AUTO_INITIALIZE", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ - .name = "Line(s)", - .optional = true, - .kind = .mmc_client_line, - }, + .short_description = "Release Carrier", + .long_description = std.fmt.comptimePrint( + \\Release Carrier, allows to move Carrier through external force. Carrier + \\stays initialized. + \\Optional: Provide filter to specify selection of Carrier(s). To apply + \\filter, provide ID with filter suffix (e.g., 1c). Supported suffixes + \\are: + \\ - "a" or "axis" to filter by Axis + \\ - "c" or "carrier" to filter by Carrier + \\ - "d" or "driver" to filter by Driver + \\ + \\Example: Release Carrier(s) on Line "line1". + \\RELEASE_CARRIER line1 + \\ + \\Example: Release Carrier(s) on Driver "2" on Line "line1". + \\RELEASE_CARRIER line1 2d + \\ + \\Example: Release Carrier "3" on Line "line1". + \\RELEASE_CARRIER line1 3c + , .{}), + .execute = &commands.release_carrier.impl, + } }, + .{ .executable = .{ + .name = "AUTO_INITIALIZE", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ + .name = "Line(s)", + .optional = true, + .kind = .mmc_client_line, }, - .short_description = "Initialize all Carriers automatically.", - .long_description = std.fmt.comptimePrint( - \\Automatically initializes all uninitialized Carriers. This process - \\operates on carrier clusters, where a cluster is defined as a group of - \\uninitialized Carriers located on adjacent Axis. Each cluster requires - \\at least one free Axis to successfully initialize cluster. Multiple - \\Line auto initialization is supported (e.g., line1,line2,line3). If - \\Line is not provided, auto initializes Carriers on all Lines. - \\ - \\Example: Auto initialize Carrier(s) on all Lines. - \\AUTO_INITIALIZE - \\ - \\Example: Auto initialize Carrier(s) on Line "line1". - \\AUTO_INITIALIZE line1 - \\ - \\Example: Auto initialize Carrier(s) on Line "line1" and "line2". - \\AUTO_INITIALIZE line1,line2 - , .{}), - .execute = &commands.auto_initialize.impl, }, - }); - errdefer command.registry.orderedRemove("AUTO_INITIALIZE"); - try command.registry.put(.{ - .executable = .{ - .name = "CALIBRATE", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - }, - .short_description = "Calibrate Track Line.", - .long_description = std.fmt.comptimePrint( - \\Calibrate Track Line. Uninitialized Carrier must be positioned at start - \\of Line and first Axis Hall Sensors are both in "on" state. - \\ - \\Example: Calibrate Line "line1". - \\CALIBRATE line1 - , .{}), - .execute = &commands.calibrate.impl, + .short_description = "Initialize all Carriers automatically.", + .long_description = std.fmt.comptimePrint( + \\Automatically initializes all uninitialized Carriers. This process + \\operates on carrier clusters, where a cluster is defined as a group of + \\uninitialized Carriers located on adjacent Axis. Each cluster requires + \\at least one free Axis to successfully initialize cluster. Multiple + \\Line auto initialization is supported (e.g., line1,line2,line3). If + \\Line is not provided, auto initializes Carriers on all Lines. + \\ + \\Example: Auto initialize Carrier(s) on all Lines. + \\AUTO_INITIALIZE + \\ + \\Example: Auto initialize Carrier(s) on Line "line1". + \\AUTO_INITIALIZE line1 + \\ + \\Example: Auto initialize Carrier(s) on Line "line1" and "line2". + \\AUTO_INITIALIZE line1,line2 + , .{}), + .execute = &commands.auto_initialize.impl, + } }, + .{ .executable = .{ + .name = "CALIBRATE", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, }, - }); - errdefer command.registry.orderedRemove("CALIBRATE"); - try command.registry.put(.{ - .executable = .{ - .name = "SET_ZERO", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - }, - .short_description = "Set Line zero position.", - .long_description = std.fmt.comptimePrint( - \\Set zero position for specified Line. Initialized Carrier must be on - \\first Axis of specified Line. - \\ - \\Example: Set zero position for Line "line1". - \\SET_ZERO line1 - , .{}), - .execute = &commands.set_line_zero.impl, + .short_description = "Calibrate Track Line.", + .long_description = std.fmt.comptimePrint( + \\Calibrate Track Line. Uninitialized Carrier must be positioned at start + \\of Line and first Axis Hall Sensors are both in "on" state. + \\ + \\Example: Calibrate Line "line1". + \\CALIBRATE line1 + , .{}), + .execute = &commands.calibrate.impl, + } }, + .{ .executable = .{ + .name = "SET_ZERO", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, }, - }); - errdefer command.registry.orderedRemove("SET_ZERO"); - try command.registry.put(.{ - .executable = .{ - .name = "INITIALIZE", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Axis", .kind = .mmc_client_axis }, - .{ .name = "direction", .kind = .mmc_client_direction }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - .{ - .name = "link Axis", - .resolve = false, - .optional = true, - .kind = .mmc_client_link_axis, - }, + .short_description = "Set Line zero position.", + .long_description = std.fmt.comptimePrint( + \\Set zero position for specified Line. Initialized Carrier must be on + \\first Axis of specified Line. + \\ + \\Example: Set zero position for Line "line1". + \\SET_ZERO line1 + , .{}), + .execute = &commands.set_line_zero.impl, + } }, + .{ .executable = .{ + .name = "INITIALIZE", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Axis", .kind = .mmc_client_axis }, + .{ .name = "direction", .kind = .mmc_client_direction }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, + .{ + .name = "link Axis", + .resolve = false, + .optional = true, + .kind = .mmc_client_link_axis, }, - .short_description = "Initialize Carrier.", - .long_description = std.fmt.comptimePrint( - \\Slowly moves an uninitialized Carrier to the next Hall Sensor in the - \\provided direction, and assign provided Carrier ID to the Carrier. - \\Direction options: - \\ - forward (direction of increasing Axis number) - \\ - backward (direction of decreasing Axis number) - \\ - \\Optional: Provide link Axis if the uninitialized Carrier is located - \\between two Axis. Link Axis options: - \\ - next (direction of increasing Axis number) - \\ - prev (direction of decreasing Axis number) - \\ - right (direction of increasing Axis number) - \\ - left (direction of decreasing Axis number) - \\ - \\Example: Initialize Carrier on Axis "3" on Line "line1". Assign Carrier - \\ID "123". Initializing movement direction toward Axis "4". - \\INITIALIZE line1 3 forward 123 - \\ - \\Example: Initialize Carrier on Axis "3" and "4" on Line "line1". Assign - \\Carrier ID "123". Initializing movement direction toward Axis "3". - \\INITIALIZE line1 3 backward 123 next - , .{}), - .execute = &commands.isolate.impl, }, - }); - errdefer command.registry.orderedRemove("INITIALIZE"); - try command.registry.put(.{ .executable = .{ + .short_description = "Initialize Carrier.", + .long_description = std.fmt.comptimePrint( + \\Slowly moves an uninitialized Carrier to the next Hall Sensor in the + \\provided direction, and assign provided Carrier ID to the Carrier. + \\Direction options: + \\ - forward (direction of increasing Axis number) + \\ - backward (direction of decreasing Axis number) + \\ + \\Optional: Provide link Axis if the uninitialized Carrier is located + \\between two Axis. Link Axis options: + \\ - next (direction of increasing Axis number) + \\ - prev (direction of decreasing Axis number) + \\ - right (direction of increasing Axis number) + \\ - left (direction of decreasing Axis number) + \\ + \\Example: Initialize Carrier on Axis "3" on Line "line1". Assign Carrier + \\ID "123". Initializing movement direction toward Axis "4". + \\INITIALIZE line1 3 forward 123 + \\ + \\Example: Initialize Carrier on Axis "3" and "4" on Line "line1". Assign + \\Carrier ID "123". Initializing movement direction toward Axis "3". + \\INITIALIZE line1 3 backward 123 next + , .{}), + .execute = &commands.isolate.impl, + } }, + .{ .executable = .{ .name = "WAIT_INITIALIZE", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line }, @@ -881,88 +606,81 @@ pub fn init(c: Config) !void { standard.time.unit_short, }), .execute = &commands.wait.isolate, - } }); - errdefer command.registry.orderedRemove("WAIT_INITIALIZE"); - try command.registry.put(.{ - .executable = .{ - .name = "WAIT_MOVE_CARRIER", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - .{ .name = "timeout", .optional = true }, - }, - .short_description = "Wait until Carrier movement complete.", - .long_description = std.fmt.comptimePrint( - \\Pauses command execution until specified Carrier completes movement. - \\Optional: Provide timeout. Returns error if specified timeout is - \\exceeded. Timeout must be provided in {s}. - \\ - \\Example: Wait for movement completion of Carrier "3" on Line "line1". - \\WAIT_MOVE_CARRIER line1 3 - \\ - \\Example: Wait max 5000 {s} movement completion of Carrier "3" on Line - \\"line1". - \\WAIT_MOVE_CARRIER line1 3 5000 - , .{ - standard.time.unit_long, - standard.time.unit_short, - }), - .execute = &commands.wait.moveCarrier, + } }, + .{ .executable = .{ + .name = "WAIT_MOVE_CARRIER", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, + .{ .name = "timeout", .optional = true }, }, - }); - errdefer command.registry.orderedRemove("WAIT_MOVE_CARRIER"); - try command.registry.put(.{ - .executable = .{ - .name = "MOVE_CARRIER", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - .{ .name = "target", .kind = .mmc_client_target }, - .{ - .name = "control mode", - .optional = true, - .kind = .mmc_client_control_mode, - }, - .{ .name = "CAS", .optional = true, .kind = .mmc_client_cas }, + .short_description = "Wait until Carrier movement complete.", + .long_description = std.fmt.comptimePrint( + \\Pauses command execution until specified Carrier completes movement. + \\Optional: Provide timeout. Returns error if specified timeout is + \\exceeded. Timeout must be provided in {s}. + \\ + \\Example: Wait for movement completion of Carrier "3" on Line "line1". + \\WAIT_MOVE_CARRIER line1 3 + \\ + \\Example: Wait max 5000 {s} movement completion of Carrier "3" on Line + \\"line1". + \\WAIT_MOVE_CARRIER line1 3 5000 + , .{ + standard.time.unit_long, + standard.time.unit_short, + }), + .execute = &commands.wait.moveCarrier, + } }, + .{ .executable = .{ + .name = "MOVE_CARRIER", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, + .{ .name = "target", .kind = .mmc_client_target }, + .{ + .name = "control mode", + .optional = true, + .kind = .mmc_client_control_mode, }, - .short_description = "Move Carrier to specified target.", - .long_description = std.fmt.comptimePrint( - \\Move initialized Carrier to specified target. Provide target value - \\followed by suffix to specify target movement (e.g., 1a). Supported - \\suffixes are: - \\- "a" or "axis" to target Axis. - \\- "l" or "location" to target absolute location in Line, provided in - \\ {s}. - \\- "d" or "distance" to target relative distance to current Carrier - \\ position, provided in {s}. - \\ - \\Optional: Provide following to specify movement control mode: - \\- "speed" to move Carrier with speed profile feedback. - \\- "position" to move Carrier with position profile feedback. - \\Optional: Provide "on" or "off" to specify CAS (Collision Avoidance - \\System) activation (enabled by default). - \\ - \\Example: Move Carrier "2" to Axis "3" on Line "line1". - \\MOVE_CARRIER line1 2 3a - \\ - \\Example: Move Carrier "2" to location 150 {s} on Line "line1" and move - \\Carrier with speed profile feedback. - \\MOVE_CARRIER line1 2 150l speed - \\ - \\Example: Move Carrier "2" to location 150 {s} on Line "line1" and disable - \\CAS. - \\MOVE_CARRIER line1 2 150l position off - , .{ - standard.length.unit_short, - standard.length.unit_short, - standard.length.unit_short, - standard.length.unit_short, - }), - .execute = &commands.move.impl, + .{ .name = "CAS", .optional = true, .kind = .mmc_client_cas }, }, - }); - errdefer command.registry.orderedRemove("MOVE_CARRIER"); - try command.registry.put(.{ .executable = .{ + .short_description = "Move Carrier to specified target.", + .long_description = std.fmt.comptimePrint( + \\Move initialized Carrier to specified target. Provide target value + \\followed by suffix to specify target movement (e.g., 1a). Supported + \\suffixes are: + \\- "a" or "axis" to target Axis. + \\- "l" or "location" to target absolute location in Line, provided in + \\ {s}. + \\- "d" or "distance" to target relative distance to current Carrier + \\ position, provided in {s}. + \\ + \\Optional: Provide following to specify movement control mode: + \\- "speed" to move Carrier with speed profile feedback. + \\- "position" to move Carrier with position profile feedback. + \\Optional: Provide "on" or "off" to specify CAS (Collision Avoidance + \\System) activation (enabled by default). + \\ + \\Example: Move Carrier "2" to Axis "3" on Line "line1". + \\MOVE_CARRIER line1 2 3a + \\ + \\Example: Move Carrier "2" to location 150 {s} on Line "line1" and move + \\Carrier with speed profile feedback. + \\MOVE_CARRIER line1 2 150l speed + \\ + \\Example: Move Carrier "2" to location 150 {s} on Line "line1" and disable + \\CAS. + \\MOVE_CARRIER line1 2 150l position off + , .{ + standard.length.unit_short, + standard.length.unit_short, + standard.length.unit_short, + standard.length.unit_short, + }), + .execute = &commands.move.impl, + } }, + .{ .executable = .{ .name = "PUSH_CARRIER", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line }, @@ -996,9 +714,8 @@ pub fn init(c: Config) !void { \\PUSH_CARRIER line1 3 forward 2 , .{}), .execute = &commands.push.impl, - } }); - errdefer command.registry.orderedRemove("PUSH_CARRIER"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "PULL_CARRIER", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line }, @@ -1047,9 +764,8 @@ pub fn init(c: Config) !void { standard.length.unit_short, }), .execute = &commands.pull.impl, - } }); - errdefer command.registry.orderedRemove("PULL_CARRIER"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "STOP_PULL_CARRIER", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line }, @@ -1071,9 +787,8 @@ pub fn init(c: Config) !void { \\STOP_PULL_CARRIER line1 1a , .{}), .execute = &commands.stop_pull.impl, - } }); - errdefer command.registry.orderedRemove("STOP_PULL_CARRIER"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "STOP_PUSH_CARRIER", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line }, @@ -1096,9 +811,8 @@ pub fn init(c: Config) !void { \\STOP_PUSH_CARRIER line1 3a , .{}), .execute = &commands.stop_push.impl, - } }); - errdefer command.registry.orderedRemove("STOP_PUSH_CARRIER"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "WAIT_AXIS_EMPTY", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line }, @@ -1124,39 +838,35 @@ pub fn init(c: Config) !void { standard.time.unit_short, }), .execute = &commands.wait.axisEmpty, - } }); - errdefer command.registry.orderedRemove("WAIT_AXIS_EMPTY"); - try command.registry.put(.{ - .executable = .{ - .name = "ADD_LOG_INFO", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "kind", .kind = .mmc_client_log_kind }, - .{ .name = "range", .optional = true }, - }, - .short_description = "Add logging configuration.", - .long_description = std.fmt.comptimePrint( - \\Add logging configuration. Overwrites existing logging configuration - \\of specified Line. Parameter "kind" specifies information to add to - \\logging configuration. Valid "kind" options: - \\ - driver - \\ - axis - \\ - all - \\ - \\Optional: "range" defines Axis range and must be provided as - \\start:end value (e.g., "1:9"). - \\ - \\Example: Add Driver(s) on Line "line1" to logging configuration. - \\ADD_LOG_INFO line1 driver - \\ - \\Example: Add Axis "2" to "3" on Line "line1" to logging configuration. - \\ADD_LOG_INFO line1 axis 2:3 - , .{}), - .execute = &commands.log.add, + } }, + .{ .executable = .{ + .name = "ADD_LOG_INFO", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "kind", .kind = .mmc_client_log_kind }, + .{ .name = "range", .optional = true }, }, - }); - errdefer command.registry.orderedRemove("ADD_LOG_INFO"); - try command.registry.put(.{ .executable = .{ + .short_description = "Add logging configuration.", + .long_description = std.fmt.comptimePrint( + \\Add logging configuration. Overwrites existing logging configuration + \\of specified Line. Parameter "kind" specifies information to add to + \\logging configuration. Valid "kind" options: + \\ - driver + \\ - axis + \\ - all + \\ + \\Optional: "range" defines Axis range and must be provided as + \\start:end value (e.g., "1:9"). + \\ + \\Example: Add Driver(s) on Line "line1" to logging configuration. + \\ADD_LOG_INFO line1 driver + \\ + \\Example: Add Axis "2" to "3" on Line "line1" to logging configuration. + \\ADD_LOG_INFO line1 axis 2:3 + , .{}), + .execute = &commands.log.add, + } }, + .{ .executable = .{ .name = "START_LOG_INFO", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "duration" }, @@ -1181,68 +891,61 @@ pub fn init(c: Config) !void { \\START_LOG_INFO 10 folder/log_info.csv , .{}), .execute = &commands.log.start, - } }); - errdefer command.registry.orderedRemove("START_LOG_INFO"); - try command.registry.put(.{ - .executable = .{ - .name = "REMOVE_LOG_INFO", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "kind", .kind = .mmc_client_log_kind }, - .{ .name = "range", .optional = true }, - }, - .short_description = "Remove logging configuration.", - .long_description = std.fmt.comptimePrint( - \\Remove logging configuration. Parameter "kind" specifies information to - \\remove from logging configuration. Valid "kind" options: - \\ - driver - \\ - axis - \\ - all - \\ - \\Optional: "range" defines Axis range and must be provided as start:end - \\value (e.g., "1:9"). - \\ - \\Example: Clear logging configuration for Line "line1". - \\REMOVE_LOG_INFO line1 all - \\ - \\Example: Remove Driver(s) on Line "line1" from logging configuration. - \\REMOVE_LOG_INFO line1 driver - \\ - \\Example: Remove Axis "1" to "3" on Line "line1" from logging configuration. - \\REMOVE_LOG_INFO line1 axis 1:3 - , .{}), - .execute = &commands.log.remove, + } }, + .{ .executable = .{ + .name = "REMOVE_LOG_INFO", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "kind", .kind = .mmc_client_log_kind }, + .{ .name = "range", .optional = true }, }, - }); - errdefer command.registry.orderedRemove("REMOVE_LOG_INFO"); - try command.registry.put(.{ .executable = .{ + .short_description = "Remove logging configuration.", + .long_description = std.fmt.comptimePrint( + \\Remove logging configuration. Parameter "kind" specifies information to + \\remove from logging configuration. Valid "kind" options: + \\ - driver + \\ - axis + \\ - all + \\ + \\Optional: "range" defines Axis range and must be provided as start:end + \\value (e.g., "1:9"). + \\ + \\Example: Clear logging configuration for Line "line1". + \\REMOVE_LOG_INFO line1 all + \\ + \\Example: Remove Driver(s) on Line "line1" from logging configuration. + \\REMOVE_LOG_INFO line1 driver + \\ + \\Example: Remove Axis "1" to "3" on Line "line1" from logging configuration. + \\REMOVE_LOG_INFO line1 axis 1:3 + , .{}), + .execute = &commands.log.remove, + } }, + .{ .executable = .{ .name = "STATUS_LOG_INFO", .short_description = "Show logging configuration.", .long_description = std.fmt.comptimePrint( \\Show logging configuration for specified Line(s). , .{}), .execute = &commands.log.status, - } }); - errdefer command.registry.orderedRemove("STATUS_LOG_INFO"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "STOP_LOG_INFO", .short_description = "Stop MMC logging.", .long_description = std.fmt.comptimePrint( \\Stop MMC logging and save logging data to file. , .{}), .execute = &commands.log.stop, - } }); - errdefer command.registry.orderedRemove("STOP_LOG_INFO"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "CANCEL_LOG_INFO", .short_description = "Cancel MMC logging process.", .long_description = std.fmt.comptimePrint( \\Cancel MMC logging without saving the logging data. , .{}), .execute = &commands.log.cancel, - } }); - errdefer command.registry.orderedRemove("CANCEL_LOG_INFO"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "PRINT_ERRORS", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line }, @@ -1264,9 +967,8 @@ pub fn init(c: Config) !void { \\PRINT_ERRORS line1 3a , .{}), .execute = &commands.show_errors.impl, - } }); - errdefer command.registry.orderedRemove("PRINT_ERRORS"); - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "STOP", .parameters = &[_]command.Command.Executable.Parameter{ .{ .name = "Line", .kind = .mmc_client_line, .optional = true }, @@ -1277,58 +979,258 @@ pub fn init(c: Config) !void { \\Optional: Stop all running and queued processes only on specified Line. , .{}), .execute = &commands.stop.impl, - } }); - errdefer command.registry.orderedRemove("STOP"); - try command.registry.put(.{ - .executable = .{ - .name = "PAUSE", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line, .optional = true }, - }, - .short_description = "Pause all processes.", - .long_description = std.fmt.comptimePrint( - \\Pause all processes on System. - \\Optional: Pause all processes only on specified Line. - , .{}), - .execute = &commands.pause.impl, + } }, + .{ .executable = .{ + .name = "PAUSE", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, }, - }); - errdefer command.registry.orderedRemove("PAUSE"); - try command.registry.put(.{ - .executable = .{ - .name = "RESUME", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line, .optional = true }, - }, - .short_description = "Resume all paused processes.", - .long_description = std.fmt.comptimePrint( - \\Resume all paused processes on System. - \\Optional: Resume all paused processes only on specified Line. - , .{}), - .execute = &commands.@"resume".impl, + .short_description = "Pause all processes.", + .long_description = std.fmt.comptimePrint( + \\Pause all processes on System. + \\Optional: Pause all processes only on specified Line. + , .{}), + .execute = &commands.pause.impl, + } }, + .{ .executable = .{ + .name = "RESUME", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, }, - }); - errdefer command.registry.orderedRemove("RESUME"); - try command.registry.put(.{ - .executable = .{ - .name = "SET_CARRIER_ID", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - .{ .name = "new Carrier id", .kind = .mmc_client_carrier }, - }, - .short_description = "Modify Carrier ID.", - .long_description = std.fmt.comptimePrint( - \\Modify Carrier ID of initialized Carrier. Carrier ID must be unique per - \\Line. - \\ - \\Example: Modify Carrier ID of Carrier "3" to "4" on Line "line1". - \\SET_CARRIER_ID line1 3 4 - , .{}), - .execute = &commands.set_carrier_id.impl, + .short_description = "Resume all paused processes.", + .long_description = std.fmt.comptimePrint( + \\Resume all paused processes on System. + \\Optional: Resume all paused processes only on specified Line. + , .{}), + .execute = &commands.@"resume".impl, + } }, + .{ .executable = .{ + .name = "SET_CARRIER_ID", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, + .{ .name = "new Carrier id", .kind = .mmc_client_carrier }, }, - }); - errdefer command.registry.orderedRemove("SET_CARRIER_ID"); + .short_description = "Modify Carrier ID.", + .long_description = std.fmt.comptimePrint( + \\Modify Carrier ID of initialized Carrier. Carrier ID must be unique per + \\Line. + \\ + \\Example: Modify Carrier ID of Carrier "3" to "4" on Line "line1". + \\SET_CARRIER_ID line1 3 4 + , .{}), + .execute = &commands.set_carrier_id.impl, + } }, +}; + +pub const Filter = union(enum) { + carrier: [1]u32, + driver: u32, + axis: u32, + + pub fn parse(filter: []const u8) (error{InvalidParameter} || std.fmt.ParseIntError)!Filter { + var suffix_idx: usize = 0; + for (filter) |c| { + if (std.ascii.isDigit(c)) suffix_idx += 1 else break; + } + // No digit is recognized. + if (suffix_idx == 0) return error.InvalidParameter; + const id = try std.fmt.parseUnsigned(u32, filter[0..suffix_idx], 0); + + // Check for single character suffix. + if (filter.len - suffix_idx == 1) { + if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "a")) + return Filter{ .axis = id } + else if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "c")) + return Filter{ .carrier = [1]u32{id} } + else if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "d")) + return Filter{ .driver = id }; + } + // Check for `axis` suffix + else if (filter.len - suffix_idx == 4 and + std.ascii.eqlIgnoreCase(filter[suffix_idx..], "axis")) + return Filter{ .axis = id } + // Check for `driver` suffix + else if (filter.len - suffix_idx == 6 and + std.ascii.eqlIgnoreCase(filter[suffix_idx..], "driver")) + return Filter{ .driver = id } + // Check for `carrier` suffix + else if (std.ascii.eqlIgnoreCase(filter[suffix_idx..], "carrier")) + return Filter{ .carrier = [1]u32{id} }; + return error.InvalidParameter; + } + + /// Invalidates original filter's allocated memory ownership. + pub fn toProtobuf(filter: *Filter) api.protobuf.mmc.info.Request.Track.filter_union { + return switch (filter.*) { + .axis => |axis_id| .{ + .axes = .{ + .start = axis_id, + .end = axis_id, + }, + }, + .driver => |driver_id| .{ + .drivers = .{ + .start = driver_id, + .end = driver_id, + }, + }, + .carrier => .{ + .carriers = .{ .ids = .fromOwnedSlice(&filter.carrier) }, + }, + }; + } +}; + +pub const standard = struct { + pub const time = struct { + pub const unit_short: []const u8 = "ms"; + pub const unit_long: []const u8 = "millisecond"; + }; + pub const length = struct { + pub const unit_short: []const u8 = "mm"; + pub const unit_long: []const u8 = "millimeter"; + }; + pub const speed = struct { + pub const range = struct { + pub const min: u16 = 0; + pub const max: u16 = 6000; + }; + pub const unit: []const u8 = "mm/s"; + }; + pub const acceleration = struct { + pub const range = struct { + pub const min: f32 = 0; + pub const max: f32 = 24_500; + }; + pub const unit: []const u8 = "mm/s²"; + }; +}; + +pub const error_response = struct { + /// Throw an error if receive response of core request error + pub fn throwCoreError(err: api.protobuf.mmc.core.Request.Error) anyerror { + return switch (err) { + .CORE_REQUEST_ERROR_UNSPECIFIED => error.InvalidResponse, + .CORE_REQUEST_ERROR_REQUEST_UNKNOWN => error.RequestUnknown, + _ => return error.UnexpectedResponse, + }; + } + + pub fn throwCommandError(err: api.protobuf.mmc.command.Request.Error) anyerror { + return switch (err) { + .COMMAND_REQUEST_ERROR_UNSPECIFIED, + => error.InvalidResponse, + .COMMAND_REQUEST_ERROR_INVALID_LOCATION => error.InvalidLocation, + .COMMAND_REQUEST_ERROR_INVALID_DISTANCE => error.InvalidDistance, + .COMMAND_REQUEST_ERROR_INVALID_LINE => error.InvalidLine, + .COMMAND_REQUEST_ERROR_INVALID_AXIS => error.InvalidAxis, + .COMMAND_REQUEST_ERROR_INVALID_DRIVER => error.InvalidDriver, + .COMMAND_REQUEST_ERROR_INVALID_ACCELERATION => error.InvalidAcceleration, + .COMMAND_REQUEST_ERROR_INVALID_VELOCITY => error.InvalidSpeed, + .COMMAND_REQUEST_ERROR_INVALID_DIRECTION => error.InvalidDirection, + .COMMAND_REQUEST_ERROR_INVALID_CARRIER => error.InvalidCarrier, + .COMMAND_REQUEST_ERROR_MISSING_PARAMETER => error.MissingParameter, + .COMMAND_REQUEST_ERROR_COMMAND_NOT_FOUND => error.CommandNotFound, + .COMMAND_REQUEST_ERROR_CARRIER_NOT_FOUND => error.CarrierNotFound, + .COMMAND_REQUEST_ERROR_OUT_OF_MEMORY => error.ServerRunningOutOfMemory, + .COMMAND_REQUEST_ERROR_MAXIMUM_AUTO_INITIALIZE_EXCEEDED => error.MaximumAutoInitializeExceeded, + .COMMAND_REQUEST_ERROR_CONFLICTING_CARRIER_ID => error.ConflictingCarrierId, + .COMMAND_REQUEST_ERROR_INVALID_COMMAND => error.InvalidCommand, + _ => return error.UnexpectedResponse, + }; + } + + pub fn throwInfoError(err: api.protobuf.mmc.info.Request.Error) anyerror { + return switch (err) { + .INFO_REQUEST_ERROR_UNSPECIFIED => error.InvalidResponse, + .INFO_REQUEST_ERROR_INVALID_LINE => error.InvalidLine, + .INFO_REQUEST_ERROR_INVALID_AXIS => error.InvalidAxis, + .INFO_REQUEST_ERROR_INVALID_DRIVER => error.InvalidDriver, + .INFO_REQUEST_ERROR_MISSING_PARAMETER => error.MissingParameter, + .INFO_REQUEST_ERROR_COMMAND_NOT_FOUND => error.CommandNotFound, + .INFO_REQUEST_ERROR_INVALID_COMMAND => error.InvalidCommand, + .INFO_REQUEST_ERROR_INVALID_CARRIER => error.InvalidCarrier, + _ => return error.UnexpectedResponse, + }; + } + + pub fn throwMmcError(err: api.protobuf.mmc.Request.Error) anyerror { + return switch (err) { + .MMC_REQUEST_ERROR_UNSPECIFIED => error.InvalidResponse, + .MMC_REQUEST_ERROR_INVALID_MESSAGE => error.InvalidMessage, + _ => return error.UnexpectedResponse, + }; + } +}; + +parameter: Parameter, +/// `lines` is initialized once the client is connected to a server. +/// Deinitialized once disconnected from a server. +lines: []Line, +/// The logging configuration is initialized once the client is connected, and +/// deinitialized if the client is disconnected. +log_config: log.Config, +/// Currently connected socket. Nulled when disconnect. +sock: ?zignet.Socket, +/// Currently saved endpoint. The endpoint will be overwritten if the client +/// is connected to a different server. Stays null before connected to a socket. +endpoint: ?zignet.Endpoint, +allocator: std.mem.Allocator, +/// Store the configuration. +config: Config, + +pub fn create(c: Config) !MmcClient { + var self: MmcClient = .{ + .allocator = if (builtin.mode == .Debug) + debug_allocator.allocator() + else + std.heap.smp_allocator, + + // Store the configuration. + .config = undefined, + .parameter = undefined, + // `lines` is initialized once the client is connected to a server. + // Deinitialized once disconnected from a server. + .lines = &.{}, + // The logging configuration is initialized once the client is connected, and + // deinitialized if the client is disconnected. + .log_config = undefined, + // Currently connected socket. Nulled when disconnect. + .sock = null, + // Currently saved endpoint. The endpoint will be overwritten if the client + // is connected to a different server. Stays null before connected to a socket. + .endpoint = null, + }; + + self.config = .{ + .host = try self.allocator.dupe(u8, c.host), + .port = c.port, + }; + errdefer self.allocator.free(self.config.host); + self.parameter = .init(self.allocator); + errdefer self.parameter.deinit(); + return self; +} + +var debug_allocator = std.heap.DebugAllocator(.{}){}; + +var current: ?MmcClient = null; + +pub fn get() *MmcClient { + // scaffold: + // throw error here + // e.g. ModuleNotInitialized + return &(current orelse std.debug.panic("Mmc_client module is not initialized", .{})); + // if (current) |*c| { + // return c; + // } + // return error.ModuleNotInitialized; +} + +pub fn init(c: Config) !void { + current = try MmcClient.create(c); + errdefer current = null; } pub fn deinit() void { diff --git a/src/modules/mes07.zig b/src/modules/mes07.zig index ad6afb57..878f560c 100644 --- a/src/modules/mes07.zig +++ b/src/modules/mes07.zig @@ -13,27 +13,8 @@ const Command = command.Command; pub const Config = struct {}; -var connection_buf: [128]u8 = .{0} ** 128; -var connection: []u8 = connection_buf[0..0]; -var io_map: [4096]u8 = .{0} ** 4096; - -var output_bytes: u32 = 0; -var input_bytes: u32 = 0; -var expected_WKC: u16 = 0; - -/// Used by main thread to signal process thread to stop. -var stop_processing = std.atomic.Value(bool).init(false); -/// Used by process thread to signal it is currently processing. -var processing = std.atomic.Value(bool).init(false); - -/// Used to share last updated laser value from process thread. -var laser_value = std.atomic.Value(i32).init(0); -/// Used to signal if last value was read, so main thread can error if the -/// process thread has unexpectedly quit. -var read_laser_value = std.atomic.Value(bool).init(false); - -pub fn init(_: Config) !void { - try command.registry.put(.{ .executable = .{ +pub const module_commands = [_]command.Command{ + .{ .executable = .{ .name = "MES07_CONNECT", .parameters = &.{ .{ .name = "adapter", .optional = true }, @@ -43,9 +24,8 @@ pub fn init(_: Config) !void { \\Connect to MES07 laser device. Must be called before `MES07_READ`. , .execute = &connect, - } }); - - try command.registry.put(.{ .executable = .{ + } }, + .{ .executable = .{ .name = "MES07_READ", .parameters = &.{ .{ .name = "variable", .optional = true, .resolve = false }, @@ -56,8 +36,29 @@ pub fn init(_: Config) !void { \\names are case sensitive and shall not begin with digit. , .execute = &read, - } }); -} + } }, +}; + +var connection_buf: [128]u8 = .{0} ** 128; +var connection: []u8 = connection_buf[0..0]; +var io_map: [4096]u8 = .{0} ** 4096; + +var output_bytes: u32 = 0; +var input_bytes: u32 = 0; +var expected_WKC: u16 = 0; + +/// Used by main thread to signal process thread to stop. +var stop_processing = std.atomic.Value(bool).init(false); +/// Used by process thread to signal it is currently processing. +var processing = std.atomic.Value(bool).init(false); + +/// Used to share last updated laser value from process thread. +var laser_value = std.atomic.Value(i32).init(0); +/// Used to signal if last value was read, so main thread can error if the +/// process thread has unexpectedly quit. +var read_laser_value = std.atomic.Value(bool).init(false); + +pub fn init(_: Config) !void {} pub fn deinit() void { if (connection.len > 0) { From 4de15df9896970a095dc7180274b017fd8a6be61 Mon Sep 17 00:00:00 2001 From: kf Date: Mon, 6 Apr 2026 14:01:47 +0900 Subject: [PATCH 05/12] refactor: check FilePathTooLong --- src/command.zig | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/command.zig b/src/command.zig index f6bed391..390e53f4 100644 --- a/src/command.zig +++ b/src/command.zig @@ -1063,6 +1063,8 @@ fn deinitLoadedConfigs() void { } fn openConfigFile(path: []const u8) !std.fs.File { + if (path.len >= std.fs.max_path_bytes) return error.FilePathTooLong; + return if (path.len > 0) std.fs.cwd().openFile(path, .{}) catch error.InvalidParameter else @@ -1181,10 +1183,24 @@ fn activateLoadedConfig(id: []const u8) !void { } fn loadConfig(params: [][]const u8) !void { - const file_path = if (params.len > 0) params[0] else ""; - const config_id = if (params.len > 1) params[1] else ""; - const resolved_source_path = - if (file_path.len > 0) file_path else "config.json5"; + const file_path: []const u8 = if (params.len > 0) params[0] else ""; + const config_id: []const u8 = if (params.len > 1) params[1] else ""; + var buf: [std.fs.max_path_bytes]u8 = undefined; + + // Validate path length + if (std.mem.endsWith(u8, file_path, ".json5") and + file_path.len > buf.len) + return error.PathTooLong + else if (file_path.len + ".json5".len > buf.len) + return error.PathTooLong; + + const resolved_file_path: []const u8 = + if (file_path.len == 0) + "config.json5" + else if (std.mem.endsWith(u8, file_path, ".json5")) + file_path + else + try std.fmt.bufPrint(&buf, "{s}.json5", .{file_path}); var config_file = try openConfigFile(file_path); defer config_file.close(); From 1186e756e2884a37882a0402871ccdfa1c21186d Mon Sep 17 00:00:00 2001 From: kf Date: Mon, 6 Apr 2026 14:48:54 +0900 Subject: [PATCH 06/12] refactor: loading config file without providing `.json5` extension --- src/command.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/command.zig b/src/command.zig index 390e53f4..68822365 100644 --- a/src/command.zig +++ b/src/command.zig @@ -1066,7 +1066,7 @@ fn openConfigFile(path: []const u8) !std.fs.File { if (path.len >= std.fs.max_path_bytes) return error.FilePathTooLong; return if (path.len > 0) - std.fs.cwd().openFile(path, .{}) catch error.InvalidParameter + std.fs.cwd().openFile(path, .{}) catch error.FileNotFound else std.fs.cwd().openFile("config.json5", .{}) catch exe_local: { var exe_dir_buf: [std.fs.max_path_bytes]u8 = undefined; @@ -1202,7 +1202,7 @@ fn loadConfig(params: [][]const u8) !void { else try std.fmt.bufPrint(&buf, "{s}.json5", .{file_path}); - var config_file = try openConfigFile(file_path); + var config_file = try openConfigFile(resolved_file_path); defer config_file.close(); const config_arena = try std.heap.smp_allocator.create(std.heap.ArenaAllocator); @@ -1228,7 +1228,7 @@ fn loadConfig(params: [][]const u8) !void { const source_path = try std.heap.smp_allocator.dupe( u8, - resolved_source_path, + resolved_file_path, ); errdefer std.heap.smp_allocator.free(source_path); From 30cb35b90fe22d0a418021489b10aecd371e3da3 Mon Sep 17 00:00:00 2001 From: kf Date: Mon, 6 Apr 2026 16:30:30 +0900 Subject: [PATCH 07/12] refactor: openConfigFile() print fallback information --- src/command.zig | 149 +++++++++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 53 deletions(-) diff --git a/src/command.zig b/src/command.zig index 68822365..c97056d6 100644 --- a/src/command.zig +++ b/src/command.zig @@ -1062,66 +1062,109 @@ fn deinitLoadedConfigs() void { active_config_id = null; } +/// Attempts to open a config file at `path` with additional fallback locations. +/// Fallback: +/// 1. Current working directory +/// 2. Executable directory +/// 3. Platform specific directory fn openConfigFile(path: []const u8) !std.fs.File { if (path.len >= std.fs.max_path_bytes) return error.FilePathTooLong; + if (path.len == 0) return error.InvalidParameter; - return if (path.len > 0) - std.fs.cwd().openFile(path, .{}) catch error.FileNotFound - else - std.fs.cwd().openFile("config.json5", .{}) catch exe_local: { - var exe_dir_buf: [std.fs.max_path_bytes]u8 = undefined; - const exe_dir_path = std.fs.selfExeDirPath(&exe_dir_buf) catch - break :exe_local error.FileNotFound; - var exe_dir = std.fs.cwd().openDir(exe_dir_path, .{}) catch - break :exe_local error.FileNotFound; - defer exe_dir.close(); - break :exe_local exe_dir.openFile("config.json5", .{}); - } catch config_local: { - var config_dir = switch (comptime builtin.os.tag) { - .windows => b: { - var path_buf: [std.fs.max_path_bytes]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&path_buf); - const fba_alloc = fba.allocator(); - const home_path = try std.process.getEnvVarOwned( - fba_alloc, - "USERPROFILE", - ); + return std.fs.cwd().openFile(path, .{}) catch exe_local: { + var exe_dir_buf: [std.fs.max_path_bytes]u8 = undefined; + const exe_dir_path = std.fs.selfExeDirPath(&exe_dir_buf) catch + break :exe_local error.FileNotFound; - var home_dir = try std.fs.cwd().openDir(home_path, .{}); - defer home_dir.close(); - var config_root = try home_dir.openDir(".config", .{}); - defer config_root.close(); - break :b try config_root.openDir("mmc-cli", .{}); - }, - .linux => b: { - var path_buf: [std.fs.max_path_bytes]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&path_buf); - const fba_alloc = fba.allocator(); - const config_path = std.process.getEnvVarOwned( - fba_alloc, - "XDG_CONFIG_HOME", - ) catch ""; - if (config_path.len > 0) { - break :b try std.fs.cwd().openDir(config_path, .{}); - } - const home_path = try std.process.getEnvVarOwned( - fba_alloc, - "HOME", + std.log.info( + \\Config file '{s}' not found in current working directory! + \\Trying executable directory: {s} + \\ + , + .{ path, exe_dir_path }, + ); + + var exe_dir = std.fs.cwd().openDir(exe_dir_path, .{}) catch + break :exe_local error.FileNotFound; + defer exe_dir.close(); + + break :exe_local exe_dir.openFile(path, .{}); + } catch config_local: { + var config_dir = switch (comptime builtin.os.tag) { + .windows => b: { + var path_buf: [std.fs.max_path_bytes]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&path_buf); + const fba_alloc = fba.allocator(); + + const home_path = try std.process.getEnvVarOwned( + fba_alloc, + "USERPROFILE", + ); + + std.log.info( + \\Config file '{s}' not found in executable directory! + \\Trying Windows user config directory under: {s}\.config\mmc-cli + \\ + , + .{ path, home_path }, + ); + + var home_dir = try std.fs.cwd().openDir(home_path, .{}); + defer home_dir.close(); + + var config_root = try home_dir.openDir(".config", .{}); + defer config_root.close(); + + break :b try config_root.openDir("mmc-cli", .{}); + }, + .linux => b: { + var path_buf: [std.fs.max_path_bytes]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&path_buf); + const fba_alloc = fba.allocator(); + + const config_path = std.process.getEnvVarOwned( + fba_alloc, + "XDG_CONFIG_HOME", + ) catch ""; + + if (config_path.len > 0) { + std.log.info( + \\Config file '{s}' not found in executable directory! + \\Trying XDG config home directory: {s} + \\ + , + .{ path, config_path }, ); - var home_dir = try std.fs.cwd().openDir(home_path, .{}); - defer home_dir.close(); - var config_root = try home_dir.openDir(".config", .{}); - defer config_root.close(); - break :b try config_root.openDir("mmc-cli", .{}); - }, - else => return error.UnsupportedOs, - }; + break :b try std.fs.cwd().openDir(config_path, .{}); + } - break :config_local try config_dir.openFile( - "config.json5", - .{}, - ); + const home_path = try std.process.getEnvVarOwned( + fba_alloc, + "HOME", + ); + + std.log.info( + \\Config file '{s}' not found in executable directory! + \\Trying Linux user config directory under: {s}/.config/mmc-cli + \\ + , + .{ path, home_path }, + ); + + var home_dir = try std.fs.cwd().openDir(home_path, .{}); + defer home_dir.close(); + + var config_root = try home_dir.openDir(".config", .{}); + defer config_root.close(); + + break :b try config_root.openDir("mmc-cli", .{}); + }, + else => return error.UnsupportedOs, }; + defer config_dir.close(); + + break :config_local try config_dir.openFile(path, .{}); + }; } /// Generates a unique config ID. From a0ea9d08bc1b34bb5be77a1c5f816fa96bbb541e Mon Sep 17 00:00:00 2001 From: kf Date: Tue, 7 Apr 2026 10:33:05 +0900 Subject: [PATCH 08/12] refactor: add documentation comments --- src/command.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/command.zig b/src/command.zig index c97056d6..0d298b51 100644 --- a/src/command.zig +++ b/src/command.zig @@ -21,16 +21,20 @@ const mes07 = if (config.mes07) @import("modules/mes07.zig") else void; const Config = @import("Config.zig"); +/// Module lifecycle hooks to load and unload modules and its module +/// commands set. const ModuleSpec = struct { init: *const fn (Config.ModuleConfig) anyerror!void, deinit: *const fn () void, commands: []const Command, }; +/// Stub initializer for modules disabled at build time. fn initModuleDisabled(_: Config.ModuleConfig) !void { return error.ModuleDisabledAtBuildTime; } +/// Stub deinitializer for modules disabled at build time. fn deinitModuleDisabled() void {} fn initReturnDemo2(module_config: Config.ModuleConfig) !void { @@ -1217,9 +1221,11 @@ fn initModulesFromConfig(conf: *Config) !void { } } +/// Activates a loaded configuration by config ID. Deinit current modules, +/// initializes modules from target config and updates `active_config_id`. fn activateLoadedConfig(id: []const u8) !void { const loaded = loaded_configs.getPtr(id) orelse - return error.InvalidParameter; + return error.ConfigIdNotFound; deinitModules(); try initModulesFromConfig(&loaded.config); active_config_id = loaded.id; From bfa900dd870e71a48bc05cb8cf087792d9f0d036 Mon Sep 17 00:00:00 2001 From: kf Date: Mon, 13 Apr 2026 16:16:30 +0900 Subject: [PATCH 09/12] feat: loadConfig() show already loaded config id and reject config with disabled modules --- src/command.zig | 80 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/command.zig b/src/command.zig index 0d298b51..3035adea 100644 --- a/src/command.zig +++ b/src/command.zig @@ -1231,6 +1231,47 @@ fn activateLoadedConfig(id: []const u8) !void { active_config_id = loaded.id; } +/// Returns module tag if module exists in configuration else null. +fn findModule( + modules: []const Config.ModuleConfig, + comptime tag: Config.Module, +) ?*const Config.ModuleConfig { + for (modules) |*mod| { + if (std.meta.activeTag(mod.*) == tag) return mod; + } + return null; +} + +/// Returns true when both configs contain the same modules. +/// For `mmc_client` module, `host` and `port` must also match. +fn configEql(a: *const Config, b: *const Config) bool { + const a_modules = a.parsed.value.modules; + const b_modules = b.parsed.value.modules; + + inline for (@typeInfo(Config.Module).@"enum".fields) |field| { + const tag: Config.Module = @enumFromInt(field.value); + + const a_mod = findModule(a_modules, tag); + const b_mod = findModule(b_modules, tag); + + if ((a_mod == null) != (b_mod == null)) return false; + + if (a_mod != null) { + switch (a_mod.?.*) { + .mmc_client => |a_mmc| { + if (a_mmc.port != b_mod.?.mmc_client.port) return false; + if (!std.mem.eql(u8, a_mmc.host, b_mod.?.mmc_client.host)) + return false; + }, + .mes07 => {}, + .return_demo2 => {}, + } + } + } + + return true; +} + fn loadConfig(params: [][]const u8) !void { const file_path: []const u8 = if (params.len > 0) params[0] else ""; const config_id: []const u8 = if (params.len > 1) params[1] else ""; @@ -1265,11 +1306,48 @@ fn loadConfig(params: [][]const u8) !void { var conf = try Config.parse(config_allocator, config_file); errdefer conf.deinit(); + // Check if Config file exclusively contains disabled module(s) + const parsed_mods = conf.parsed.value.modules; + var any_enabled = false; + + for (parsed_mods) |mod| { + const tag = std.meta.activeTag(mod); + switch (tag) { + .return_demo2 => { + if (config.return_demo2) any_enabled = true; + }, + .mmc_client => { + if (config.mmc_client) any_enabled = true; + }, + .mes07 => { + if (config.mes07) any_enabled = true; + }, + } + } + + if (!any_enabled) return error.ModuleDisabledAtBuildTime; + // Check if Config already loaded var it_loaded = loaded_configs.iterator(); while (it_loaded.next()) |entry| { - if (configEql(&conf, &entry.value_ptr.config)) + if (configEql(&conf, &entry.value_ptr.config)) { + std.log.info( + "Config already loaded as '{s}'.", + .{entry.value_ptr.id}, + ); + if (std.mem.eql(u8, entry.value_ptr.id, active_config_id orelse "")) + std.log.info( + "Config '{s}' is currently active.\n", + .{entry.value_ptr.id}, + ) + else + std.log.info( + "Type 'USE_CONFIG {s}' to activate this config.\n", + .{entry.value_ptr.id}, + ); + return error.ConfigAlreadyLoaded; + } } const id = try genLoadedConfigId(file_path, config_id); From c9cc14ebfb6d8c172012e1534660912d533807b3 Mon Sep 17 00:00:00 2001 From: kf Date: Fri, 10 Apr 2026 10:12:28 +0900 Subject: [PATCH 10/12] refactor: remove comments, unnecessary steps, debug messages --- src/command.zig | 13 ----------- src/modules/MmcClient.zig | 47 +++++++++++++-------------------------- 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/command.zig b/src/command.zig index 3035adea..3a22ef66 100644 --- a/src/command.zig +++ b/src/command.zig @@ -1198,9 +1198,6 @@ fn genLoadedConfigId(file_path: []const u8, config_id: []const u8) ![]u8 { if (!loaded_configs.contains(candidate)) return candidate; std.heap.smp_allocator.free(candidate); - - // if (suffix == std.math.maxInt(@TypeOf(suffix))) return error.InvalidConfigID; - // std.log.debug("maxInt: {d}\n", .{std.math.maxInt(@TypeOf(suffix))}); } } @@ -1401,16 +1398,6 @@ fn unloadConfig(params: [][]const u8) !void { } } - std.log.debug( - "Unload config: idx={d}, id={s}, was active={}, fallback={s}", - .{ - idx, - id, - was_active, - fallback_id orelse "nope", - }, - ); - const removed_entry = loaded_configs.fetchOrderedRemove(id) orelse return error.InvalidParameter; diff --git a/src/modules/MmcClient.zig b/src/modules/MmcClient.zig index 12549c8c..41e5e490 100644 --- a/src/modules/MmcClient.zig +++ b/src/modules/MmcClient.zig @@ -1180,8 +1180,16 @@ allocator: std.mem.Allocator, /// Store the configuration. config: Config, -pub fn create(c: Config) !MmcClient { - var self: MmcClient = .{ +var debug_allocator = std.heap.DebugAllocator(.{}){}; + +var current: ?MmcClient = null; + +pub fn get() *MmcClient { + return &(current orelse std.debug.panic("Mmc_client module is not initialized", .{})); +} + +pub fn init(c: Config) !void { + current = .{ .allocator = if (builtin.mode == .Debug) debug_allocator.allocator() else @@ -1202,35 +1210,15 @@ pub fn create(c: Config) !MmcClient { // is connected to a different server. Stays null before connected to a socket. .endpoint = null, }; + errdefer current = null; - self.config = .{ - .host = try self.allocator.dupe(u8, c.host), + current.?.config = .{ + .host = try current.?.allocator.dupe(u8, c.host), .port = c.port, }; - errdefer self.allocator.free(self.config.host); - self.parameter = .init(self.allocator); - errdefer self.parameter.deinit(); - return self; -} - -var debug_allocator = std.heap.DebugAllocator(.{}){}; - -var current: ?MmcClient = null; - -pub fn get() *MmcClient { - // scaffold: - // throw error here - // e.g. ModuleNotInitialized - return &(current orelse std.debug.panic("Mmc_client module is not initialized", .{})); - // if (current) |*c| { - // return c; - // } - // return error.ModuleNotInitialized; -} - -pub fn init(c: Config) !void { - current = try MmcClient.create(c); - errdefer current = null; + errdefer current.allocator.free(current.?.config.host); + current.?.parameter = .init(current.?.allocator); + errdefer current.?.parameter.deinit(); } pub fn deinit() void { @@ -1315,9 +1303,6 @@ pub fn waitCommandCompleted(gpa: std.mem.Allocator, net: zignet.Socket) !void { }, }, }; - // try sendRequest(allocator, net, request); - // var decoded = try getResponse(allocator, net); - // defer decoded.deinit(allocator); try sendRequest(get().allocator, net, request); var decoded = try getResponse(get().allocator, net); defer decoded.deinit(get().allocator); From b419b931f38c0ea0b94c0c0b77b86b4aaa3e3b7d Mon Sep 17 00:00:00 2001 From: kf Date: Fri, 17 Apr 2026 14:57:23 +0900 Subject: [PATCH 11/12] refactor: remove module use of return_demo2 --- build.zig | 6 ------ src/Config.zig | 6 ------ src/command.zig | 23 ----------------------- 3 files changed, 35 deletions(-) diff --git a/build.zig b/build.zig index 7b638c57..c49c5e89 100644 --- a/build.zig +++ b/build.zig @@ -6,12 +6,6 @@ pub fn build(b: *std.Build) !void { const options = b.addOptions(); // Enable/disable backends selectively through options. - const return_demo2 = b.option( - bool, - "return_demo2", - "Enable the `return_demo2` backend (default false).", - ) orelse false; - options.addOption(bool, "return_demo2", return_demo2); const mmc_client = b.option( bool, "mmc_client", diff --git a/src/Config.zig b/src/Config.zig index 22e8f2ef..d21fc7ab 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -5,10 +5,6 @@ const std = @import("std"); const config = @import("config"); const json5 = @import("json5"); -const ReturnDemo2Config = if (config.return_demo2) - @import("modules/return_demo2.zig").Config -else - void; const ClientCliConfig = if (config.mmc_client) @import("modules/MmcClient.zig").Config else void; const Mes07Config = @@ -17,13 +13,11 @@ const Mes07Config = parsed: json5.Parsed(Parse), pub const Module = enum { - return_demo2, mmc_client, mes07, }; pub const ModuleConfig = union(Module) { - return_demo2: ReturnDemo2Config, mmc_client: ClientCliConfig, mes07: Mes07Config, }; diff --git a/src/command.zig b/src/command.zig index 3a22ef66..bbde9fa1 100644 --- a/src/command.zig +++ b/src/command.zig @@ -13,8 +13,6 @@ const build = @import("build.zig.zon"); const config = @import("config"); // Command modules. -const return_demo2 = - if (config.return_demo2) @import("modules/return_demo2.zig") else void; const mmc_client = if (config.mmc_client) @import("modules/MmcClient.zig") else void; const mes07 = if (config.mes07) @import("modules/mes07.zig") else void; @@ -37,14 +35,6 @@ fn initModuleDisabled(_: Config.ModuleConfig) !void { /// Stub deinitializer for modules disabled at build time. fn deinitModuleDisabled() void {} -fn initReturnDemo2(module_config: Config.ModuleConfig) !void { - const cfg = switch (module_config) { - .return_demo2 => |cfg| cfg, - else => unreachable, - }; - try return_demo2.init(cfg); -} - fn initMmcClient(module_config: Config.ModuleConfig) !void { const cfg = switch (module_config) { .mmc_client => |cfg| cfg, @@ -62,15 +52,6 @@ fn initMes07(module_config: Config.ModuleConfig) !void { } const module_specs = std.EnumArray(Config.Module, ModuleSpec).init(.{ - .return_demo2 = if (config.return_demo2) .{ - .init = initReturnDemo2, - .deinit = return_demo2.deinit, - .commands = return_demo2.module_commands[0..], - } else .{ - .init = initModuleDisabled, - .deinit = deinitModuleDisabled, - .commands = &.{}, - }, .mmc_client = if (config.mmc_client) .{ .init = initMmcClient, .deinit = mmc_client.deinit, @@ -1261,7 +1242,6 @@ fn configEql(a: *const Config, b: *const Config) bool { return false; }, .mes07 => {}, - .return_demo2 => {}, } } } @@ -1310,9 +1290,6 @@ fn loadConfig(params: [][]const u8) !void { for (parsed_mods) |mod| { const tag = std.meta.activeTag(mod); switch (tag) { - .return_demo2 => { - if (config.return_demo2) any_enabled = true; - }, .mmc_client => { if (config.mmc_client) any_enabled = true; }, From 79ce90d82ce6bf088336a385058cd1edeab7a0ad Mon Sep 17 00:00:00 2001 From: kf Date: Wed, 22 Apr 2026 13:30:31 +0900 Subject: [PATCH 12/12] refactor: replacing module_specs EnumArry with reflection --- src/command.zig | 92 +++++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/src/command.zig b/src/command.zig index bbde9fa1..6b672dbd 100644 --- a/src/command.zig +++ b/src/command.zig @@ -19,58 +19,40 @@ const mes07 = if (config.mes07) @import("modules/mes07.zig") else void; const Config = @import("Config.zig"); -/// Module lifecycle hooks to load and unload modules and its module -/// commands set. -const ModuleSpec = struct { - init: *const fn (Config.ModuleConfig) anyerror!void, - deinit: *const fn () void, - commands: []const Command, -}; +fn moduleCommands(tag: Config.Module) []const Command { + switch (tag) { + inline else => |t| { + const module = @field(@This(), @tagName(t)); + + if (@TypeOf(module) == void) return &.{}; -/// Stub initializer for modules disabled at build time. -fn initModuleDisabled(_: Config.ModuleConfig) !void { - return error.ModuleDisabledAtBuildTime; + return module.module_commands[0..]; + }, + } } -/// Stub deinitializer for modules disabled at build time. -fn deinitModuleDisabled() void {} +fn initModule(module_config: Config.ModuleConfig) !void { + switch (module_config) { + inline else => |module, tag| { + const tag_name = &@field(@This(), @tagName(tag)); -fn initMmcClient(module_config: Config.ModuleConfig) !void { - const cfg = switch (module_config) { - .mmc_client => |cfg| cfg, - else => unreachable, - }; - try mmc_client.init(cfg); -} + if (@TypeOf(tag_name.*) == void) + return error.ModuleDisabledAtBuildTime; -fn initMes07(module_config: Config.ModuleConfig) !void { - const cfg = switch (module_config) { - .mes07 => |cfg| cfg, - else => unreachable, - }; - try mes07.init(cfg); + try tag_name.init(module); + }, + } } -const module_specs = std.EnumArray(Config.Module, ModuleSpec).init(.{ - .mmc_client = if (config.mmc_client) .{ - .init = initMmcClient, - .deinit = mmc_client.deinit, - .commands = mmc_client.module_commands[0..], - } else .{ - .init = initModuleDisabled, - .deinit = deinitModuleDisabled, - .commands = &.{}, - }, - .mes07 = if (config.mes07) .{ - .init = initMes07, - .deinit = mes07.deinit, - .commands = mes07.module_commands[0..], - } else .{ - .init = initModuleDisabled, - .deinit = deinitModuleDisabled, - .commands = &.{}, - }, -}); +fn deinitModule(tag: Config.Module) void { + switch (tag) { + inline else => |t| { + const module = @field(@This(), @tagName(t)); + + if (module != void) module.deinit(); + }, + } +} pub const Registry = struct { mapping: std.StringArrayHashMap(Command.Executable), @@ -1029,9 +1011,8 @@ fn deinitModules() void { while (mod_it.next()) |entry| { if (!entry.value.*) continue; - const spec = module_specs.get(entry.key); - unregisterCommands(spec.commands); - spec.deinit(); + unregisterCommands(moduleCommands(entry.key)); + deinitModule(entry.key); initialized_modules.set(entry.key, false); } active_config_id = null; @@ -1185,17 +1166,16 @@ fn genLoadedConfigId(file_path: []const u8, config_id: []const u8) ![]u8 { fn initModulesFromConfig(conf: *Config) !void { errdefer deinitModules(); - for (conf.modules()) |module| { - const module_id = std.meta.activeTag(module); - const spec = module_specs.get(module_id); + for (conf.modules()) |module_config| { + const tag = std.meta.activeTag(module_config); - try spec.init(module); - errdefer spec.deinit(); + try initModule(module_config); + errdefer deinitModule(tag); - try registerCommands(spec.commands); - errdefer unregisterCommands(spec.commands); + try registerCommands(moduleCommands(tag)); + errdefer unregisterCommands(moduleCommands(tag)); - initialized_modules.set(module_id, true); + initialized_modules.set(tag, true); } }