const std = @import("std"); const Session = @import("Session.zig"); const style = @import("style.zig"); const tools = @import("tools.zig"); const PtgSession = @import("PtgSession.zig"); const zli = @import("cli"); const File = std.Io.File; const Init = std.process.Init; fn fail(init: Init, err: anyerror) !void { var err_buf: [256]u8 = undefined; var err_writer = File.stderr().writer(init.io, &err_buf); try style.errPrefix(init.io, &err_writer.interface); try err_writer.interface.print("{s}: {s}\n", .{ @errorName(err), ptgErrorHint(err) }); try err_writer.interface.flush(); std.process.exit(1); } fn ptgErrorHint(err: anyerror) []const u8 { return switch (err) { error.NoActiveImage => "no active image set on tool", error.NameTooLong => "image name too long", error.ToolFailure => "tool rejected the request (is an image loaded?)", error.ToolProtocol, error.ToolBusy => "unexpected tool response", error.Timeout => "tool timed out", else => "communication with tool failed", }; } fn openSession(init: Init, serial: ?[]const u8) !Session { const info = try tools.findPickit5(init, serial); defer info.deinit(init.gpa); var sess: Session = try .init( init.io, init.gpa, info.node, ); errdefer sess.deinit(); var out_buf: [4096]u8 = undefined; var out_writer = File.stdout().writer(init.io, &out_buf); try out_writer.interface.print( "Connecting to {s} ({s}, serial {s})\n", .{ info.name, info.node, info.serial }, ); try out_writer.interface.flush(); return sess; } fn closeSession(sess: *Session) void { sess.deinit(); } fn run( init: Init, op: PtgSession.Op, image: ?[]const u8, serial: ?[]const u8, ) !void { var out_buf: [4096]u8 = undefined; var out_writer = File.stdout().writer(init.io, &out_buf); const w = &out_writer.interface; var sess = try openSession(init, serial); defer closeSession(&sess); var tool: PtgSession = .init(init.gpa, &sess); switch (op) { .list => { const entries = try tool.listImages(init.gpa); defer { for (entries) |entry| entry.deinit(init.gpa); init.gpa.free(entries); } if (entries.len == 0) { try w.print("No images on tool\n", .{}); } for (entries) |entry| { if (std.mem.eql(u8, entry.name, PtgSession.truncated_marker)) { try w.print("{s}\n", .{PtgSession.truncated_marker}); } else { try w.print( "{s}: {d} total, {d} pass\n", .{ entry.name, entry.total, entry.pass }, ); } } }, .active => { const name = try tool.getActiveImage(init.gpa); defer init.gpa.free(name); try w.print("{s}\n", .{name}); }, .set => { try tool.setActiveImage(image.?); try w.print("Active image: {s}\n", .{image.?}); }, .reset_stats => { try tool.resetStats(image.?); try w.print("Statistics reset: {s}\n", .{image.?}); }, .remove => { try tool.deleteImage(image.?); try w.print("Removed image: {s}\n", .{image.?}); }, } try w.flush(); } const serial_param_doc = "Serial number of tool (required when multiple tools are attached)"; pub fn reset( init: Init, image: zli.Param( "image", []const u8, .required, "Image name", ), serial: zli.Param( "serial", ?[]const u8, .optional(null), serial_param_doc, ), ) !void { run(init, .reset_stats, image.value, serial.value) catch |err| return fail(init, err); } pub fn list( init: Init, serial: zli.Param( "serial", ?[]const u8, .optional(null), serial_param_doc, ), ) !void { run(init, .list, null, serial.value) catch |err| return fail(init, err); } pub fn active( init: Init, serial: zli.Param( "serial", ?[]const u8, .optional(null), serial_param_doc, ), ) !void { run(init, .active, null, serial.value) catch |err| return fail(init, err); } pub fn set( init: Init, image: zli.Param( "image", []const u8, .required, "Image name", ), serial: zli.Param( "serial", ?[]const u8, .optional(null), serial_param_doc, ), ) !void { run(init, .set, image.value, serial.value) catch |err| return fail(init, err); } pub fn remove( init: Init, image: zli.Param( "image", []const u8, .required, "Image name", ), serial: zli.Param( "serial", ?[]const u8, .optional(null), serial_param_doc, ), ) !void { run(init, .remove, image.value, serial.value) catch |err| return fail(init, err); } pub fn add( init: Init, name: zli.Param( "name", []const u8, .required, "Name to assign image", ), path: zli.Param( "path", []const u8, .required, "Path to image file", ), ) !void { _ = name; _ = path; // MPLAB X does not upload a single image file: during a PTG program // cycle it builds a multi-file bundle on the tool (conf.ptg, // pm5Data.json, ptgdata, checksum) derived from the live programming // environment. Reproducing that bundle generator is still // unimplemented; see ProgrammerToGoImpl/Pm5PtgHandler in java_src. var err_buf: [512]u8 = undefined; var err_writer = File.stderr().writer(init.io, &err_buf); try style.errPrefix(init.io, &err_writer.interface); try err_writer.interface.writeAll( \\sending images is not implemented yet \\ MPLAB X builds PTG images from a live programming environment; this bundle generation is not reproduced yet. \\ ); try err_writer.interface.flush(); std.process.exit(1); }