diff --git a/src/app.zig b/src/app.zig index 2c3ab9d..1803f64 100644 --- a/src/app.zig +++ b/src/app.zig @@ -739,13 +739,15 @@ pub const View = struct { channel: Id }; + const Name = std.BoundedArray(u8, 128); + // Persistent reference: Reference, height: f32 = 300, // TODO: Implement different styles of following: Look ahead, sliding, sliding window follow: bool = false, graph_opts: Graph.ViewOptions = .{}, - name: std.BoundedArray(u8, 128) = .{}, + name: Name = .{}, // Runtime graph_cache: Graph.RenderCache = .{}, @@ -1137,6 +1139,9 @@ pub const Project = struct { try self.views.insertUndefinedAt(id); + const name = try readString(reader, allocator); + defer allocator.free(name); + const reference_tag = try readInt(reader, u8); var reference: View.Reference = undefined; if (reference_tag == @intFromEnum(View.Reference.file)) { @@ -1157,7 +1162,8 @@ pub const Project = struct { const view = self.views.get(id).?; view.* = View{ .reference = reference, - .transformed_samples = sample_list_id + .transformed_samples = sample_list_id, + .name = try View.Name.fromSlice(name) }; } } @@ -1202,6 +1208,7 @@ pub const Project = struct { const view = self.views.get(view_id).?; try writeId(writer, view_id); + try writeString(writer, view.name.constSlice()); try writeInt(writer, u8, @intFromEnum(view.reference)); switch (view.reference) { .channel => |channel_id| { @@ -1622,7 +1629,7 @@ pub fn showUI(self: *App) !void { fn exportProject(self: *App) !void { log.debug("Export", .{}); - const project = self.project; + const project = &self.project; const export_location = project.export_location orelse return error.NoExportLocation; const experiment_name = project.experiment_name.items; if (experiment_name.len == 0) { @@ -1654,16 +1661,19 @@ fn exportProject(self: *App) !void { const sample_rate = self.project.sample_rate orelse 1; - var file = try export_dir.createFile(export_filename, .{}); + const txt_filename = try std.mem.concat(self.allocator, u8, &.{ export_filename, ".txt" }); + defer self.allocator.free(txt_filename); + + var file = try export_dir.createFile(txt_filename, .{}); defer file.close(); const writer = file.writer(); - { + { //Filename try writer.writeAll(export_filename); try writer.writeAll("\n\n"); } - { + { // Solutions try writer.writeAll("Time Solution\n"); for (self.project.solutions.items) |solution| { const total_seconds = @as(f64, @floatFromInt(solution.sample)) / sample_rate; @@ -1676,13 +1686,20 @@ fn exportProject(self: *App) !void { try writer.writeAll("\n"); } - { + { // Gain try writer.writeAll("Time 100/Gain\n"); - // TODO: + for (project.gain_changes.constSlice()) |gain_change| { + const total_seconds = gain_change.gain / sample_rate; + + const line = try std.fmt.allocPrint(self.allocator, "{d:.2} {d:.2}\n", .{ total_seconds, gain_change.gain }); + defer self.allocator.free(line); + + try writer.writeAll(line); + } try writer.writeAll("\n"); } - { + { // Statistics points try writer.writeAll("Time of Statistic points\n"); for (project.statistic_points.items) |sample_timestamp| { const total_seconds = @as(f64, @floatFromInt(sample_timestamp)) / sample_rate; @@ -1690,25 +1707,55 @@ fn exportProject(self: *App) !void { const text = try std.fmt.allocPrint(self.allocator, "{d:.2}\n", .{total_seconds}); defer self.allocator.free(text); - _ = try writer.writeAll(text); + try writer.writeAll(text); } try writer.writeAll("\n"); } - { + { // Pipete solutions try writer.writeAll("Pipete Solution:\n"); try writer.writeAll(self.project.pipete_solution.items); try writer.writeAll("\n"); + try writer.writeAll("\n"); } - { + { // Note try writer.writeAll("Note:\n"); try writer.writeAll(self.project.notes.items); try writer.writeAll("\n"); } - // TODO: export channels + { + var view_iter = project.views.iterator(); + while (view_iter.next()) |view| { + if (view.reference != .channel) { + continue; + } + const referenced_channel = project.channels.get(view.reference.channel) orelse continue; + + var channel_name: []u8 = undefined; + if (view.name.len > 0) { + channel_name = try self.allocator.dupe(u8, view.name.constSlice()); + } else { + channel_name = try self.allocator.dupe(u8, referenced_channel.name.constSlice()); + } + defer self.allocator.free(channel_name); + + std.mem.replaceScalar(u8, channel_name, '/', '_'); + const channel_filename = try std.fmt.allocPrint(self.allocator, "{s}_{s}.bin", .{export_filename, channel_name}); + defer self.allocator.free(channel_filename); + + var channel_file = try export_dir.createFile(channel_filename, .{}); + defer channel_file.close(); + + const sample_list = project.sample_lists.get(referenced_channel.collected_samples_id) orelse continue; + var sample_iter = sample_list.iterator(0, sample_list.getLength()); + while (sample_iter.next()) |samples| { + try channel_file.writeAll(std.mem.sliceAsBytes(samples)); + } + } + } } pub fn tick(self: *App) !void { @@ -1940,6 +1987,9 @@ fn startCollection(self: *App) !void { const channel_name = utils.getBoundedStringZ(&channel.name); const channel_type = NIDaq.getChannelType(channel_name) orelse continue; + const sample_list = self.project.sample_lists.get(channel.collected_samples_id) orelse continue; + sample_list.clear(self.allocator); + if (channel_type == .analog_input) { const allowed_sample_values = channel.allowed_sample_values orelse continue; @@ -1953,6 +2003,17 @@ fn startCollection(self: *App) !void { } } + { + var view_iter = self.project.views.iterator(); + while (view_iter.next()) |view| { + if (view.reference == .channel) { + const sample_list = self.project.sample_lists.get(view.transformed_samples) orelse continue; + sample_list.clear(self.allocator); + view.graph_cache.invalidate(); + } + } + } + try task.setContinousSampleRate(.{ .sample_rate = sample_rate }); try task.start();