using System.Collections; using System.Reflection; using System.Runtime.InteropServices; namespace WCS.Core; public class DataBlock : EntityEx { public static List DBList = new(); private byte[] Data = new byte[0]; private DateTime faildTime = DateTime.MinValue; private readonly int id; private readonly List ProxyList = new(); public DataBlock(DBInfo ent, World world) : base(ent) { World = world; DBList.Add(this); id = DBList.Count; } public ushort Start { get; private set; } public ushort Length { get; private set; } public World World { get; } public bool Failed { get; private set; } = true; public event Action DbChanged; public override string ToString() { return id.ToString(); } public void RefreshData() { try { var plc = Entity.PLCInfo.Ex(World); if (Entity.PLCInfo.Type != PLCType.VitrualRedisPLC) if (Failed) if (!plc.Ping) { if (Entity.PLCInfo.IP == "1") throw new Exception($"{Entity.PLCInfo.IP}无法访问"); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"{Entity.PLCInfo.IP}无法访问"); Console.ResetColor(); 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 (plc) { data = plc.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) { bitStart -= Start * 8; return (T)Read(typeof(T), ref bitStart, strLength, arrLength); } private object Read(Type type, ref int bitStart, int strLength, int arrLength) { if (Failed) throw new Exception(Entity.No + "连接失败"); if (type.IsArray) { var t = type.GetElementType(); if (t.IsArray) throw new Exception("只支持一维数组"); var arr = Array.CreateInstance(t, arrLength); for (var i = 0; i < arr.Length; i++) { var value = Read(t, ref bitStart, strLength, 0); arr.SetValue(value, i); } return arr; } 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; } if (type == typeof(bool)) return ReadBit(ref bitStart); if (type == typeof(string)) return ReadString(ref bitStart, strLength); if (type.IsEnum) return ReadEnum(type, ref bitStart); 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); } 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; } throw new Exception("类型不支持"); } public static object ByteToStruct(Type type, byte[] dataBuffer) { var size = Marshal.SizeOf(type); var 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(); if (type == typeof(bool)) return BitConverter.ToBoolean(data, 0); if (type == typeof(char)) return BitConverter.ToChar(data, 0); if (type == typeof(short)) return BitConverter.ToInt16(data, 0); if (type == typeof(ushort)) return BitConverter.ToUInt16(data, 0); if (type == typeof(int)) return BitConverter.ToInt32(data, 0); if (type == typeof(uint)) return BitConverter.ToUInt32(data, 0); if (type == typeof(long)) return BitConverter.ToInt64(data, 0); if (type == typeof(ulong)) return BitConverter.ToUInt64(data, 0); if (type == typeof(float)) return BitConverter.ToSingle(data, 0); if (type == typeof(double)) return BitConverter.ToDouble(data, 0); throw new Exception("类型不支持"); } private byte[] ReadBytes(ref int bitStart, ushort length) { var start = GetByteStart(bitStart); if (Data.Length < start + length) { var 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)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(World)) { var start = GetByteStart(bitStart); Entity.PLCInfo.Ex(World).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(World)) { var start = GetByteStart(bitStart); var bitIndex = GetBitIndex(bitStart); var b = Data.Skip(start).First(); b = b.SetBit(bitIndex, flag); var data = new[] { b }; Entity.PLCInfo.Ex(World).Accessor.WriteBytes(Entity.No, start, data); data.CopyTo(Data, start - Start); bitStart += 1; } } #endregion 写入 }