using RobotNet10.CANOpen.Enums; namespace RobotNet10.CANOpen.Models; /// /// PDO Mapping entry (đại diện cho 1 object trong PDO) /// public readonly struct PdoMapping(ushort index, byte subIndex, byte bitLength) { public ushort Index { get; init; } = index; public byte SubIndex { get; init; } = subIndex; public byte BitLength { get; init; } = bitLength; /// /// Convert sang mapping value format (theo DS301) /// Format: [Index:16][SubIndex:8][BitLength:8] /// public uint ToMappingValue() { return ((uint)Index << 16) | ((uint)SubIndex << 8) | BitLength; } /// /// Parse từ mapping value /// public static PdoMapping FromMappingValue(uint mappingValue) { ushort index = (ushort)((mappingValue >> 16) & 0xFFFF); byte subIndex = (byte)((mappingValue >> 8) & 0xFF); byte bitLength = (byte)(mappingValue & 0xFF); return new PdoMapping(index, subIndex, bitLength); } } /// /// PDO Configuration (TPDO hoặc RPDO) /// public class PdoConfiguration { private readonly List _mappings = []; public PdoConfiguration() { } public PdoConfiguration(byte pdoNumber, uint cobId, PdoTransmissionType transmissionType = PdoTransmissionType.Asynchronous) { if (pdoNumber < 1 || pdoNumber > 4) throw new ArgumentException("PDO number must be between 1 and 4", nameof(pdoNumber)); PdoNumber = pdoNumber; CobId = cobId; TransmissionType = transmissionType; } /// /// PDO number (1-4 cho TPDO, 1-4 cho RPDO) /// public byte PdoNumber { get; set; } /// /// COB-ID của PDO /// public uint CobId { get; set; } /// /// Transmission type /// public PdoTransmissionType TransmissionType { get; set; } /// /// Inhibit time (x100 microseconds) /// public ushort InhibitTime { get; set; } /// /// Event timer (milliseconds) /// public ushort EventTimer { get; set; } /// /// Danh sách các mapped objects (read-only) /// public IReadOnlyList Mappings => _mappings.AsReadOnly(); /// /// Check if PDO is valid (bit 31 của COB-ID = 0) /// public bool IsValid => (CobId & 0x80000000) == 0; /// /// Check if RTR is allowed (bit 30 của COB-ID = 0) /// public bool RtrAllowed => (CobId & 0x40000000) == 0; /// /// Total bits mapped in this PDO /// public int TotalMappedBits => _mappings.Sum(m => m.BitLength); /// /// Check if mapping is valid (không vượt quá 64 bits) /// public bool IsMappingValid => TotalMappedBits <= 64; /// /// Add mapping với validation /// public void AddMapping(PdoMapping mapping) { if (mapping.BitLength == 0 || mapping.BitLength > 64) throw new ArgumentException("BitLength must be between 1 and 64", nameof(mapping)); if (TotalMappedBits + mapping.BitLength > 64) throw new InvalidOperationException($"Adding this mapping would exceed 64 bits limit. Current: {TotalMappedBits}, Adding: {mapping.BitLength}"); _mappings.Add(mapping); } /// /// Remove mapping /// public bool RemoveMapping(PdoMapping mapping) { return _mappings.Remove(mapping); } /// /// Clear all mappings /// public void ClearMappings() { _mappings.Clear(); } /// /// Validate entire configuration /// public ValidationResult ValidateConfiguration() { var errors = new List(); if (PdoNumber < 1 || PdoNumber > 4) errors.Add("PDO number must be between 1 and 4"); if (!IsValid) errors.Add("PDO is disabled (bit 31 of COB-ID is set)"); if (!IsMappingValid) errors.Add($"Total mapped bits ({TotalMappedBits}) exceeds 64 bits limit"); if (_mappings.Count == 0) errors.Add("No mappings configured"); // Validate individual mappings for (int i = 0; i < _mappings.Count; i++) { var mapping = _mappings[i]; if (mapping.Index == 0) errors.Add($"Mapping {i}: Invalid index (0)"); if (mapping.BitLength == 0) errors.Add($"Mapping {i}: Invalid bit length (0)"); } return new ValidationResult(errors.Count == 0, errors); } } /// /// PDO Data (received or to-send) /// public readonly struct PdoData(byte pdoNumber, uint cobId, byte[] data, DateTime timestamp) { public byte PdoNumber { get; init; } = pdoNumber; public uint CobId { get; init; } = cobId; public byte[] Data { get; init; } = data; public DateTime Timestamp { get; init; } = timestamp; /// /// Extract một value từ PDO data theo mapping /// public T ExtractValue(PdoMapping mapping) where T : struct { if (mapping.BitLength == 0 || mapping.BitLength > 64) throw new ArgumentException("BitLength must be 1-64"); int bitOffset = 0; foreach (var _ in Enumerable.Range(0, mapping.SubIndex)) { // Tính bit offset dựa trên các mappings trước đó // (simplified - trong thực tế cần biết tất cả mappings) } return ExtractValue(bitOffset, mapping.BitLength); } /// /// Extract value tại bit offset cụ thể /// public T ExtractValue(int bitOffset, int bitLength) where T : struct { ulong value = 0; int byteOffset = bitOffset / 8; int bitInByteOffset = bitOffset % 8; int bitsRead = 0; while (bitsRead < bitLength && byteOffset < Data.Length) { int bitsToRead = Math.Min(8 - bitInByteOffset, bitLength - bitsRead); byte mask = (byte)((1 << bitsToRead) - 1); byte byteValue = (byte)((Data[byteOffset] >> bitInByteOffset) & mask); value |= (ulong)byteValue << bitsRead; bitsRead += bitsToRead; byteOffset++; bitInByteOffset = 0; } // Convert to target type with sign extension if needed return ConvertValue(value, bitLength); } private static T ConvertValue(ulong value, int bitLength) where T : struct { var type = typeof(T); if (type == typeof(bool)) return (T)(object)(value != 0); if (type == typeof(byte)) return (T)(object)(byte)value; if (type == typeof(sbyte)) { // Sign extension if (bitLength < 8 && (value & (1UL << (bitLength - 1))) != 0) value |= (ulong)((1L << bitLength) - 1) << bitLength; return (T)(object)(sbyte)value; } if (type == typeof(ushort)) return (T)(object)(ushort)value; if (type == typeof(short)) { if (bitLength < 16 && (value & (1UL << (bitLength - 1))) != 0) value |= (ulong)((1L << bitLength) - 1) << bitLength; return (T)(object)(short)value; } if (type == typeof(uint)) return (T)(object)(uint)value; if (type == typeof(int)) { if (bitLength < 32 && (value & (1UL << (bitLength - 1))) != 0) value |= (ulong)((1L << bitLength) - 1) << bitLength; return (T)(object)(int)value; } if (type == typeof(ulong)) return (T)(object)value; if (type == typeof(long)) { if (bitLength < 64 && (value & (1UL << (bitLength - 1))) != 0) value |= ulong.MaxValue << bitLength; return (T)(object)(long)value; } throw new NotSupportedException($"Type {type.Name} is not supported"); } }