using DBHelper_SqlSugar; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using WCS.Entity; namespace WCS.Core.DataTrans { /// /// IL动态生成的类型所继承的基类 /// public abstract class ProtocolProxyBase : IProtocolProxy { protected Type ProtocolType, ProtocolDataType; public static Func DataChanged; private readonly Dictionary _items = new Dictionary(); public string Id { get; set; } public WCS_DEVICEPROTOCOL Protocol { get; private set; } /// /// 起始便宜量,按字节算 /// public readonly ushort _start; /// /// 长度,按字节算 /// public readonly ushort _length; public WCS_DATABLOCK Db; private static readonly Dictionary LockObjs = new Dictionary(); protected ProtocolProxyBase(string id, WCS_DATABLOCK db, ushort start, WCS_DEVICEPROTOCOL deviceItem) { var type = this.GetType(); if (!LockObjs.ContainsKey(type)) LockObjs[type] = new object(); this.Db = db; db.Ex().DataChanged += PLCData_DataChanged; this._start = start; var bitStart = start * 8;//偏移量,按位算 this.Id = id; this.Protocol = deviceItem; var lst = this.GetType().GetInterfaces().ToList(); var ps = lst.SelectMany(v => v.GetProperties()).ToArray(); foreach (var p in ps) { if (p.GetIndexParameters().Length > 0) continue; if (typeof(ProtocolProxyBase).GetProperty(p.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null) { continue; } #region 计算偏移量 var modeNum = 0; var t = p.PropertyType; if (t.IsEnum) t = t.GetEnumUnderlyingType(); if (t == typeof(bool))//bit类型,db块中无须补齐16位 { modeNum = 1; } else if (t.IsPrimitive && Marshal.SizeOf(t) == 1)//byte char 等单字节类型,db块中无须补齐16位 { modeNum = 8; } else//其他类型,db块中必须补齐16位 { modeNum = 16; } var mod = bitStart % modeNum; if (mod > 0) { bitStart += modeNum - mod; } #endregion 计算偏移量 byte arrlen = 0; byte strLen = 0; if (t.IsArray) { var attr = p.GetCustomAttributes(typeof(MaxLengthAttribute), true).FirstOrDefault() as MaxLengthAttribute; if (attr != null) arrlen = (byte)attr.Length; } else if (p.PropertyType == typeof(string)) { var attr = p.GetCustomAttributes(typeof(StringLengthAttribute), true).FirstOrDefault() as StringLengthAttribute; strLen = (byte)attr.MaximumLength; } var m = typeof(DataBlock).GetMethod("Regist"); m = m.MakeGenericMethod(p.PropertyType); var item = m.Invoke(db.Ex(), new object[] { this.Id, p.Name, bitStart, arrlen, strLen }) as PlcItem; _items.Add(p.Name, item); bitStart += item.DataSizeOfBits; _length += item.DataSize; } ProtocolType = this.Protocol.DB.GetProtocolType(); ProtocolDataType = ProtocolType.Assembly.GetTypes().Where(v => v.IsClass).First(v => v.GetInterface(ProtocolType.Name) != null && v != this.GetType()); } private byte[] Data = null; private static object _locobj = new object(); private void InvokeUpdate(string user, Db db) { SaveChangs(user, db); } private WCS_PROTOCOLDATA _last = null; public DateTime UpdateTime; protected virtual void SaveChangs(string user, Db db) { var obj = Copy(this, ProtocolDataType) as WCS_PROTOCOLDATA; obj!.WCSVERSION = this.WcsVersion; if (_last == null) { Publish(this.Protocol.DEVICE.CODE, obj); _last = GetLastData(db); } if (_last != null) { if (Equals(obj, _last, ProtocolType)) return; } this.UpdateTime = DateTime.Now; Publish(this.Protocol.DEVICE.CODE, obj); _last = SaveNewData(db, _last, obj, user); } public virtual void Publish(string code, WCS_PROTOCOLDATA obj) { } protected abstract WCS_PROTOCOLDATA GetLastData(Db db); protected abstract WCS_PROTOCOLDATA SaveNewData(Db db, WCS_PROTOCOLDATA last, WCS_PROTOCOLDATA newobj, string user); private void PLCData_DataChanged(Db db, byte[] data) { var temp = data.Skip(_start).Take(_length).ToArray(); try { if (Data != null && Data.SequenceEqual(temp)) return; var d1 = DateTime.Now; InvokeUpdate("PLC", db); var d = (DateTime.Now - d1).TotalMilliseconds; } finally { Data = temp; } } private static object Copy(object obj, Type type) { var res = Activator.CreateInstance(type); foreach (var p in type.GetProperties()) { var p2 = obj.GetType().GetProperty(p.Name); if (p2 != null && p2.PropertyType == p.PropertyType) { p.SetValue(res, p2.GetValue(obj)); } } return res; } private static bool Equals(object a, object b, Type type) { foreach (var p in type.GetProperties()) { var v1 = p.GetValue(a); var v2 = p.GetValue(b); var attr = p.GetCustomAttribute(); if (attr != null) { if (attr.IgnoreValueRange == 0) continue; try { var d = Math.Abs((decimal.Parse(v1.ToString())) - (decimal.Parse(v2.ToString()))); if (d < attr.IgnoreValueRange) continue; } catch { Console.WriteLine("IgnoreValueRange属性只能用于数字类型"); } } if (p.PropertyType == typeof(string)) { v1 = v1 == null ? "" : v1.ToString(); v2 = v2 == null ? "" : v2.ToString(); } if (p.PropertyType.IsArray) { var m = typeof(Enumerable).GetMethods().Where(v => v.Name == nameof(Enumerable.SequenceEqual)).First(v => v.GetParameters().Length == 2); m = m.MakeGenericMethod(p.PropertyType.GetElementType()!); var res = (bool)m.Invoke(null, new[] { v1, v2 })!; if (!res) return false; } else { if (!v1!.Equals(v2)) return false; } } return true; } #region IGenerator /// /// 索引器Get /// /// /// /// protected T GetItem(params object[] args) { Console.WriteLine("GetItem:" + string.Join(",", args.Select(v => v.ToString()))); return default(T); } /// /// 索引器Set /// /// /// protected void SetItem(params object[] args) { Console.WriteLine("SetItem:" + ":" + string.Join(",", args.Select(v => v.ToString()))); } /// /// 版本号,WCS每修改一次数据,版本号变化一次 /// public int WcsVersion { get; private set; } public void Set(string propertyName, T value) { var str = Protocol.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName; try { str += " = " + (value == null ? "null" : value.ToString()); var item = _items[propertyName] as PlcItem; item.Value = value; DBHelper_SqlSugar.Db.Do(db => { InvokeUpdate("WCS", db); }); Data = null; WcsVersion++; str += " 写入成功"; } catch { str += " 写入失败"; throw; } finally { Ltc.Log(str); } } public void Update(string prop, object value, string user) { var item = _items[prop]; item.Value = value; DBHelper_SqlSugar.Db.Do(db => { InvokeUpdate(user, db); }); Data = null; if (user == "PLC") return; WcsVersion++; } public T Get(string propertyName) { var str = Protocol.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName; try { var item = _items[propertyName] as PlcItem; var res = item.Value; str += " : " + (res == null ? "null" : res.ToString()); return res; } catch { str += " 读取失败"; throw; } } public T CallReturn(string methodName, params object[] args) { Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString()))); return (T)(object)1.321f; } public void CallVoid(string methodName, params object[] args) { Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString()))); } public void AddEvent(string eventName, Delegate delgate) { Console.WriteLine("Add:" + eventName); } public void RemoveEvent(string eventName, Delegate delgate) { Console.WriteLine("Remove:" + eventName); } #endregion IGenerator public override string ToString() { var str = this.GetType().GetProperties().Aggregate("", (current, p) => current + (p.Name + ":" + p.GetValue(this, null)!.ToString() + ",")); str = str[..^1]; return str; } } }