using System; using System.Collections.Generic; using System.IO; using System.Linq; using ln.collections; using ln.ethercat.controller; using ln.logging; using ln.type; namespace ln.ethercat { public class ECSlave { internal ECMaster ECMaster; public UInt16 Id { get; } MappingBTree sdoCache = new MappingBTree((sdo)=>sdo.Index); public ECSlave(ECMaster ecMaster,UInt16 slave_id) { ECMaster = ecMaster; Id = slave_id; ECMBind.ecmbind_enumerate_servicedescriptors(Id, (int slave, int index)=>{ sdoCache.Add(new SDODescriptor(this, (UInt16)index)); }); foreach (SDODescriptor sdo in sdoCache) sdo.Update(); } public IEnumerable GetSDODescriptors() => sdoCache.Values; public bool GetDescriptor(UInt16 index, out SDODescriptor descriptor) => sdoCache.TryGet(index, out descriptor); } public class SDODescriptor { internal ECSlave Slave; public UInt16 SlaveId => Slave.Id; public UInt16 Index { get; } public string Name { get; private set; } public ECDataTypes DataType { get; set; } public ECObjectCodes ObjectCode { get; set; } public int MaxSubIndex { get; set; } MappingBTree valueCache; public SDODescriptor(ECSlave slave, UInt16 index) { Slave = slave; Index = index; } public void Update() { if (ECMBind.ecmbind_read_objectdescription(Slave.Id, Index, (int slave, int index, ECDataTypes dataType, ECObjectCodes objectCode, int maxsub, ushort bitlength, String name)=>{ Name = name; DataType = dataType; ObjectCode = objectCode; MaxSubIndex = maxsub; }) <= 0) { Logging.Log(LogLevel.WARNING, "cannot create SDO instance for {0}:{1:X4} not found.", Slave.Id, Index); } } void EnsureValueCache() { if (valueCache == null) { valueCache = new MappingBTree((v)=>v.SubIndex); for (byte subIndex = 0; subIndex <= MaxSubIndex; subIndex++) valueCache.Add(new SDOValue(this, subIndex)); } } public IEnumerable GetValues() { EnsureValueCache(); return valueCache.Values; } public SDOValue GetValue(byte subIndex) { EnsureValueCache(); return valueCache[subIndex]; } public bool GetValue(byte subIndex, out SDOValue sdoValue) { EnsureValueCache(); return valueCache.TryGet(subIndex, out sdoValue); } public override string ToString() => String.Format("[SDODescriptor Slave={0} Index=0x{1:X4} ObjectCode={2} DataType={3}]", Slave.Id, Index, ObjectCode, DataType); } public class SDOValue { public SDODescriptor Descriptor; public byte SubIndex { get; } public ushort BitLength { get; private set; } public string Name { get; private set; } public ECDataTypes DataType { get; private set; } public SDOValue(SDODescriptor descriptor,byte subIndex) { Descriptor = descriptor; SubIndex = subIndex; if ((Descriptor.MaxSubIndex > 0) && (SubIndex == 0)) { DataType = descriptor.DataType; Name = descriptor.Name; } else { if (ECMBind.ecmbind_read_objectdescription_entry(Descriptor.SlaveId, descriptor.Index, SubIndex, (int slave, int index, ECDataTypes dataType, ECObjectCodes objectCode, int si, ushort bitlength, String name)=>{ Name = (SubIndex == 0) ? descriptor.Name : name; BitLength = bitlength; DataType = dataType; }) <= 0) { Logging.Log(LogLevel.WARNING, "SDOValue: could not read object description entry {0}:{1:X4}.{2}", descriptor.SlaveId, descriptor.Index, SubIndex); } } } public T GetValue() => (T)GetValue(); public object GetValue(){ if (GetValue(out object value)) return value; throw new IOException(); } public bool GetValue(out object value) { if (GetRawValue(out byte[] data)) return ECDataTypeConverter.FromEthercat(DataType, data, out value); value = null; return false; } public bool GetRawValue(out byte[] rawValue) { if (Descriptor.Slave.ECMaster.GetPDOItem(this, out PDO pdo)) { return Descriptor.Slave.ECMaster.GetIOmapData(pdo.AddressOffset, pdo.ByteLength, out rawValue); } else { return Descriptor.Slave.ECMaster.ReadSDO(Descriptor.SlaveId, Descriptor.Index, SubIndex, out rawValue); } } public void SetValue(T value) { if (ECDataTypeConverter.ToEthercat(DataType, value, out byte[] rawValue)) { SetRawValue(rawValue); } else { Logging.Log(LogLevel.ERROR, "SDOValue: SetValue<>({0}): could not convert to ethercat", value); } } public void SetRawValue(byte[] rawValue) { if (Descriptor.Slave.ECMaster.GetPDOItem(this, out PDO pdo)) { Descriptor.Slave.ECMaster.SetIOmapData(pdo.AddressOffset, pdo.ByteLength, rawValue); } else { Descriptor.Slave.ECMaster.WriteSDO(Descriptor.SlaveId, Descriptor.Index, SubIndex, rawValue); } } } }