using System.Collections; using System.Net.NetworkInformation; using System.Reflection; using System.Runtime.InteropServices; namespace WCS.Core { public class DataBlock : EntityEx { public event Action DbChanged; public static List DBList = new List(); private List ProxyList = new List(); public ushort Start { get; private set; } public ushort Length { get; private set; } private bool failed = false; private byte[] Data = new byte[0]; private int id = 0; public override string ToString() { return id.ToString(); } public DataBlock(DBInfo ent) : base(ent) { DBList.Add(this); id = DBList.Count; } DateTime faildTime = DateTime.MinValue; public void RefreshData() { try { if (failed) { if (!Entity.PLCInfo.Ex().Ping) { throw new Exception($"{Entity.PLCInfo.IP}无法访问"); } } Start = (ushort)ProxyList.Min(v => v.Info.Position); var last = ProxyList.OrderBy(v => v.Info.Position).Last(); Length = (ushort)(last.Info.Position + last.BytesCount); var data = new byte[0]; lock (Entity.PLCInfo.Ex()) { data = Entity.PLCInfo.Ex().Accessor.ReadBytes(Entity.No, Start, (ushort)(Length - Start)); } if (!Data.SequenceEqual(data)) { Data = data; DbChanged?.Invoke(Data); } failed = false; } catch (Exception ex) { failed = true; throw; } } public PlcItem Regist(ProtocolProxyBase proxy, string objid, string name, int start, byte arrLen = 1, byte strLen = 0) { if (!ProxyList.Contains(proxy)) ProxyList.Add(proxy); var t = typeof(T); var pv = new PlcItem(objid, name, this, start, arrLen, strLen); //Values.Add(pv); return pv; } private ushort GetByteStart(int bitStart) { var res = bitStart / 8; return (ushort)res; } private ushort GetBitIndex(int bitStart) { return (ushort)(bitStart % 8); } #region 读取 public T Read(int bitStart, int strLength, int arrLength) { try { bitStart -= Start * 8; return (T)Read(typeof(T), ref bitStart, strLength, arrLength); } catch (Exception ex) { throw; } } private object Read(Type type, ref int bitStart, int strLength, int arrLength) { if (failed) throw new Exception(this.Entity.No + "连接失败"); if (type.IsArray) { var t = type.GetElementType(); if (t.IsArray) { throw new Exception("只支持一维数组"); } var arr = Array.CreateInstance(t, arrLength); for (int i = 0; i < arr.Length; i++) { var value = Read(t, ref bitStart, strLength, 0); arr.SetValue(value, i); } return arr; } else if (typeof(IList).IsAssignableFrom(type)) { var t = type.GetGenericArguments().First(); var res = Activator.CreateInstance(type) as IList; while (bitStart < Length * 8) { var value = Read(t, ref bitStart, strLength, 0); res.Add(value); } return res; } else if (type == typeof(bool)) { return ReadBit(ref bitStart); } else if (type == typeof(string)) { return ReadString(ref bitStart, strLength); } else if (type.IsEnum) { return ReadEnum(type, ref bitStart); } else if (type.IsPrimitive) { var revers = Entity.PLCInfo.Type == PLCType.Siemens; return ReadPrimitive(type, ref bitStart, revers); //var revers = Entity.PLC.TYPE == PLCType.西门子; //return ReadPrimitive(type, ref bitStart, false); } else { if (type.IsValueType) { //ushort size = (ushort)Marshal.SizeOf(type); //var s = bitStart; //var bytes = ReadBytes(ref s, size).Reverse().ToArray(); //var o = ByteToStruct(type, bytes); var res = Activator.CreateInstance(type); foreach (var p in type.GetProperties()) { var value = Read(p.PropertyType, ref bitStart, strLength, arrLength); p.SetValue(res, value); } return res; } else throw new Exception("类型不支持"); } } public static object ByteToStruct(Type type, byte[] dataBuffer) { int size = Marshal.SizeOf(type); IntPtr allocIntPtr = Marshal.AllocHGlobal(size); try { Marshal.Copy(dataBuffer, 0, allocIntPtr, size); return Marshal.PtrToStructure(allocIntPtr, type); } finally { Marshal.FreeHGlobal(allocIntPtr); } } private string ReadString(ref int bitStart, int strLen) { if (Entity.PLCInfo.Type == PLCType.Siemens) { var data = ReadBytes(ref bitStart, (ushort)(strLen)); //var len = data[1]; //data = data.Skip(2).Take(len).ToArray(); var str = Configs.StringEncoding.GetString(data); return str; } else { var data = ReadBytes(ref bitStart, (ushort)strLen); var str = Configs.StringEncoding.GetString(data); return str; } } private object ReadEnum(Type enumType, ref int bitStart) { var type = enumType.GetEnumUnderlyingType(); var reverse = false; if (enumType.GetCustomAttribute() == null) { reverse = Entity.PLCInfo.Type == PLCType.Siemens; } var value = ReadPrimitive(type, ref bitStart, reverse); var res = Enum.Parse(enumType, value.ToString()); return res; } private object ReadPrimitive(Type type, ref int bitStart, bool reverse) { var size = (ushort)Marshal.SizeOf(type); if (type == typeof(bool)) size = 1; var data = ReadBytes(ref bitStart, size); if (reverse) data = data.Reverse().ToArray(); if (type == typeof(byte)) { return data.First(); } else if (type == typeof(bool)) { return BitConverter.ToBoolean(data, 0); } else if (type == typeof(char)) { return BitConverter.ToChar(data, 0); } else if (type == typeof(short)) { return BitConverter.ToInt16(data, 0); } else if (type == typeof(ushort)) { return BitConverter.ToUInt16(data, 0); } else if (type == typeof(int)) { return BitConverter.ToInt32(data, 0); } else if (type == typeof(uint)) { return BitConverter.ToUInt32(data, 0); } else if (type == typeof(long)) { return BitConverter.ToInt64(data, 0); } else if (type == typeof(ulong)) { return BitConverter.ToUInt64(data, 0); } else if (type == typeof(float)) { return BitConverter.ToSingle(data, 0); } else if (type == typeof(double)) { return BitConverter.ToDouble(data, 0); } else { throw new Exception("类型不支持"); } } private byte[] ReadBytes(ref int bitStart, ushort length) { var start = GetByteStart(bitStart); if (Data.Length < start + length) { string data1 = ""; foreach (var item in Data.ToList()) { data1 = data1 + $"[{item}]"; } var msg = $"读取数据的位置超出数据总长度:"; msg = msg + $"Data--{Data.Length};"; msg = msg + $"bitStart--{bitStart};"; msg = msg + $"start--{start};"; msg = msg + $"length--{length};"; msg = msg + $"读取PLC传入参数{Entity.No},{Start},{(ushort)(Length - Start)};"; msg = msg + $"Data内容--{data1}"; throw new Exception(msg); } var data = Data.Skip(start).Take(length).ToArray(); bitStart += length * 8; return data; } private bool ReadBit(ref int bitStart) { var start = GetByteStart(bitStart); var b = Data.Skip(start).First(); var bitIndex = GetBitIndex(bitStart); bitStart++; return b.GetBit(bitIndex); } #endregion 读取 #region 写入 public void Write(int bitStart, T value, int strLength, int arrLength) { Write(typeof(T), ref bitStart, value, strLength, arrLength); } private void Write(Type type, ref int bitStart, object value, int strLength, int arrLength) { if (type.IsArray) { var t = type.GetElementType(); if (t.IsArray) { throw new Exception("只支持一维数组"); } var arr = value as Array; if (arr.Length > arrLength) throw new Exception("数组长度超出"); foreach (var obj in arr) { Write(obj.GetType(), ref bitStart, obj, strLength, arrLength); } } else if (type == typeof(bool)) { WriteBit(ref bitStart, (bool)value); } else if (type == typeof(string)) { WriteString(ref bitStart, value.ToString(), strLength); } else if (type.IsEnum) { WriteEnum(ref bitStart, value); } else if (type.IsPrimitive) { var revers = Entity.PLCInfo.Type == PLCType.Siemens; WritePrimitive(ref bitStart, value, revers); } else throw new Exception("类型不支持"); } private void WriteString(ref int bitStart, string value, int strLen) { var data = Configs.StringEncoding.GetBytes(value); if (data.Length > strLen) throw new Exception("字符串长度超出"); //if (Entity.PLCInfo.Type == PLCType.Siemens) //{ // var a = Encoding.UTF8.GetString(data); // var title = new byte[] { (byte)strLen, (byte)data.Length }; // data = title.Concat(data).ToArray(); // var b = Encoding.UTF8.GetString(data); // var c = 0; //} WriteBytes(ref bitStart, data); } private void WriteEnum(ref int bitStart, object value) { var etype = value.GetType(); var type = etype.GetEnumUnderlyingType(); var obj = Convert.ChangeType(value, type); var reverse = false; if (etype.GetCustomAttribute() == null) { reverse = Entity.PLCInfo.Type == PLCType.Siemens; } WritePrimitive(ref bitStart, obj, reverse); } private void WritePrimitive(ref int bitStart, object value, bool reverse) { var type = value.GetType(); byte[] data; if (type == typeof(char)) { data = BitConverter.GetBytes((char)value); } else if (type == typeof(byte)) { data = new byte[] { (byte)value }; } else if (type == typeof(short)) { data = BitConverter.GetBytes((short)value); } else if (type == typeof(ushort)) { data = BitConverter.GetBytes((ushort)value); } else if (type == typeof(int)) { data = BitConverter.GetBytes((int)value); } else if (type == typeof(uint)) { data = BitConverter.GetBytes((uint)value); } else if (type == typeof(long)) { data = BitConverter.GetBytes((long)value); } else if (type == typeof(ulong)) { data = BitConverter.GetBytes((ulong)value); } else { throw new Exception("类型不支持"); } if (reverse) data = data.Reverse().ToArray(); WriteBytes(ref bitStart, data); } private void WriteBytes(ref int bitStart, byte[] data) { lock (Entity.PLCInfo.Ex()) { var start = GetByteStart(bitStart); Entity.PLCInfo.Ex().Accessor.WriteBytes(Entity.No, start, data); data.CopyTo(Data, start - Start); bitStart += data.Length * 8; } } private void WriteBit(ref int bitStart, bool flag) { lock (Entity.PLCInfo.Ex()) { var start = GetByteStart(bitStart); var bitIndex = GetBitIndex(bitStart); var b = Data.Skip(start).First(); b = b.SetBit(bitIndex, flag); var data = new byte[] { b }; Entity.PLCInfo.Ex().Accessor.WriteBytes(Entity.No, (ushort)start, data); data.CopyTo(Data, start - Start); bitStart += 1; } } #endregion 写入 } }