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 64cd755e..d21fc7ab 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -5,25 +5,19 @@ 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/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; parsed: json5.Parsed(Parse), pub const Module = enum { - return_demo2, mmc_client, mes07, }; -const ModuleConfig = union(Module) { - return_demo2: ReturnDemo2Config, +pub const ModuleConfig = union(Module) { mmc_client: ClientCliConfig, mes07: Mes07Config, }; diff --git a/src/command.zig b/src/command.zig index 66bda462..6b672dbd 100644 --- a/src/command.zig +++ b/src/command.zig @@ -13,14 +13,47 @@ 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/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"); +fn moduleCommands(tag: Config.Module) []const Command { + switch (tag) { + inline else => |t| { + const module = @field(@This(), @tagName(t)); + + if (@TypeOf(module) == void) return &.{}; + + return module.module_commands[0..]; + }, + } +} + +fn initModule(module_config: Config.ModuleConfig) !void { + switch (module_config) { + inline else => |module, tag| { + const tag_name = &@field(@This(), @tagName(tag)); + + if (@TypeOf(tag_name.*) == void) + return error.ModuleDisabledAtBuildTime; + + try tag_name.init(module); + }, + } +} + +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), @@ -62,6 +95,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 +131,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 +231,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; @@ -287,7 +355,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, ); @@ -345,6 +413,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 +446,52 @@ 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, provided a + \\config ID 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. The active configuration is marked with '*'. + , + .execute = &listLoadedConfigs, + } }); try registry.put(.{ .executable = .{ .name = "WAIT", .parameters = &[_]Command.Executable.Parameter{ @@ -566,11 +673,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(); } @@ -898,112 +1008,402 @@ 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); - } + while (mod_it.next()) |entry| { + if (!entry.value.*) continue; + + unregisterCommands(moduleCommands(entry.key)); + deinitModule(entry.key); + initialized_modules.set(entry.key, false); + } + active_config_id = null; +} + +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; +} + +/// 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 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; + + 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 }, + ); + 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 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. +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; + + 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); + } +} + +fn initModulesFromConfig(conf: *Config) !void { + errdefer deinitModules(); + + for (conf.modules()) |module_config| { + const tag = std.meta.activeTag(module_config); + + try initModule(module_config); + errdefer deinitModule(tag); + + try registerCommands(moduleCommands(tag)); + errdefer unregisterCommands(moduleCommands(tag)); + + initialized_modules.set(tag, true); + } +} + +/// 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.ConfigIdNotFound; + deinitModules(); + try initModulesFromConfig(&loaded.config); + 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; }, - else => unreachable, + .mes07 => {}, } } } + + return true; } fn loadConfig(params: [][]const u8) !void { - // De-initialize any previously initialized modules. - deinitModules(); + 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(resolved_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 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) { + .mmc_client => { + if (config.mmc_client) any_enabled = true; + }, + .mes07 => { + if (config.mes07) any_enabled = true; + }, + } + } + + if (!any_enabled) return error.ModuleDisabledAtBuildTime; - // Load config file. - const config_file = if (params[0].len > 0) - std.fs.cwd().openFile(params[0], .{}) catch - try std.fs.openFileAbsolute(params[0], .{}) + // Check if Config already loaded + var it_loaded = loaded_configs.iterator(); + while (it_loaded.next()) |entry| { + 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); + errdefer std.heap.smp_allocator.free(id); + + const source_path = try std.heap.smp_allocator.dupe( + u8, + resolved_file_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 - 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", - ); + active_config_id orelse return error.InvalidParameter; - 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", - ); - 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, - }; + const idx = loaded_configs.getIndex(id) orelse + return error.InvalidParameter; - break :config_local try config_dir.openFile( - "config.json5", - .{}, - ); - }; - 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); - - // Initialize only the modules specified in config file. - const fields = @typeInfo(Config.Module).@"enum".fields; - 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 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; + } } } - conf.deinit(); - m_arena.deinit(); + + 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 { diff --git a/src/modules/MmcClient.zig b/src/modules/MmcClient.zig new file mode 100644 index 00000000..41e5e490 --- /dev/null +++ b/src/modules/MmcClient.zig @@ -0,0 +1,1532 @@ +const MmcClient = @This(); +const std = @import("std"); +const builtin = @import("builtin"); + +const chrono = @import("chrono"); + +const command = @import("../command.zig"); +const commands = @import("mmc_client/commands.zig"); +pub const Parameter = @import("mmc_client/Parameter.zig"); +pub const Line = @import("mmc_client/Line.zig"); +pub const log = @import("mmc_client/log.zig"); +pub const zignet = @import("zignet"); +pub const api = @import("mmc-api"); + +pub const Config = struct { + host: []u8, + port: u16, +}; + +pub const module_commands = [_]command.Command{ + .{ .executable = .{ + .name = "SERVER_VERSION", + .short_description = "Display the connected MMC server version.", + .long_description = + \\Display the version of the currently connected MMC server in Semantic + \\Version format ({major}.{minor}.{patch}). + , + .execute = &commands.server_version.impl, + } }, + .{ .executable = .{ + .name = "CONNECT", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "endpoint", .optional = true }, + }, + .short_description = "Connect to MMC server.", + .long_description = std.fmt.comptimePrint( + \\Attempt to connect to MMC server. + \\ + \\Endpoint may be specified using one of the following formats: + \\ - `HOSTNAME:PORT` + \\ - `IPv4_ADDRESS:PORT` + \\ - `[IPv6_ADDRESS%SCOPE]:PORT` + \\ + \\If no endpoint provided, last connected server is used. If no server has + \\been connected since `LOAD_CONFIG`, connect to the default endpoint + \\provided in the configuration file. + , .{}), + .execute = &commands.connect.impl, + } }, + .{ .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, + } }, + .{ .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_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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .executable = .{ + .name = "RESET_SYSTEM", + .short_description = "Reset system state.", + .long_description = std.fmt.comptimePrint( + \\Reset system: + \\ - Deinitialize all Carriers. + \\ - Clear all system errors. + \\ - Reset all push and pull states. + , .{}), + .execute = &commands.reset_system.impl, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .executable = .{ + .name = "WAIT_INITIALIZE", + .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 initialization complete.", + .long_description = std.fmt.comptimePrint( + \\Pauses command execution until specified Carrier completes initialization. + \\Optional: Provide timeout. Returns error if specified timeout is exceeded. + \\Timeout must be provided in {s}. + \\ + \\Example: Wait for initialization of Carrier "3" on Line "line1". + \\WAIT_INITIALIZE line1 3 + \\ + \\Example: Wait max 5000 {s} for initialization of Carrier "3" on + \\Line "line1". + \\WAIT_INITIALIZE line1 3 5000 + , .{ + standard.time.unit_long, + standard.time.unit_short, + }), + .execute = &commands.wait.isolate, + } }, + .{ .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 = "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 = "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 }, + .{ .name = "Axis", .kind = .mmc_client_axis }, + .{ .name = "direction", .kind = .mmc_client_direction }, + .{ + .name = "Carrier", + .optional = true, + .kind = .mmc_client_carrier, + }, + }, + .short_description = "Push Carrier on the specified Axis.", + .long_description = std.fmt.comptimePrint( + \\Push a Carrier on the specified Axis. This movement targets a + \\distance of Carrier length, and thus if it is used to cross a Line + \\boundary, the receiving Axis at the destination Line must be in + \\pulling state. Direction must be provided as: + \\- forward (direction of increasing Axis number) + \\- backward (direction of decreasing Axis number) + \\ + \\Optional: Provide Carrier to move the specified Carrier to the center + \\of the specified Axis, then push it according to direction. + \\ + \\Example: Push Carrier on Axis "3" to Axis "4". If Line "line1" only has + \\3 Axes, push Carrier out from Line "line1" to Line "line2". + \\PUSH_CARRIER line1 3 forward + \\ + \\Example: Move Carrier "2" to Axis "3" and transition to push movement to + \\Axis "4". If Line "line1" only has 3 Axes, then transition to push movement + \\out from Line "line1" to Line "line2". + \\PUSH_CARRIER line1 3 forward 2 + , .{}), + .execute = &commands.push.impl, + } }, + .{ .executable = .{ + .name = "PULL_CARRIER", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Axis", .kind = .mmc_client_axis }, + .{ .name = "Carrier", .kind = .mmc_client_carrier }, + .{ .name = "direction", .kind = .mmc_client_direction }, + .{ .name = "location", .optional = true }, + .{ .name = "CAS", .optional = true, .kind = .mmc_client_cas }, + }, + .short_description = "Pull incoming Carrier.", + .long_description = std.fmt.comptimePrint( + \\Sets the specified Axis to a pulling state, enabling Axis to initialize + \\and move incoming carrier to specified Axis. The pulled Carrier is + \\assigned with the specified Carrier ID. There must be no Carrier on + \\pulling Axis upon invocation. Direction must be provided as: + \\ - forward (direction of increasing Axis number) + \\ - backward (direction of decreasing Axis number) + \\ + \\Optional: Provide location to move Carrier after completed pulling + \\Carrier. Location must be provided as: + \\- {s} (move Carrier to specified location after pulled to + \\ specified Axis), or + \\- "nan" (Carrier can move through external force after pulled to + \\ specified Axis). + \\ + \\Optional: Provide "on" or "off" to specify CAS (Collision + \\Avoidance System) activation (enabled by default) while Carrier is + \\being moved to specified location. + \\ + \\Example: Pull Carrier onto Axis "1" on Line "line2" from Line "line1" and + \\assign Carrier ID to "123". + \\PULL_CARRIER line2 1 123 forward + \\ + \\Example: Pull Carrier to Line "line2" from Line "line1", assign Carrier ID + \\to "123" and move Carrier "123" to location 1500 {s} upon recognized on + \\Line "line2". + \\PULL_CARRIER line2 1 123 forward 1500 + \\ + \\Example: Pull Carrier to Line "line2" from Line "line1", assign Carrier ID + \\to "123", and move Carrier "123" to location 1500 {s} with CAS deactivated + \\upon recognized on Line "line2". + \\PULL_CARRIER line2 1 123 forward 1500 off + , .{ + standard.length.unit_long, + standard.length.unit_short, + standard.length.unit_short, + }), + .execute = &commands.pull.impl, + } }, + .{ .executable = .{ + .name = "STOP_PULL_CARRIER", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "filter", .optional = true, .kind = .mmc_client_filter }, + }, + .short_description = "Stop pulling Carrier at axis.", + .long_description = std.fmt.comptimePrint( + \\Stop active Carrier pull of specified Line. + \\Optional: Provide filter to specify selection of pull. 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: Stop pull Carrier(s) on Line "line1". + \\STOP_PULL_CARRIER line1 + \\ + \\Example: Stop pull for Axis "1" on Line "line1". + \\STOP_PULL_CARRIER line1 1a + , .{}), + .execute = &commands.stop_pull.impl, + } }, + .{ .executable = .{ + .name = "STOP_PUSH_CARRIER", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "filter", .optional = true, .kind = .mmc_client_filter }, + }, + .short_description = "Stop pushing Carrier at axis.", + .long_description = std.fmt.comptimePrint( + \\Stop active Carrier push on specified Line. + \\Optional: Provide filter to specify selection of push. 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: Stop push Carrier(s) on Line "line1". + \\STOP_PUSH_CARRIER line1 + \\ + \\Example: Stop push for Axis "3" on Line "line1". + \\STOP_PUSH_CARRIER line1 3a + , .{}), + .execute = &commands.stop_push.impl, + } }, + .{ .executable = .{ + .name = "WAIT_AXIS_EMPTY", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "Axis", .kind = .mmc_client_axis }, + .{ .name = "timeout", .optional = true }, + }, + .short_description = "Wait until no Carrier on Axis.", + .long_description = std.fmt.comptimePrint( + \\Pause execution of commands until specified Axis has: + \\ - no carriers, + \\ - no active hall alarms, + \\ - no wait for push/pull. + \\Optional: timeout will return error if timeout duration is exceeded. + \\Timeout duration must be provided in {s}. + \\ + \\Example: Wait until no Carrier on Axis "2" on Line "line1". + \\WAIT_AXIS_EMPTY line1 2 + \\ + \\Example: Wait max 5000 {s} until no Carrier on Axis "2" on Line "line1". + \\WAIT_AXIS_EMPTY line1 2 5000 + , .{ + standard.time.unit_long, + standard.time.unit_short, + }), + .execute = &commands.wait.axisEmpty, + } }, + .{ .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 = "START_LOG_INFO", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "duration" }, + .{ .name = "path", .optional = true }, + }, + .short_description = "Start logging.", + .long_description = std.fmt.comptimePrint( + \\Start logging process. Log file contains only recent data during specified + \\time (in seconds). Logging runs until: + \\ - error occurs or + \\ - cancelled by "STOP_LOGGING" command. + \\ + \\If path not specified, log file will be created in working directory: + \\ - "mmc-logging-YYYY.MM.DD-HH.MM.SS.csv". + \\ + \\Example: Start logging process and provide logging data for a duration of + \\10 s before logging is stopped. + \\START_LOG_INFO 10 + \\ + \\Example: Start logging process and save logging file at + \\"folder/log_info.csv". + \\START_LOG_INFO 10 folder/log_info.csv + , .{}), + .execute = &commands.log.start, + } }, + .{ .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 = "STATUS_LOG_INFO", + .short_description = "Show logging configuration.", + .long_description = std.fmt.comptimePrint( + \\Show logging configuration for specified Line(s). + , .{}), + .execute = &commands.log.status, + } }, + .{ .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, + } }, + .{ .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, + } }, + .{ .executable = .{ + .name = "PRINT_ERRORS", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + .{ .name = "filter", .optional = true, .kind = .mmc_client_filter }, + }, + .short_description = "Print Axis and Driver errors.", + .long_description = std.fmt.comptimePrint( + \\Print Axis and Driver errors on specified Line. + \\Optional: Provide filter to specify selection of error(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: Print Axis and Driver errors on Line "line1". + \\PRINT_ERRORS line1 + \\ + \\Example: Print errors on Axis "3" on Line "line1". + \\PRINT_ERRORS line1 3a + , .{}), + .execute = &commands.show_errors.impl, + } }, + .{ .executable = .{ + .name = "STOP", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line, .optional = true }, + }, + .short_description = "Stop all processes.", + .long_description = std.fmt.comptimePrint( + \\Stop all running and queued processes on System. + \\Optional: Stop all running and queued processes only on specified Line. + , .{}), + .execute = &commands.stop.impl, + } }, + .{ .executable = .{ + .name = "PAUSE", + .parameters = &[_]command.Command.Executable.Parameter{ + .{ .name = "Line", .kind = .mmc_client_line }, + }, + .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 }, + }, + .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 }, + }, + .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, + +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 + 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, + }; + errdefer current = null; + + current.?.config = .{ + .host = try current.?.allocator.dupe(u8, c.host), + .port = c.port, + }; + errdefer current.allocator.free(current.?.config.host); + current.?.parameter = .init(current.?.allocator); + errdefer current.?.parameter.deinit(); +} + +pub fn deinit() void { + if (current == null) return; + commands.disconnect.impl(&.{}) catch {}; + get().parameter.deinit(); + get().allocator.free(get().config.host); + current = null; + if (debug_allocator.detectLeaks()) { + std.log.debug("Leaks detected", .{}); + } + if (builtin.os.tag == .windows) std.os.windows.WSACleanup() catch return; +} + +pub fn matchLine(name: []const u8) !usize { + for (get().lines) |line| { + if (std.mem.eql(u8, line.name, name)) return line.index; + } else return error.LineNameNotFound; +} + +/// Track a command until it executed completely followed by removing that +/// command from the server. +pub fn waitCommandCompleted(gpa: std.mem.Allocator, net: zignet.Socket) !void { + const command_id = command_id: { + // If command is cancelled while fetching the command ID, client has to + // keep waiting until command ID response is arrived. Client forces + // command removal even cancelled in this block, thus multithreading is + // implemented in this block. + // TODO: When upgrading to zig 0.16.0, implement the new concurrent method. + var cancel: std.atomic.Value(bool) = .init(false); + var finish: std.atomic.Value(bool) = .init(false); + const cancel_thread: std.Thread = try .spawn( + .{}, + checkInterrupt, + .{ finish, &cancel }, + ); + var decoded = getResponse(gpa, net) catch |err| { + // It is impossible to remove command from the server if the + // connection is suddenly closed during reading this response. + // + // TODO: This block means there is no way to literally remove the + // command from the queue. It seems we have to manually clear it up + // in the server side. Give timeout to command queue in the server? + finish.store(true, .monotonic); + // Expected to be finished. Ensure no dangling thread. + cancel_thread.join(); + return err; + }; + defer decoded.deinit(gpa); + finish.store(true, .monotonic); + // Expected to be finished. Ensure no dangling thread. + cancel_thread.join(); + const id = switch (decoded.body orelse return error.InvalidResponse) { + .request_error => |req_err| { + return error_response.throwMmcError(req_err); + }, + .command => |command_resp| switch (command_resp.body orelse + return error.InvalidResponse) { + .id => |id| id, + .request_error => |req_err| { + return error_response.throwCommandError(req_err); + }, + else => return error.InvalidResponse, + }, + else => return error.InvalidResponse, + }; + if (cancel.load(.monotonic)) { + try removeCommand(id); + return error.CommandStopped; + } else break :command_id id; + }; + defer removeCommand(command_id) catch {}; + while (true) { + try command.checkCommandInterrupt(); + + const request: api.protobuf.mmc.Request = .{ + .body = .{ + .info = .{ + .body = .{ + .command = .{ .id = command_id }, + }, + }, + }, + }; + 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| { + return error_response.throwMmcError(req_err); + }, + .info => |info_resp| switch (info_resp.body orelse + return error.InvalidResponse) { + .command => |commands_resp| commands_resp, + .request_error => |req_err| { + return error_response.throwInfoError(req_err); + }, + else => return error.InvalidResponse, + }, + else => return error.InvalidResponse, + }; + const comm = commands_resp.items.pop() orelse + return error.InvalidResponse; + switch (comm.status) { + .COMMAND_STATUS_PROGRESSING => {}, // continue the loop + .COMMAND_STATUS_COMPLETED => break, + .COMMAND_STATUS_FAILED => { + return switch (comm.@"error".?) { + .COMMAND_ERROR_INVALID_SYSTEM_STATE => error.InvalidSystemState, + .COMMAND_ERROR_DRIVER_DISCONNECTED => error.DriverDisconnected, + .COMMAND_ERROR_UNEXPECTED => error.Unexpected, + .COMMAND_ERROR_CARRIER_NOT_FOUND => error.CarrierNotFound, + .COMMAND_ERROR_CONFLICTING_CARRIER_ID => error.ConflictingCarrierId, + .COMMAND_ERROR_CARRIER_ALREADY_INITIALIZED => error.CarrierAlreadyInitialized, + .COMMAND_ERROR_INVALID_CARRIER_TARGET => error.InvalidCarrierTarget, + .COMMAND_ERROR_DRIVER_STOPPED => error.DriverStopped, + else => error.UnexpectedResponse, + }; + }, + else => return error.UnexpectedResponse, + } + } +} + +/// Send request to server. +pub fn sendRequest( + /// Internally used by zig-protobuf. + gpa: std.mem.Allocator, + net: zignet.Socket, + request: api.protobuf.mmc.Request, +) !void { + var writer_buf: [4096]u8 = undefined; + var net_writer = net.writer(&writer_buf); + try request.encode(&net_writer.interface, gpa); + net_writer.interface.flush() catch { + if (net_writer.error_state) |err| { + commands.disconnect.impl(&.{}) catch {}; + return err; + } + }; +} + +/// Wait until response is received and decode the message. The caller is +/// responsible to free the decoded message. +/// TODO: This implementation can now use a blocking socket call. Command that +/// is cancellable is only command that require to remove command from server. +pub fn getResponse( + gpa: std.mem.Allocator, + net: zignet.Socket, +) !api.protobuf.mmc.Response { + var reader_buf: [4096]u8 = undefined; + var net_reader = net.reader(&reader_buf); + while (true) { + if (net_reader.interface.peekByte()) |_| { + break; + } else |e| { + switch (e) { + std.Io.Reader.Error.EndOfStream => continue, + std.Io.Reader.Error.ReadFailed => { + switch (net_reader.error_state orelse error.Unexpected) { + else => |err| { + commands.disconnect.impl(&.{}) catch {}; + return err; + }, + } + }, + } + } + } + var proto_reader: std.Io.Reader = .fixed(net_reader.interface.buffered()); + return try .decode(&proto_reader, gpa); +} + +fn removeCommand(id: u32) !void { + const net = if (get().sock) |net| net else return error.ServerNotConnected; + const request: api.protobuf.mmc.Request = .{ + .body = .{ + .command = .{ + .body = .{ + .remove_command = .{ .command = id }, + }, + }, + }, + }; + 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 + return error.InvalidResponse) { + .removed_id => |removed_id| removed_id, + .request_error => |req_err| { + return error_response.throwCommandError(req_err); + }, + else => return error.InvalidResponse, + }, + .request_error => |req_err| { + return error_response.throwMmcError(req_err); + }, + else => return error.InvalidResponse, + }; + std.log.debug("removed_id {}, id {}", .{ removed_id, id }); +} + +pub fn nestedWrite( + name: []const u8, + val: anytype, + indent: usize, + w: *std.Io.Writer, +) !usize { + var written_bytes: usize = 0; + const ti = @typeInfo(@TypeOf(val)); + switch (ti) { + .optional => { + if (val) |v| { + written_bytes += try nestedWrite( + name, + v, + indent, + w, + ); + } else { + try w.splatBytesAll(" ", indent); + written_bytes += 4 * indent; + try w.print("{s}: ", .{name}); + written_bytes += name.len + 2; + try w.print("None,\n", .{}); + written_bytes += std.fmt.count("None,\n", .{}); + } + }, + .@"struct" => { + try w.splatBytesAll(" ", indent); + written_bytes += 4 * indent; + try w.print("{s}: {{\n", .{name}); + written_bytes += name.len + 4; + inline for (ti.@"struct".fields) |field| { + if (field.name[0] == '_') { + continue; + } + written_bytes += try nestedWrite( + field.name, + @field(val, field.name), + indent + 1, + w, + ); + } + try w.splatBytesAll(" ", indent); + written_bytes += 4 * indent; + try w.writeAll("},\n"); + written_bytes += 3; + }, + .bool, .int => { + try w.splatBytesAll(" ", indent); + written_bytes += 4 * indent; + try w.print("{s}: ", .{name}); + written_bytes += name.len + 2; + try w.print("{},\n", .{val}); + written_bytes += std.fmt.count("{},\n", .{val}); + }, + .float => { + try w.splatBytesAll(" ", indent); + written_bytes += 4 * indent; + try w.print("{s}: ", .{name}); + written_bytes += name.len + 2; + try w.print("{d},\n", .{val}); + written_bytes += std.fmt.count("{d},\n", .{val}); + }, + .@"enum" => { + try w.splatBytesAll(" ", indent); + written_bytes += 4 * indent; + try w.print("{s}: ", .{name}); + written_bytes += name.len + 2; + try w.print("{t},\n", .{val}); + written_bytes += std.fmt.count("{t},\n", .{val}); + }, + .@"union" => { + switch (val) { + inline else => |_, tag| { + const union_val = @field(val, @tagName(tag)); + try w.splatBytesAll(" ", indent); + written_bytes += 4 * indent; + try w.print("{s}: ", .{name}); + written_bytes += name.len + 2; + try w.print("{d},\n", .{union_val}); + written_bytes += std.fmt.count("{d},\n", .{union_val}); + }, + } + }, + else => { + error.InvalidValueType; + }, + } + return written_bytes; +} + +/// Looping to check command interrupt. Returned if other task is finished. +fn checkInterrupt( + /// Flag to check if task on other thread is finished. + finish: std.atomic.Value(bool), + /// Flag to let the caller know that interrupt is detected. + cancel: *std.atomic.Value(bool), +) void { + while (finish.load(.monotonic)) { + command.checkCommandInterrupt() catch { + cancel.store(true, .monotonic); + return; + }; + } +} 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) { diff --git a/src/modules/mmc_client.zig b/src/modules/mmc_client.zig deleted file mode 100644 index 6555166a..00000000 --- a/src/modules/mmc_client.zig +++ /dev/null @@ -1,1603 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -const chrono = @import("chrono"); - -const command = @import("../command.zig"); -const commands = @import("mmc_client/commands.zig"); -pub const Parameter = @import("mmc_client/Parameter.zig"); -pub const Line = @import("mmc_client/Line.zig"); -pub const log = @import("mmc_client/log.zig"); -pub const zignet = @import("zignet"); -pub const api = @import("mmc-api"); - -pub const Config = struct { - host: []u8, - 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, - }; - } -}; -pub var parameter: Parameter = undefined; - -/// `lines` is initialized once the client is connected to a server. -/// Deinitialized once disconnected from a server. -pub var 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; -/// Currently connected socket. Nulled when disconnect. -pub var sock: ?zignet.Socket = 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. -pub var endpoint: ?zignet.Endpoint = null; -pub var allocator: std.mem.Allocator = undefined; - -/// Store the configuration. -pub var config: Config = undefined; - -var debug_allocator = std.heap.DebugAllocator(.{}){}; - -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), - .port = c.port, - }; - errdefer allocator.free(config.host); - parameter = .init(allocator); - errdefer parameter.deinit(); - - try command.registry.put(.{ .executable = .{ - .name = "SERVER_VERSION", - .short_description = "Display the connected MMC server version.", - .long_description = - \\Display the version of the currently connected MMC server in Semantic - \\Version format ({major}.{minor}.{patch}). - , - .execute = &commands.server_version.impl, - } }); - errdefer command.registry.orderedRemove("SERVER_VERSION"); - try command.registry.put(.{ .executable = .{ - .name = "CONNECT", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "endpoint", .optional = true }, - }, - .short_description = "Connect to MMC server.", - .long_description = std.fmt.comptimePrint( - \\Attempt to connect to MMC server. - \\ - \\Endpoint may be specified using one of the following formats: - \\ - `HOSTNAME:PORT` - \\ - `IPv4_ADDRESS:PORT` - \\ - `[IPv6_ADDRESS%SCOPE]:PORT` - \\ - \\If no endpoint provided, last connected server is used. If no server has - \\been connected since `LOAD_CONFIG`, connect to the default endpoint - \\provided in the configuration file. - , .{}), - .execute = &commands.connect.impl, - } }); - errdefer command.registry.orderedRemove("CONNECT"); - try command.registry.put(.{ .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, - }, - }); - 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, - }, - }); - 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, - }, - }); - 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, - }, - }); - 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, - }, - }); - 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, - }, - }); - 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 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 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 = "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, - }, - }); - 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 = "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, - }, - }); - 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 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 = "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 = "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 = "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 = .{ - .name = "RESET_SYSTEM", - .short_description = "Reset system state.", - .long_description = std.fmt.comptimePrint( - \\Reset system: - \\ - Deinitialize all Carriers. - \\ - Clear all system errors. - \\ - 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, - }, - }, - .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 = "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, - }, - }); - 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, - }, - }); - 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 = "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 = .{ - .name = "WAIT_INITIALIZE", - .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 initialization complete.", - .long_description = std.fmt.comptimePrint( - \\Pauses command execution until specified Carrier completes initialization. - \\Optional: Provide timeout. Returns error if specified timeout is exceeded. - \\Timeout must be provided in {s}. - \\ - \\Example: Wait for initialization of Carrier "3" on Line "line1". - \\WAIT_INITIALIZE line1 3 - \\ - \\Example: Wait max 5000 {s} for initialization of Carrier "3" on - \\Line "line1". - \\WAIT_INITIALIZE line1 3 5000 - , .{ - standard.time.unit_long, - 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, - }, - }); - 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 = "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, - }, - }); - errdefer command.registry.orderedRemove("MOVE_CARRIER"); - try command.registry.put(.{ .executable = .{ - .name = "PUSH_CARRIER", - .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", - .optional = true, - .kind = .mmc_client_carrier, - }, - }, - .short_description = "Push Carrier on the specified Axis.", - .long_description = std.fmt.comptimePrint( - \\Push a Carrier on the specified Axis. This movement targets a - \\distance of Carrier length, and thus if it is used to cross a Line - \\boundary, the receiving Axis at the destination Line must be in - \\pulling state. Direction must be provided as: - \\- forward (direction of increasing Axis number) - \\- backward (direction of decreasing Axis number) - \\ - \\Optional: Provide Carrier to move the specified Carrier to the center - \\of the specified Axis, then push it according to direction. - \\ - \\Example: Push Carrier on Axis "3" to Axis "4". If Line "line1" only has - \\3 Axes, push Carrier out from Line "line1" to Line "line2". - \\PUSH_CARRIER line1 3 forward - \\ - \\Example: Move Carrier "2" to Axis "3" and transition to push movement to - \\Axis "4". If Line "line1" only has 3 Axes, then transition to push movement - \\out from Line "line1" to Line "line2". - \\PUSH_CARRIER line1 3 forward 2 - , .{}), - .execute = &commands.push.impl, - } }); - errdefer command.registry.orderedRemove("PUSH_CARRIER"); - try command.registry.put(.{ .executable = .{ - .name = "PULL_CARRIER", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Axis", .kind = .mmc_client_axis }, - .{ .name = "Carrier", .kind = .mmc_client_carrier }, - .{ .name = "direction", .kind = .mmc_client_direction }, - .{ .name = "location", .optional = true }, - .{ .name = "CAS", .optional = true, .kind = .mmc_client_cas }, - }, - .short_description = "Pull incoming Carrier.", - .long_description = std.fmt.comptimePrint( - \\Sets the specified Axis to a pulling state, enabling Axis to initialize - \\and move incoming carrier to specified Axis. The pulled Carrier is - \\assigned with the specified Carrier ID. There must be no Carrier on - \\pulling Axis upon invocation. Direction must be provided as: - \\ - forward (direction of increasing Axis number) - \\ - backward (direction of decreasing Axis number) - \\ - \\Optional: Provide location to move Carrier after completed pulling - \\Carrier. Location must be provided as: - \\- {s} (move Carrier to specified location after pulled to - \\ specified Axis), or - \\- "nan" (Carrier can move through external force after pulled to - \\ specified Axis). - \\ - \\Optional: Provide "on" or "off" to specify CAS (Collision - \\Avoidance System) activation (enabled by default) while Carrier is - \\being moved to specified location. - \\ - \\Example: Pull Carrier onto Axis "1" on Line "line2" from Line "line1" and - \\assign Carrier ID to "123". - \\PULL_CARRIER line2 1 123 forward - \\ - \\Example: Pull Carrier to Line "line2" from Line "line1", assign Carrier ID - \\to "123" and move Carrier "123" to location 1500 {s} upon recognized on - \\Line "line2". - \\PULL_CARRIER line2 1 123 forward 1500 - \\ - \\Example: Pull Carrier to Line "line2" from Line "line1", assign Carrier ID - \\to "123", and move Carrier "123" to location 1500 {s} with CAS deactivated - \\upon recognized on Line "line2". - \\PULL_CARRIER line2 1 123 forward 1500 off - , .{ - standard.length.unit_long, - standard.length.unit_short, - standard.length.unit_short, - }), - .execute = &commands.pull.impl, - } }); - errdefer command.registry.orderedRemove("PULL_CARRIER"); - try command.registry.put(.{ .executable = .{ - .name = "STOP_PULL_CARRIER", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "filter", .optional = true, .kind = .mmc_client_filter }, - }, - .short_description = "Stop pulling Carrier at axis.", - .long_description = std.fmt.comptimePrint( - \\Stop active Carrier pull of specified Line. - \\Optional: Provide filter to specify selection of pull. 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: Stop pull Carrier(s) on Line "line1". - \\STOP_PULL_CARRIER line1 - \\ - \\Example: Stop pull for Axis "1" on Line "line1". - \\STOP_PULL_CARRIER line1 1a - , .{}), - .execute = &commands.stop_pull.impl, - } }); - errdefer command.registry.orderedRemove("STOP_PULL_CARRIER"); - try command.registry.put(.{ .executable = .{ - .name = "STOP_PUSH_CARRIER", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "filter", .optional = true, .kind = .mmc_client_filter }, - }, - .short_description = "Stop pushing Carrier at axis.", - .long_description = std.fmt.comptimePrint( - \\Stop active Carrier push on specified Line. - \\Optional: Provide filter to specify selection of push. 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: Stop push Carrier(s) on Line "line1". - \\STOP_PUSH_CARRIER line1 - \\ - \\Example: Stop push for Axis "3" on Line "line1". - \\STOP_PUSH_CARRIER line1 3a - , .{}), - .execute = &commands.stop_push.impl, - } }); - errdefer command.registry.orderedRemove("STOP_PUSH_CARRIER"); - try command.registry.put(.{ .executable = .{ - .name = "WAIT_AXIS_EMPTY", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "Axis", .kind = .mmc_client_axis }, - .{ .name = "timeout", .optional = true }, - }, - .short_description = "Wait until no Carrier on Axis.", - .long_description = std.fmt.comptimePrint( - \\Pause execution of commands until specified Axis has: - \\ - no carriers, - \\ - no active hall alarms, - \\ - no wait for push/pull. - \\Optional: timeout will return error if timeout duration is exceeded. - \\Timeout duration must be provided in {s}. - \\ - \\Example: Wait until no Carrier on Axis "2" on Line "line1". - \\WAIT_AXIS_EMPTY line1 2 - \\ - \\Example: Wait max 5000 {s} until no Carrier on Axis "2" on Line "line1". - \\WAIT_AXIS_EMPTY line1 2 5000 - , .{ - standard.time.unit_long, - 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, - }, - }); - errdefer command.registry.orderedRemove("ADD_LOG_INFO"); - try command.registry.put(.{ .executable = .{ - .name = "START_LOG_INFO", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "duration" }, - .{ .name = "path", .optional = true }, - }, - .short_description = "Start logging.", - .long_description = std.fmt.comptimePrint( - \\Start logging process. Log file contains only recent data during specified - \\time (in seconds). Logging runs until: - \\ - error occurs or - \\ - cancelled by "STOP_LOGGING" command. - \\ - \\If path not specified, log file will be created in working directory: - \\ - "mmc-logging-YYYY.MM.DD-HH.MM.SS.csv". - \\ - \\Example: Start logging process and provide logging data for a duration of - \\10 s before logging is stopped. - \\START_LOG_INFO 10 - \\ - \\Example: Start logging process and save logging file at - \\"folder/log_info.csv". - \\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, - }, - }); - errdefer command.registry.orderedRemove("REMOVE_LOG_INFO"); - try command.registry.put(.{ .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 = .{ - .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 = .{ - .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 = .{ - .name = "PRINT_ERRORS", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line }, - .{ .name = "filter", .optional = true, .kind = .mmc_client_filter }, - }, - .short_description = "Print Axis and Driver errors.", - .long_description = std.fmt.comptimePrint( - \\Print Axis and Driver errors on specified Line. - \\Optional: Provide filter to specify selection of error(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: Print Axis and Driver errors on Line "line1". - \\PRINT_ERRORS line1 - \\ - \\Example: Print errors on Axis "3" on Line "line1". - \\PRINT_ERRORS line1 3a - , .{}), - .execute = &commands.show_errors.impl, - } }); - errdefer command.registry.orderedRemove("PRINT_ERRORS"); - try command.registry.put(.{ .executable = .{ - .name = "STOP", - .parameters = &[_]command.Command.Executable.Parameter{ - .{ .name = "Line", .kind = .mmc_client_line, .optional = true }, - }, - .short_description = "Stop all processes.", - .long_description = std.fmt.comptimePrint( - \\Stop all running and queued processes on System. - \\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, - }, - }); - 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, - }, - }); - 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, - }, - }); - errdefer command.registry.orderedRemove("SET_CARRIER_ID"); -} - -pub fn deinit() void { - commands.disconnect.impl(&.{}) catch {}; - parameter.deinit(); - allocator.free(config.host); - if (debug_allocator.detectLeaks()) { - std.log.debug("Leaks detected", .{}); - } - if (builtin.os.tag == .windows) std.os.windows.WSACleanup() catch return; -} - -pub fn matchLine(name: []const u8) !usize { - for (lines) |line| { - if (std.mem.eql(u8, line.name, name)) return line.index; - } else return error.LineNameNotFound; -} - -/// Track a command until it executed completely followed by removing that -/// command from the server. -pub fn waitCommandCompleted(gpa: std.mem.Allocator, net: zignet.Socket) !void { - const command_id = command_id: { - // If command is cancelled while fetching the command ID, client has to - // keep waiting until command ID response is arrived. Client forces - // command removal even cancelled in this block, thus multithreading is - // implemented in this block. - // TODO: When upgrading to zig 0.16.0, implement the new concurrent method. - var cancel: std.atomic.Value(bool) = .init(false); - var finish: std.atomic.Value(bool) = .init(false); - const cancel_thread: std.Thread = try .spawn( - .{}, - checkInterrupt, - .{ finish, &cancel }, - ); - var decoded = getResponse(gpa, net) catch |err| { - // It is impossible to remove command from the server if the - // connection is suddenly closed during reading this response. - // - // TODO: This block means there is no way to literally remove the - // command from the queue. It seems we have to manually clear it up - // in the server side. Give timeout to command queue in the server? - finish.store(true, .monotonic); - // Expected to be finished. Ensure no dangling thread. - cancel_thread.join(); - return err; - }; - defer decoded.deinit(gpa); - finish.store(true, .monotonic); - // Expected to be finished. Ensure no dangling thread. - cancel_thread.join(); - const id = switch (decoded.body orelse return error.InvalidResponse) { - .request_error => |req_err| { - return error_response.throwMmcError(req_err); - }, - .command => |command_resp| switch (command_resp.body orelse - return error.InvalidResponse) { - .id => |id| id, - .request_error => |req_err| { - return error_response.throwCommandError(req_err); - }, - else => return error.InvalidResponse, - }, - else => return error.InvalidResponse, - }; - if (cancel.load(.monotonic)) { - try removeCommand(id); - return error.CommandStopped; - } else break :command_id id; - }; - defer removeCommand(command_id) catch {}; - while (true) { - try command.checkCommandInterrupt(); - - const request: api.protobuf.mmc.Request = .{ - .body = .{ - .info = .{ - .body = .{ - .command = .{ .id = command_id }, - }, - }, - }, - }; - try sendRequest(allocator, net, request); - var decoded = try getResponse(allocator, net); - defer decoded.deinit(allocator); - var commands_resp = switch (decoded.body orelse - return error.InvalidResponse) { - .request_error => |req_err| { - return error_response.throwMmcError(req_err); - }, - .info => |info_resp| switch (info_resp.body orelse - return error.InvalidResponse) { - .command => |commands_resp| commands_resp, - .request_error => |req_err| { - return error_response.throwInfoError(req_err); - }, - else => return error.InvalidResponse, - }, - else => return error.InvalidResponse, - }; - const comm = commands_resp.items.pop() orelse - return error.InvalidResponse; - switch (comm.status) { - .COMMAND_STATUS_PROGRESSING => {}, // continue the loop - .COMMAND_STATUS_COMPLETED => break, - .COMMAND_STATUS_FAILED => { - return switch (comm.@"error".?) { - .COMMAND_ERROR_INVALID_SYSTEM_STATE => error.InvalidSystemState, - .COMMAND_ERROR_DRIVER_DISCONNECTED => error.DriverDisconnected, - .COMMAND_ERROR_UNEXPECTED => error.Unexpected, - .COMMAND_ERROR_CARRIER_NOT_FOUND => error.CarrierNotFound, - .COMMAND_ERROR_CONFLICTING_CARRIER_ID => error.ConflictingCarrierId, - .COMMAND_ERROR_CARRIER_ALREADY_INITIALIZED => error.CarrierAlreadyInitialized, - .COMMAND_ERROR_INVALID_CARRIER_TARGET => error.InvalidCarrierTarget, - .COMMAND_ERROR_DRIVER_STOPPED => error.DriverStopped, - else => error.UnexpectedResponse, - }; - }, - else => return error.UnexpectedResponse, - } - } -} - -/// Send request to server. -pub fn sendRequest( - /// Internally used by zig-protobuf. - gpa: std.mem.Allocator, - net: zignet.Socket, - request: api.protobuf.mmc.Request, -) !void { - var writer_buf: [4096]u8 = undefined; - var net_writer = net.writer(&writer_buf); - try request.encode(&net_writer.interface, gpa); - net_writer.interface.flush() catch { - if (net_writer.error_state) |err| { - commands.disconnect.impl(&.{}) catch {}; - return err; - } - }; -} - -/// Wait until response is received and decode the message. The caller is -/// responsible to free the decoded message. -/// TODO: This implementation can now use a blocking socket call. Command that -/// is cancellable is only command that require to remove command from server. -pub fn getResponse( - gpa: std.mem.Allocator, - net: zignet.Socket, -) !api.protobuf.mmc.Response { - var reader_buf: [4096]u8 = undefined; - var net_reader = net.reader(&reader_buf); - while (true) { - if (net_reader.interface.peekByte()) |_| { - break; - } else |e| { - switch (e) { - std.Io.Reader.Error.EndOfStream => continue, - std.Io.Reader.Error.ReadFailed => { - switch (net_reader.error_state orelse error.Unexpected) { - else => |err| { - commands.disconnect.impl(&.{}) catch {}; - return err; - }, - } - }, - } - } - } - var proto_reader: std.Io.Reader = .fixed(net_reader.interface.buffered()); - return try .decode(&proto_reader, gpa); -} - -fn removeCommand(id: u32) !void { - const net = if (sock) |net| net else return error.ServerNotConnected; - const request: api.protobuf.mmc.Request = .{ - .body = .{ - .command = .{ - .body = .{ - .remove_command = .{ .command = id }, - }, - }, - }, - }; - try sendRequest(allocator, net, request); - var decoded = try getResponse(allocator, net); - defer decoded.deinit(allocator); - const removed_id = switch (decoded.body orelse - return error.InvalidResponse) { - .command => |command_resp| switch (command_resp.body orelse - return error.InvalidResponse) { - .removed_id => |removed_id| removed_id, - .request_error => |req_err| { - return error_response.throwCommandError(req_err); - }, - else => return error.InvalidResponse, - }, - .request_error => |req_err| { - return error_response.throwMmcError(req_err); - }, - else => return error.InvalidResponse, - }; - std.log.debug("removed_id {}, id {}", .{ removed_id, id }); -} - -pub fn nestedWrite( - name: []const u8, - val: anytype, - indent: usize, - w: *std.Io.Writer, -) !usize { - var written_bytes: usize = 0; - const ti = @typeInfo(@TypeOf(val)); - switch (ti) { - .optional => { - if (val) |v| { - written_bytes += try nestedWrite( - name, - v, - indent, - w, - ); - } else { - try w.splatBytesAll(" ", indent); - written_bytes += 4 * indent; - try w.print("{s}: ", .{name}); - written_bytes += name.len + 2; - try w.print("None,\n", .{}); - written_bytes += std.fmt.count("None,\n", .{}); - } - }, - .@"struct" => { - try w.splatBytesAll(" ", indent); - written_bytes += 4 * indent; - try w.print("{s}: {{\n", .{name}); - written_bytes += name.len + 4; - inline for (ti.@"struct".fields) |field| { - if (field.name[0] == '_') { - continue; - } - written_bytes += try nestedWrite( - field.name, - @field(val, field.name), - indent + 1, - w, - ); - } - try w.splatBytesAll(" ", indent); - written_bytes += 4 * indent; - try w.writeAll("},\n"); - written_bytes += 3; - }, - .bool, .int => { - try w.splatBytesAll(" ", indent); - written_bytes += 4 * indent; - try w.print("{s}: ", .{name}); - written_bytes += name.len + 2; - try w.print("{},\n", .{val}); - written_bytes += std.fmt.count("{},\n", .{val}); - }, - .float => { - try w.splatBytesAll(" ", indent); - written_bytes += 4 * indent; - try w.print("{s}: ", .{name}); - written_bytes += name.len + 2; - try w.print("{d},\n", .{val}); - written_bytes += std.fmt.count("{d},\n", .{val}); - }, - .@"enum" => { - try w.splatBytesAll(" ", indent); - written_bytes += 4 * indent; - try w.print("{s}: ", .{name}); - written_bytes += name.len + 2; - try w.print("{t},\n", .{val}); - written_bytes += std.fmt.count("{t},\n", .{val}); - }, - .@"union" => { - switch (val) { - inline else => |_, tag| { - const union_val = @field(val, @tagName(tag)); - try w.splatBytesAll(" ", indent); - written_bytes += 4 * indent; - try w.print("{s}: ", .{name}); - written_bytes += name.len + 2; - try w.print("{d},\n", .{union_val}); - written_bytes += std.fmt.count("{d},\n", .{union_val}); - }, - } - }, - else => { - error.InvalidValueType; - }, - } - return written_bytes; -} - -/// Looping to check command interrupt. Returned if other task is finished. -fn checkInterrupt( - /// Flag to check if task on other thread is finished. - finish: std.atomic.Value(bool), - /// Flag to let the caller know that interrupt is detected. - cancel: *std.atomic.Value(bool), -) void { - while (finish.load(.monotonic)) { - command.checkCommandInterrupt() catch { - cancel.store(true, .monotonic); - return; - }; - } -} 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,