Compare commits
No commits in common. "dbb076f69b5dd8b9b2b8dcad75ec7dc8b443de74" and "b0aac111a2a581c2d06fabc70ec7c6812031b844" have entirely different histories.
dbb076f69b
...
b0aac111a2
@ -22,12 +22,6 @@ pub fn build(b: *std.Build) void {
|
|||||||
.{ .target = target, .optimize = optimize, .use_udev = use_udev },
|
.{ .target = target, .optimize = optimize, .use_udev = use_udev },
|
||||||
);
|
);
|
||||||
exe.linkLibrary(ljacklm_dep.artifact("ljacklm"));
|
exe.linkLibrary(ljacklm_dep.artifact("ljacklm"));
|
||||||
exe.root_module.addImport(
|
|
||||||
"udev_rules",
|
|
||||||
b.addModule("udev_rules", .{
|
|
||||||
.root_source_file = b.path("deps/labjack/exodriver/udev_rules.zig"),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
b.installArtifact(exe);
|
b.installArtifact(exe);
|
||||||
}
|
}
|
||||||
|
2
deps/labjack/exodriver/udev_rules.zig
vendored
2
deps/labjack/exodriver/udev_rules.zig
vendored
@ -1,2 +0,0 @@
|
|||||||
pub const rules_filename = "90-labjack.rules";
|
|
||||||
pub const rules = @embedFile(rules_filename);
|
|
@ -8,7 +8,7 @@ const Config = @This();
|
|||||||
var global_internal: Config = undefined;
|
var global_internal: Config = undefined;
|
||||||
pub const global: *const Config = &global_internal;
|
pub const global: *const Config = &global_internal;
|
||||||
|
|
||||||
pub fn load(allocator: std.mem.Allocator, reader: anytype, err_writer: anytype) !void {
|
pub fn load(allocator: std.mem.Allocator, reader: anytype) !void {
|
||||||
var jread = std.json.Reader(1024, @TypeOf(reader)).init(allocator, reader);
|
var jread = std.json.Reader(1024, @TypeOf(reader)).init(allocator, reader);
|
||||||
defer jread.deinit();
|
defer jread.deinit();
|
||||||
|
|
||||||
@ -18,8 +18,6 @@ pub fn load(allocator: std.mem.Allocator, reader: anytype, err_writer: anytype)
|
|||||||
&jread,
|
&jread,
|
||||||
.{},
|
.{},
|
||||||
);
|
);
|
||||||
|
|
||||||
try global_internal.validate(err_writer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn loadDefault(allocator: std.mem.Allocator) void {
|
pub fn loadDefault(allocator: std.mem.Allocator) void {
|
||||||
@ -32,57 +30,6 @@ pub fn destroy(allocator: std.mem.Allocator) void {
|
|||||||
_ = allocator;
|
_ = allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(self: Config, err_writer: anytype) !void {
|
|
||||||
var valid: bool = true;
|
|
||||||
|
|
||||||
// zig fmt: off
|
|
||||||
if (
|
|
||||||
self.controller.parking_posture.azimuth < (
|
|
||||||
self.labjack.feedback_calibration.azimuth.minimum.angle
|
|
||||||
+ self.controller.angle_offset.azimuth
|
|
||||||
) or self.controller.parking_posture.azimuth > (
|
|
||||||
self.labjack.feedback_calibration.azimuth.maximum.angle
|
|
||||||
+ self.controller.angle_offset.azimuth
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// zig fmt: on
|
|
||||||
valid = false;
|
|
||||||
try err_writer.print(
|
|
||||||
"Config validation failed: Parking azimuth {d:.1} is outside of the valid azimuth range {d:.1} - {d:.1}\n",
|
|
||||||
.{
|
|
||||||
self.controller.parking_posture.azimuth,
|
|
||||||
self.labjack.feedback_calibration.azimuth.minimum.angle + self.controller.angle_offset.azimuth,
|
|
||||||
self.labjack.feedback_calibration.azimuth.maximum.angle + self.controller.angle_offset.azimuth,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// zig fmt: off
|
|
||||||
if (
|
|
||||||
self.controller.parking_posture.elevation < (
|
|
||||||
self.labjack.feedback_calibration.elevation.minimum.angle
|
|
||||||
+ self.controller.angle_offset.elevation
|
|
||||||
) or self.controller.parking_posture.elevation > (
|
|
||||||
self.labjack.feedback_calibration.elevation.maximum.angle
|
|
||||||
+ self.controller.angle_offset.elevation
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// zig fmt: on
|
|
||||||
valid = false;
|
|
||||||
try err_writer.print(
|
|
||||||
"Config validation failed: Parking elevation {d:.1} is outside of the valid elevation range {d:.1} - {d:.1}\n",
|
|
||||||
.{
|
|
||||||
self.controller.parking_posture.elevation,
|
|
||||||
self.labjack.feedback_calibration.elevation.minimum.angle + self.controller.angle_offset.elevation,
|
|
||||||
self.labjack.feedback_calibration.elevation.maximum.angle + self.controller.angle_offset.elevation,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid)
|
|
||||||
return error.InvalidConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
rotctl: RotControlConfig = .{
|
rotctl: RotControlConfig = .{
|
||||||
.listen_address = "127.0.0.1",
|
.listen_address = "127.0.0.1",
|
||||||
.listen_port = 4533,
|
.listen_port = 4533,
|
||||||
|
@ -35,44 +35,18 @@ pub fn init(allocator: std.mem.Allocator) !LabjackYaesu {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inRange(request: f64, comptime dof: enum { azimuth, elevation }) bool {
|
pub fn setTarget(self: LabjackYaesu, target: AzEl) void {
|
||||||
return switch (dof) {
|
|
||||||
// zig fmt: off
|
|
||||||
.azimuth => request >= (
|
|
||||||
config.labjack.feedback_calibration.azimuth.minimum.angle
|
|
||||||
+ config.controller.angle_offset.azimuth
|
|
||||||
) and request <= (
|
|
||||||
config.labjack.feedback_calibration.azimuth.maximum.angle
|
|
||||||
+ config.controller.angle_offset.azimuth
|
|
||||||
),
|
|
||||||
.elevation => request >= (
|
|
||||||
config.labjack.feedback_calibration.elevation.minimum.angle
|
|
||||||
+ config.controller.angle_offset.elevation
|
|
||||||
) and request <= (
|
|
||||||
config.labjack.feedback_calibration.elevation.maximum.angle
|
|
||||||
+ config.controller.angle_offset.elevation
|
|
||||||
),
|
|
||||||
// zig fmt: on
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setTarget(self: LabjackYaesu, target: AzEl) error{OutOfRange}!void {
|
|
||||||
self.lock.lock();
|
self.lock.lock();
|
||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
const masked_target: AzEl = .{
|
const controller = @constCast(self.controller);
|
||||||
|
controller.target = .{
|
||||||
.azimuth = target.azimuth,
|
.azimuth = target.azimuth,
|
||||||
.elevation = @min(
|
.elevation = @min(
|
||||||
@max(target.elevation, config.controller.elevation_mask),
|
@max(target.elevation, config.controller.elevation_mask),
|
||||||
180.0 - config.controller.elevation_mask,
|
180.0 - config.controller.elevation_mask,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!inRange(masked_target.azimuth, .azimuth) or !inRange(masked_target.elevation, .elevation))
|
|
||||||
return error.OutOfRange;
|
|
||||||
|
|
||||||
const controller = @constCast(self.controller);
|
|
||||||
controller.target = masked_target;
|
|
||||||
controller.requested_state = .running;
|
controller.requested_state = .running;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +87,7 @@ pub fn stop(self: LabjackYaesu) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn startPark(self: LabjackYaesu) void {
|
pub fn startPark(self: LabjackYaesu) void {
|
||||||
self.setTarget(config.controller.parking_posture) catch unreachable;
|
self.setTarget(config.controller.parking_posture);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runController(controller: *Controller) void {
|
fn runController(controller: *Controller) void {
|
||||||
|
@ -142,19 +142,19 @@ fn setPosition(self: *RotCtl, _: []const u8, tokens: *TokenIter) CommandError!vo
|
|||||||
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!inRange(azimuth, .azimuth))
|
||||||
|
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
||||||
|
|
||||||
const elevation = std.fmt.parseFloat(f64, tokens.next() orelse {
|
const elevation = std.fmt.parseFloat(f64, tokens.next() orelse {
|
||||||
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
||||||
}) catch {
|
}) catch {
|
||||||
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.rotator.setTarget(.{
|
if (!inRange(elevation, .elevation))
|
||||||
.azimuth = azimuth,
|
return self.replyStatus(.invalid_parameter) catch error.BadOutput;
|
||||||
.elevation = elevation,
|
|
||||||
}) catch |err| switch (err) {
|
|
||||||
error.OutOfRange => return self.replyStatus(.invalid_parameter) catch error.BadOutput,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
self.rotator.setTarget(.{ .azimuth = azimuth, .elevation = elevation });
|
||||||
return self.replyStatus(.okay) catch error.BadOutput;
|
return self.replyStatus(.okay) catch error.BadOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
150
src/main.zig
150
src/main.zig
@ -4,157 +4,35 @@ const Config = @import("./Config.zig");
|
|||||||
const lj = @import("./labjack.zig");
|
const lj = @import("./labjack.zig");
|
||||||
const RotCtl = @import("./RotCtl.zig");
|
const RotCtl = @import("./RotCtl.zig");
|
||||||
|
|
||||||
const udev = @import("udev_rules");
|
|
||||||
|
|
||||||
const log = std.log.scoped(.main);
|
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 {
|
pub fn main() !u8 {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
const args = std.process.argsAlloc(allocator) catch {
|
blk: {
|
||||||
printStderr("Couldn't allocate arguments array", .{});
|
const conf_file = std.fs.cwd().openFile("yaes.json", .{}) catch {
|
||||||
return 1;
|
log.warn("Could not load config file yaes.json. Using default config.", .{});
|
||||||
};
|
Config.loadDefault(allocator);
|
||||||
defer std.process.argsFree(allocator, args);
|
break :blk;
|
||||||
|
};
|
||||||
|
defer conf_file.close();
|
||||||
|
|
||||||
if (args.len < 2) {
|
Config.load(allocator, conf_file.reader()) catch {
|
||||||
printHelp();
|
log.err("Could not parse config file yaes.json. Good luck figuring out why.", .{});
|
||||||
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 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
printHelp();
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
}
|
defer Config.destroy(allocator);
|
||||||
|
|
||||||
fn installUdevRules(outpath: ?[]const u8) u8 {
|
const ver = lj.getDriverVersion();
|
||||||
const rules_path = outpath orelse "/etc/udev/rules.d";
|
std.debug.print("Driver version: {d}\n", .{ver});
|
||||||
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| {
|
RotCtl.run(allocator) catch |err| {
|
||||||
printStderr(
|
log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)});
|
||||||
"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 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
return 0;
|
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) },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user