ProtocolProxyBase.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Runtime.InteropServices;
  8. using WCS.Core.DbHelper;
  9. using WCS.Core.IL;
  10. using WCS.Core.Log;
  11. using WCS.Entity;
  12. namespace WCS.Core.DataTrans
  13. {
  14. /// <summary>
  15. /// IL动态生成的类型所继承的基类
  16. /// </summary>
  17. public abstract class ProtocolProxyBase : IProtocolProxy
  18. {
  19. protected Type ProtocolType, ProtocolDataType;
  20. public static Func<WCS_PROTOCOLDATA, string, WCS_PROTOCOLDATA> DataChanged;
  21. private readonly Dictionary<string, PlcItem> _items = new Dictionary<string, PlcItem>();
  22. public string Id { get; set; }
  23. /// <summary>
  24. /// 设备在DB块中所处位置所记录
  25. /// </summary>
  26. public WCS_DEVICEPROTOCOL Protocol { get; private set; }
  27. /// <summary>
  28. /// 起始偏移量,按字节算
  29. /// </summary>
  30. public readonly ushort Start;
  31. /// <summary>
  32. /// 长度,按字节算
  33. /// </summary>
  34. public readonly ushort Length;
  35. /// <summary>
  36. ///
  37. /// </summary>
  38. public WCS_DATABLOCK Db;
  39. private static readonly Dictionary<Type, object> LockObjs = new Dictionary<Type, object>();
  40. /// <summary>
  41. /// 对从PLC中读取出的数据进行处理
  42. /// </summary>
  43. /// <param name="id"></param>
  44. /// <param name="db"></param>
  45. /// <param name="start"></param>
  46. /// <param name="deviceItem"></param>
  47. protected ProtocolProxyBase(string id, WCS_DATABLOCK db, ushort start, WCS_DEVICEPROTOCOL deviceItem)
  48. {
  49. try
  50. {
  51. if (id.Contains("SRM"))
  52. {
  53. var a = 1;
  54. }
  55. var type = this.GetType();
  56. if (!LockObjs.ContainsKey(type))
  57. LockObjs[type] = new object();
  58. this.Db = db;
  59. db.Ex().DataChanged += PLCData_DataChanged;
  60. this.Start = start;
  61. var bitStart = start * 8;//偏移量,按位算
  62. this.Id = id;
  63. this.Protocol = deviceItem;
  64. var lst = this.GetType().GetInterfaces().ToList();
  65. var ps = lst.SelectMany(v => v.GetProperties()).ToArray();
  66. foreach (var p in ps)
  67. {
  68. if (p.GetIndexParameters().Length > 0)
  69. continue;
  70. if (typeof(ProtocolProxyBase).GetProperty(p.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null)
  71. {
  72. continue;
  73. }
  74. #region 计算偏移量
  75. var modeNum = 0;
  76. var t = p.PropertyType;
  77. if (t.IsEnum)
  78. t = t.GetEnumUnderlyingType();
  79. if (t == typeof(bool))//bit类型,db块中无须补齐16位
  80. {
  81. modeNum = 1;
  82. }
  83. else if (t.IsPrimitive && Marshal.SizeOf(t) == 1)//byte char 等单字节类型,db块中无须补齐16位
  84. {
  85. modeNum = 8;
  86. }
  87. else//其他类型,db块中必须补齐16位
  88. {
  89. modeNum = 16;
  90. }
  91. var mod = bitStart % modeNum;
  92. if (mod > 0)
  93. {
  94. bitStart += modeNum - mod;
  95. }
  96. #endregion 计算偏移量
  97. byte arrlen = 0;
  98. byte strLen = 0;
  99. if (t.IsArray)
  100. {
  101. var attr = p.GetCustomAttributes(typeof(MaxLengthAttribute), true).FirstOrDefault() as MaxLengthAttribute;
  102. if (attr != null)
  103. arrlen = (byte)attr.Length;
  104. }
  105. else if (p.PropertyType == typeof(string))
  106. {
  107. var attr = p.GetCustomAttributes(typeof(StringLengthAttribute), true).FirstOrDefault() as StringLengthAttribute;
  108. strLen = (byte)attr.MaximumLength;
  109. }
  110. var m = typeof(DataBlock).GetMethod("Regist");
  111. m = m.MakeGenericMethod(p.PropertyType);
  112. var item = m.Invoke(db.Ex(), new object[] { this.Id, p.Name, bitStart, arrlen, strLen }) as PlcItem;
  113. _items.Add(p.Name, item);
  114. bitStart += item.DataSizeOfBits;
  115. Length += item.DataSize;
  116. }
  117. ProtocolType = this.Protocol.DB.GetProtocolType();
  118. ProtocolDataType = ProtocolType.Assembly.GetTypes().Where(v => v.IsClass).First(v => v.GetInterface(ProtocolType.Name) != null && v != this.GetType());
  119. InfoLog.INFO_SRMINFO($"{id}");
  120. }
  121. catch (Exception e)
  122. {
  123. // ignored
  124. }
  125. }
  126. /// <summary>
  127. /// 上一次从PLC中读取出的设备信息
  128. /// </summary>
  129. private byte[] _data;
  130. private static object _locobj = new object();
  131. private void InvokeUpdate(string user, Db db)
  132. {
  133. SaveChangs(user, db);
  134. }
  135. private WCS_PROTOCOLDATA _last = null;
  136. public DateTime UpdateTime;
  137. protected virtual void SaveChangs(string user, Db db)
  138. {
  139. var obj = Copy(this, ProtocolDataType) as WCS_PROTOCOLDATA;
  140. obj!.WCSVERSION = this.WcsVersion;
  141. if (_last == null)
  142. {
  143. Publish(this.Protocol.DEVICE.CODE, obj);
  144. _last = GetLastData(db);
  145. }
  146. if (_last != null)
  147. {
  148. if (Equals(obj, _last, ProtocolType))
  149. return;
  150. }
  151. this.UpdateTime = DateTime.Now;
  152. Publish(this.Protocol.DEVICE.CODE, obj);
  153. _last = SaveNewData(db, _last, obj, user);
  154. }
  155. public virtual void Publish(string code, WCS_PROTOCOLDATA obj)
  156. {
  157. }
  158. protected abstract WCS_PROTOCOLDATA GetLastData(Db db);
  159. protected abstract WCS_PROTOCOLDATA SaveNewData(Db db, WCS_PROTOCOLDATA last, WCS_PROTOCOLDATA newobj, string user);
  160. /// <summary>
  161. /// PLC更新数据
  162. /// </summary>
  163. /// <param name="db">数据库上下文</param>
  164. /// <param name="data">设备数据字节组</param>
  165. private void PLCData_DataChanged(Db db, byte[] data)
  166. {
  167. try
  168. {
  169. //按照配置信息截取对应长度的字节组
  170. var temp = data.Skip(Start).Take(Length).ToArray();
  171. try
  172. {
  173. //如果本次数据与上次一直,结束处理流程
  174. //if (_data != null && _data.SequenceEqual(temp)) return;
  175. var sw = new Stopwatch();
  176. sw.Start();
  177. InvokeUpdate("PLC", db);
  178. sw.Stop();
  179. //if (sw.ElapsedMilliseconds <= 400) return;
  180. //Console.WriteLine($"{Id}:{Protocol.DEVICECODE}:访问耗时:{sw.ElapsedMilliseconds}");
  181. //Console.ResetColor();
  182. }
  183. finally
  184. {
  185. _data = temp;
  186. }
  187. }
  188. catch (Exception ex)
  189. {
  190. Console.WriteLine(ex.Message);
  191. Console.ResetColor();
  192. }
  193. }
  194. private static object Copy(object obj, Type type)
  195. {
  196. var res = Activator.CreateInstance(type);
  197. foreach (var p in type.GetProperties())
  198. {
  199. var p2 = obj.GetType().GetProperty(p.Name);
  200. if (p2 != null && p2.PropertyType == p.PropertyType)
  201. {
  202. p.SetValue(res, p2.GetValue(obj));
  203. }
  204. }
  205. return res;
  206. }
  207. private static bool Equals(object a, object b, Type type)
  208. {
  209. foreach (var p in type.GetProperties())
  210. {
  211. var v1 = p.GetValue(a);
  212. var v2 = p.GetValue(b);
  213. var attr = p.GetCustomAttribute<IgnoreChangingAttribute>();
  214. if (attr != null)
  215. {
  216. if (attr.IgnoreValueRange == 0)
  217. continue;
  218. try
  219. {
  220. var d = Math.Abs((decimal.Parse(v1.ToString())) - (decimal.Parse(v2.ToString())));
  221. if (d < attr.IgnoreValueRange)
  222. continue;
  223. }
  224. catch
  225. {
  226. Console.WriteLine("IgnoreValueRange属性只能用于数字类型");
  227. }
  228. }
  229. if (p.PropertyType == typeof(string))
  230. {
  231. v1 = v1 == null ? "" : v1.ToString();
  232. v2 = v2 == null ? "" : v2.ToString();
  233. }
  234. if (p.PropertyType.IsArray)
  235. {
  236. var m = typeof(Enumerable).GetMethods().Where(v => v.Name == nameof(Enumerable.SequenceEqual)).First(v => v.GetParameters().Length == 2);
  237. m = m.MakeGenericMethod(p.PropertyType.GetElementType()!);
  238. var res = (bool)m.Invoke(null, new[] { v1, v2 })!;
  239. if (!res)
  240. return false;
  241. }
  242. else
  243. {
  244. if (!v1!.Equals(v2))
  245. return false;
  246. }
  247. }
  248. return true;
  249. }
  250. #region IGenerator
  251. /// <summary>
  252. /// 索引器Get
  253. /// </summary>
  254. /// <typeparam name="T"></typeparam>
  255. /// <param name="args"></param>
  256. /// <returns></returns>
  257. protected T GetItem<T>(params object[] args)
  258. {
  259. Console.WriteLine("GetItem:" + string.Join(",", args.Select(v => v.ToString())));
  260. return default(T);
  261. }
  262. /// <summary>
  263. /// 索引器Set
  264. /// </summary>
  265. /// <param name="args"></param>
  266. /// <returns></returns>
  267. protected void SetItem(params object[] args)
  268. {
  269. Console.WriteLine("SetItem:" + ":" + string.Join(",", args.Select(v => v.ToString())));
  270. }
  271. /// <summary>
  272. /// 版本号,WCS每修改一次数据,版本号变化一次
  273. /// </summary>
  274. public int WcsVersion { get; private set; }
  275. public void Set<T>(string propertyName, T value)
  276. {
  277. var str = Protocol.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName;
  278. try
  279. {
  280. str += " = " + (value == null ? "null" : value.ToString());
  281. var item = _items[propertyName] as PlcItem<T>;
  282. item.Value = value;
  283. DbHelper.Db.Do(db =>
  284. {
  285. InvokeUpdate("WCS", db);
  286. });
  287. _data = null;
  288. WcsVersion++;
  289. str += " 写入成功";
  290. }
  291. catch
  292. {
  293. str += " 写入失败";
  294. throw;
  295. }
  296. finally
  297. {
  298. Ltc.Log(str);
  299. }
  300. }
  301. public void Update(string prop, object value, string user)
  302. {
  303. var item = _items[prop];
  304. item.Value = value;
  305. DbHelper.Db.Do(db =>
  306. {
  307. InvokeUpdate(user, db);
  308. });
  309. _data = null;
  310. if (user == "PLC")
  311. return;
  312. WcsVersion++;
  313. }
  314. public T Get<T>(string propertyName)
  315. {
  316. var str = Protocol.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName;
  317. try
  318. {
  319. var item = _items[propertyName] as PlcItem<T>;
  320. var res = item.Value;
  321. str += " : " + (res == null ? "null" : res.ToString());
  322. return res;
  323. }
  324. catch
  325. {
  326. str += " 读取失败";
  327. throw;
  328. }
  329. }
  330. public T CallReturn<T>(string methodName, params object[] args)
  331. {
  332. Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString())));
  333. return (T)(object)1.321f;
  334. }
  335. public void CallVoid(string methodName, params object[] args)
  336. {
  337. Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString())));
  338. }
  339. public void AddEvent(string eventName, Delegate delgate)
  340. {
  341. Console.WriteLine("Add:" + eventName);
  342. }
  343. public void RemoveEvent(string eventName, Delegate delgate)
  344. {
  345. Console.WriteLine("Remove:" + eventName);
  346. }
  347. #endregion IGenerator
  348. public override string ToString()
  349. {
  350. var str = this.GetType().GetProperties().Aggregate("", (current, p) => current + (p.Name + ":" + p.GetValue(this, null)!.ToString() + ","));
  351. str = str[..^1];
  352. return str;
  353. }
  354. }
  355. }