using RobotNet10.CANOpen.Models; namespace RobotNet10.CANOpen.CiA402; /// /// CiA402 Object Dictionary - có thể customize địa chỉ cho từng device /// Format mô tả: [Access] [Type/Size] - Ý nghĩa /// Access: RO=Read Only, WO=Write Only, RW=Read/Write /// public class CiA402ObjectDictionary { // ==================== State Machine Control ==================== /// /// 0x6040 - [WO] UINT16 (2 bytes) - Controlword /// Điều khiển state machine của motor (Enable, Disable, Fault Reset, Quick Stop, etc.) /// Bit-mapped command word để transition giữa các states /// public ushort Controlword { get; set; } = 0x6040; /// /// 0x6041 - [RO] UINT16 (2 bytes) - Statusword /// Trạng thái hiện tại của motor drive (Ready, Enabled, Fault, Warning, etc.) /// Bit-mapped status word cho biết state và các cờ trạng thái /// public ushort Statusword { get; set; } = 0x6041; // ==================== Operation Mode ==================== /// /// 0x6060 - [RW] INT8 (1 byte) - Modes of Operation /// Chọn chế độ điều khiển: 1=PP, 3=PV, 4=PT, 6=HM, 8=CSP, 9=CSV, 10=CST /// Write để set mode, read để xem mode được request /// public ushort ModesOfOperation { get; set; } = 0x6060; /// /// 0x6061 - [RO] INT8 (1 byte) - Modes of Operation Display /// Chế độ điều khiển hiện tại đang active/running /// Read để kiểm tra mode thực tế đã được apply /// public ushort ModesOfOperationDisplay { get; set; } = 0x6061; // ==================== Position Control ==================== /// /// 0x6064 - [RO] INT32 (4 bytes) - Position Actual Value /// Vị trí thực tế hiện tại của motor (encoder counts) /// Real-time feedback từ encoder, cập nhật liên tục /// public ushort PositionActualValue { get; set; } = 0x6064; /// /// 0x6062 - [RO] INT32 (4 bytes) - Position Demand Value /// Vị trí mục tiêu nội bộ của trajectory generator /// Giá trị trajectory planner đang yêu cầu motor đến /// public ushort PositionDemandValue { get; set; } = 0x6062; /// /// 0x607A - [RW] INT32 (4 bytes) - Target Position /// Vị trí đích mà motor cần di chuyển đến (Profile Position mode) /// Write target position, motor sẽ di chuyển đến đó theo profile /// public ushort TargetPosition { get; set; } = 0x607A; /// /// 0x6067 - [RW] UINT32 (4 bytes) - Position Window /// Dung sai vị trí cho phép (encoder counts) /// Nếu |Actual - Target| < Window → Target reached /// public ushort PositionWindow { get; set; } = 0x6067; /// /// 0x6068 - [RW] UINT16 (2 bytes) - Position Window Time /// Thời gian motor phải ở trong Position Window (ms) /// Phải trong window ít nhất time này mới set "Target Reached" bit /// public ushort PositionWindowTime { get; set; } = 0x6068; /// /// 0x607B - [RW] INT32 (4 bytes) - Position Range Limit /// Giới hạn khoảng di chuyển tối đa cho 1 lần move /// Sub-index 1: Min position, Sub-index 2: Max position /// public ushort PositionRangeLimit { get; set; } = 0x607B; /// /// 0x607D - [RW] INT32 (4 bytes) - Software Position Limit /// Giới hạn vị trí phần mềm (software limit switches) /// Sub-index 1: Min limit, Sub-index 2: Max limit /// public ushort SoftwarePositionLimit { get; set; } = 0x607D; // ==================== Profile Parameters ==================== /// /// 0x6081 - [RW] UINT32 (4 bytes) - Profile Velocity /// Vận tốc tối đa trong Profile Position mode (user units/s) /// Vận tốc motor sẽ di chuyển khi execute position command /// public ushort ProfileVelocity { get; set; } = 0x6081; /// /// 0x6083 - [RW] UINT32 (4 bytes) - Profile Acceleration /// Gia tốc tăng tốc trong profile motion (user units/s²) /// Tốc độ tăng vận tốc khi bắt đầu di chuyển /// public ushort ProfileAcceleration { get; set; } = 0x6083; /// /// 0x6084 - [RW] UINT32 (4 bytes) - Profile Deceleration /// Gia tốc giảm tốc trong profile motion (user units/s²) /// Tốc độ giảm vận tốc khi gần đến target /// public ushort ProfileDeceleration { get; set; } = 0x6084; /// /// 0x6085 - [RW] UINT32 (4 bytes) - Quick Stop Deceleration /// Gia tốc phanh khẩn cấp (user units/s²) /// Sử dụng khi Quick Stop command được trigger /// public ushort QuickStopDeceleration { get; set; } = 0x6085; /// /// 0x6086 - [RW] INT16 (2 bytes) - Motion Profile Type /// Loại trajectory: 0=Linear ramp, 1=Sin² ramp /// Xác định hình dạng của velocity profile /// public ushort MotionProfileType { get; set; } = 0x6086; // ==================== Velocity Control ==================== /// /// 0x606C - [RO] INT32 (4 bytes) - Velocity Actual Value /// Vận tốc thực tế hiện tại của motor (user units/s hoặc RPM) /// Real-time feedback, cập nhật liên tục /// public ushort VelocityActualValue { get; set; } = 0x606C; /// /// 0x606B - [RO] INT32 (4 bytes) - Velocity Demand Value /// Vận tốc mục tiêu nội bộ của controller /// Giá trị controller đang yêu cầu motor chạy /// public ushort VelocityDemandValue { get; set; } = 0x606B; /// /// 0x60FF - [RW] INT32 (4 bytes) - Target Velocity /// Vận tốc đích trong Profile Velocity mode (user units/s) /// Write target velocity, motor sẽ tăng/giảm tốc đến giá trị này /// public ushort TargetVelocity { get; set; } = 0x60FF; /// /// 0x60FF - [RW] INT32 (4 bytes) - Profile Speed /// Vận tốc đích trong Profile Position mode (user units/s) /// Write Profile Speed, motor sẽ tăng/giảm tốc đến giá trị này /// public ushort ProfileSpeed { get; set; } = 0x6081; /// /// 0x606D - [RW] UINT16 (2 bytes) - Velocity Window /// Dung sai vận tốc (user units/s) /// Nếu |Actual - Target| < Window → Target reached /// public ushort VelocityWindow { get; set; } = 0x606D; /// /// 0x606E - [RW] UINT16 (2 bytes) - Velocity Window Time /// Thời gian trong Velocity Window để set "Target Reached" (ms) /// public ushort VelocityWindowTime { get; set; } = 0x606E; /// /// 0x606F - [RW] UINT16 (2 bytes) - Velocity Threshold /// Ngưỡng vận tốc tối thiểu để coi như motor đang chạy /// Dùng để detect motor đã dừng hoàn toàn /// public ushort VelocityThreshold { get; set; } = 0x606F; /// /// 0x607F - [RW] UINT32 (4 bytes) - Max Profile Velocity /// Vận tốc tối đa cho phép trong profile modes (user units/s) /// Giới hạn trên cho ProfileVelocity và TargetVelocity /// public ushort MaxProfileVelocity { get; set; } = 0x607F; /// /// 0x6080 - [RW] UINT32 (4 bytes) - Max Motor Speed /// Tốc độ tối đa tuyệt đối của motor (RPM) /// Hardware limit, vượt quá sẽ trigger error /// public ushort MaxMotorSpeed { get; set; } = 0x6080; // ==================== Torque Control ==================== /// /// 0x6077 - [RO] INT16 (2 bytes) - Torque Actual Value /// Mô-men xoắn thực tế hiện tại (phần nghìn của rated torque) /// 1000 = 100% rated torque, real-time feedback /// public ushort TorqueActualValue { get; set; } = 0x6077; /// /// 0x6071 - [RW] INT16 (2 bytes) - Target Torque /// Mô-men xoắn đích trong Torque mode (phần nghìn của rated torque) /// 1000 = 100%, 500 = 50%, -500 = -50% (reverse direction) /// public ushort TargetTorque { get; set; } = 0x6071; /// /// 0x6072 - [RW] UINT16 (2 bytes) - Max Torque /// Mô-men xoắn tối đa cho phép (phần nghìn của rated torque) /// Safety limit, motor không được vượt quá giá trị này /// public ushort MaxTorque { get; set; } = 0x6072; /// /// 0x6087 - [RW] UINT32 (4 bytes) - Torque Slope /// Tốc độ thay đổi torque tối đa (delta torque per second) /// Giới hạn tốc độ tăng/giảm torque để bảo vệ cơ khí /// public ushort TorqueSlope { get; set; } = 0x6087; // ==================== Homing ==================== /// /// 0x6098 - [RW] INT8 (1 byte) - Homing Method /// Phương pháp tìm home: 1-14 (negative limit), 17-30 (positive limit), 33-35 (index pulse) /// Xác định cách motor tìm vị trí home (zero position) /// public ushort HomingMethod { get; set; } = 0x6098; /// /// 0x6099 - [RW] UINT32 (4 bytes) - Homing Speeds /// Vận tốc khi tìm home (user units/s) /// Sub-index 1: Speed during search, Sub-index 2: Speed during zero search /// public ushort HomingSpeed { get; set; } = 0x6099; /// /// 0x607C - [RW] INT32 (4 bytes) - Home Offset / Homing Offset /// Offset áp dụng sau khi homing hoàn tất (user units / encoder counts) /// public ushort HomingOffset { get; set; } = 0x607C; /// /// 0x609A - [RW] UINT32 (4 bytes) - Homing Acceleration /// Gia tốc khi thực hiện homing (user units/s²) /// public ushort HomingAcceleration { get; set; } = 0x609A; // ==================== Interpolation (Cyclic Synchronous Modes) ==================== /// /// 0x60C2 - [RW] UINT8 (1 byte) - Interpolation Time Period /// Chu kỳ thời gian giữa các setpoint trong CSP/CSV/CST mode (ms) /// Thường là 1-10ms, phải match với cycle time của master /// public ushort InterpolationTimePeriod { get; set; } = 0x60C2; /// /// 0x60C1 - [RW] Variable - Interpolation Data Record /// Buffer chứa dữ liệu interpolation cho cyclic modes /// Dùng trong các mode CSP, CSV, CST /// public ushort InterpolationDataRecord { get; set; } = 0x60C1; // ==================== Following Error ==================== /// /// 0x6065 - [RW] UINT32 (4 bytes) - Following Error Window /// Dung sai sai số theo dõi cho phép (encoder counts) /// Nếu |Demand - Actual| > Window → Following error fault /// public ushort FollowingErrorWindow { get; set; } = 0x6065; /// /// 0x6066 - [RW] UINT16 (2 bytes) - Following Error Timeout /// Thời gian cho phép vượt quá Following Error Window (ms) /// Vượt quá window trong time này → Trigger fault /// public ushort FollowingErrorTimeout { get; set; } = 0x6066; // ==================== Motor Data ==================== /// /// 0x6075 - [RW] UINT32 (4 bytes) - Motor Rated Current /// Dòng điện định mức của motor (mA) /// Thông số motor được ghi trong datasheet /// public ushort MotorRatedCurrent { get; set; } = 0x6075; /// /// 0x6076 - [RW] UINT32 (4 bytes) - Motor Rated Torque /// Mô-men xoắn định mức của motor (mNm - milli Newton-meter) /// Basis cho tính toán phần trăm torque (1000 = rated torque) /// public ushort MotorRatedTorque { get; set; } = 0x6076; // ==================== Factory Methods ==================== /// /// Tạo Object Dictionary mặc định theo CiA402 standard /// public static CiA402ObjectDictionary CreateDefault() { return new CiA402ObjectDictionary(); } /// /// Tạo Object Dictionary với custom addresses (cho vendor-specific devices) /// /// Action để customize addresses /// /// var dict = CiA402ObjectDictionary.CreateCustom(od => { /// od.Controlword = 0x2040; // Vendor-specific address /// od.TargetPosition = 0x2100; /// }); /// public static CiA402ObjectDictionary CreateCustom(Action configure) { var dict = new CiA402ObjectDictionary(); configure(dict); return dict; } /// /// Clone Object Dictionary (tạo bản sao độc lập) /// public CiA402ObjectDictionary Clone() { return (CiA402ObjectDictionary)MemberwiseClone(); } /// /// Validate all object addresses (check for conflicts) /// public ValidationResult ValidateAddresses() { var addresses = new Dictionary { { Controlword, nameof(Controlword) }, { Statusword, nameof(Statusword) }, { ModesOfOperation, nameof(ModesOfOperation) }, { ModesOfOperationDisplay, nameof(ModesOfOperationDisplay) }, { PositionActualValue, nameof(PositionActualValue) }, { PositionDemandValue, nameof(PositionDemandValue) }, { TargetPosition, nameof(TargetPosition) }, { PositionWindow, nameof(PositionWindow) }, { PositionWindowTime, nameof(PositionWindowTime) }, { PositionRangeLimit, nameof(PositionRangeLimit) }, { SoftwarePositionLimit, nameof(SoftwarePositionLimit) }, { ProfileVelocity, nameof(ProfileVelocity) }, { ProfileAcceleration, nameof(ProfileAcceleration) }, { ProfileDeceleration, nameof(ProfileDeceleration) }, { QuickStopDeceleration, nameof(QuickStopDeceleration) }, { MotionProfileType, nameof(MotionProfileType) }, { VelocityActualValue, nameof(VelocityActualValue) }, { VelocityDemandValue, nameof(VelocityDemandValue) }, { TargetVelocity, nameof(TargetVelocity) }, { VelocityWindow, nameof(VelocityWindow) }, { VelocityWindowTime, nameof(VelocityWindowTime) }, { VelocityThreshold, nameof(VelocityThreshold) }, { MaxProfileVelocity, nameof(MaxProfileVelocity) }, { MaxMotorSpeed, nameof(MaxMotorSpeed) }, { TorqueActualValue, nameof(TorqueActualValue) }, { TargetTorque, nameof(TargetTorque) }, { MaxTorque, nameof(MaxTorque) }, { TorqueSlope, nameof(TorqueSlope) }, { HomingMethod, nameof(HomingMethod) }, { HomingSpeed, nameof(HomingSpeed) }, { HomingOffset, nameof(HomingOffset) }, { HomingAcceleration, nameof(HomingAcceleration) }, { InterpolationTimePeriod, nameof(InterpolationTimePeriod) }, { InterpolationDataRecord, nameof(InterpolationDataRecord) }, { FollowingErrorWindow, nameof(FollowingErrorWindow) }, { FollowingErrorTimeout, nameof(FollowingErrorTimeout) }, { MotorRatedCurrent, nameof(MotorRatedCurrent) }, { MotorRatedTorque, nameof(MotorRatedTorque) } }; var duplicates = addresses.GroupBy(x => x.Key) .Where(g => g.Count() > 1) .ToList(); if (duplicates.Any()) { var errors = duplicates.Select(g => $"Address 0x{g.Key:X4} is used by: {string.Join(", ", g.Select(x => x.Value))}") .ToList(); return ValidationResult.Failure(errors); } return ValidationResult.Success(); } /// /// Get object address by name /// public ushort? GetAddress(string objectName) { var property = GetType().GetProperty(objectName); return property?.GetValue(this) as ushort?; } /// /// Set object address by name /// public bool SetAddress(string objectName, ushort address) { var property = GetType().GetProperty(objectName); if (property?.PropertyType == typeof(ushort)) { property.SetValue(this, address); return true; } return false; } /// /// Get all object addresses as dictionary /// public Dictionary GetAllAddresses() { return GetType().GetProperties() .Where(p => p.PropertyType == typeof(ushort)) .ToDictionary(p => p.Name, p => (ushort)p.GetValue(this)!); } }