main: handle various forms of process termination more safely
This attempts to solve the problem where, if the rotator is actively rotating and the program is killed, the LabJack will not be reset and the rotator will keep running. We install various signal handlers to try to catch common cases (ctrl+c, terminal getting closed out from under us). There are still ways to end the process that leave the LabJack running (such as if it crashes, though there are currently no known crashes), but I don't think it's possible to completely avoid that.
This commit is contained in:
parent
c8cfc95938
commit
5be7ad0831
@ -10,7 +10,7 @@ const log = std.log.scoped(.RotCtl);
|
||||
|
||||
writer: std.io.BufferedWriter(512, std.net.Stream.Writer),
|
||||
running: bool,
|
||||
rotator: YaesuController,
|
||||
rotator: *YaesuController,
|
||||
|
||||
pub fn run(allocator: std.mem.Allocator) !void {
|
||||
// var server = std.net.StreamServer.init(.{ .reuse_address = true });
|
||||
@ -30,7 +30,7 @@ pub fn run(allocator: std.mem.Allocator) !void {
|
||||
var interface: RotCtl = .{
|
||||
.writer = undefined,
|
||||
.running = true,
|
||||
.rotator = try YaesuController.init(allocator),
|
||||
.rotator = try YaesuController.create(allocator),
|
||||
};
|
||||
|
||||
while (interface.running) {
|
||||
|
@ -8,6 +8,8 @@ const log = std.log.scoped(.yaesu_controller);
|
||||
|
||||
const YaesuController = @This();
|
||||
|
||||
pub var singleton: ?*YaesuController = null;
|
||||
|
||||
control_thread: std.Thread,
|
||||
lock: *std.Thread.Mutex,
|
||||
controller: *const Controller,
|
||||
@ -23,7 +25,7 @@ pub const CalibrationRoutine = enum {
|
||||
};
|
||||
|
||||
pub fn calibrate(allocator: std.mem.Allocator, routine: CalibrationRoutine) !void {
|
||||
const controller = try YaesuController.init(allocator);
|
||||
const controller = try YaesuController.create(allocator);
|
||||
defer {
|
||||
controller.quit();
|
||||
controller.control_thread.join();
|
||||
@ -35,7 +37,12 @@ pub fn calibrate(allocator: std.mem.Allocator, routine: CalibrationRoutine) !voi
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !YaesuController {
|
||||
pub fn create(allocator: std.mem.Allocator) !*YaesuController {
|
||||
if (singleton) |_| {
|
||||
log.err("Controller singleton already exists.", .{});
|
||||
return error.AlreadyInitialized;
|
||||
}
|
||||
|
||||
const controller = try allocator.create(Controller);
|
||||
errdefer allocator.destroy(controller);
|
||||
controller.* = try Controller.init(allocator);
|
||||
@ -43,11 +50,16 @@ pub fn init(allocator: std.mem.Allocator) !YaesuController {
|
||||
// do this in the main thread so we can throw the error about it synchronously.
|
||||
try controller.connectLabjack();
|
||||
|
||||
return .{
|
||||
const self = try allocator.create(YaesuController);
|
||||
errdefer allocator.destroy(self);
|
||||
self.* = .{
|
||||
.control_thread = try std.Thread.spawn(.{}, runController, .{controller}),
|
||||
.lock = &controller.lock,
|
||||
.controller = controller,
|
||||
};
|
||||
|
||||
singleton = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
fn inRange(request: f64, comptime dof: enum { azimuth, elevation }) bool {
|
||||
@ -88,7 +100,7 @@ pub fn setTarget(self: YaesuController, target: AzEl) error{OutOfRange}!void {
|
||||
|
||||
const controller = @constCast(self.controller);
|
||||
controller.target = masked_target;
|
||||
controller.requested_state = .running;
|
||||
controller.requestState(.running);
|
||||
}
|
||||
|
||||
pub fn currentPosition(self: YaesuController) AzEl {
|
||||
@ -114,7 +126,7 @@ pub fn quit(self: YaesuController) void {
|
||||
defer self.lock.unlock();
|
||||
|
||||
const controller = @constCast(self.controller);
|
||||
controller.requested_state = .stopped;
|
||||
controller.requestState(.stopped);
|
||||
}
|
||||
|
||||
pub fn stop(self: YaesuController) void {
|
||||
@ -123,7 +135,7 @@ pub fn stop(self: YaesuController) void {
|
||||
|
||||
const controller = @constCast(self.controller);
|
||||
controller.target = controller.position;
|
||||
controller.requested_state = .idle;
|
||||
controller.requestState(.idle);
|
||||
}
|
||||
|
||||
pub fn startPark(self: YaesuController) void {
|
||||
@ -254,6 +266,15 @@ const Controller = struct {
|
||||
self.labjack.id = info.local_id;
|
||||
}
|
||||
|
||||
fn requestState(self: *Controller, request: ControllerState) void {
|
||||
// If stopped has been requested, don't allow anything to override it. We need
|
||||
// to exit.
|
||||
switch (self.requested_state) {
|
||||
.stopped => {},
|
||||
else => self.requested_state = request,
|
||||
}
|
||||
}
|
||||
|
||||
fn lerpOne(input: f64, cal_points: Config.MinMax) f64 {
|
||||
return (input - cal_points.minimum.voltage) * cal_points.slope() + cal_points.minimum.angle;
|
||||
}
|
||||
@ -383,7 +404,7 @@ const Controller = struct {
|
||||
|
||||
// run calibration routine. psych, this does nothing. gottem
|
||||
self.current_state = .idle;
|
||||
self.requested_state = self.current_state;
|
||||
self.requestState(.idle);
|
||||
},
|
||||
.running => {
|
||||
const pos_error: AzEl = blk: {
|
||||
|
53
src/main.zig
53
src/main.zig
@ -10,6 +10,45 @@ const udev = @import("udev_rules");
|
||||
|
||||
const log = std.log.scoped(.main);
|
||||
|
||||
fn die() noreturn {
|
||||
if (YaesuController.singleton) |controller| {
|
||||
controller.quit();
|
||||
controller.control_thread.join();
|
||||
}
|
||||
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
fn posixSignalHandler(signal: c_int) callconv(.C) void {
|
||||
_ = signal;
|
||||
die();
|
||||
}
|
||||
|
||||
fn windowsEventHandler(code: std.os.windows.DWORD) callconv(std.os.windows.WINAPI) std.os.windows.BOOL {
|
||||
_ = code;
|
||||
die();
|
||||
}
|
||||
|
||||
fn addAbortHandler() !void {
|
||||
if (comptime builtin.os.tag == .windows) {
|
||||
try std.os.windows.SetConsoleCtrlHandler(windowsEventHandler, true);
|
||||
} else {
|
||||
const action = std.posix.Sigaction{
|
||||
.handler = .{ .handler = posixSignalHandler },
|
||||
.mask = std.posix.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
for ([_]u6{
|
||||
std.posix.SIG.INT,
|
||||
std.posix.SIG.HUP,
|
||||
std.posix.SIG.QUIT,
|
||||
}) |sig| {
|
||||
try std.posix.sigaction(sig, &action, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn printStderr(comptime fmt: []const u8, args: anytype) void {
|
||||
std.debug.print(fmt ++ "\n", args);
|
||||
}
|
||||
@ -66,9 +105,14 @@ pub fn main() !u8 {
|
||||
|
||||
defer Config.deinit();
|
||||
|
||||
addAbortHandler() catch {
|
||||
log.err("Could not install quit handler.", .{});
|
||||
return 1;
|
||||
};
|
||||
|
||||
RotCtl.run(allocator) catch |err| {
|
||||
log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)});
|
||||
return 1;
|
||||
die();
|
||||
};
|
||||
} else if (std.mem.eql(u8, args[1], commands.calibrate)) {
|
||||
if (args.len < 3 or args.len > 4) {
|
||||
@ -86,9 +130,14 @@ pub fn main() !u8 {
|
||||
return 1;
|
||||
};
|
||||
|
||||
addAbortHandler() catch {
|
||||
log.err("Could not install quit handler.", .{});
|
||||
return 1;
|
||||
};
|
||||
|
||||
YaesuController.calibrate(allocator, routine) catch |err| {
|
||||
log.err("Calibration failed: {s}", .{@errorName(err)});
|
||||
return 1;
|
||||
die();
|
||||
};
|
||||
} else if (std.mem.eql(u8, args[1], commands.help)) {
|
||||
if (args.len != 3) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user