const std = @import("std"); const Config = @import("./Config.zig"); const lj = @import("./labjack.zig"); const RotCtl = @import("./RotCtl.zig"); const udev = @import("udev_rules"); const log = std.log.scoped(.main); fn printStderr(comptime fmt: []const u8, args: anytype) void { std.debug.print(fmt ++ "\n", args); } const commands = .{ .install_udev = "install-udev-rules", .write_config = "write-default-config", .run = "run", }; const command_help = .{ .install_udev = "[udev rules.d path]: Install the built-in LabJack u12 udev rules file. May require sudo privileges. Linux only.", .write_config = "[path]: write the default configuration to a json file.", .run = "[config path]: run the rotctl interface with the provided config.", }; pub fn main() !u8 { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const args = std.process.argsAlloc(allocator) catch { printStderr("Couldn't allocate arguments array", .{}); return 1; }; defer std.process.argsFree(allocator, args); if (args.len < 2) { printHelp(); return 1; } if (std.mem.eql(u8, args[1], commands.install_udev)) { if (args.len > 3) { printHelp(); return 1; } return installUdevRules(if (args.len == 3) args[2] else null); } else if (std.mem.eql(u8, args[1], commands.write_config)) { if (args.len > 3) { printHelp(); return 1; } Config.loadDefault(allocator); return writeDefaultConfig(if (args.len == 3) args[2] else null); } else if (std.mem.eql(u8, args[1], commands.run)) { if (args.len > 3) { printHelp(); return 1; } blk: { const confpath = if (args.len == 3) args[2] else "yaes.json"; const conf_file = std.fs.cwd().openFile(confpath, .{}) catch { log.warn("Could not load config file '{s}'. Using default config.", .{confpath}); Config.loadDefault(allocator); break :blk; }; defer conf_file.close(); Config.load(allocator, conf_file.reader(), std.io.getStdErr().writer()) catch |err| { log.err("Could not parse config file '{s}': {s}.", .{ confpath, @errorName(err) }); return 1; }; } defer Config.destroy(allocator); const ver = lj.getDriverVersion(); std.debug.print("Driver version: {d}\n", .{ver}); RotCtl.run(allocator) catch |err| { log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)}); return 1; }; return 0; } else { printHelp(); return 1; } } fn installUdevRules(outpath: ?[]const u8) u8 { const rules_path = outpath orelse "/etc/udev/rules.d"; var rules_d = std.fs.cwd().openDir(rules_path, .{}) catch |err| { printStderr( "could not open udev rules path '{s}': {s}", .{ rules_path, @errorName(err) }, ); return 1; }; defer rules_d.close(); rules_d.writeFile(.{ .sub_path = udev.rules_filename, .data = udev.rules }) catch |err| { printStderr( "could not write rules file '{s}{s}{s}': {s}", .{ rules_path, if (rules_path.len == 0) "./" else if (rules_path[rules_path.len - 1] == '/') "" else "/", udev.rules_filename, @errorName(err), }, ); return 1; }; return 0; } fn writeDefaultConfig(outarg: ?[]const u8) u8 { const outpath = outarg orelse "yaes.json"; const outfile = std.fs.cwd().createFile(outpath, .{}) catch |err| { printStderr("Could not write config file '{s}': {s}", .{ outpath, @errorName(err) }); return 1; }; defer outfile.close(); std.json.stringify(Config.global.*, .{ .whitespace = .indent_4 }, outfile.writer()) catch |err| { printStderr("Could not serialize config file '{s}': {s}", .{ outpath, @errorName(err) }); return 1; }; printStderr("config written to {s}", .{outpath}); return 0; } fn printHelp() void { printStderr( \\Usage: yaes [command] [args...] \\ \\ Control a Yaesu G-5500DC rotator through a LabJack U12 using the hamlib TCP interface. \\ \\Available commands: \\ , .{}); inline for (@typeInfo(@TypeOf(commands)).Struct.fields) |field| { printStderr( " {s} {s}", .{ @field(commands, field.name), @field(command_help, field.name) }, ); } }