daq-view/src/screens/channel_from_device.zig

237 lines
7.9 KiB
Zig

const std = @import("std");
const App = @import("../app.zig");
const UI = @import("../ui.zig");
const srcery = @import("../srcery.zig");
const NIDaq = @import("../ni-daq/root.zig");
const assert = std.debug.assert;
const log = std.log.scoped(.channel_from_device_screen);
const Screen = @This();
app: *App,
hot_channel: ?[:0]const u8 = null,
// TODO: 32 limit
selected_channels: std.BoundedArray([:0]u8, 32) = .{},
// TODO: Don't use arena
channel_names: std.heap.ArenaAllocator,
pub fn init(app: *App) Screen {
return Screen{
.app = app,
.channel_names = std.heap.ArenaAllocator.init(app.allocator)
};
}
pub fn deinit(self: *Screen) void {
_ = self.channel_names.reset(.free_all);
}
fn isChannelSelected(self: *Screen, channel: []const u8) bool {
for (self.selected_channels.slice()) |selected_channel| {
if (std.mem.eql(u8, selected_channel, channel)) {
return true;
}
}
return false;
}
fn selectChannel(self: *Screen, channel: []const u8) void {
if (self.selected_channels.unusedCapacitySlice().len == 0) {
log.warn("Maximum number of selected channels reached", .{});
return;
}
if (self.isChannelSelected(channel)) {
return;
}
const allocator = self.channel_names.allocator();
const channel_dupe = allocator.dupeZ(u8, channel) catch |e| {
log.err("Failed to duplicate channel name: {}", .{e});
return;
};
self.selected_channels.appendAssumeCapacity(channel_dupe);
}
fn deselectChannel(self: *Screen, channel: []const u8) void {
for (0.., self.selected_channels.slice()) |i, selected_channel| {
if (std.mem.eql(u8, selected_channel, channel)) {
_ = self.selected_channels.swapRemove(i);
return;
}
}
}
fn toggleChannel(self: *Screen, channel: []const u8) void {
if (self.isChannelSelected(channel)) {
self.deselectChannel(channel);
} else {
self.selectChannel(channel);
}
}
pub fn tick(self: *Screen) !void {
var ni_daq = self.app.ni_daq orelse return;
var ui = &self.app.ui;
if (ui.isKeyboardPressed(.key_escape)) {
self.app.screen = .main;
}
const root = ui.parentBox().?;
root.layout_direction = .left_to_right;
{
const panel = ui.beginScrollbar(ui.keyFromString("Channels"));
defer ui.endScrollbar();
panel.layout_direction = .top_to_bottom;
const devices = try ni_daq.listDeviceNames();
for (devices) |device| {
var ai_voltage_physical_channels: []const [:0]const u8 = &.{};
if (try ni_daq.checkDeviceAIMeasurementType(device, .Voltage)) {
ai_voltage_physical_channels = try ni_daq.listDeviceAIPhysicalChannels(device);
}
// var ao_physical_channels: []const [:0]const u8 = &.{};
// if (try ni_daq.checkDeviceAOOutputType(device, .Voltage)) {
// ao_physical_channels = try ni_daq.listDeviceAOPhysicalChannels(device);
// }
inline for (.{ ai_voltage_physical_channels }) |channels| {
for (channels) |channel| {
const channel_button = ui.textButton(channel);
channel_button.background = srcery.black;
if (self.isChannelSelected(channel)) {
channel_button.background = srcery.bright_white;
channel_button.text_color = srcery.black;
}
if (self.app.getChannelByName(channel) != null) {
channel_button.text_color = srcery.white;
channel_button.background = srcery.hard_black;
} else {
const signal = ui.signal(channel_button);
if (signal.clicked()) {
self.toggleChannel(channel);
}
if (signal.hot) {
self.hot_channel = channel;
}
}
}
}
}
}
{
const panel = ui.createBox(.{
.size_x = UI.Sizing.initGrowFull(),
.size_y = UI.Sizing.initGrowFull(),
.borders = .{
.left = .{ .color = srcery.hard_black, .size = 4 }
},
.layout_direction = .top_to_bottom,
.padding = UI.Padding.all(ui.rem(2))
});
panel.beginChildren();
defer panel.endChildren();
const info_container = ui.createBox(.{
.size_x = UI.Sizing.initGrowFull(),
.size_y = UI.Sizing.initGrowFull(),
.layout_direction = .top_to_bottom,
});
if (self.hot_channel) |hot_channel| {
info_container.beginChildren();
defer info_container.endChildren();
var maybe_hot_device: ?[:0]const u8 = null;
var device_buff: NIDaq.BoundedDeviceName = .{};
if (NIDaq.getDeviceNameFromChannel(hot_channel)) |device| {
device_buff.appendSliceAssumeCapacity(device);
device_buff.buffer[device_buff.len] = 0;
maybe_hot_device = device_buff.buffer[0..device_buff.len :0];
}
var channel_type_name: []const u8 = "unknown";
if (NIDaq.getChannelType(hot_channel)) |channel_type| {
channel_type_name = channel_type.name();
}
{
const channel_info = ui.createBox(.{
.size_x = UI.Sizing.initGrowFull(),
.size_y = UI.Sizing.initFitChildren(),
.padding = .{
.bottom = ui.rem(2)
},
.layout_direction = .top_to_bottom
});
channel_info.beginChildren();
defer channel_info.endChildren();
_ = ui.label("Channel properties", .{});
_ = ui.label("Name: {s}", .{hot_channel});
_ = ui.label("Type: {s}", .{channel_type_name});
}
if (maybe_hot_device) |hot_device| {
_ = ui.label("Device properties", .{});
if (ni_daq.listDeviceAIMeasurementTypes(hot_device)) |measurement_types| {
_ = ui.label("Measurement types: {} types", .{measurement_types.len});
} else |e| {
log.err("ni_daq.listDeviceAIMeasurementTypes(): {}", .{ e });
}
}
} else {
info_container.alignment.x = .center;
info_container.alignment.y = .center;
info_container.setText("Hover on a channel");
info_container.flags.insert(.wrap_text);
info_container.text_color = srcery.hard_black;
info_container.font = .{
.variant = .bold_italic,
.size = ui.rem(3)
};
}
const add_button = ui.button(ui.keyFromString("Add channels"));
add_button.setFmtText("Add {} selected channels", .{self.selected_channels.len});
add_button.size.x = UI.Sizing.initGrowFull();
add_button.alignment.x = .center;
if (self.selected_channels.len > 0) {
add_button.borders = UI.Borders.all(.{
.color = srcery.green,
.size = 2
});
const signal = ui.signal(add_button);
if (signal.clicked()) {
self.app.screen = .main;
for (self.selected_channels.slice()) |channel| {
_ = try self.app.addView(.{
.channel = try self.app.addChannel(channel)
});
}
_ = self.channel_names.reset(.free_all);
self.selected_channels.len = 0;
}
} else {
add_button.borders = UI.Borders.all(.{
.color = srcery.hard_black,
.size = 2
});
}
}
}