From a82b0650e5c0a973ef9edcd09158f7205f832021 Mon Sep 17 00:00:00 2001 From: aaumar25 Date: Wed, 21 Oct 2026 10:29:46 +0900 Subject: [PATCH 1/5] refactor: Remove John's firmware details --- README.md | 15 - build.zig | 38 - build.zig.zon | 7 +- src/Config.zig | 1237 +++++------------------------ src/{_Config => Config}/Info.zig | 0 src/Log.zig | 632 ++++----------- src/{_Message.zig => Message.zig} | 4 +- src/OldConfig.zig | 462 ----------- src/_Config.zig | 257 ------ src/_Log.zig | 152 ---- src/drivercom.zig | 26 +- src/library.zig | 0 src/message.zig | 283 ------- 13 files changed, 348 insertions(+), 2765 deletions(-) rename src/{_Config => Config}/Info.zig (100%) rename src/{_Message.zig => Message.zig} (99%) delete mode 100644 src/OldConfig.zig delete mode 100644 src/_Config.zig delete mode 100644 src/_Log.zig delete mode 100644 src/library.zig delete mode 100644 src/message.zig diff --git a/README.md b/README.md index cba9435..f5e5458 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,3 @@ # drivercom > PMF Smart Driver Communications Library. - -## Building - -### Library - -To build `drivercom` as a static or dynamic C library, the `library` option -must be specified. - -```console -zig build -Dlibrary=static -``` - -```console -zig build -Dlibrary=dynamic -``` diff --git a/build.zig b/build.zig index e248c8f..acb80b7 100644 --- a/build.zig +++ b/build.zig @@ -4,60 +4,22 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const library = b.option( - std.builtin.LinkMode, - "library", - "Build an exportable C library.", - ); - const build_zig_zon = b.createModule(.{ .root_source_file = b.path("build.zig.zon"), .target = target, .optimize = optimize, }); - const mmc_api = b.dependency("mmc_api", .{ - .target = target, - .optimize = optimize, - }); - const mod = b.addModule("drivercom", .{ .root_source_file = b.path("src/drivercom.zig"), .target = target, .optimize = optimize, }); mod.addImport("build.zig.zon", build_zig_zon); - mod.addImport("mmc_api", mmc_api.module("mmc-api")); const mod_unit_tests = b.addTest(.{ .root_module = mod }); const run_mod_unit_tests = b.addRunArtifact(mod_unit_tests); const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&run_mod_unit_tests.step); - - if (library) |l| { - const lib_mod = b.createModule(.{ - .root_source_file = b.path("src/library.zig"), - .target = target, - .optimize = optimize, - }); - lib_mod.addImport("drivercom", mod); - - // Library Artifact - { - const lib = b.addLibrary(.{ - .name = "drivercom", - .root_module = lib_mod, - .linkage = l, - }); - b.installArtifact(lib); - } - - // Library Tests - { - const lib_unit_tests = b.addTest(.{ .root_module = lib_mod }); - const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); - test_step.dependOn(&run_lib_unit_tests.step); - } - } } diff --git a/build.zig.zon b/build.zig.zon index d05e4c7..c3b07d2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,12 +3,7 @@ .version = "0.6.1", .fingerprint = 0xc1b53adfd36fc2f0, .minimum_zig_version = "0.16.0", - .dependencies = .{ - .mmc_api = .{ - .url = "https://github.com/pmotionf/mmc-api/archive/0a212c9.tar.gz", - .hash = "mmc_api-0.0.0-P5raR6pjMgCpq795vTCW5EnQ4eIrbLpCvA8SRw_8GS9Z", - }, - }, + .dependencies = .{}, .paths = .{ "build.zig", "build.zig.zon", diff --git a/src/Config.zig b/src/Config.zig index 3c2e59a..b584ef9 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -1,1076 +1,257 @@ //! This module represents driver's configuration. const std = @import("std"); -const drivercom = @import("drivercom.zig"); - -pub const MAX_AXES = 3; - const Config = @This(); +pub const Info = @import("Config/Info.zig"); -/// Driver configuration field. These fields are used directly in messages -/// with driver; their ordering matches firmware field kind ordering. -/// Names reflect nested structure within `Config` struct, and types represent -/// the type of value in the `Config` struct. -pub const Field = union(enum(u16)) { - id: u16, - station: u16, - flags: Flags, - @"line.axes": u16, - @"voltage.target": u16, - @"voltage.warmup": u16, - @"voltage.limit.lower": u16, - @"voltage.limit.upper": u16, - @"magnet.pitch": f32, - @"magnet.length": f32, - @"carrier.mass": u16, - @"carrier.arrival.threshold.position": f32, - @"carrier.arrival.threshold.velocity": f32, - @"carrier.cas.buffer": u16, - @"carrier.cas.acceleration": f32, - mechanical_angle_offset: f32, - @"axis.length": f32, - @"coil.length": f32, - @"coil.max_current": f32, - @"coil.continuous_current": f32, - @"coil.rs": f32, - @"coil.ls": f32, - @"coil.center.kf": f32, - @"coil.between.kf": f32, - @"coil.kbm": f32, - @"sensor.default_magnet_length": f32, - @"sensor.ignore_distance": f32, - zero_position: f32, - @"axis.center.gain.current.p": f32, - @"axis.center.gain.current.i": f32, - @"axis.center.gain.current.denominator": u32, - @"axis.center.gain.velocity.p": f32, - @"axis.center.gain.velocity.i": f32, - @"axis.center.gain.velocity.denominator": u32, - @"axis.center.gain.velocity.denominator_pi": u32, - @"axis.center.gain.position.p": f32, - @"axis.center.gain.position.denominator": u32, - @"axis.between.gain.current.p": f32, - @"axis.between.gain.current.i": f32, - @"axis.between.gain.current.denominator": u32, - @"axis.between.gain.velocity.p": f32, - @"axis.between.gain.velocity.i": f32, - @"axis.between.gain.velocity.denominator": u32, - @"axis.between.gain.velocity.denominator_pi": u32, - @"axis.between.gain.position.p": f32, - @"axis.between.gain.position.denominator": u32, - @"hall_sensors.magnet_length.backward": f32, - @"hall_sensors.magnet_length.forward": f32, - @"hall_sensors.position.on.backward": f32, - @"hall_sensors.position.on.forward": f32, - - pub const Kind = std.meta.Tag(@This()); - - fn setInner( - parent: anytype, - value: anytype, - comptime access: []const u8, - ) void { - const nested = comptime std.mem.indexOfScalar(u8, access, '.'); - if (nested) |index| { - setInner( - &@field(parent, access[0..index]), - value, - access[index + 1 ..], - ); - } else { - @field(parent, access) = value; - } - } - - test setInner { - var config: Config = undefined; - setInner(&config, 1360, "carrier.mass"); - try std.testing.expectEqual(1360, config.carrier.mass); - } - - fn getInner( - parent: anytype, - comptime return_type: type, - comptime access: []const u8, - ) return_type { - const nested = comptime std.mem.indexOfScalar(u8, access, '.'); - if (nested) |index| { - return getInner( - @field(parent, access[0..index]), - return_type, - access[index + 1 ..], - ); - } else { - return @field(parent, access); - } - } - - test getInner { - var config: Config = undefined; - config.carrier.mass = 1320; - const mass: u16 = getInner(config, u16, "carrier.mass"); - try std.testing.expectEqual(config.carrier.mass, mass); - - config.hall_sensors[3].position.on.forward = 17.48; - try std.testing.expectEqual( - config.hall_sensors[3].position.on.forward, - getInner(config.hall_sensors[3], f32, "position.on.forward"), - ); - } - - /// Set a field in provided Config struct with value in Field. - pub fn setConfig(self: Field, config: *Config, opts: struct { - index: usize = 0, - }) void { - switch (self) { - inline else => |value, kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - const hall_sensor: *HallSensor = - &config.hall_sensors[opts.index]; - setInner(hall_sensor, value, name[index + 1 ..]); - } else { - setInner(config, value, name); - } - } else { - setInner(config, value, name); - } - }, - } - } - - pub fn fromConfig( - config: Config, - field: Field.Kind, - opts: struct { - index: usize = 0, - }, - ) Field { - switch (field) { - inline else => |kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - return @unionInit(Field, name, getInner( - config.hall_sensors[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - )); - } else if (comptime std.mem.eql( - u8, - "axes", - name[0..index], - )) { - return @unionInit(Field, name, getInner( - config.axes[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - )); - } - } - return @unionInit(Field, name, getInner( - config, - @FieldType(Field, name), - name, - )); - }, - } - } - - test fromConfig { - var config: Config = std.mem.zeroInit(Config, .{}); - { - config.axis.center.gain.position.p = 32.6; - const field = fromConfig( - config, - .@"axis.center.gain.position.p", - .{}, - ); - - try std.testing.expectEqual( - config.axis.center.gain.position.p, - field.@"axis.center.gain.position.p", - ); - } - - { - config.hall_sensors[4].position.on.forward = 0.9534; - const field = fromConfig( - config, - .@"hall_sensors.position.on.forward", - .{ .index = 4 }, - ); - - try std.testing.expectEqual( - config.hall_sensors[4].position.on.forward, - field.@"hall_sensors.position.on.forward", - ); - } - } - - pub fn toConfig( - field: Field, - opts: struct { - /// Existing Config, used as base for returned "modified" Config - config: Config = std.mem.zeroInit(Config, .{}), - index: usize = 0, - }, - ) Config { - var config: Config = opts.config; - switch (field) { - inline else => |value, kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - const hall_sensor: *HallSensor = - &config.hall_sensors[opts.index]; - setInner(hall_sensor, value, name[index + 1 ..]); - } else { - setInner(&config, value, name); - } - } else { - setInner(&config, value, name); - } - }, - } - return config; - } - - test toConfig { - var config: Config = std.mem.zeroInit(Config, .{ .id = 3 }); - const field: Field = .{ .station = 15 }; - - config = field.toConfig(.{ .config = config }); - - try std.testing.expectEqual(3, config.id); - try std.testing.expectEqual(15, config.station); - } -}; - -/// Recursively walk structure fields, checking if leaf fields exist in -/// `FieldKind` enum. -fn walkFields(structure: anytype, comptime prefix: []const u8) !void { - switch (@typeInfo(structure)) { - .@"struct" => |ti| { - inline for (ti.fields) |field| { - const new_prefix = if (prefix.len > 0) - prefix ++ "." ++ field.name - else - field.name; - - const fti = @typeInfo(field.type); - switch (fti) { - .@"struct", .array => { - // Special case Config flags field. - if (field.type == Flags) { - try std.testing.expectEqual( - true, - @hasField(Field, new_prefix), - ); - try std.testing.expectEqual( - field.type, - @FieldType(Field, new_prefix), - ); - } else try walkFields( - field.type, - new_prefix, - ); - }, - else => { - try std.testing.expectEqual( - true, - @hasField(Field, new_prefix), - ); - try std.testing.expectEqual( - field.type, - @FieldType(Field, new_prefix), - ); - }, - } - } - }, - .array => |ar| { - const cti = @typeInfo(ar.child); - switch (cti) { - .@"struct", .array => try walkFields(ar.child, prefix), - else => { - try std.testing.expectEqual( - true, - @hasField(Field, prefix), - ); - try std.testing.expectEqual( - ar.child, - @FieldType(Field, prefix), - ); - }, - } - }, - else => unreachable, - } -} - -test Field { - try walkFields(@This(), ""); -} - -/// Provides field type of config fields and its metadata. -pub const Info = struct { - id: Type = "u16", - __id: Meta = .{ - .description = - \\ID is an identifier number and unique per Line. - \\First Driver in Line must have ID number 1. - \\Each Driver in Line must have consecutively increasing IDs. - , +/// Driver ID +id: u16, +/// CC-Link Station ID +station: u16, +/// CC-Link baud rate +cc_link_speed: CcLinkSpeed, +flags: Flags, +line: struct { + /// Total number of axes in line. + axes: u32, + /// Axis length used in the whole line. + axis_length: f32, + /// Slider configuration in the whole line. + slider: struct { + mass: f32, + length: f32, }, - - station: Type = "u16", - __station: Meta = .{ - .description = - \\CC-Link Station ID. Identifier number, unique per Track. - , + /// Magnet pitch (m) + magnet_pitch: f32, +}, +voltage_warmup: f32, +retry_count: u8, +hall_cutoff_freq: f32, +overcurrent_timeout: f32, +pos_offset: f32, +right_sensor_distance: f32, +angle_offset: f32, // Can be changed from `set_calibration_info` command +section_count: SectionCount, +axes: [3]Axis, +state: State, // Immutable fields, written from driver + +/// Immutable states of driver +pub const State = struct { + servo_enabled: bool, + cc_link_enabled: bool, + calibrated: bool, + vdc: f32, + thermo: f32, + sys_version: f32, + sensor: struct { + home: u8, + id0: u8, + id1: u8, + id2: u8, }, - - flags: struct { - has_neighbor: struct { - backward: Type = "bool", - __backward: Meta = .{ - .description = - \\True if this Driver ID is greater than 1. - , - }, - forward: Type = "bool", - __forward: Meta = .{ - .description = - \\True if this Driver ID is less than number of Drivers in this - \\Line. - , - }, - } = .{}, - - uses_axis: struct { - axis2: Type = "bool", - axis3: Type = "bool", - __axis2: Meta = .{ - .description = - \\Whether this Driver uses second Axis. - , + synchronization_request_position: struct { + forward: f32, + backward: f32, + }, + section_count: struct { + /// Count at which the secondary axis is switched off, depends on the + /// slider movement direction. + deactivate_secondary: struct { + left: struct { + forward: struct { axis2: i16, axis3: i16 }, + backward: struct { axis1: i16, axis2: i16 }, }, - __axis3: Meta = .{ - .description = - \\Whether this Driver uses third Axis. - \\`axis2` must be true if using third Axis. - , + right: struct { + forward: struct { axis2: i16, axis3: i16 }, + backward: struct { axis1: i16, axis2: i16 }, }, - } = .{}, - __uses_axis: Meta = .{ - .description = - \\Axes used by this driver. - , }, - - calibration_completed: Type = "bool", - __calibration_completed: Meta = .{ .hidden = true }, - - rockwell_magnet: Type = "bool", - __rockwell_magnet: Meta = .{ - .description = - \\True if Carriers are Rockwell magnets in this Line. - , - }, - - flip_sensors: struct { - axis1: Type = "bool", - axis2: Type = "bool", - axis3: Type = "bool", - } = .{}, - __flip_sensors: Meta = .{ - .description = - \\Invert Carrier movement direction readings. - , - }, - - swap_sensors: struct { - axis1: Type = "bool", - axis2: Type = "bool", - axis3: Type = "bool", - } = .{}, - __swap_sensors: Meta = .{ - .description = - \\Switch order of the two sensors in Axis. - , - }, - } = .{}, - - line: struct { - axes: Type = "u16", - __axes: Meta = .{ - .description = - \\Total number of Axes used in this Line. - , - }, - } = .{}, - - voltage: struct { - target: Type = "u16", - __target: Meta = .{ - .description = "Supplied voltage for motor coils.", - .unit_short = "V", - .unit_long = "Volt", - }, - warmup: Type = "u16", - __warmup: Meta = .{ - .description = "Voltage applied to find sensor angle offset.", - .unit_short = "V", - .unit_long = "Volt", - }, - limit: struct { - lower: Type = "u16", - __lower: Meta = .{ - .description = "Minimum voltage threshold before alarm.", - .unit_short = "V", - .unit_long = "Volt", + /// Count at which the axis is activated as the primary, depends on + /// the slider movement direction. + activate_primary: struct { + left: struct { + forward: struct { axis1: i16, axis2: i16, axis3: i16 }, + backward: struct { axis1: i16, axis2: i16, axis3: i16 }, }, - upper: Type = "u16", - __upper: Meta = .{ - .description = "Maximum voltage threshold before alarm.", - .unit_short = "V", - .unit_long = "Volt", + right: struct { + forward: struct { axis1: i16, axis2: i16, axis3: i16 }, + backward: struct { axis1: i16, axis2: i16, axis3: i16 }, }, - } = .{}, - } = .{}, - - magnet: struct { - pitch: Type = "f32", - __pitch: Meta = .{ - .description = "Pole pitch of Carrier magnet.", - .unit_short = "m", - .unit_long = "Meter", - }, - length: Type = "f32", - __length: Meta = .{ - .description = "Length of Carrier magnet.", - .unit_short = "m", - .unit_long = "Meter", }, - } = .{}, - - carrier: struct { - mass: Type = "u16", - __mass: Meta = .{ - .description = "Mass of Carrier.", - .unit_short = "dag", - .unit_long = "Decagram", - }, - arrival: struct { - threshold: struct { - velocity: Type = "f32", - __velocity: Meta = .{ - .description = - \\Upper velocity threshold for Carrier movement completion. - , - .unit_short = "m/s", - .unit_long = "Meters per Second", - }, - position: Type = "f32", - __position: Meta = .{ - .description = - \\Absolute position error tolerance for Carrier movement - \\completion. - , - .unit_short = "m", - .unit_long = "Meter", - }, - } = .{}, - } = .{}, - - cas: struct { - buffer: Type = "u16", - __buffer: Meta = .{ - .description = - \\Minimum distance between two Carriers before CAS triggers. - , - .unit_short = "mm", - .unit_long = "Millimeter", - }, - acceleration: Type = "f32", - __acceleration: Meta = .{ - .description = - \\Acceleration used to stop Carrier movement when CAS triggers. - , - .unit_short = "m/s²", - .unit_long = "Meters per Second squared", - }, - } = .{}, - __cas: Meta = .{ .description = "Collision Avoidance System" }, - } = .{}, - - mechanical_angle_offset: Type = "f32", - __mechanical_angle_offset: Meta = .{ - .description = - \\Normalized angle offset between the magnet movement direction and - \\the sensor installation position. - , }, + axes: [3]State.Axis, + + pub const Axis = struct { + enabled: bool, + primary: bool, + slider: struct { id: u8, state: Fsm }, + entrance: Entrance, + overcharge_state: Fsm, + pitch_count: i16, + mechanical_position: f32, + // a and b needs confirmation. + bias: struct { a: f32, b: f32 }, + }; +}; - axis: struct { - length: Type = "f32", - __length: Meta = .{ - .description = "Distance between each Axis' coil centers.", - .unit_short = "m", - .unit_long = "Meter", - }, - center: struct { - gain: struct { - current: struct { - p: Type = "f32", - i: Type = "f32", - denominator: Type = "u32", - } = .{}, - velocity: struct { - p: Type = "f32", - i: Type = "f32", - denominator: Type = "u32", - denominator_pi: Type = "u32", - } = .{}, - position: struct { - p: Type = "f32", - denominator: Type = "u32", - } = .{}, - } = .{}, - } = .{}, - between: struct { - gain: struct { - current: struct { - p: Type = "f32", - i: Type = "f32", - denominator: Type = "u32", - } = .{}, - velocity: struct { - p: Type = "f32", - i: Type = "f32", - denominator: Type = "u32", - denominator_pi: Type = "u32", - } = .{}, - position: struct { - p: Type = "f32", - denominator: Type = "u32", - } = .{}, - } = .{}, - } = .{}, - } = .{}, +pub const SectionCount = struct { + /// Count at which the next axis in the travel direction becomes the + /// primary and the current axis becomes the secondary. + skip: i16, + max: struct { axis1: i16, axis2: i16, axis3: i16 }, +}; - coil: struct { - length: Type = "f32", - __length: Meta = .{ - .description = "Length of motor coil.", - .unit_short = "m", - .unit_long = "Meter", - }, - max_current: Type = "f32", - __max_current: Meta = .{ - .description = "Maximum motor coil current.", - .unit_short = "A", - .unit_long = "Ampere", - }, - continuous_current: Type = "f32", - __continuous_current: Meta = .{ - .description = "Maximum continuous motor coil current.", - .unit_short = "A", - .unit_long = "Ampere", - }, - rs: Type = "f32", - __rs: Meta = .{ - .description = "Stator Resistance (line to neutral)", - .unit_short = "Ω", - .unit_long = "Ohm", - }, - ls: Type = "f32", - __ls: Meta = .{ - .description = "Stator Inductance (line to neutral)", - .unit_short = "H", - .unit_long = "Henry", - }, - center: struct { - kf: Type = "f32", - __kf: Meta = .{ - .description = "Force Constant", - .unit_short = "N/A_rms", +pub const Axis = struct { + rs: f32, + ls: f32, + kf: f32, + kbm: f32, + max_current: f32, + continuous_current: f32, + gain: struct { + current: CurrentGain, + speed: SpeedGain, + position: PositionGain, + }, + arrival_threshold: f32, + base_position: f32, + sensor: struct { + position: struct { + on: struct { + /// Position of right sensor when slider moves forward and + /// turned on the sensor + forward: f32, + /// Position of left sensor when slider moves backward and + /// turned on the sensor + backward: f32, }, - } = .{}, - between: struct { - kf: Type = "f32", - __kf: Meta = .{ - .description = "Force Constant", - .unit_short = "N/A_rms", + off: struct { + /// Position of left sensor when slider moves forward and + /// turned off the sensor + forward: f32, + /// Position of right sensor when slider moves backward and + /// turned off the sensor + backward: f32, }, - } = .{}, - kbm: Type = "f32", - __kbm: Meta = .{ - .description = "Back EMF Constant", - .unit_short = "V/(m/s)", }, - } = .{}, - - sensor: struct { - default_magnet_length: Type = "f32", - __default_magnet_length: Meta = .{ - .description = "Default Carrier magnet length", - .unit_short = "m", - .unit_long = "Meter", - }, - ignore_distance: Type = "f32", - __ignore_distance: Meta = .{ - .description = - \\Distance a magnet needs to travel over the sensor before the - \\read data is valid to use. - , - .unit_short = "m", - .unit_long = "Meter", - }, - } = .{}, - - zero_position: Type = "f32", - __zero_position: Meta = .{ - .description = - \\Distance the magnet must travel over the sensor from the backward position - \\of Axis 1 until its center aligns with the center of the Axis 1 motor. - , - .unit_short = "m", - .unit_long = "Meter", - }, - - hall_sensors: []const struct { - magnet_length: struct { - backward: Type = "f32", - __backward: Meta = .{ - .description = - \\Magnet length based on sensor readings in backward direction. - , - .unit_short = "m", - .unit_long = "Meter", + section_count: struct { + on: struct { + /// Section count of right sensor when slider moves forward and + /// turned on the sensor + forward: i16, + /// Section count of left sensor when slider moves backward and + /// turned on the sensor + backward: i16, }, - forward: Type = "f32", - __forward: Meta = .{ - .description = - \\Magnet length based on sensor readings in forward direction. - , - .unit_short = "m", - .unit_long = "Meter", + off: struct { + /// Section count of left sensor when slider moves forward and + /// turned off the sensor + forward: i16, + /// Section count of right sensor when slider moves backward and + /// turned off the sensor + backward: i16, }, - } = .{}, - position: struct { - on: struct { - backward: Type = "f32", - __backward: Meta = .{ - .unit_short = "m", - .unit_long = "Meter", - }, - forward: Type = "f32", - __forward: Meta = .{ - .unit_short = "m", - .unit_long = "Meter", - }, - } = .{}, - } = .{}, - } = &.{ - .{}, .{}, .{}, .{}, .{}, .{}, - }, - - pub const Type = []const u8; - pub const Meta = struct { - hidden: bool = false, - description: ?[]const u8 = null, - unit_short: ?[]const u8 = null, - unit_long: ?[]const u8 = null, - }; -}; - -/// Driver ID -id: u16, - -/// CC-Link Station ID -station: u16, - -flags: Flags, - -line: struct { - /// Total number of axes in line. - axes: u16, -}, - -voltage: struct { - /// Target DC voltage. - target: u16, - /// Reference voltage used for warmup to find mechanical angle offset. - warmup: u16, - limit: struct { - /// Lower DC voltage limit, inclusive. - lower: u16, - /// Upper DC voltage limit, inclusive. - upper: u16, - }, -}, - -magnet: struct { - /// Magnet pole pair pitch in meters. - pitch: f32, - length: f32, -}, - -carrier: struct { - /// Carrier mass in decagrams (10 grams, or 1/100 of a kilogram). - mass: u16, - - /// Threshold conditions to determine carrier arrival at a position. - arrival: struct { - threshold: struct { - position: f32, - velocity: f32, }, }, +}; - cas: struct { - /// Minimum buffer to maintain between carriers, in millimeters. - buffer: u16, - acceleration: f32, - }, -}, - -mechanical_angle_offset: f32, - -axis: struct { - length: f32, - center: struct { - gain: struct { - current: CurrentGain, - velocity: VelocityGain, - position: PositionGain, - }, - }, - between: struct { - gain: struct { - current: CurrentGain, - velocity: VelocityGain, - position: PositionGain, - }, +pub const Flags = struct { + home_exists: bool, + has_neighbor: packed struct(u2) { + backward: bool, + forward: bool, }, -}, - -coil: struct { - /// Motor coil length in meters. - length: f32, - max_current: f32, - continuous_current: f32, - /// Resistance. - rs: f32, - /// Inductance. - ls: f32, - center: struct { - /// Force constant. - kf: f32, + use_axis: packed struct(u2) { + axis2: bool, + axis3: bool, }, - between: struct { - /// Force constant. - kf: f32, + calibration_spare: packed struct(u2) { + backward: bool, + forward: bool, }, - kbm: f32, -}, - -sensor: struct { - default_magnet_length: f32, - ignore_distance: f32, -}, - -zero_position: f32, + collision_avoidance: bool, + calibration_use: bool, + xts: bool, + /// Flip all sensors in a driver + flip: bool, + /// Swap all sensors in a driver + swap: bool, +}; -hall_sensors: [6]HallSensor, +pub const CcLinkSpeed = enum(u8) { + @"156 kbps", + @"625 kbps", + @"2.5 Mbps", + @"5 Mbps", + @"10 Mbps", + _, +}; pub const CurrentGain = struct { - /// Current P-gain. By default, inductance * Wcc. p: f32, - /// Current I-gain. By default, resistance * Wcc. i: f32, - /// Wcc denominator. Wcc = 2pi * (15000 / denominator). - denominator: u32, + denominator: u16, }; -pub const VelocityGain = struct { - /// Speed P-gain. By default, inertia * Wsc / torque constant. +pub const SpeedGain = struct { p: f32, - /// Speed I-gain. By default, Wpi * p. i: f32, - /// Wsc denominator. Wsc = Wcc / denominator. - denominator: u32, - /// Wpi denominator. Wpi = Wsc / denominator_pi. - denominator_pi: u32, - - // radius = magnet pole pair pitch / 2pi - // inertia = carrier mass * radius * radius - // torque constant = radius * force constant + denominator: u16, + denominator_pi: u16, }; pub const PositionGain = struct { - /// Position P-gain. By default, Wpc. p: f32, - /// Wpc denominator. Wpc = Wsc / denominator. - denominator: u32, + denominator: u16, }; -pub const HallSensor = struct { - magnet_length: struct { - backward: f32, - forward: f32, - }, - position: struct { - on: struct { - backward: f32, - forward: f32, - }, - }, +pub const Fsm = enum(u8) { + none = 0, + warm_up = 1, + warm_up_comp = 2, + warm_up_fault = 3, + curr_bias = 4, + curr_bias_comp = 5, + fwd_ramp = 8, + fwd_ramp_comp = 9, + fwd_ramp_fault = 10, + bwd_ramp = 11, + bwd_ramp_comp = 12, + bwd_ramp_fault = 13, + curr_step = 20, + curr_step_comp = 21, + curr_step_fault = 22, + speed_step = 23, + speed_step_comp = 24, + speed_step_fault = 25, + pos_step = 26, + pos_step_comp = 27, + pos_step_fault = 28, + pos_prof = 29, + pos_prof_comp = 30, + pos_prof_fault = 31, + fwd_calib = 32, + fwd_calib_comp = 33, + bwd_calib = 34, + bwd_calib_comp = 35, + speed_prof = 40, + speed_prof_comp = 41, + speed_prof_fault = 42, + fwd_slave = 43, + fwd_slave_comp = 44, + bwd_slave = 45, + bwd_slave_comp = 46, + over_charge = 50, + synch_com_error = 51, + _, }; -pub const Flags = packed struct { - has_neighbor: packed struct(u2) { - backward: bool, - forward: bool, - }, - uses_axis: packed struct(u2) { - axis2: bool, - axis3: bool, - }, - calibration_completed: bool, - rockwell_magnet: bool, - flip_sensors: packed struct(u3) { - axis1: bool, - axis2: bool, - axis3: bool, - }, - swap_sensors: packed struct(u3) { - axis1: bool, - axis2: bool, - axis3: bool, - }, - - pub fn toInt(self: Flags) u16 { - return @as(u12, @bitCast(self)); - } - - pub fn fromInt(int: u12) Flags { - return @bitCast(int); - } +pub const Entrance = enum(u8) { + none = 0, + left = 1, + right = 2, + _, }; -/// Set value in provided field with value from Config. -pub fn setField(self: Config, field: *Field, opts: struct { - index: usize = 0, -}) void { - switch (field.*) { - inline else => |*value, kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - value.* = Field.getInner( - self.hall_sensors[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - ); - } else if (comptime std.mem.eql( - u8, - "axes", - name[0..index], - )) { - value.* = Field.getInner( - self.axes[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - ); - } else { - value.* = Field.getInner( - self, - @FieldType(Field, name), - name, - ); - } - } else { - value.* = Field.getInner(self, @FieldType(Field, name), name); - } - }, - } -} - -pub fn calcCurrentGain( - self: *const @This(), - denominator: u32, -) CurrentGain { - const wcc = drivercom.gain.current.wcc(denominator); - return .{ - .denominator = denominator, - .p = @floatCast( - drivercom.gain.current.p(self.coil.ls, wcc), - ), - .i = @floatCast( - drivercom.gain.current.i(self.coil.rs, wcc), - ), - }; -} - -pub fn calcVelocityGain( - self: *const @This(), - current_gain: *const CurrentGain, - kf: f32, - denominator: u32, - denominator_pi: u32, -) VelocityGain { - const wcc = drivercom.gain.current.wcc(current_gain.denominator); - const radius = drivercom.gain.velocity.radius(self.magnet.pitch); - const inertia = drivercom.gain.velocity.inertia( - @as(f64, @floatFromInt(self.carrier.mass)) / 100.0, - radius, - ); - const torque_constant = - drivercom.gain.velocity.torqueConstant(kf, radius); - const wsc = drivercom.gain.velocity.wsc(denominator, wcc); - const wpi = drivercom.gain.velocity.wpi(denominator_pi, wsc); - const p = drivercom.gain.velocity.p(inertia, torque_constant, wsc); - - return .{ - .p = @floatCast(p), - .i = @floatCast(drivercom.gain.velocity.i(p, wpi)), - .denominator = denominator, - .denominator_pi = denominator_pi, - }; -} - -pub fn calcPositionGain( - _: *const @This(), - current_gain: *const CurrentGain, - velocity_gain: *const VelocityGain, - denominator: u32, -) PositionGain { - const wcc = drivercom.gain.current.wcc(current_gain.denominator); - const wsc = - drivercom.gain.velocity.wsc(velocity_gain.denominator, wcc); - - const wpc = drivercom.gain.position.wpc(denominator, wsc); - const p = drivercom.gain.position.p(wpc); - - return .{ - .p = @floatCast(p), - .denominator = denominator, - }; -} - -const OldConfig = @import("OldConfig.zig"); -fn migrateWalkFields(new: anytype, old: anytype) void { - const ti = @typeInfo(@typeInfo(@TypeOf(new)).pointer.child); - const old_type = @TypeOf(old); - const old_ti = @typeInfo(old_type); - - if (comptime std.meta.activeTag(old_ti) != std.meta.activeTag(ti)) return; - - switch (comptime ti) { - .@"struct" => |s_ti| { - inline for (s_ti.fields) |field| { - if (comptime @hasField(old_type, field.name)) { - const child_ti = @typeInfo(field.type); - switch (comptime child_ti) { - .@"struct", .array => { - migrateWalkFields( - &@field(new, field.name), - @field(old, field.name), - ); - }, - else => { - const oc_type = @TypeOf(@field(old, field.name)); - if (comptime field.type == oc_type) { - @field(new, field.name) = - @field(old, field.name); - } - }, - } - } - } - }, - .array => |a_ti| { - inline for (0..@min(a_ti.len, old_ti.array.len)) |i| { - const elem_ti = @typeInfo(a_ti.child); - switch (comptime elem_ti) { - .@"struct", .array => { - migrateWalkFields(&new[i], old[i]); - }, - else => { - if (comptime a_ti.child == old_ti.array.child) { - new[i] = old[i]; - } - }, - } - } - }, - else => { - @compileError( - std.fmt.comptimePrint( - "migrate walked unexpected field kind: {}, {}", - .{ - @TypeOf(new), - @TypeOf(old), - }, - ), - ); - }, - } -} -pub fn migrate(old: OldConfig) Config { - var result: Config = undefined; - if (comptime @import("builtin").is_test) { - @memset(std.mem.asBytes(&result), 0xFF); - } - - // Migrate identical fields. - migrateWalkFields(&result, old); - - // Migrate changed fields. - - // Default-initialize new fields. - result.flags.swap_sensors.axis1 = false; - result.flags.swap_sensors.axis2 = false; - result.flags.swap_sensors.axis3 = false; - - return result; -} - -test migrate { - const old = std.mem.zeroes(OldConfig); - var new = std.mem.zeroes(Config); - new.flags.swap_sensors.axis1 = false; - new.flags.swap_sensors.axis2 = false; - new.flags.swap_sensors.axis3 = false; - - const migrated = migrate(old); - - try std.testing.expectEqualDeep(new, migrated); -} - test { std.testing.refAllDecls(@This()); } diff --git a/src/_Config/Info.zig b/src/Config/Info.zig similarity index 100% rename from src/_Config/Info.zig rename to src/Config/Info.zig diff --git a/src/Log.zig b/src/Log.zig index c4d5b30..23974db 100644 --- a/src/Log.zig +++ b/src/Log.zig @@ -1,514 +1,152 @@ +//! This module represents driver's logging configuration. The driver runs the +//! command while recording the configured items of the selected axes at 15000 +//! samples per second. const std = @import("std"); -const drivercom = @import("drivercom.zig"); - -cycles: u32 = 0, -config: Config = .{}, -axis: [3]bool = .{false} ** 3, -hall_sensor: [6]bool = .{false} ** 6, -carrier: [4]u12 = .{0} ** 4, -start: struct { - kind: Start = .immediate, - combinator: Start.Combinator = .@"or", - hall_sensor: [6]bool = .{false} ** 6, - carrier: [4]u12 = .{0} ** 4, -} = .{}, - -pub const Config = packed struct(u32) { - // Driver log. - driver: packed struct { - cycle: bool = false, - cycle_time: bool = false, - vdc: bool = false, - com_bwd_sent: bool = false, - com_bwd_arrived: bool = false, - com_fwd_sent: bool = false, - com_fwd_arrived: bool = false, - com_bwd_sent_cycles: bool = false, - com_fwd_sent_cycles: bool = false, - } = .{}, - - // Sensor log. - sensor: packed struct { - alarm: bool = false, - valid: bool = false, - active: bool = false, - angle: bool = false, - average_angle: bool = false, - distance: bool = false, - velocity: bool = false, - } = .{}, - - // Axis log. - axis: packed struct { - current_d: bool = false, - current_q: bool = false, - reference_current_d: bool = false, - reference_current_q: bool = false, - carrier_id: bool = false, - carrier_position: bool = false, - carrier_state: bool = false, - average_angle_diff: bool = false, - carrier_reference_velocity: bool = false, - carrier_velocity: bool = false, - } = .{}, - _: u6 = 0, -}; - -/// Provides metadata for the field, type. -pub const Info = struct { - cycles: []const u8 = "u32", - __cycles: Meta = .{ - .description = - \\Duration of logging process (15000 cycles per second). - , - }, - config: struct { - // Driver log. - driver: struct { - cycle: []const u8 = "bool", - __cycle: Meta = .{ - .description = - \\Number of performed internal loop (resets every 15000 cycles). - , - }, - cycle_time: []const u8 = "bool", - __cycle_time: Meta = .{ - .description = - \\Number of performed internal loop per one control loop. - , - }, - vdc: []const u8 = "bool", - __vdc: Meta = .{ - .description = - \\Supplied voltage to Driver. - , - }, - com_bwd_sent: []const u8 = "bool", - __com_bwd_sent: Meta = .{ - .description = - \\Sent Driver state to backward Driver. - , - }, - com_bwd_arrived: []const u8 = "bool", - __com_bwd_arrived: Meta = .{ - .description = - \\Driver state of backward Driver. - , - }, - com_fwd_sent: []const u8 = "bool", - __com_fwd_sent: Meta = .{ - .description = - \\Sent Driver state to forward Driver. - , - }, - com_fwd_arrived: []const u8 = "bool", - __com_fwd_arrived: Meta = .{ - .description = - \\Driver state of forward Driver. - , - }, - com_bwd_sent_cycles: []const u8 = "bool", - __com_bwd_sent_cycles: Meta = .{ - .description = - \\Elapsed cycles since last Driver state sent to backward Driver. - , - }, - com_fwd_sent_cycles: []const u8 = "bool", - __com_fwd_sent_cycles: Meta = .{ - .description = - \\Elapsed cycles since last Driver state sent to forward Driver. - , - }, - } = .{}, - __driver: Meta = .{ - .description = - \\Driver properties to be logged. - , - }, - - // Sensor log. - sensor: struct { - alarm: []const u8 = "bool", - __alarm: Meta = .{ - .description = - \\Magnet is detected by Sensor. - , - }, - valid: []const u8 = "bool", - __valid: Meta = .{ - .description = - \\Sensor value is valid to be used. - , - }, - active: []const u8 = "bool", - __active: Meta = .{ - .description = - \\Sensor value is actively used for carrier state. - , - }, - angle: []const u8 = "bool", - __angle: Meta = .{ - .description = - \\Detected magnet angle by Sensor. - , - }, - average_angle: []const u8 = "bool", - __average_angle: Meta = .{ - .description = - \\Detected average magnet angle by Sensor. - , - .hidden = true, - }, - distance: []const u8 = "bool", - __distance: Meta = .{ - .description = - \\Distance traveled by detected magnet on Sensor. - , - }, - velocity: []const u8 = "bool", - __velocity: Meta = .{ - .description = - \\Velocity of traveling magnet on Sensor. - , - }, - } = .{}, - __sensor: Meta = .{ - .description = - \\Sensor properties to be logged. - , - }, - - // Axis log. - axis: struct { - current_d: []const u8 = "bool", - __current_d: Meta = .{ - .description = - \\Supplied d current to Axis' motor. - , - }, - current_q: []const u8 = "bool", - __current_q: Meta = .{ - .description = - \\Supplied q current to Axis' motor. - , - }, - reference_current_d: []const u8 = "bool", - __reference_current_d: Meta = .{ - .description = - \\Reference value for d current to Axis' motor. - , - }, - reference_current_q: []const u8 = "bool", - __reference_current_q: Meta = .{ - .description = - \\Reference value for q current to Axis' motor. - , - }, - carrier_id: []const u8 = "bool", - __carrier_id: Meta = .{ - .description = - \\Detected Carrier ID on Axis. - , - }, - carrier_position: []const u8 = "bool", - __carrier_position: Meta = .{ - .description = - \\Carrier position relative to Line's zero position. - , - }, - carrier_state: []const u8 = "bool", - __carrier_state: Meta = .{ - .description = - \\State of detected Carrier on Axis. - , - }, - average_angle_diff: []const u8 = "bool", - __average_angle_diff: Meta = .{ - .description = - \\Average angle difference between two Sensors of Axis. - , - }, - carrier_reference_velocity: []const u8 = "bool", - __carrier_reference_velocity: Meta = .{ - .description = - \\Reference value for Carrier velocity. - , - }, - carrier_velocity: []const u8 = "bool", - __carrier_velocity: Meta = .{ - .description = - \\Velocity of detected Carrier on Axis. - , - }, - } = .{}, - _: []const u8 = "u6", - __axis: Meta = .{ - .description = - \\Axis properties to be logged. - , - }, - } = .{}, - - axis: [3][]const u8 = .{"bool"} ** 3, - __axis: Meta = .{ - .description = - \\Axes to be included in the logging process. - , - }, - hall_sensor: [6][]const u8 = .{"bool"} ** 6, - __hall_sensor: Meta = .{ - .description = - \\Hall Sensors to be included in the logging process. - , - }, - carrier: [4][]const u8 = .{"u12"} ** 4, - __carrier: Meta = .{ - .hidden = true, - .description = - \\Carriers IDs to be included in the logging process. - , - }, - - start: struct { - kind: []const u8 = "enum", - __kind: Meta = .{ - .description = - \\Event to trigger logging process. - , - }, - combinator: []const u8 = "enum", - __combinator: Meta = .{ - .description = - \\Specifies if one or all conditions must be met to trigger logging. - , - }, - hall_sensor: [6][]const u8 = .{"bool"} ** 6, - __hall_sensor: Meta = .{ - .description = - \\Hall Sensors used to trigger logging. - , - }, - carrier: [4][]const u8 = .{"u12"} ** 4, - __carrier: Meta = .{ - .description = - \\Carriers used to trigger logging. - , - }, - } = .{}, - __start: Meta = .{ - .description = - \\Configuration to start logging process. - , - }, - - pub const Meta = struct { - hidden: bool = false, - description: ?[]const u8 = null, - unit_short: ?[]const u8 = null, - unit_long: ?[]const u8 = null, +const Fsm = @import("Config.zig").Fsm; + +/// Number of cycles per 1 second in the firmware. +pub const FREQUENCY: u32 = 15000; + +/// Logging duration (>0 s). +duration: f32, +/// Items to be logged on every selected axis. +config: Config, +/// Axes to be logged. +axes: [3]bool, +/// Command run by the driver while logging. +command: Command, + +pub const Command = union(Kind) { + // TODO: Support the remaining commands SHDrv provide: + // - Offline commands with applied voltage (V) and applied angle (turn): + // warm_up = 1, move_home = 2, move_forward = 3, + // move_backward = 4, restart = 5, move_synchronous = 6, move_dq = 7 + // - current_step = 10 with Q current step (A) + // - speed_step = 11 with speed step (m/s) + // - position_step = 12 with position step + /// Offline command that logs the driver as it stands. + current_state: struct {}, + position_profile: Profile, + speed_profile: Profile, + + pub const Kind = enum(u8) { + current_state = 0, + position_profile = 13, + speed_profile = 14, }; -}; -pub const Start = enum(u3) { - immediate = 0, - sensor_on = 1, - sensor_off = 2, - carrier_present = 3, - carrier_absent = 4, - - pub const Combinator = enum(u1) { @"and", @"or" }; + pub const Profile = struct { + /// ID of the slider targeted by the command (>0). + slider_id: u8, + // Unit is unconfirmed: SHDrv labels it m but limits it as rad. + /// Travel distance. Negative distance travels backward. + distance: f32, + /// Percentage of the maximum speed (%). + speed: f32, + /// Percentage of the maximum acceleration (%). + acceleration: f32, + }; }; -pub const Status = enum(u2) { - stopped = 0, - started = 1, - waiting = 2, - invalid = 3, +/// Field order is the item index. The response tags the item with its index +/// plus one. +pub const Config = packed struct(u32) { + /// Output position of the profile. + profile_position: bool, + /// Applied speed of the profile. + profile_speed: bool, + /// Output of the position controller. + position_output: bool, + /// Reference of the position controller (rad). + position_reference: bool, + /// Feedback of the position controller (rad). + position_feedback: bool, + /// Reference of the speed controller (rad/s). + speed_reference: bool, + /// Feedback of the speed controller (rad/s). + speed_feedback: bool, + current_q_reference: bool, + current_q_feedback: bool, + current_d_feedback: bool, + voltage_q_reference: bool, + voltage_d_reference: bool, + voltage_q_feedforward: bool, + voltage_d_feedforward: bool, + /// Calculation time of the control loop. + calculation_time: bool, + theta_section: bool, + electrical_angular_speed: bool, + in_position: bool, + current_a: bool, + current_b: bool, + current_c: bool, + voltage_alpha: bool, + voltage_beta: bool, + /// State of the slider's FSM. + slider_state: bool, + applied_angle: bool, + encoder_angle: bool, + voltage_a: bool, + voltage_b: bool, + voltage_c: bool, + vector_sector: bool, + hall_alarm_left: bool, + hall_alarm_right: bool, }; -pub const Tag: type = b: { - const tag_type = u5; - var tag_value: tag_type = 0; - const enum_fields = unwrapConfig(Config, &tag_value, ""); - var field_names: [enum_fields.len][]const u8 = undefined; - var field_values: [enum_fields.len]tag_type = undefined; - for (enum_fields, 0..) |field, i| { - field_names[i] = field.name; - field_values[i] = field.value; +/// Logged item, valued by its index in `Config`. +pub const Item = Item: { + const fields = @typeInfo(Config).@"struct".fields; + var names: [fields.len][]const u8 = undefined; + var values: [fields.len]u5 = undefined; + for (fields, &names, &values, 0..) |field, *name, *value, index| { + name.* = field.name; + value.* = index; } - break :b @Enum(tag_type, .exhaustive, &field_names, &field_values); -}; - -pub const TagKind = enum { - driver, - axis, - sensor, - carrier, + break :Item @Enum(u5, .exhaustive, &names, &values); }; -/// Returns size in bytes of data tag. -pub fn tagSize(tag: Tag) u3 { - return switch (tag) { - .@"driver.cycle", - .@"driver.cycle_time", - .@"driver.vdc", - .@"sensor.alarm", - .@"sensor.valid", - .@"sensor.active", - .@"axis.carrier_id", - .@"driver.com_bwd_sent", - .@"driver.com_bwd_arrived", - .@"driver.com_fwd_sent", - .@"driver.com_fwd_arrived", - .@"driver.com_bwd_sent_cycles", - .@"driver.com_fwd_sent_cycles", - .@"axis.carrier_state", - => 2, - .@"sensor.angle", - .@"sensor.distance", - .@"sensor.average_angle", - .@"sensor.velocity", - .@"axis.current_d", - .@"axis.current_q", - .@"axis.reference_current_d", - .@"axis.reference_current_q", - .@"axis.carrier_position", - .@"axis.average_angle_diff", - .@"axis.carrier_reference_velocity", - .@"axis.carrier_velocity", - => 4, - }; -} - -pub fn tagParse(comptime tag: Tag, data: []const u8) TagType(tag) { - std.debug.assert(data.len == tagSize(tag)); - switch (tag) { - .@"driver.cycle", - .@"driver.cycle_time", - .@"sensor.angle", - .@"sensor.average_angle", - .@"sensor.distance", - .@"sensor.velocity", - .@"axis.current_d", - .@"axis.current_q", - .@"axis.reference_current_d", - .@"axis.reference_current_q", - .@"axis.carrier_id", - .@"axis.carrier_position", - .@"axis.average_angle_diff", - .@"driver.com_bwd_sent", - .@"driver.com_bwd_arrived", - .@"driver.com_fwd_sent", - .@"driver.com_fwd_arrived", - .@"driver.com_bwd_sent_cycles", - .@"driver.com_fwd_sent_cycles", - .@"axis.carrier_state", - .@"axis.carrier_reference_velocity", - .@"axis.carrier_velocity", - => { - return std.mem.bytesToValue(TagType(tag), data); - }, - .@"driver.vdc" => { - const result: u16 = std.mem.bytesToValue(u16, data); - var result_f: f32 = @floatFromInt(result); - result_f /= 100.0; - return result_f; - }, - .@"sensor.alarm", - .@"sensor.valid", - .@"sensor.active", - => { - return data[0] != 0; - }, - } -} - -test tagParse { - const vdc: u16 = 12089; - try std.testing.expectEqual( - 120.89, - tagParse(.@"driver.vdc", std.mem.asBytes(&vdc)), - ); -} - -pub fn TagType(comptime tag: Tag) type { - return switch (tag) { - .@"driver.com_bwd_sent", - .@"driver.com_bwd_arrived", - .@"driver.com_fwd_sent", - .@"driver.com_fwd_arrived", - => drivercom.DriverMessage, - .@"axis.carrier_state", - => drivercom.CarrierState, - .@"driver.cycle", - .@"driver.cycle_time", - .@"axis.carrier_id", - .@"driver.com_bwd_sent_cycles", - .@"driver.com_fwd_sent_cycles", - => u16, - .@"sensor.alarm", - .@"sensor.valid", - .@"sensor.active", - => bool, - .@"driver.vdc", - .@"sensor.angle", - .@"sensor.average_angle", - .@"sensor.distance", - .@"sensor.velocity", - .@"axis.current_d", - .@"axis.current_q", - .@"axis.reference_current_d", - .@"axis.reference_current_q", - .@"axis.carrier_position", - .@"axis.average_angle_diff", - .@"axis.carrier_reference_velocity", - .@"axis.carrier_velocity", +/// Value type of a logged item. +pub fn ConfigType(comptime item: Item) type { + return switch (item) { + .profile_position, + .profile_speed, + .position_output, + .position_reference, + .position_feedback, + .speed_reference, + .speed_feedback, + .current_q_reference, + .current_q_feedback, + .current_d_feedback, + .voltage_q_reference, + .voltage_d_reference, + .voltage_q_feedforward, + .voltage_d_feedforward, + .calculation_time, + .electrical_angular_speed, + .current_a, + .current_b, + .current_c, + .voltage_alpha, + .voltage_beta, + .applied_angle, + .encoder_angle, + .voltage_a, + .voltage_b, + .voltage_c, => f32, + .theta_section, + .vector_sector, + => i32, + .slider_state => Fsm, + .in_position, + .hall_alarm_left, + .hall_alarm_right, + => bool, }; } -pub fn tagKind(tag: Tag) TagKind { - if (@tagName(tag)[0] == 'd') return .driver; - if (@tagName(tag)[0] == 's') return .sensor; - if (@tagName(tag)[0] == 'a') return .axis; - if (@tagName(tag)[0] == 'v') return .carrier; - - unreachable; -} - -fn unwrapConfig( - T: anytype, - tag_value: *u5, - name: []const u8, -) []const std.builtin.Type.EnumField { - const ti = @typeInfo(T).@"struct"; - var result: []const std.builtin.Type.EnumField = &.{}; - for (ti.fields) |field| { - const field_ti = @typeInfo(field.type); - switch (field_ti) { - .@"struct" => { - result = result ++ unwrapConfig( - field.type, - tag_value, - if (name.len > 0) - name ++ field.name ++ "." - else - field.name ++ ".", - ); - }, - .bool => { - result = result ++ .{std.builtin.Type.EnumField{ - .name = name ++ field.name, - .value = tag_value.*, - }}; - tag_value.* += 1; - }, - else => {}, - } - } - return result; -} - test { std.testing.refAllDecls(@This()); } diff --git a/src/_Message.zig b/src/Message.zig similarity index 99% rename from src/_Message.zig rename to src/Message.zig index 455ce8e..587e8ae 100644 --- a/src/_Message.zig +++ b/src/Message.zig @@ -7,8 +7,8 @@ const std = @import("std"); const builtin = @import("builtin"); -const Config = @import("_Config.zig"); -const Log = @import("_Log.zig"); +const Config = @import("Config.zig"); +const Log = @import("Log.zig"); const Message = @This(); const STX = 0x02; diff --git a/src/OldConfig.zig b/src/OldConfig.zig deleted file mode 100644 index c9b8239..0000000 --- a/src/OldConfig.zig +++ /dev/null @@ -1,462 +0,0 @@ -//! This module represents driver's configuration. -const std = @import("std"); -const drivercom = @import("drivercom.zig"); - -pub const MAX_AXES = 3; - -const NewConfig = @import("Config.zig"); -const Config = @This(); - -/// `drivercom` version for this `Config` struct. -pub const version: std.SemanticVersion = .{ - .major = 0, - .minor = 4, - .patch = 0, -}; -/// Driver configuration field. These fields are used directly in messages -/// with driver; their ordering matches firmware field kind ordering. -/// Names reflect nested structure within `Config` struct, and types represent -/// the type of value in the `Config` struct. -pub const Field = union(enum(u16)) { - id: u16, - station: u16, - flags: Flags, - @"line.axes": u16, - @"voltage.target": u16, - @"voltage.warmup": u16, - @"voltage.limit.lower": u16, - @"voltage.limit.upper": u16, - @"magnet.pitch": f32, - @"magnet.length": f32, - @"carrier.mass": u16, - @"carrier.arrival.threshold.position": f32, - @"carrier.arrival.threshold.velocity": f32, - @"carrier.cas.buffer": u16, - @"carrier.cas.acceleration": f32, - mechanical_angle_offset: f32, - @"axis.length": f32, - @"coil.length": f32, - @"coil.max_current": f32, - @"coil.continuous_current": f32, - @"coil.rs": f32, - @"coil.ls": f32, - @"coil.center.kf": f32, - @"coil.between.kf": f32, - @"coil.kbm": f32, - @"sensor.default_magnet_length": f32, - @"sensor.ignore_distance": f32, - zero_position: f32, - @"axis.center.gain.current.p": f32, - @"axis.center.gain.current.i": f32, - @"axis.center.gain.current.denominator": u32, - @"axis.center.gain.velocity.p": f32, - @"axis.center.gain.velocity.i": f32, - @"axis.center.gain.velocity.denominator": u32, - @"axis.center.gain.velocity.denominator_pi": u32, - @"axis.center.gain.position.p": f32, - @"axis.center.gain.position.denominator": u32, - @"axis.between.gain.current.p": f32, - @"axis.between.gain.current.i": f32, - @"axis.between.gain.current.denominator": u32, - @"axis.between.gain.velocity.p": f32, - @"axis.between.gain.velocity.i": f32, - @"axis.between.gain.velocity.denominator": u32, - @"axis.between.gain.velocity.denominator_pi": u32, - @"axis.between.gain.position.p": f32, - @"axis.between.gain.position.denominator": u32, - @"hall_sensors.magnet_length.backward": f32, - @"hall_sensors.magnet_length.forward": f32, - @"hall_sensors.position.on.backward": f32, - @"hall_sensors.position.on.forward": f32, - @"hall_sensors.position.off.backward": f32, - @"hall_sensors.position.off.forward": f32, - - pub const Kind = std.meta.Tag(@This()); - - fn setInner( - parent: anytype, - value: anytype, - comptime access: []const u8, - ) void { - const nested = comptime std.mem.indexOfScalar(u8, access, '.'); - if (nested) |index| { - setInner( - &@field(parent, access[0..index]), - value, - access[index + 1 ..], - ); - } else { - @field(parent, access) = value; - } - } - - test setInner { - var config: Config = undefined; - setInner(&config, 1360, "carrier.mass"); - try std.testing.expectEqual(1360, config.carrier.mass); - } - - fn getInner( - parent: anytype, - comptime return_type: type, - comptime access: []const u8, - ) return_type { - const nested = comptime std.mem.indexOfScalar(u8, access, '.'); - if (nested) |index| { - return getInner( - @field(parent, access[0..index]), - return_type, - access[index + 1 ..], - ); - } else { - return @field(parent, access); - } - } - - test getInner { - var config: Config = undefined; - config.carrier.mass = 1320; - const mass: u16 = getInner(config, u16, "carrier.mass"); - try std.testing.expectEqual(config.carrier.mass, mass); - - config.hall_sensors[3].position.off.forward = 17.48; - try std.testing.expectEqual( - config.hall_sensors[3].position.off.forward, - getInner(config.hall_sensors[3], f32, "position.off.forward"), - ); - } - - /// Set a field in provided Config struct with value in Field. - pub fn setConfig(self: Field, config: *Config, opts: struct { - index: usize = 0, - }) void { - switch (self) { - inline else => |value, kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - const hall_sensor: *HallSensor = - &config.hall_sensors[opts.index]; - setInner(hall_sensor, value, name[index + 1 ..]); - } else { - setInner(config, value, name); - } - } else { - setInner(config, value, name); - } - }, - } - } - - pub fn fromConfig( - config: Config, - field: Field.Kind, - opts: struct { - index: usize = 0, - }, - ) Field { - switch (field) { - inline else => |kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - return @unionInit(Field, name, getInner( - config.hall_sensors[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - )); - } else if (comptime std.mem.eql( - u8, - "axes", - name[0..index], - )) { - return @unionInit(Field, name, getInner( - config.axes[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - )); - } - } - return @unionInit(Field, name, getInner( - config, - @FieldType(Field, name), - name, - )); - }, - } - } - - test fromConfig { - var config: Config = std.mem.zeroInit(Config, .{}); - { - config.axis.center.gain.position.p = 32.6; - const field = fromConfig( - config, - .@"axis.center.gain.position.p", - .{}, - ); - - try std.testing.expectEqual( - config.axis.center.gain.position.p, - field.@"axis.center.gain.position.p", - ); - } - - { - config.hall_sensors[4].position.on.forward = 0.9534; - const field = fromConfig( - config, - .@"hall_sensors.position.on.forward", - .{ .index = 4 }, - ); - - try std.testing.expectEqual( - config.hall_sensors[4].position.on.forward, - field.@"hall_sensors.position.on.forward", - ); - } - } - - pub fn toConfig( - field: Field, - opts: struct { - /// Existing Config, used as base for returned "modified" Config - config: Config = std.mem.zeroInit(Config, .{}), - index: usize = 0, - }, - ) Config { - var config: Config = opts.config; - switch (field) { - inline else => |value, kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - const hall_sensor: *HallSensor = - &config.hall_sensors[opts.index]; - setInner(hall_sensor, value, name[index + 1 ..]); - } else { - setInner(&config, value, name); - } - } else { - setInner(&config, value, name); - } - }, - } - return config; - } - - test toConfig { - var config: Config = std.mem.zeroInit(Config, .{ .id = 3 }); - const field: Field = .{ .station = 15 }; - - config = field.toConfig(.{ .config = config }); - - try std.testing.expectEqual(3, config.id); - try std.testing.expectEqual(15, config.station); - } -}; - -/// Driver ID -id: u16, - -/// CC-Link Station ID -station: u16, - -flags: Flags, - -line: struct { - /// Total number of axes in line. - axes: u16, -}, - -voltage: struct { - /// Target DC voltage. - target: u16, - /// Reference voltage used for warmup to find mechanical angle offset. - warmup: u16, - limit: struct { - /// Lower DC voltage limit, inclusive. - lower: u16, - /// Upper DC voltage limit, inclusive. - upper: u16, - }, -}, - -magnet: struct { - /// Magnet pole pair pitch in meters. - pitch: f32, - length: f32, -}, - -carrier: struct { - /// Carrier mass in decagrams (10 grams, or 1/100 of a kilogram). - mass: u16, - - /// Threshold conditions to determine carrier arrival at a position. - arrival: struct { - threshold: struct { - position: f32, - velocity: f32, - }, - }, - - cas: struct { - /// Minimum buffer to maintain between carriers, in millimeters. - buffer: u16, - acceleration: f32, - }, -}, - -mechanical_angle_offset: f32, - -axis: struct { - length: f32, - center: struct { - gain: struct { - current: CurrentGain, - velocity: VelocityGain, - position: PositionGain, - }, - }, - between: struct { - gain: struct { - current: CurrentGain, - velocity: VelocityGain, - position: PositionGain, - }, - }, -}, - -coil: struct { - /// Motor coil length in meters. - length: f32, - max_current: f32, - continuous_current: f32, - /// Resistance. - rs: f32, - /// Inductance. - ls: f32, - center: struct { - /// Force constant. - kf: f32, - }, - between: struct { - /// Force constant. - kf: f32, - }, - kbm: f32, -}, - -sensor: struct { - default_magnet_length: f32, - ignore_distance: f32, -}, - -zero_position: f32, - -hall_sensors: [6]HallSensor, - -pub const HallSensor = struct { - magnet_length: struct { - backward: f32, - forward: f32, - }, - position: struct { - on: struct { - backward: f32, - forward: f32, - }, - off: struct { - backward: f32, - forward: f32, - }, - }, -}; - -pub const Flags = packed struct { - has_neighbor: packed struct(u2) { - backward: bool, - forward: bool, - }, - uses_axis: packed struct(u2) { - axis2: bool, - axis3: bool, - }, - calibration_completed: bool, - rockwell_magnet: bool, - flip_sensors: packed struct(u3) { - axis1: bool, - axis2: bool, - axis3: bool, - }, - - pub fn toInt(self: Flags) u16 { - return @as(u9, @bitCast(self)); - } - - pub fn fromInt(int: u9) Flags { - return @bitCast(int); - } -}; - -/// Set value in provided field with value from Config. -pub fn setField(self: Config, field: *Field, opts: struct { - index: usize = 0, -}) void { - switch (field.*) { - inline else => |*value, kind| { - const name = @tagName(kind); - const nested = comptime std.mem.indexOfScalar(u8, name, '.'); - if (comptime nested) |index| { - if (comptime std.mem.eql( - u8, - "hall_sensors", - name[0..index], - )) { - value.* = Field.getInner( - self.hall_sensors[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - ); - } else if (comptime std.mem.eql( - u8, - "axes", - name[0..index], - )) { - value.* = Field.getInner( - self.axes[opts.index], - @FieldType(Field, name), - name[index + 1 ..], - ); - } else { - value.* = Field.getInner( - self, - @FieldType(Field, name), - name, - ); - } - } else { - value.* = Field.getInner(self, @FieldType(Field, name), name); - } - }, - } -} - -pub const CurrentGain = NewConfig.CurrentGain; -pub const VelocityGain = NewConfig.VelocityGain; -pub const PositionGain = NewConfig.PositionGain; diff --git a/src/_Config.zig b/src/_Config.zig deleted file mode 100644 index cc2550e..0000000 --- a/src/_Config.zig +++ /dev/null @@ -1,257 +0,0 @@ -//! This module represents driver's configuration. -const std = @import("std"); -const Config = @This(); -pub const Info = @import("_Config/Info.zig"); - -/// Driver ID -id: u16, -/// CC-Link Station ID -station: u16, -/// CC-Link baud rate -cc_link_speed: CcLinkSpeed, -flags: Flags, -line: struct { - /// Total number of axes in line. - axes: u32, - /// Axis length used in the whole line. - axis_length: f32, - /// Slider configuration in the whole line. - slider: struct { - mass: f32, - length: f32, - }, - /// Magnet pitch (m) - magnet_pitch: f32, -}, -voltage_warmup: f32, -retry_count: u8, -hall_cutoff_freq: f32, -overcurrent_timeout: f32, -pos_offset: f32, -right_sensor_distance: f32, -angle_offset: f32, // Can be changed from `set_calibration_info` command -section_count: SectionCount, -axes: [3]Axis, -state: State, // Immutable fields, written from driver - -/// Immutable states of driver -pub const State = struct { - servo_enabled: bool, - cc_link_enabled: bool, - calibrated: bool, - vdc: f32, - thermo: f32, - sys_version: f32, - sensor: struct { - home: u8, - id0: u8, - id1: u8, - id2: u8, - }, - synchronization_request_position: struct { - forward: f32, - backward: f32, - }, - section_count: struct { - /// Count at which the secondary axis is switched off, depends on the - /// slider movement direction. - deactivate_secondary: struct { - left: struct { - forward: struct { axis2: i16, axis3: i16 }, - backward: struct { axis1: i16, axis2: i16 }, - }, - right: struct { - forward: struct { axis2: i16, axis3: i16 }, - backward: struct { axis1: i16, axis2: i16 }, - }, - }, - /// Count at which the axis is activated as the primary, depends on - /// the slider movement direction. - activate_primary: struct { - left: struct { - forward: struct { axis1: i16, axis2: i16, axis3: i16 }, - backward: struct { axis1: i16, axis2: i16, axis3: i16 }, - }, - right: struct { - forward: struct { axis1: i16, axis2: i16, axis3: i16 }, - backward: struct { axis1: i16, axis2: i16, axis3: i16 }, - }, - }, - }, - axes: [3]State.Axis, - - pub const Axis = struct { - enabled: bool, - primary: bool, - slider: struct { id: u8, state: Fsm }, - entrance: Entrance, - overcharge_state: Fsm, - pitch_count: i16, - mechanical_position: f32, - // a and b needs confirmation. - bias: struct { a: f32, b: f32 }, - }; -}; - -pub const SectionCount = struct { - /// Count at which the next axis in the travel direction becomes the - /// primary and the current axis becomes the secondary. - skip: i16, - max: struct { axis1: i16, axis2: i16, axis3: i16 }, -}; - -pub const Axis = struct { - rs: f32, - ls: f32, - kf: f32, - kbm: f32, - max_current: f32, - continuous_current: f32, - gain: struct { - current: CurrentGain, - speed: SpeedGain, - position: PositionGain, - }, - arrival_threshold: f32, - base_position: f32, - sensor: struct { - position: struct { - on: struct { - /// Position of right sensor when slider moves forward and - /// turned on the sensor - forward: f32, - /// Position of left sensor when slider moves backward and - /// turned on the sensor - backward: f32, - }, - off: struct { - /// Position of left sensor when slider moves forward and - /// turned off the sensor - forward: f32, - /// Position of right sensor when slider moves backward and - /// turned off the sensor - backward: f32, - }, - }, - section_count: struct { - on: struct { - /// Section count of right sensor when slider moves forward and - /// turned on the sensor - forward: i16, - /// Section count of left sensor when slider moves backward and - /// turned on the sensor - backward: i16, - }, - off: struct { - /// Section count of left sensor when slider moves forward and - /// turned off the sensor - forward: i16, - /// Section count of right sensor when slider moves backward and - /// turned off the sensor - backward: i16, - }, - }, - }, -}; - -pub const Flags = struct { - home_exists: bool, - has_neighbor: packed struct(u2) { - backward: bool, - forward: bool, - }, - use_axis: packed struct(u2) { - axis2: bool, - axis3: bool, - }, - calibration_spare: packed struct(u2) { - backward: bool, - forward: bool, - }, - collision_avoidance: bool, - calibration_use: bool, - xts: bool, - /// Flip all sensors in a driver - flip: bool, - /// Swap all sensors in a driver - swap: bool, -}; - -pub const CcLinkSpeed = enum(u8) { - @"156 kbps", - @"625 kbps", - @"2.5 Mbps", - @"5 Mbps", - @"10 Mbps", - _, -}; - -pub const CurrentGain = struct { - p: f32, - i: f32, - denominator: u16, -}; - -pub const SpeedGain = struct { - p: f32, - i: f32, - denominator: u16, - denominator_pi: u16, -}; - -pub const PositionGain = struct { - p: f32, - denominator: u16, -}; - -pub const Fsm = enum(u8) { - none = 0, - warm_up = 1, - warm_up_comp = 2, - warm_up_fault = 3, - curr_bias = 4, - curr_bias_comp = 5, - fwd_ramp = 8, - fwd_ramp_comp = 9, - fwd_ramp_fault = 10, - bwd_ramp = 11, - bwd_ramp_comp = 12, - bwd_ramp_fault = 13, - curr_step = 20, - curr_step_comp = 21, - curr_step_fault = 22, - speed_step = 23, - speed_step_comp = 24, - speed_step_fault = 25, - pos_step = 26, - pos_step_comp = 27, - pos_step_fault = 28, - pos_prof = 29, - pos_prof_comp = 30, - pos_prof_fault = 31, - fwd_calib = 32, - fwd_calib_comp = 33, - bwd_calib = 34, - bwd_calib_comp = 35, - speed_prof = 40, - speed_prof_comp = 41, - speed_prof_fault = 42, - fwd_slave = 43, - fwd_slave_comp = 44, - bwd_slave = 45, - bwd_slave_comp = 46, - over_charge = 50, - synch_com_error = 51, - _, -}; - -pub const Entrance = enum(u8) { - none = 0, - left = 1, - right = 2, - _, -}; - -test { - std.testing.refAllDecls(@This()); -} diff --git a/src/_Log.zig b/src/_Log.zig deleted file mode 100644 index 66465bc..0000000 --- a/src/_Log.zig +++ /dev/null @@ -1,152 +0,0 @@ -//! This module represents driver's logging configuration. The driver runs the -//! command while recording the configured items of the selected axes at 15000 -//! samples per second. -const std = @import("std"); -const _Config = @import("_Config.zig"); - -/// Number of cycles per 1 second in the firmware. -pub const FREQUENCY: u32 = 15000; - -/// Logging duration (>0 s). -duration: f32, -/// Items to be logged on every selected axis. -config: Config, -/// Axes to be logged. -axes: [3]bool, -/// Command run by the driver while logging. -command: Command, - -pub const Command = union(Kind) { - // TODO: Support the remaining commands SHDrv provide: - // - Offline commands with applied voltage (V) and applied angle (turn): - // warm_up = 1, move_home = 2, move_forward = 3, - // move_backward = 4, restart = 5, move_synchronous = 6, move_dq = 7 - // - current_step = 10 with Q current step (A) - // - speed_step = 11 with speed step (m/s) - // - position_step = 12 with position step - /// Offline command that logs the driver as it stands. - current_state: struct {}, - position_profile: Profile, - speed_profile: Profile, - - pub const Kind = enum(u8) { - current_state = 0, - position_profile = 13, - speed_profile = 14, - }; - - pub const Profile = struct { - /// ID of the slider targeted by the command (>0). - slider_id: u8, - // Unit is unconfirmed: SHDrv labels it m but limits it as rad. - /// Travel distance. Negative distance travels backward. - distance: f32, - /// Percentage of the maximum speed (%). - speed: f32, - /// Percentage of the maximum acceleration (%). - acceleration: f32, - }; -}; - -/// Field order is the item index. The response tags the item with its index -/// plus one. -pub const Config = packed struct(u32) { - /// Output position of the profile. - profile_position: bool, - /// Applied speed of the profile. - profile_speed: bool, - /// Output of the position controller. - position_output: bool, - /// Reference of the position controller (rad). - position_reference: bool, - /// Feedback of the position controller (rad). - position_feedback: bool, - /// Reference of the speed controller (rad/s). - speed_reference: bool, - /// Feedback of the speed controller (rad/s). - speed_feedback: bool, - current_q_reference: bool, - current_q_feedback: bool, - current_d_feedback: bool, - voltage_q_reference: bool, - voltage_d_reference: bool, - voltage_q_feedforward: bool, - voltage_d_feedforward: bool, - /// Calculation time of the control loop. - calculation_time: bool, - theta_section: bool, - electrical_angular_speed: bool, - in_position: bool, - current_a: bool, - current_b: bool, - current_c: bool, - voltage_alpha: bool, - voltage_beta: bool, - /// State of the slider's FSM. - slider_state: bool, - applied_angle: bool, - encoder_angle: bool, - voltage_a: bool, - voltage_b: bool, - voltage_c: bool, - vector_sector: bool, - hall_alarm_left: bool, - hall_alarm_right: bool, -}; - -/// Logged item, valued by its index in `Config`. -pub const Item = Item: { - const fields = @typeInfo(Config).@"struct".fields; - var names: [fields.len][]const u8 = undefined; - var values: [fields.len]u5 = undefined; - for (fields, &names, &values, 0..) |field, *name, *value, index| { - name.* = field.name; - value.* = index; - } - break :Item @Enum(u5, .exhaustive, &names, &values); -}; - -/// Value type of a logged item. -pub fn ConfigType(comptime item: Item) type { - return switch (item) { - .profile_position, - .profile_speed, - .position_output, - .position_reference, - .position_feedback, - .speed_reference, - .speed_feedback, - .current_q_reference, - .current_q_feedback, - .current_d_feedback, - .voltage_q_reference, - .voltage_d_reference, - .voltage_q_feedforward, - .voltage_d_feedforward, - .calculation_time, - .electrical_angular_speed, - .current_a, - .current_b, - .current_c, - .voltage_alpha, - .voltage_beta, - .applied_angle, - .encoder_angle, - .voltage_a, - .voltage_b, - .voltage_c, - => f32, - .theta_section, - .vector_sector, - => i32, - .slider_state => _Config.Fsm, - .in_position, - .hall_alarm_left, - .hall_alarm_right, - => bool, - }; -} - -test { - std.testing.refAllDecls(@This()); -} diff --git a/src/drivercom.zig b/src/drivercom.zig index 0c263e2..fa0a9fd 100644 --- a/src/drivercom.zig +++ b/src/drivercom.zig @@ -5,12 +5,8 @@ pub const version = std.SemanticVersion.parse(build.version) catch unreachable; pub const Config = @import("Config.zig"); -pub const _Config = @import("_Config.zig"); -pub const _Message = @import("_Message.zig"); -pub const _Log = @import("_Log.zig"); -pub const OldConfig = @import("OldConfig.zig"); +pub const Message = @import("Message.zig"); pub const Log = @import("Log.zig"); -pub const Message = @import("message.zig").Message; pub const gain = struct { pub const current = struct { @@ -71,26 +67,6 @@ pub const gain = struct { }; }; -pub const DriverMessage = enum(u16) { - none, - update, - set_aux, - set_move, - set_carrier_id, - set_isol_bwd, - set_isol_fwd, - prof_noti, - update_cali_home, - update_mech_angle_offset, - cali_on_pos_req, - cali_on_pos_rsp, - cali_off_pos_req, - cali_off_pos_rsp, - clear_carrier_info, -}; - -pub const CarrierState = @import("mmc_api").cclink.Wr.Carrier.State; - test { std.testing.refAllDecls(@This()); } diff --git a/src/library.zig b/src/library.zig deleted file mode 100644 index e69de29..0000000 diff --git a/src/message.zig b/src/message.zig deleted file mode 100644 index 9d6fd17..0000000 --- a/src/message.zig +++ /dev/null @@ -1,283 +0,0 @@ -const std = @import("std"); - -const Config = @import("Config.zig"); -const Log = @import("Log.zig"); - -pub const Message = packed struct { - kind: Kind, - sequence: u16, - _payload: u64, - cycle: u16 = 0, - _120: u8 = 0, - bcc: u8 = undefined, - - pub const Payload = extern union { - response: void, - ping: u32, - firmware_version: extern struct { - major: u16 = 0, - minor: u16 = 0, - patch: u16 = 0, - }, - start_sequence: void, - end_sequence: void, - - config_get: ConfigField, - config_set: ConfigField, - config_save: void, - - log_start: void, - log_stop: void, - log_status: packed struct(u64) { - status: Log.Status, - _: u30 = 0, - cycles_completed: u32, - }, - log_get_cycles: void, - log_set_cycles: u32, - log_get_config: void, - log_set_config: Log.Config, - log_add_config: Log.Config, - log_remove_config: Log.Config, - log_get_axis: void, - log_set_axis: packed struct(u16) { - axis_1: bool, - axis_2: bool, - axis_3: bool, - _: u13 = 0, - }, - log_get_carrier: void, - log_set_carrier: [4]u16, - log_get_hall_sensor: void, - log_set_hall_sensor: packed struct(u16) { - hall_sensor_1: bool, - hall_sensor_2: bool, - hall_sensor_3: bool, - hall_sensor_4: bool, - hall_sensor_5: bool, - hall_sensor_6: bool, - _: u10 = 0, - }, - log_get_start: void, - log_set_start: packed struct(u64) { - carrier_1: u12, - kind: Log.Start, - _: u1 = 0, - carrier_2: u12, - hall_sensor_1: bool = false, - hall_sensor_2: bool = false, - combinator_and: bool = false, - combinator_or: bool = false, - carrier_3: u12, - hall_sensor_3: bool = false, - hall_sensor_4: bool = false, - _1: u2 = 0, - carrier_4: u12, - hall_sensor_5: bool = false, - hall_sensor_6: bool = false, - _2: u2 = 0, - }, - log_get: packed struct(u64) { - cycle: u24, - data: Log.Tag, - id: u3, - cycles: u32, - }, - u8: [8]u8, - - pub const ConfigField = extern struct { - kind: Config.Field.Kind, - index: u16 = 0, - value: extern union { - i16: i16, - u16: u16, - u32: u32, - f32: f32, - } = undefined, - - /// Set the value from Config struct. Valid kind and (if needed) - /// index must be provided. - pub fn getConfig(self: *@This(), config: Config) void { - const field = Config.Field.fromConfig(config, self.kind, .{ - .index = self.index, - }); - switch (field) { - inline else => |value| { - const value_type = @TypeOf(value); - // Special case for flags. - if (comptime value_type == Config.Flags) { - self.value.u16 = @as(u12, @bitCast(value)); - } else if (comptime value_type == i16) { - self.value.i16 = value; - } else if (comptime value_type == u16) { - self.value.u16 = value; - } else if (comptime value_type == u32) { - self.value.u32 = value; - } else if (comptime value_type == f32) { - self.value.f32 = value; - } else { - @compileError("unsupported Config field type"); - } - }, - } - } - - test getConfig { - var pl: Payload = .{ .config_set = .{ - .kind = .station, - .value = undefined, - } }; - const conf: Config = std.mem.zeroInit(Config, .{ - .station = 13, - }); - pl.config_set.getConfig(conf); - try std.testing.expectEqual(13, pl.config_set.value.u16); - } - - /// Set the corresponding Config struct value from this message. - pub fn setConfig(self: @This(), config: *Config) void { - switch (self.kind) { - inline else => |kind| { - const name = @tagName(kind); - const value_type = @FieldType(Config.Field, name); - const field: Config.Field = b: { - // Special case for flags. - if (comptime value_type == Config.Flags) { - break :b .{ .flags = Config.Flags.fromInt( - @truncate(self.value.u16), - ) }; - } else if (comptime value_type == i16) { - break :b @unionInit( - Config.Field, - name, - self.value.i16, - ); - } else if (comptime value_type == u16) { - break :b @unionInit( - Config.Field, - name, - self.value.u16, - ); - } else if (comptime value_type == u32) { - break :b @unionInit( - Config.Field, - name, - self.value.u32, - ); - } else if (comptime value_type == f32) { - break :b @unionInit( - Config.Field, - name, - self.value.f32, - ); - } else { - @compileError("unsupported Config field type"); - } - }; - field.setConfig(config, .{ .index = self.index }); - }, - } - } - - test setConfig { - var config: Config = std.mem.zeroInit(Config, .{}); - setConfig(.{ - .kind = .station, - .value = .{ .u16 = 3 }, - }, &config); - try std.testing.expectEqual(3, config.station); - } - }; - }; - - pub const Kind = b: { - const ti = @typeInfo(Payload).@"union"; - const tag_type = u16; - var field_names: [ti.fields.len - 1][]const u8 = undefined; - var field_values: [ti.fields.len - 1]tag_type = undefined; - var val: tag_type = 1; - for (ti.fields, 0..) |field, i| { - if (std.mem.eql(u8, "u8", field.name)) continue; - if (std.mem.eql(u8, "config_get", field.name)) { - val = 0x10; - } else if (std.mem.eql(u8, "log_start", field.name)) { - val = 0x100; - } - field_names[i] = field.name; - field_values[i] = val; - val += 1; - } - break :b @Enum(tag_type, .nonexhaustive, &field_names, &field_values); - }; - - pub fn PayloadType(comptime kind: Kind) type { - const ti = @typeInfo(Payload).@"union"; - @setEvalBranchQuota(4000); - inline for (ti.fields) |field| { - if (std.mem.eql(u8, field.name, @tagName(kind))) { - return field.type; - } - } - } - - pub fn init( - comptime kind: Kind, - sequence: u16, - p: PayloadType(kind), - ) Message { - return switch (kind) { - inline else => b: { - var msg: Message = .{ - .kind = kind, - .sequence = sequence, - ._payload = undefined, - }; - var _payload: Payload = .{ .u8 = .{0} ** 8 }; - @field(_payload, @tagName(kind)) = p; - msg._payload = @bitCast(_payload); - msg.bcc = msg.getBcc(); - break :b msg; - }, - }; - } - - pub fn payload( - self: *const Message, - comptime kind: Kind, - ) PayloadType(kind) { - const _payload: Payload = @bitCast(self._payload); - return switch (kind) { - inline else => @field(_payload, @tagName(kind)), - }; - } - - pub fn getBcc(self: *const Message) u8 { - const bytes: []const u8 = std.mem.asBytes(self); - var bcc: u8 = 0; - for (bytes[0..15]) |b| { - bcc ^= b; - } - return bcc; - } -}; - -comptime { - if (@sizeOf(Message) != 16) { - @compileError(std.fmt.comptimePrint( - "Message is invalid size {}", - .{@sizeOf(Message)}, - )); - } - - const payload_ti = @typeInfo(Message.Payload).@"union"; - for (payload_ti.fields) |field| { - if (field.name.len < 3) continue; - if (!std.mem.eql(u8, "set", field.name[0..3])) continue; - - // const field_ti = @typeInfo(field.type); - } -} - -test { - std.testing.refAllDecls(@This()); -} From e8b575f3a3981b42617f939f147e9d753e530576 Mon Sep 17 00:00:00 2001 From: aaumar25 Date: Wed, 21 Oct 2026 10:41:25 +0900 Subject: [PATCH 2/5] refactor: Required changes for zig 0.17.0 --- src/Log.zig | 10 ++++------ src/Message.zig | 22 ++++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Log.zig b/src/Log.zig index 23974db..af6342c 100644 --- a/src/Log.zig +++ b/src/Log.zig @@ -96,14 +96,12 @@ pub const Config = packed struct(u32) { /// Logged item, valued by its index in `Config`. pub const Item = Item: { - const fields = @typeInfo(Config).@"struct".fields; - var names: [fields.len][]const u8 = undefined; - var values: [fields.len]u5 = undefined; - for (fields, &names, &values, 0..) |field, *name, *value, index| { - name.* = field.name; + const names = @typeInfo(Config).@"struct".field_names; + var values: [names.len]u5 = undefined; + for (&values, 0..) |*value, index| { value.* = index; } - break :Item @Enum(u5, .exhaustive, &names, &values); + break :Item @Enum(u5, .exhaustive, names, &values); }; /// Value type of a logged item. diff --git a/src/Message.zig b/src/Message.zig index 587e8ae..2d563c7 100644 --- a/src/Message.zig +++ b/src/Message.zig @@ -27,7 +27,7 @@ bcc: u8, /// Write the message into the writer. The message is writen in Big Endian due /// to firmware implementation are using Big Endianness. pub fn write(self: Message, writer: *std.Io.Writer) std.Io.Writer.Error!void { - const endian: std.builtin.Endian = .big; + const endian: std.lang.Endian = .big; try writer.writeStruct(self.header, endian); try writer.writeAll(self.payload); try writer.writeInt(u8, self.etx, endian); @@ -173,8 +173,8 @@ pub const Header = extern struct { if (header.stx != STX) return ParseError.MissingStx; // Validate kind const kind_ti = @typeInfo(Kind).@"enum"; - if (@intFromEnum(header.kind) > - kind_ti.fields[kind_ti.fields.len - 1].value) + if (@backingInt(header.kind) > + kind_ti.field_values[kind_ti.field_values.len - 1]) { return error.InvalidPayload; } @@ -468,9 +468,10 @@ pub const Request = union(Kind) { }, } for (&result.config, log.axes) |*axis_config, is_logged| { - inline for (@typeInfo(Log.Config).@"struct".fields) |field| { - @field(axis_config.*, field.name) = AxisConfig.fromBool( - is_logged and @field(log.config, field.name), + const names = @typeInfo(Log.Config).@"struct".field_names; + inline for (names) |name| { + @field(axis_config.*, name) = AxisConfig.fromBool( + is_logged and @field(log.config, name), ); } } @@ -1427,13 +1428,14 @@ fn getBcc(buf: []const u8) u8 { } test "get_ responses have setConfig and set_ requests have fromConfig" { - inline for (@typeInfo(Kind).@"enum".fields) |field| { - const kind: Kind = @enumFromInt(field.value); - if (comptime std.mem.startsWith(u8, field.name, "get_")) { + const kind_ti = @typeInfo(Kind).@"enum"; + inline for (kind_ti.field_names, kind_ti.field_values) |name, value| { + const kind: Kind = @fromBackingInt(@intCast(value)); + if (comptime std.mem.startsWith(u8, name, "get_")) { const T = PayloadType(kind, .response); try std.testing.expect(@hasDecl(T, "setConfig")); } - if (comptime std.mem.startsWith(u8, field.name, "set_")) { + if (comptime std.mem.startsWith(u8, name, "set_")) { const T = PayloadType(kind, .request); try std.testing.expect(@hasDecl(T, "fromConfig")); } From 5c96a50f91007d8e3facbef1047a32ce297f8f43 Mon Sep 17 00:00:00 2001 From: aaumar25 Date: Wed, 21 Oct 2026 10:50:20 +0900 Subject: [PATCH 3/5] ci: Upgrade to zig 0.17.0 --- .github/workflows/main.yml | 2 +- build.zig.zon | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 89a1380..f298563 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,7 +31,7 @@ jobs: - uses: mlugg/setup-zig@v2 with: - version: 0.16.0 + version: 0.17.0 - name: Run zig fmt if: matrix.os == 'Linux' diff --git a/build.zig.zon b/build.zig.zon index c3b07d2..51983f2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,7 +2,7 @@ .name = .drivercom, .version = "0.6.1", .fingerprint = 0xc1b53adfd36fc2f0, - .minimum_zig_version = "0.16.0", + .minimum_zig_version = "0.17.0", .dependencies = .{}, .paths = .{ "build.zig", From 3e63874d025d35878962dc03ed87efa7e3ef7a7a Mon Sep 17 00:00:00 2001 From: aaumar25 Date: Wed, 21 Oct 2026 10:51:09 +0900 Subject: [PATCH 4/5] refactor: Move reusable types to `drivercom.zig` --- src/Config.zig | 66 ++++------------------------------------------- src/Log.zig | 4 +-- src/Message.zig | 23 +++++++++-------- src/drivercom.zig | 57 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 74 deletions(-) diff --git a/src/Config.zig b/src/Config.zig index b584ef9..075a1a1 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -1,5 +1,6 @@ //! This module represents driver's configuration. const std = @import("std"); +const drivercom = @import("drivercom.zig"); const Config = @This(); pub const Info = @import("Config/Info.zig"); @@ -8,7 +9,7 @@ id: u16, /// CC-Link Station ID station: u16, /// CC-Link baud rate -cc_link_speed: CcLinkSpeed, +cc_link_speed: drivercom.CcLinkSpeed, flags: Flags, line: struct { /// Total number of axes in line. @@ -83,9 +84,9 @@ pub const State = struct { pub const Axis = struct { enabled: bool, primary: bool, - slider: struct { id: u8, state: Fsm }, - entrance: Entrance, - overcharge_state: Fsm, + slider: struct { id: u8, state: drivercom.Fsm }, + entrance: drivercom.Entrance, + overcharge_state: drivercom.Fsm, pitch_count: i16, mechanical_position: f32, // a and b needs confirmation. @@ -177,15 +178,6 @@ pub const Flags = struct { swap: bool, }; -pub const CcLinkSpeed = enum(u8) { - @"156 kbps", - @"625 kbps", - @"2.5 Mbps", - @"5 Mbps", - @"10 Mbps", - _, -}; - pub const CurrentGain = struct { p: f32, i: f32, @@ -204,54 +196,6 @@ pub const PositionGain = struct { denominator: u16, }; -pub const Fsm = enum(u8) { - none = 0, - warm_up = 1, - warm_up_comp = 2, - warm_up_fault = 3, - curr_bias = 4, - curr_bias_comp = 5, - fwd_ramp = 8, - fwd_ramp_comp = 9, - fwd_ramp_fault = 10, - bwd_ramp = 11, - bwd_ramp_comp = 12, - bwd_ramp_fault = 13, - curr_step = 20, - curr_step_comp = 21, - curr_step_fault = 22, - speed_step = 23, - speed_step_comp = 24, - speed_step_fault = 25, - pos_step = 26, - pos_step_comp = 27, - pos_step_fault = 28, - pos_prof = 29, - pos_prof_comp = 30, - pos_prof_fault = 31, - fwd_calib = 32, - fwd_calib_comp = 33, - bwd_calib = 34, - bwd_calib_comp = 35, - speed_prof = 40, - speed_prof_comp = 41, - speed_prof_fault = 42, - fwd_slave = 43, - fwd_slave_comp = 44, - bwd_slave = 45, - bwd_slave_comp = 46, - over_charge = 50, - synch_com_error = 51, - _, -}; - -pub const Entrance = enum(u8) { - none = 0, - left = 1, - right = 2, - _, -}; - test { std.testing.refAllDecls(@This()); } diff --git a/src/Log.zig b/src/Log.zig index af6342c..1e7b678 100644 --- a/src/Log.zig +++ b/src/Log.zig @@ -2,7 +2,7 @@ //! command while recording the configured items of the selected axes at 15000 //! samples per second. const std = @import("std"); -const Fsm = @import("Config.zig").Fsm; +const drivercom = @import("drivercom.zig"); /// Number of cycles per 1 second in the firmware. pub const FREQUENCY: u32 = 15000; @@ -137,7 +137,7 @@ pub fn ConfigType(comptime item: Item) type { .theta_section, .vector_sector, => i32, - .slider_state => Fsm, + .slider_state => drivercom.Fsm, .in_position, .hall_alarm_left, .hall_alarm_right, diff --git a/src/Message.zig b/src/Message.zig index 2d563c7..36e5fa2 100644 --- a/src/Message.zig +++ b/src/Message.zig @@ -7,6 +7,7 @@ const std = @import("std"); const builtin = @import("builtin"); +const drivercom = @import("drivercom.zig"); const Config = @import("Config.zig"); const Log = @import("Log.zig"); const Message = @This(); @@ -306,7 +307,7 @@ pub const Request = union(Kind) { voltage_warmup: f32 align(1), retry_count: u8, station: u16 align(1), - cc_link_speed: Config.CcLinkSpeed, + cc_link_speed: drivercom.CcLinkSpeed, hall_cutoff_freq: f32 align(1), overcurrent_timeout: f32 align(1), pos_offset: f32 align(1), @@ -835,7 +836,7 @@ pub const Response = union(Kind) { }, /// Driver ID on the line id: u16 align(1), - cc_link_speed: Config.CcLinkSpeed, + cc_link_speed: drivercom.CcLinkSpeed, /// Whether there is space on the edge of driver during calibration calibration_spare: extern struct { backward: bool, @@ -955,9 +956,9 @@ pub const Response = union(Kind) { }, theta_offset: f32 align(1), slide_state: extern struct { - axis1: Config.Fsm, - axis2: Config.Fsm, - axis3: Config.Fsm, + axis1: drivercom.Fsm, + axis2: drivercom.Fsm, + axis3: drivercom.Fsm, }, is_calibrate: bool, bias: extern struct { @@ -990,9 +991,9 @@ pub const Response = union(Kind) { id2: u8, }, entrance: extern struct { - axis1: Config.Entrance, - axis2: Config.Entrance, - axis3: Config.Entrance, + axis1: drivercom.Entrance, + axis2: drivercom.Entrance, + axis3: drivercom.Entrance, }, pitch_cnt: extern struct { axis1: i16 align(1), @@ -1030,9 +1031,9 @@ pub const Response = union(Kind) { /// Bytes 162..165: not read. _4: [4]u8, over_charge_state: extern struct { - axis1: Config.Fsm, - axis2: Config.Fsm, - axis3: Config.Fsm, + axis1: drivercom.Fsm, + axis2: drivercom.Fsm, + axis3: drivercom.Fsm, }, fwd_rsen_on_pos: extern struct { axis1: f32 align(1), diff --git a/src/drivercom.zig b/src/drivercom.zig index fa0a9fd..b28811e 100644 --- a/src/drivercom.zig +++ b/src/drivercom.zig @@ -8,6 +8,63 @@ pub const Config = @import("Config.zig"); pub const Message = @import("Message.zig"); pub const Log = @import("Log.zig"); +pub const CcLinkSpeed = enum(u8) { + @"156 kbps", + @"625 kbps", + @"2.5 Mbps", + @"5 Mbps", + @"10 Mbps", + _, +}; + +pub const Fsm = enum(u8) { + none = 0, + warm_up = 1, + warm_up_comp = 2, + warm_up_fault = 3, + curr_bias = 4, + curr_bias_comp = 5, + fwd_ramp = 8, + fwd_ramp_comp = 9, + fwd_ramp_fault = 10, + bwd_ramp = 11, + bwd_ramp_comp = 12, + bwd_ramp_fault = 13, + curr_step = 20, + curr_step_comp = 21, + curr_step_fault = 22, + speed_step = 23, + speed_step_comp = 24, + speed_step_fault = 25, + pos_step = 26, + pos_step_comp = 27, + pos_step_fault = 28, + pos_prof = 29, + pos_prof_comp = 30, + pos_prof_fault = 31, + fwd_calib = 32, + fwd_calib_comp = 33, + bwd_calib = 34, + bwd_calib_comp = 35, + speed_prof = 40, + speed_prof_comp = 41, + speed_prof_fault = 42, + fwd_slave = 43, + fwd_slave_comp = 44, + bwd_slave = 45, + bwd_slave_comp = 46, + over_charge = 50, + synch_com_error = 51, + _, +}; + +pub const Entrance = enum(u8) { + none = 0, + left = 1, + right = 2, + _, +}; + pub const gain = struct { pub const current = struct { pub fn wcc(denominator: u32) f64 { From 94c3d076d47ceb7cafd5d51268ca627d744d3463 Mon Sep 17 00:00:00 2001 From: aaumar25 Date: Wed, 21 Oct 2026 10:54:26 +0900 Subject: [PATCH 5/5] ci: Remove unnecessary test --- .github/workflows/main.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f298563..550e448 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,6 +39,3 @@ jobs: - name: Run module tests run: zig build test --summary all - - - name: Run library tests - run: zig build -Dlibrary=static test --summary all