DeviceExtension.cs 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using WCS.Core;
  7. using WCS.Core.BaseExtensions;
  8. using WCS.Core.DataTrans;
  9. using WCS.Core.DbHelper;
  10. using WCS.Core.Log;
  11. using WCS.Core.Redis;
  12. using WCS.Entity;
  13. using WCS.Entity.Protocol;
  14. using WCS.Entity.Protocol.RGV;
  15. using WCS.Entity.Protocol.Station;
  16. using WCS.Service.Helpers;
  17. using LogHelper = WCS.Service.Helpers.LogHelper;
  18. using TaskStatus = WCS.Entity.TaskStatus;
  19. namespace WCS.Service.Extensions
  20. {
  21. public static class WCS_DEVICEExtension
  22. {
  23. private static ConcurrentDictionary<string, object> DeviceValues = new ConcurrentDictionary<string, object>();
  24. public static void AddFlag(this WCS_DEVICE source, DF flag)
  25. {
  26. var df = source.Get<DF>("DeviceFlag");
  27. df = df | flag;
  28. source.Set("DeviceFlag", df);
  29. }
  30. public static bool Is(this WCS_DEVICE source, DF flag)
  31. {
  32. var df = source.Get<DF>("DeviceFlag");
  33. return (df & flag) == flag;
  34. }
  35. public static void Set<T>(this WCS_DEVICE source, string key, T value)
  36. {
  37. DeviceValues[source.CODE + key] = value;
  38. }
  39. public static T Get<T>(this WCS_DEVICE source, string key)
  40. {
  41. if (!DeviceValues.ContainsKey(source.CODE + key))
  42. return default(T);
  43. return (T)DeviceValues[source.CODE + key];
  44. }
  45. public static short Code(this WCS_DEVICE source)
  46. {
  47. return short.Parse(source.CODE);
  48. }
  49. public static string Tunnel(this WCS_DEVICE source)
  50. {
  51. return source.Get<string>("Tunnel");
  52. }
  53. public static int TunnelNum(this WCS_DEVICE source)
  54. {
  55. return int.Parse(source.Tunnel().Last().ToString());
  56. }
  57. public static int Floor(this WCS_DEVICE source)
  58. {
  59. return source.Get<int>("Floor");
  60. }
  61. public static WCS_DEVICE SC(this WCS_DEVICE source)
  62. {
  63. return source.Get<WCS_DEVICE>("SC");
  64. }
  65. public static WCS_DEVICE RGV(this WCS_DEVICE source)
  66. {
  67. return source.Get<WCS_DEVICE>("RGV");
  68. }
  69. public static LocInfo LocInfo(this WCS_DEVICE source)
  70. {
  71. return source.Get<LocInfo>("LocInfo");
  72. }
  73. public static bool WakeupOn(this WCS_DEVICE source, int sec, string key)
  74. {
  75. var str = "WakeupOn" + key;
  76. var last = source.Get<DateTime>(str);
  77. if ((DateTime.Now - last).TotalMilliseconds > sec)
  78. {
  79. source.Set(str, DateTime.Now);
  80. return true;
  81. }
  82. else
  83. {
  84. Ltc.Log("OnSleep");
  85. return false;
  86. }
  87. }
  88. public static bool WakeupOn(this WCS_DEVICE source, int sec)
  89. {
  90. return source.WakeupOn(sec, "");
  91. }
  92. public static IBCR80 BCR(this WCS_DEVICE source)
  93. {
  94. var dev = Device.Find("BCR" + source.CODE);
  95. return dev.DEVICEPROTOCOLS.First().Data<IBCR80>();
  96. }
  97. public static short PalletType(this WCS_DEVICE source)
  98. {
  99. return (short)source.Get<int>("PalletType");
  100. }
  101. public static short ProductLine(this WCS_DEVICE source)
  102. {
  103. return (short)source.Get<int>("ProductLine");
  104. }
  105. public static short WorkShop(this WCS_DEVICE source)
  106. {
  107. return (short)source.Get<int>("WorkShop");
  108. }
  109. /// <summary>
  110. /// 获取从source到endaddr的路径中的所有位置
  111. /// </summary>
  112. /// <param name="source">起始位置</param>
  113. /// <param name="endAddr">目标位置</param>
  114. /// <param name="condition">路径筛选条件</param>
  115. /// <returns></returns>
  116. public static List<WCS_DEVICE> GetPath(this WCS_DEVICE source, string endAddr, Func<List<WCS_DEVICE>, bool> condition = null)
  117. {
  118. var q = source.PATHS.Where(v => v.START == source & v.END.CODE.Contains(endAddr.ToUpper()))
  119. .Select(v => v.PATH.Split('-').Select(v => Device.Find(v)).ToList());
  120. if (condition != null)
  121. q = q.Where(condition);
  122. return q.FirstOrDefault();
  123. }
  124. /// <summary>
  125. /// 获取从source到endaddr的路径中的第一个位置
  126. /// </summary>
  127. /// <param name="source">起始位置</param>
  128. /// <param name="endAddr">目标位置</param>
  129. /// <param name="condition">路径筛选条件</param>
  130. /// <returns></returns>
  131. public static WCS_DEVICE GetPath(this WCS_DEVICE source, string endAddr)
  132. {
  133. var q = source.PATHS.Where(v => v.START == source && v.END.CODE.Contains(endAddr.ToUpper()))
  134. .Select(v => v.PATH.Split('-').Select(v => Device.Find(v)).ToList());
  135. return q.FirstOrDefault().FirstOrDefault();
  136. }
  137. public static WCS_DEVICE GetNext(this WCS_DEVICE source, string endAddr)
  138. {
  139. return source.GetNext(endAddr, null);
  140. }
  141. /// <summary>
  142. /// 获取从source到endaddr的路径中的下一个位置
  143. /// </summary>
  144. /// <param name="source">起始位置</param>
  145. /// <param name="endAddr">目标位置</param>
  146. /// <param name="condition">路径筛选条件</param>
  147. /// <returns></returns>
  148. public static WCS_DEVICE GetNext(this WCS_DEVICE source, string endAddr, Func<List<WCS_DEVICE>, bool> condition)
  149. {
  150. var path = source.GetPath(endAddr, condition);
  151. if (Ltc.Do(path, v => v == null || v.Count == 0, "判断数量", "数量异常"))
  152. return null;
  153. if (source.IsConv())
  154. {
  155. var obj = path.FirstOrDefault(v => !v.IsConv());
  156. if (obj == null)
  157. {
  158. return path.Last();
  159. }
  160. else if (obj.IsRGV())
  161. {
  162. var target = path.Skip(path.IndexOf(obj) + 1).FirstOrDefault();
  163. return target;
  164. }
  165. else
  166. return null;
  167. }
  168. else
  169. {
  170. return path.First();
  171. }
  172. }
  173. public static bool IsDevGroup(this WCS_DEVICE source)
  174. {
  175. return source.CODE.Contains("G");
  176. }
  177. public static bool IsRGV(this WCS_DEVICE source)
  178. {
  179. return source.DEVICEPROTOCOLS.Any(v => v.DB.PROTOCOL == typeof(IRGV520).AssemblyQualifiedName);
  180. }
  181. public static bool IsConv(this WCS_DEVICE source)
  182. {
  183. return source.DEVICEPROTOCOLS.Any(v => v.DB.PROTOCOL == typeof(IStation521).AssemblyQualifiedName);
  184. }
  185. /// <summary>
  186. /// 是否堆垛机
  187. /// </summary>
  188. /// <param name="source"></param>
  189. /// <returns></returns>
  190. public static bool IsSC(this WCS_DEVICE source)
  191. {
  192. return source.DEVICEPROTOCOLS.Any(v => v.DB.PROTOCOL == typeof(ISRM520).AssemblyQualifiedName);
  193. }
  194. public static bool IsTunnel(this WCS_DEVICE source)
  195. {
  196. return source.DEVICEPROTOCOLS.Count == 0 && source.CODE.StartsWith("T");
  197. }
  198. public static void AddFlag(DF flag, params string[] devices)
  199. {
  200. var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
  201. Parallel.ForEach(arr, v =>
  202. {
  203. v.AddFlag(flag);
  204. });
  205. }
  206. public static void AddFlag(DF flag, Action<WCS_DEVICE> callbck, params string[] devices)
  207. {
  208. var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
  209. Parallel.ForEach(arr, v =>
  210. {
  211. v.AddFlag(flag);
  212. callbck?.Invoke(v);
  213. });
  214. }
  215. public static void Set<T>(string key, T value, params string[] devices)
  216. {
  217. var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
  218. Parallel.ForEach(arr, v =>
  219. {
  220. v.Set(key, value);
  221. });
  222. }
  223. public static void Set<T>(string key, T value, Func<WCS_DEVICE, bool> func)
  224. {
  225. var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(func).ToArray();
  226. Parallel.ForEach(arr, v =>
  227. {
  228. v.Set(key, value);
  229. });
  230. }
  231. }
  232. /// <summary>
  233. /// 输送机设备组
  234. /// </summary>
  235. public class StationDeviceGroup : DeviceGroup<IStation520, IStation521, IStation523>
  236. {
  237. /// <summary>
  238. /// 当前设备可用的RGV
  239. /// </summary>
  240. private static List<RGVDevice> AllRGVList;
  241. static StationDeviceGroup()
  242. {
  243. AllRGVList = Device.Where(v => v.IsRGV() && v.CODE != "RGV8").Select(v => v.Create<RGVDevice>()).ToList();
  244. }
  245. public StationDeviceGroup(WCS_DEVICE entity) : base(entity)
  246. {
  247. }
  248. /// <summary>
  249. /// 执行输送机设备组任务 单例锁
  250. /// </summary>
  251. /// <param name="act"></param>
  252. public void EX(Action<StationDeviceGroup> act)
  253. {
  254. var key = $"WCS:Lock:{Entity.CODE}";
  255. try
  256. {
  257. if (RedisHelper.Default.Get(key) != null) throw new WarnException($"[{Entity.CODE}]--触发并发管控");
  258. RedisHelper.Default.Set(key, Entity.CODE);
  259. act(this);
  260. }
  261. catch (DoException ex)
  262. {
  263. ex.DoExceptionEX(Entity);
  264. }
  265. catch (WarnException ex)
  266. {
  267. ex.WarnExceptionEX(Entity);
  268. }
  269. catch (Exception ex)
  270. {
  271. ex.ExceptionEx(Entity);
  272. }
  273. finally
  274. {
  275. RedisHelper.Default.Del(key);
  276. }
  277. }
  278. /// <summary>
  279. /// 当前设备可用的RGV
  280. /// </summary>
  281. public List<RGVDevice> RgvList
  282. {
  283. get
  284. {
  285. return AllRGVList.Where(v => v.LocationList.Any(p => p.Entity == Entity) && v.Data2.WorkMode != 0).ToList();
  286. }
  287. }
  288. /// <summary>
  289. /// 当前设备环穿组
  290. /// </summary>
  291. private List<StationLocation> LoncationList
  292. {
  293. get
  294. {
  295. var dev = StationLocation.ALLlocations.FirstOrDefault(v => v.Station == Entity.CODE);
  296. return StationLocation.ALLlocations.Where(v => v.PLC == dev.PLC).ToList();
  297. }
  298. }
  299. /// <summary>
  300. /// 设备组自身的位置
  301. /// </summary>
  302. public float Position
  303. {
  304. get
  305. {
  306. return StationLocation.ALLlocations.FirstOrDefault(v => v.Station == Entity.CODE).Location;
  307. }
  308. }
  309. /// <summary>
  310. /// 设备组所在环穿的总长度
  311. /// </summary>
  312. public float Length
  313. {
  314. get
  315. {
  316. return StationLocation.ALLlocations.FirstOrDefault(v => v.Station == Entity.CODE).Length;
  317. }
  318. }
  319. /// <summary>
  320. /// 设备组是否满足任务执行条件
  321. /// </summary>
  322. /// <param name="type">给当前设备组下发任务时需要的请求</param>
  323. /// <returns>true:不满足执行条件需要进行停止执行 false:表示满足条件不需要停止执行 </returns>
  324. /// <exception cref="Exception"></exception>
  325. public void WhetherToExecute(IstationRequest type = IstationRequest.None)
  326. {
  327. foreach (var item in Items)
  328. {
  329. if (item.Data.VoucherNo != item.Data2.VoucherNo) throw new WarnException($"等待{item.Entity.CODE}执行任务{item.Data.TaskNumber},凭证号不一致");
  330. if (item.Data2.Status.HasFlag(IstationStatus.Run)) throw new DoException($"{item.Entity.CODE}运行中");
  331. if (!item.Data2.Status.HasFlag(IstationStatus.PhStatus)) throw new DoException($"[{item.Entity.CODE}]无光电");
  332. }
  333. }
  334. /// <summary>
  335. /// 获取设备组中需要取货的设备
  336. /// </summary>
  337. /// <returns></returns>
  338. public List<Device<IStation520, IStation521, IStation523>> RGVGetTaskedDevice()
  339. {
  340. var a = Items.Where(v => v.Data2.Status.HasFlag(IstationStatus.PhStatus) && v.Data2.TaskNumber > 10000)
  341. .Where(v => v.Entity.CODE.ToShort() != v.Data2.GoodsEnd && v.Data2.GoodsEnd != 0)
  342. .ToList();
  343. return a.Count == 0 ? null : a;
  344. }
  345. /// <summary>
  346. /// 是否可以取货
  347. /// </summary>
  348. /// <returns></returns>
  349. public bool IsPickUp(RGVDevice rgvDevice)
  350. {
  351. if (Entity.CODE is "G2" or "G3") return true;
  352. var dCount = Device.Where(v => v.CODE is "G1" or "G1340" or "G1337").Select(v => v.Create<StationDeviceGroup>()).Count(v =>
  353. {
  354. var count = v.Items.Count(v => !v.Data2.Status.HasFlag(IstationStatus.Run) && !v.Data2.Status.HasFlag(IstationStatus.PhStatus) && v.Data2.TaskNumber < 10000);
  355. return count == 2 ? true : false;
  356. });
  357. var rCount = rgvDevice.RGVList.Count(v => v.Data.DestPosition_1 == 1);
  358. return rCount < dCount;
  359. }
  360. /// <summary>
  361. /// 入库位置获取需要生产任务的设备及条码信息
  362. /// </summary>
  363. /// <returns></returns>
  364. public List<FinishTaskList<string>> GetBcrValid()
  365. {
  366. var list = new List<FinishTaskList<string>>();
  367. //获取需要执行的设备信息
  368. foreach (var dev in Items)
  369. {
  370. if (!dev.Data2.Status.HasFlag(IstationStatus.PhStatus))
  371. {
  372. InfoLog.INFO_INFO($"{dev.Entity.CODE}--没有光电");
  373. continue;
  374. }
  375. if (dev.Data2.Request != IstationRequest.Request)
  376. {
  377. InfoLog.INFO_WarnDb($"{dev.Entity.CODE}--有光电没有扫码入库请求--1", Entity.CODE);
  378. continue;
  379. };
  380. if (dev.Data2.TaskNumber > 10000)
  381. {
  382. InfoLog.INFO_WarnDb($"{dev.Entity.CODE}--有光电有请求,但已有任务号", Entity.CODE);
  383. continue;
  384. }
  385. var bcr = dev.Entity.BCR();
  386. var barcode = bcr.Content.Trim('\r');
  387. if (barcode == "")
  388. {
  389. InfoLog.INFO_WarnDb($"{dev.Entity.CODE}--扫码失败,内容为空", Entity.CODE);
  390. continue;
  391. };
  392. list.Add(new FinishTaskList<string>(barcode, dev.Entity.Create<StationDevice>()));
  393. }
  394. return list;
  395. }
  396. /// <summary>
  397. /// 获取下一个地址的有效设备
  398. /// </summary>
  399. /// <returns></returns>
  400. public List<FinishTaskList<string>> GetAddressValid()
  401. {
  402. var list = new List<FinishTaskList<string>>();
  403. //获取需要执行的设备信息
  404. foreach (var dev in Items)
  405. {
  406. if (!dev.Data2.Status.HasFlag(IstationStatus.PhStatus))
  407. {
  408. InfoLog.INFO_INFO($"{dev.Entity.CODE}--没有光电");
  409. continue;
  410. }
  411. if (dev.Data2.Request != IstationRequest.Request)
  412. {
  413. InfoLog.INFO_WarnDb($"{dev.Entity.CODE}--有光电没有分配目标地址请求--2", Entity.CODE);
  414. continue;
  415. };
  416. if (dev.Data2.TaskNumber < 10000)
  417. {
  418. InfoLog.INFO_WarnDb($"{dev.Entity.CODE}--有光电有请求没有任务号", Entity.CODE);
  419. continue;
  420. }
  421. list.Add(new FinishTaskList<string>(dev.Entity.CODE, dev.Entity.Create<StationDevice>()));
  422. }
  423. return list;
  424. }
  425. /// <summary>
  426. /// 最近的RGV
  427. /// </summary>
  428. /// <returns></returns>
  429. public RGVDevice RecentRGV()
  430. {
  431. return RgvList.OrderBy(v => v.Distance(this)).FirstOrDefault();
  432. }
  433. /// <summary>
  434. /// 计算目标RGV与站台自身的距离
  435. /// </summary>
  436. /// <param name="rgv"></param>
  437. /// <returns></returns>
  438. public float Distance(RGVDevice rgv)
  439. {
  440. return DevEX.Distance(Position, rgv.Position, Length);
  441. }
  442. /// <summary>
  443. /// 计算两个站台之间的距离
  444. /// </summary>
  445. /// <param name="rgv"></param>
  446. /// <returns></returns>
  447. public float Distance(StationDeviceGroup dev)
  448. {
  449. return DevEX.Distance(Position, dev.Position, Length);
  450. }
  451. /// <summary>
  452. /// 当前RGV
  453. /// </summary>
  454. /// <returns></returns>
  455. public RGVDevice CurrentRGV()
  456. {
  457. //RGV与站台距离误差为 正负50
  458. var max = Position + 500;
  459. var min = Position - 500;
  460. return RgvList?.FirstOrDefault(v => v.Data2.Position < max && v.Data2.Position > min);
  461. }
  462. /// <summary>
  463. /// 是否需要RGV
  464. /// </summary>
  465. /// <returns>true:需要RGV false:不需要RGV</returns>
  466. public bool NeedRgv()
  467. {
  468. var rgvs = Device.Where(v => v.IsRGV()).Select(v => v.Device<IRGV521>());
  469. var code = Entity.CODE.Replace("G", "").ToShort();
  470. if (rgvs.Any(v => v.Data.DestPosition_1 == code && v.Data.SystemStatus != RGVRunStatus.空闲))
  471. throw new WarnException("已有RGV执行中");
  472. foreach (var item in Items)
  473. {
  474. if (item.Data2.Status.HasFlag(IstationStatus.Run)) return false;
  475. if (!item.Data2.Status.HasFlag(IstationStatus.PhStatus)) return false;
  476. }
  477. return true;
  478. }
  479. /// <summary>
  480. /// BCR 站点是否被禁止
  481. /// </summary>
  482. /// <returns></returns>
  483. public void BcrStationIsForbid()
  484. {
  485. var config = RedisHelper.Default.Get("ForbidTubuEnter")?.Split(",");
  486. if (config.Contains(Entity.CODE)) throw new WarnException("当前入库口已被禁用,请联系机修人员了解具体情况");
  487. }
  488. }
  489. /// <summary>
  490. /// 输送机设备
  491. /// </summary>
  492. public class StationDevice : Device<IStation520, IStation521, IStation523>
  493. {
  494. public StationDevice(WCS_DEVICE entity) : base(entity)
  495. {
  496. }
  497. /// <summary>
  498. /// 设备组是否满足任务执行条件
  499. /// </summary>
  500. /// <param name="type">给当前设备组下发任务时需要的请求</param>
  501. /// <returns>true:不满足执行条件需要进行停止执行 false:表示满足条件不需要停止执行 </returns>
  502. /// <exception cref="Exception"></exception>
  503. public void WhetherToExecute(IstationRequest type = IstationRequest.None)
  504. {
  505. //正在运行
  506. if (Data2.Status.HasFlag(IstationStatus.Run)) throw new DoException("运行中");
  507. //上一次的任务还未执行
  508. if (Data.VoucherNo != Data2.VoucherNo)
  509. throw new WarnException($"等待任务[{Data2.TaskNumber}]执行");
  510. //没有光电
  511. if (!Data2.Status.HasFlag(IstationStatus.PhStatus)) throw new DoException("无光电"); ;
  512. //没有任务号
  513. switch (type)
  514. {
  515. case IstationRequest.None:
  516. if (Data2.TaskNumber < 10000 && Data.TaskNumber < 10000)
  517. throw new WarnException($"设备无任务");
  518. break;
  519. case IstationRequest.Request:
  520. if (Data2.TaskNumber > 10000 && Data.TaskNumber > 10000)
  521. throw new WarnException($"设备已有任务任务");
  522. break;
  523. //case IstationRequest.堆垛机放货完成请求目标地址:
  524. // if (Data2.Tasknum < 10000 && Data.Tasknum < 10000)
  525. // throw new WarnException($"设备无任务信息");
  526. // break;
  527. //case IstationRequest.请求分配目标地址:
  528. // if (Data2.Tasknum < 10000 && Data.Tasknum < 10000)
  529. // throw new WarnException($"设备无任务信息");
  530. // break;
  531. default:
  532. throw new ArgumentOutOfRangeException(nameof(type), type, null);
  533. }
  534. //没有请求
  535. if (type != IstationRequest.None && Data2.Request != type)
  536. throw new WarnException($"有光电无{type}请求");
  537. }
  538. /// <summary>
  539. /// 执行输送机任务 单例锁
  540. /// </summary>
  541. /// <param name="act"></param>
  542. public void EX(Action<StationDevice> act)
  543. {
  544. var key = $"WCS:Lock:{Entity.CODE}";
  545. try
  546. {
  547. if (RedisHelper.Default.Get(key) != null) throw new WarnException($"[{Entity.CODE}]--触发并发管控");
  548. RedisHelper.Default.Set(key, Entity.CODE);
  549. act(this);
  550. }
  551. catch (DoException ex)
  552. {
  553. ex.DoExceptionEX(Entity);
  554. }
  555. catch (WarnException ex)
  556. {
  557. ex.WarnExceptionEX(Entity);
  558. }
  559. catch (Exception ex)
  560. {
  561. ex.ExceptionEx(Entity);
  562. }
  563. finally
  564. {
  565. RedisHelper.Default.Del(key);
  566. }
  567. }
  568. }
  569. /// <summary>
  570. /// RGV设备
  571. /// </summary>
  572. public class RGVDevice : Device<IRGV520, IRGV521, IRGV523>
  573. {
  574. static RGVDevice()
  575. {
  576. AllRGVList = Device.Where(v => v.IsRGV() && v.CODE != "RGV8").Select(v => v.Create<RGVDevice>()).ToList();
  577. }
  578. public RGVDevice(WCS_DEVICE entity) : base(entity)
  579. {
  580. }
  581. /// <summary>
  582. /// 所有环穿RGV
  583. /// </summary>
  584. private static List<RGVDevice> AllRGVList { get; set; }
  585. /// <summary>
  586. /// 与当前RGV处于同一环穿的RGV
  587. /// </summary>
  588. public List<RGVDevice> RGVList
  589. {
  590. get
  591. {
  592. //利用WorkMode来排除的环穿维护设备
  593. return AllRGVList.Where(v => v.Entity.DEVICEPROTOCOLS.Any(d => Entity.DEVICEPROTOCOLS.Any(e => e.DB.PLC.IP == d.DB.PLC.IP)))
  594. .Where(v => v.Data2.WorkMode != 0)
  595. .Where(v => v.Entity.CODE != Entity.CODE).ToList();
  596. }
  597. }
  598. /// <summary>
  599. /// RGV当前位置
  600. /// </summary>
  601. public float Position
  602. {
  603. get
  604. {
  605. return Data2.Position;
  606. }
  607. }
  608. /// <summary>
  609. /// 与当前RGV处于同一环穿的站台
  610. /// </summary>
  611. public List<StationDeviceGroup> LocationList
  612. {
  613. get
  614. {
  615. return StationLocation.ALLlocations.Where(v => Entity.DEVICEPROTOCOLS.Any(p => p.DB.PLC.CODE == v.PLC))
  616. .Select(v => Device.Find(v.Station).Create<StationDeviceGroup>()).ToList();
  617. }
  618. }
  619. /// <summary>
  620. /// 总长度
  621. /// </summary>
  622. public float Length
  623. {
  624. get
  625. {
  626. return LocationList.FirstOrDefault().Length;
  627. }
  628. }
  629. /// <summary>
  630. /// 执行RGV任务 单例锁
  631. /// </summary>
  632. /// <param name="act"></param>
  633. public void EX(Action<RGVDevice> act)
  634. {
  635. var key = $"WCS:Lock:{Entity.CODE}";
  636. try
  637. {
  638. if (RedisHelper.Default.Get(key) != null) throw new WarnException($"[{Entity.CODE}]--触发并发管控");
  639. RedisHelper.Default.Set(key, Entity.CODE);
  640. act(this);
  641. }
  642. catch (DoException ex)
  643. {
  644. ex.DoExceptionEX(Entity);
  645. }
  646. catch (WarnException ex)
  647. {
  648. ex.WarnExceptionEX(Entity);
  649. }
  650. catch (Exception ex)
  651. {
  652. ex.ExceptionEx(Entity);
  653. }
  654. finally
  655. {
  656. RedisHelper.Default.Del(key);
  657. }
  658. }
  659. /// <summary>
  660. /// 获取前一个取货点
  661. /// </summary>
  662. /// <returns></returns>
  663. public StationDeviceGroup BeforeStation()
  664. {
  665. var a = LocationList.Where(v => v.Entity.Is(DF.涂布RGV取货设备组) || v.Entity.Is(DF.BOPPRGV取货设备组));
  666. return LocationList.Where(v => v.Entity.Is(DF.涂布RGV取货设备组) || v.Entity.Is(DF.BOPPRGV取货设备组) && v.Entity.CODE != this.CurrentStation().Entity.CODE).OrderBy(v => Distance(v)).FirstOrDefault();
  667. }
  668. /// <summary>
  669. /// 前一个RGV
  670. /// </summary>
  671. /// <returns></returns>
  672. public RGVDevice Before()
  673. {
  674. //按照位置排序
  675. var arr = RGVList.OrderBy(v => v.Position);
  676. var rgv = arr.FirstOrDefault(v => v.Position > Position);
  677. if (rgv == null)
  678. rgv = arr.LastOrDefault(v => v.Position < Position);
  679. return rgv;
  680. }
  681. /// <summary>
  682. /// 后一个RGV
  683. /// </summary>
  684. /// <returns></returns>
  685. public RGVDevice After()
  686. {
  687. //到当前RGV最近的一个RGV
  688. return RGVList.OrderBy(v => v.Distance(this)).FirstOrDefault();
  689. }
  690. /// <summary>
  691. /// 获取当前所在的取货站台
  692. /// </summary>
  693. /// <returns></returns>
  694. public StationDeviceGroup CurrentStation()
  695. {
  696. return LocationList.Where(v => v.Entity.Is(DF.涂布RGV取货设备组) || v.Entity.Is(DF.涂布RGV放货设备组) || v.Entity.Is(DF.BOPPRGV取货设备组) || v.Entity.Is(DF.BOPPRGV放货设备组)).Where(v =>
  697. {
  698. //RGV与站台距离误差为 正负50500
  699. var max = v.Position + 500;
  700. var min = v.Position - 500;
  701. return Data2.Position < max && Data2.Position > min;
  702. }).FirstOrDefault();
  703. }
  704. /// <summary>
  705. /// 计算当前RGV与指定RGV之间的距离
  706. /// </summary>
  707. /// <param name="rgv"></param>
  708. /// <returns></returns>
  709. public float Distance(RGVDevice rgv)
  710. {
  711. //return Math.Abs((Position - rgv.Position + Length) % Length);
  712. return DevEX.Distance(Position, rgv.Position, Length);
  713. }
  714. /// <summary>
  715. /// 计算当前RGV与指定站台之间的距离
  716. /// </summary>
  717. /// <param name="after"></param>
  718. /// <returns></returns>
  719. public float Distance(StationDeviceGroup after)
  720. {
  721. if (after == null) throw new WarnException($"不是一个有效的StationDeviceGroup,{Entity.CODE}");
  722. return DevEX.Distance(Position, after.Position, Length);
  723. }
  724. /// <summary>
  725. /// 是否需要执行放货任务
  726. /// </summary>
  727. /// <returns></returns>
  728. public bool IsPut()
  729. {
  730. if (Data2.TaskType_1 != RGVTaskType.取货) return false;
  731. if (!Data2.Status_1.HasFlag(WCS.Entity.Protocol.RGVStatus.RGV到站)) return false;
  732. if (!Data2.Status_1.HasFlag(WCS.Entity.Protocol.RGVStatus.任务完成)) return false;
  733. if (!Data2.Status_1.HasFlag(WCS.Entity.Protocol.RGVStatus.光电)) return false;
  734. return true;
  735. }
  736. /// <summary>
  737. /// 写入移动任务
  738. /// </summary>
  739. /// <param name="addr">目标地址</param>
  740. public void Move(StationDeviceGroup addr)
  741. {
  742. if (Data.TaskType_1 == RGVTaskType.取货) throw new WarnException($"当前有{Data.TaskType_1}任务,无法执行移动任务");
  743. if (Data2.WorkMode != RGVMode.自动) throw new WarnException($"RGV状态{Data2.WorkMode},无法执行移动任务");
  744. if (Data2.SystemStatus != RGVRunStatus.空闲) throw new WarnException($"rgv状态为{Data2.SystemStatus},无法执行移动任务");
  745. if (Data2.Status_1.HasFlag(WCS.Entity.Protocol.RGVStatus.光电)) throw new WarnException("RGV有光电,无法执行移动任务");
  746. if (Data2.TaskID_1 == addr.Entity.CODE.GetShortCode() && Data.TaskType_1 == RGVTaskType.移动)
  747. {
  748. InfoLog.INFO_RGVINFO($"{Entity.CODE}]--已有目标地址相同的移动任务");
  749. return;
  750. }
  751. InfoLog.INFO_RGVINFO($"[{Entity.CODE}]--写入RGV移动任务-开始:{Data.TaskID_1},{Data.TaskType_1},{Data.DestPosition_1},{Data.Trigger_1}");
  752. Data.TaskID_1 = addr.Entity.CODE.GetShortCode();
  753. Data.TaskType_1 = RGVTaskType.移动;
  754. Data.DestPosition_1 = addr.Entity.CODE.GetShortCode();
  755. Data.Trigger_1++;
  756. InfoLog.INFO_RGVINFO($"[{Entity.CODE}]--写入RGV移动任务-结束:{Data.TaskID_1},{Data.TaskType_1},{Data.DestPosition_1},{Data.Trigger_1}");
  757. }
  758. /// <summary>
  759. /// 写入取货任务
  760. /// </summary>
  761. /// <param name="addr">目标地址</param>
  762. public void Pick(StationDeviceGroup addr, int task1 = 0, int task2 = 0)
  763. {
  764. InfoLog.INFO_RGVINFO($"[{Entity.CODE}]--写入RGV取货任务-开始:{Data.TaskID_1},{Data.TaskID_2},{Data.TaskType_1},{Data.DestPosition_1},{Data.Trigger_1}");
  765. Data.TaskType_1 = RGVTaskType.取货;
  766. Data.DestPosition_1 = addr.Entity.CODE.GetShortCode();
  767. if (task1 != 0) Data.TaskID_1 = task1;
  768. if (task2 != 0) Data.TaskID_2 = task2;
  769. Data.Trigger_1++;
  770. InfoLog.INFO_RGVINFO($"[{Entity.CODE}]--写入RGV取货任务-结束:{Data.TaskID_1},{Data.TaskID_2},{Data.TaskType_1},{Data.DestPosition_1},{Data.Trigger_1}");
  771. }
  772. /// <summary>
  773. /// 写入放货任务
  774. /// </summary>
  775. /// <param name="addr">目标地址</param>
  776. public void Put(StationDeviceGroup addr, int task1 = 0, int task2 = 0)
  777. {
  778. InfoLog.INFO_RGVINFO($"[{Entity.CODE}]--写入RGV放货任务-开始:{Data.TaskID_1},{Data.TaskID_2},{Data.TaskType_1},{Data.DestPosition_1},{Data.Trigger_1}");
  779. Data.TaskType_1 = RGVTaskType.放货;
  780. Data.DestPosition_1 = addr.Entity.CODE.GetShortCode();
  781. if (task1 != 0) Data.TaskID_1 = task1;
  782. if (task2 != 0) Data.TaskID_2 = task2;
  783. Data.Trigger_1++;
  784. InfoLog.INFO_RGVINFO($"[{Entity.CODE}]--写入RGV放货任务-结束:{Data.TaskID_1},{Data.TaskID_2},{Data.TaskType_1},{Data.DestPosition_1},{Data.Trigger_1}");
  785. }
  786. /// <summary>
  787. /// 筛选出所有与当前RGV距离小于指定长度的RGV
  788. /// </summary>
  789. /// <param name="distance">指定长度</param>
  790. /// <returns></returns>
  791. public RGVDevice[] RgvAfter(float distance)
  792. {
  793. return RGVList.Where(v => Distance(v) < distance).ToArray();
  794. }
  795. /// <summary>
  796. /// 当前RGV是否有拦住指定RGV
  797. /// </summary>
  798. /// <param name="rgv">RGV</param>
  799. /// <returns></returns>
  800. public bool StopedByMe(RGVDevice rgv)
  801. {
  802. //目标站台
  803. var target = rgv.Data2.DestPosition_1;
  804. //获取目标站台的设备组信息
  805. var station = Device.Find($"G{target}").Create<StationDeviceGroup>();
  806. if (station.Distance(rgv) < 5000) return false;
  807. //当前RGV与目标站台的距离小于传入RGV到达目标站台的距离
  808. return (this.Distance(station) < rgv.Distance(station)) || station.CurrentRGV()?.Entity.CODE == this.Entity.CODE;
  809. }
  810. /// <summary>
  811. /// 获取当前RGV的下一个站台,即距离最近的一个站台
  812. /// </summary>
  813. /// <returns></returns>
  814. public StationDeviceGroup NextStation()
  815. {
  816. //先取当前RGV与所有站台的距离
  817. var dev = LocationList.OrderBy(v => v.Distance(this)).FirstOrDefault();
  818. return dev;
  819. }
  820. }
  821. /// <summary>
  822. /// 堆垛机设备
  823. /// </summary>
  824. public class SRMDevice : Device<ISRM520, ISRM521, ISRM537>
  825. {
  826. public SRMDevice(WCS_DEVICE entity) : base(entity)
  827. {
  828. }
  829. /// <summary>
  830. /// 获取放货点
  831. /// </summary>
  832. public List<StationDevice> GetDeliveryPoint()
  833. {
  834. return Entity.ROUTES.Select(v => v.NEXT) //巷道
  835. .SelectMany(v => v.ROUTES.Select(d => d.NEXT)) //放货点
  836. .Where(v => v.IsConv()) //必须是输送线
  837. .Select(v => v.Create<StationDevice>()).ToList();
  838. }
  839. /// <summary>
  840. /// 获取取货点
  841. /// </summary>
  842. public List<StationDevice> GetPickPoint()
  843. {
  844. return Device.Where(v => v.Is(DF.SRM二级品取货) || v.Is(DF.SRM涂布取货) || v.Is(DF.SRMBOPP取货))
  845. .Where(v => v.ROUTES.Any(p => p.NEXT.ROUTES.Any(d => d.NEXT == Entity)))
  846. .Select(v => v.Create<StationDevice>())
  847. .ToList();
  848. }
  849. /// <summary>
  850. /// 处理完成任务
  851. /// </summary>
  852. public void FinishedTaskHandle()
  853. {
  854. WCS_TASK task = new WCS_TASK();
  855. Db.Do(db =>
  856. {
  857. var taskIds = new List<int>() { Data2.FinishedTask_1, Data2.FinishedTask_2 }.ToArray();
  858. for (int i = 0; i < taskIds.Length; i++)
  859. {
  860. //判断当前工位是否有完成任务
  861. if (taskIds[i] == 0) continue;
  862. task = db.Default.Queryable<WCS_TASK>().Single(v => taskIds[i] == v.ID);
  863. if (task.STATUS != TaskStatus.堆垛机执行 && task.STATUS != TaskStatus.堆垛机完成)
  864. {
  865. InfoLog.INFO_WarnDb($"任务{task.ID},状态位{task.STATUS}", Entity.CODE);
  866. continue;
  867. };
  868. if (task.STATUS == TaskStatus.堆垛机完成)
  869. {
  870. if (i == 0)
  871. {
  872. Data.FinishedACK_1 = 1;
  873. Data.TaskID_1 = 0;
  874. }
  875. else
  876. {
  877. Data.FinishedACK_2 = 1;
  878. Data.TaskID_2 = 0;
  879. }
  880. throw new DoException("二次处理堆垛机完成任务");
  881. }
  882. switch (task.TYPE)
  883. {
  884. case TaskType.入库:
  885. task.ENDTIME = DateTime.Now;
  886. task.STATUS = WCS.Entity.TaskStatus.已完成;
  887. task.UPDATETIME = DateTime.Now;
  888. break;
  889. case TaskType.出库:
  890. task.STATUS = TaskStatus.堆垛机完成;
  891. task.UPDATETIME = DateTime.Now;
  892. break;
  893. case TaskType.移库:
  894. {
  895. if (task.STATUS == TaskStatus.堆垛机执行)
  896. {
  897. task.STATUS = TaskStatus.已完成;
  898. task.UPDATETIME = DateTime.Now;
  899. }
  900. break;
  901. }
  902. default:
  903. throw new Exception($"[{Entity.CODE}]任务类型错误,{task.ID}");
  904. }
  905. task.CreateStatusLog(db, $"状态由[{TaskStatus.堆垛机执行}]变更为[{task.STATUS}]", this.GetType());
  906. db.Default.Updateable(task).AddQueue();
  907. }
  908. db.Default.SaveQueues();
  909. });
  910. Db.Do(db =>
  911. {
  912. var taskIds = new List<int>() { Data2.FinishedTask_1, Data2.FinishedTask_2 }.ToArray();
  913. for (int i = 0; i < taskIds.Length; i++)
  914. {
  915. //判断当前工位是否有完成任务
  916. if (taskIds[i] == 0) continue;
  917. //获取当前工位的目标地址
  918. task = db.Default.Queryable<WCS_TASK>().Single(v => taskIds[i] == v.ID);
  919. if (i == 0)
  920. {
  921. Data.FinishedACK_1 = 1;
  922. Data.TaskID_1 = 0;
  923. }
  924. else
  925. {
  926. Data.FinishedACK_2 = 1;
  927. Data.TaskID_2 = 0;
  928. }
  929. }
  930. });
  931. }
  932. /// <summary>
  933. /// 执行堆垛机任务 单例锁
  934. /// </summary>
  935. /// <param name="act"></param>
  936. public void EX(Action<SRMDevice> act)
  937. {
  938. var key = $"WCS:Lock:{Entity.CODE}";
  939. try
  940. {
  941. if (RedisHelper.Default.Get(key) != null) throw new WarnException($"[{Entity.CODE}]--触发并发管控");
  942. RedisHelper.Default.Set(key, Entity.CODE);
  943. act(this);
  944. }
  945. catch (DoException ex)
  946. {
  947. ex.DoExceptionEX(Entity);
  948. }
  949. catch (WarnException ex)
  950. {
  951. ex.WarnExceptionEX(Entity);
  952. }
  953. catch (Exception ex)
  954. {
  955. ex.ExceptionEx(Entity);
  956. }
  957. finally
  958. {
  959. RedisHelper.Default.Del(key);
  960. }
  961. }
  962. /// <summary>
  963. /// 执行出库任务 出库单例锁
  964. /// </summary>
  965. /// <param name="act"></param>
  966. public void EXOutStock(Action<SRMDevice> act)
  967. {
  968. var key = "WCS:Lock:";
  969. try
  970. {
  971. if (Entity.CODE == "SRM3" || Entity.CODE == "SRM4")
  972. {
  973. key += "SRM3-SRM4-Out";
  974. if (RedisHelper.Default.Get(key) != null) throw new WarnException($"触发出库并发管控--[{Entity.CODE}]");
  975. RedisHelper.Default.Set(key, Entity.CODE);
  976. }
  977. if (Entity.CODE == "SRM5" || Entity.CODE == "SRM6")
  978. {
  979. key += "SRM5-SRM6-Out";
  980. if (RedisHelper.Default.Get(key) != null) throw new WarnException($"触发出库并发管控--[{Entity.CODE}]");
  981. RedisHelper.Default.Set(key, Entity.CODE);
  982. }
  983. if (Entity.CODE == "SRM7" || Entity.CODE == "SRM8")
  984. {
  985. key += "SRM7-SRM8-Out";
  986. if (RedisHelper.Default.Get(key) != null) throw new WarnException($"触发出库并发管控--[{Entity.CODE}]");
  987. RedisHelper.Default.Set(key, Entity.CODE);
  988. }
  989. act(this);
  990. }
  991. finally
  992. {
  993. if (Entity.CODE == "SRM3" || Entity.CODE == "SRM4") RedisHelper.Default.Del($"{key}SRM3-SRM4-Out");
  994. if (Entity.CODE == "SRM5" || Entity.CODE == "SRM6") RedisHelper.Default.Del($"{key}SRM5-SRM6-Out");
  995. if (Entity.CODE == "SRM7" || Entity.CODE == "SRM8") RedisHelper.Default.Del($"{key}SRM7-SRM8-Out");
  996. }
  997. }
  998. /// <summary>
  999. /// 一工位写任务
  1000. /// </summary>
  1001. /// <param name="task"></param>
  1002. /// <param name="goodsnum">货物数量</param>
  1003. public void WriteTask1(Task task, short goodsnum)
  1004. {
  1005. InfoLog.INFO_SRMINFO($"出库--写入堆垛机[{Entity.CODE}]1工位-开始:[{Data.TaskID_1}][{Data.SLine_1}][{Data.SCol_1}][{Data.SLayer_1}][{Data.ELine_1}][{Data.VoucherNo_1}]--[{Data.RES1_1}]");
  1006. Data.TaskID_1 = task.ID;
  1007. Data.SLine_1 = task.Line;
  1008. Data.SCol_1 = task.Col;
  1009. Data.SLayer_1 = task.Layer;
  1010. Data.ELine_1 = task.SRMSTATION.ToShort();
  1011. Data.ECol_1 = 0;
  1012. Data.ELayer_1 = 0;
  1013. Data.RES1_1 = goodsnum;
  1014. Data.VoucherNo_1++;
  1015. InfoLog.INFO_SRMINFO($"出库--写入堆垛机[{Entity.CODE}]1工位-结束:[{Data.TaskID_1}][{Data.SLine_1}][{Data.SCol_1}][{Data.SLayer_1}][{Data.ELine_1}][{Data.VoucherNo_1}]--[{Data.RES1_1}]");
  1016. }
  1017. /// <summary>
  1018. /// 二工位写任务
  1019. /// </summary>
  1020. /// <param name="task"></param>
  1021. /// <param name="goodsnum">货物数量</param>
  1022. public void WriteTask2(Task task, short goodsnum)
  1023. {
  1024. InfoLog.INFO_SRMINFO($"出库--写入堆垛机[{Entity.CODE}]2工位-开始:[{Data.TaskID_2}][{Data.SLine_2}][{Data.SCol_2}][{Data.SLayer_2}][{Data.ELine_2}][{Data.VoucherNo_2}]--[{Data.RES1_2}]");
  1025. Data.TaskID_2 = task.ID;
  1026. Data.SLine_2 = task.Line;
  1027. Data.SCol_2 = task.Col;
  1028. Data.SLayer_2 = task.Layer;
  1029. Data.ELine_2 = task.SRMSTATION.ToShort();
  1030. Data.ECol_2 = 0;
  1031. Data.ELayer_2 = 0;
  1032. Data.RES1_2 = goodsnum;
  1033. Data.VoucherNo_2++;
  1034. InfoLog.INFO_SRMINFO($"出库--写入堆垛机[{Entity.CODE}]2工位-结束:[{Data.TaskID_2}][{Data.SLine_2}][{Data.SCol_2}][{Data.SLayer_2}][{Data.ELine_2}][{Data.VoucherNo_2}]--[{Data.RES1_2}]");
  1035. }
  1036. /// <summary>
  1037. /// 获取任务对应的货叉
  1038. /// </summary>
  1039. /// <param name="task">任务信息</param>
  1040. /// <param name="index">任务在下发任务集合中的索引</param>
  1041. /// <returns></returns>
  1042. public SrmFork GetFork(Task task, int index)
  1043. {
  1044. return index switch
  1045. {
  1046. > 1 => throw new WarnException("一次最多下发两个任务"),
  1047. //如果索引是1,直接返回货叉2
  1048. 1 => SrmFork.货叉2,
  1049. _ => task.Col switch
  1050. {
  1051. 102 => Entity.CODE switch
  1052. {
  1053. "SRM1" => SrmFork.货叉1,
  1054. _ => SrmFork.货叉2,
  1055. },
  1056. 112 => SrmFork.货叉2,
  1057. _ => SrmFork.货叉1,
  1058. }
  1059. };
  1060. }
  1061. /// <summary>
  1062. /// 检查同组堆垛机是否有出库任务正在执行
  1063. /// </summary>
  1064. public void CheckOutTask()
  1065. {
  1066. //检查同组堆垛机是否有正在执行出库任务的
  1067. Db.Do(db =>
  1068. {
  1069. try
  1070. {
  1071. var srm = Device.Find("SRM4").Create<SRMDevice>();
  1072. var task = db.Default.Queryable<WCS_TASK>().Any(v => v.STATUS == TaskStatus.堆垛机执行 && v.DEVICE == "SRM4" && v.TYPE == TaskType.出库);
  1073. switch (Entity.CODE)
  1074. {
  1075. case "SRM3":
  1076. if (srm.Data3.SCAlarm == 0 && srm.Data2.SRMMode == WCS.Entity.Protocol.SRM.SCMode.远程 && task)
  1077. throw new DoException("SRM4正在执行出库任务");
  1078. break;
  1079. case "SRM4":
  1080. srm = Device.Find("SRM3").Create<SRMDevice>();
  1081. task = db.Default.Queryable<WCS_TASK>().Any(v => v.STATUS == TaskStatus.堆垛机执行 && v.DEVICE == "SRM3" && v.TYPE == TaskType.出库);
  1082. if (srm.Data3.SCAlarm == 0 && srm.Data2.SRMMode == WCS.Entity.Protocol.SRM.SCMode.远程 && task)
  1083. throw new DoException("SRM3正在执行出库任务");
  1084. break;
  1085. case "SRM5":
  1086. srm = Device.Find("SRM6").Create<SRMDevice>();
  1087. task = db.Default.Queryable<WCS_TASK>().Any(v => v.STATUS == TaskStatus.堆垛机执行 && v.DEVICE == "SRM6" && v.TYPE == TaskType.出库);
  1088. if (srm.Data3.SCAlarm == 0 && srm.Data2.SRMMode == WCS.Entity.Protocol.SRM.SCMode.远程 && task)
  1089. throw new DoException("SRM6正在执行出库任务");
  1090. break;
  1091. case "SRM6":
  1092. srm = Device.Find("SRM5").Create<SRMDevice>();
  1093. task = db.Default.Queryable<WCS_TASK>().Any(v => v.STATUS == TaskStatus.堆垛机执行 && v.DEVICE == "SRM5" && v.TYPE == TaskType.出库);
  1094. if (srm.Data3.SCAlarm == 0 && srm.Data2.SRMMode == WCS.Entity.Protocol.SRM.SCMode.远程 && task)
  1095. throw new DoException("SRM5正在执行出库任务");
  1096. break;
  1097. case "SRM7":
  1098. srm = Device.Find("SRM8").Create<SRMDevice>();
  1099. task = db.Default.Queryable<WCS_TASK>().Any(v => v.STATUS == TaskStatus.堆垛机执行 && v.DEVICE == "SRM8" && v.TYPE == TaskType.出库);
  1100. if (srm.Data3.SCAlarm == 0 && srm.Data2.SRMMode == WCS.Entity.Protocol.SRM.SCMode.远程 && task)
  1101. throw new DoException("SRM8正在执行出库任务");
  1102. break;
  1103. case "SRM8":
  1104. srm = Device.Find("SRM7").Create<SRMDevice>();
  1105. task = db.Default.Queryable<WCS_TASK>().Any(v => v.STATUS == TaskStatus.堆垛机执行 && v.DEVICE == "SRM7" && v.TYPE == TaskType.出库);
  1106. if (srm.Data3.SCAlarm == 0 && srm.Data2.SRMMode == WCS.Entity.Protocol.SRM.SCMode.远程 && task)
  1107. throw new DoException("SRM7正在执行出库任务");
  1108. break;
  1109. }
  1110. }
  1111. catch (Exception e)
  1112. {
  1113. InfoLog.INFO_WarnDb(e.Message, Entity.CODE);
  1114. }
  1115. });
  1116. }
  1117. }
  1118. /// <summary>
  1119. /// 异常处理
  1120. /// </summary>
  1121. public static class DevEX
  1122. {
  1123. /// <summary>
  1124. /// 计算两点距离
  1125. /// </summary>
  1126. /// <param name="start">起始点</param>
  1127. /// <param name="end">结束点</param>
  1128. /// <param name="total">总长</param>
  1129. /// <returns></returns>
  1130. public static float Distance(float start, float end, float total)
  1131. {
  1132. float distance = 0;
  1133. if (start > end) distance = (total - start) + end;
  1134. else distance = end - start;
  1135. return distance;
  1136. }
  1137. public static void DoExceptionEX(this DoException ex, WCS_DEVICE Entity)
  1138. {
  1139. InfoLog.INFO_INFO($"[{Entity.CODE}]--{ex.Message}");
  1140. }
  1141. /// <summary>
  1142. /// 警报执行记录
  1143. /// </summary>
  1144. /// <param name="ex">警报信息</param>
  1145. /// <param name="Entity">发生设备</param>
  1146. /// <param name="reportMonitor">是否上报监控</param>
  1147. /// <exception cref="Exception"></exception>
  1148. public static void WarnExceptionEX(this WarnException ex, WCS_DEVICE Entity, bool reportMonitor = true)
  1149. {
  1150. InfoLog.INFO_WARN($"[{Entity.CODE}]--{ex.Message}");
  1151. if (ex.Message.Contains("The database operation was expected")) return;
  1152. LogHelper.AddWCS_EXCEPTION(ex.Message, Entity.CODE, WCS_EXCEPTIONTYPE.无.ToString());
  1153. //排除部分频繁触发的异常上报
  1154. if (ex.Message.Contains("触发并发管控")) return;
  1155. if (reportMonitor)
  1156. {
  1157. Ltc.Log(ex.GetBaseException().Message);
  1158. throw new Exception($"[{Entity.CODE}]--{ex.Message}");
  1159. }
  1160. }
  1161. public static void ExceptionEx(this Exception ex, WCS_DEVICE Entity)
  1162. {
  1163. InfoLog.INFO_ERROR($"[{Entity.CODE}]--{ex.Message}--{ex.StackTrace}");
  1164. //排除部分频繁触发的异常上报
  1165. if (ex.Message.Contains("Collection was modified; enumeration operation may not execute.")) return;
  1166. Ltc.Log(ex.GetBaseException().Message);
  1167. }
  1168. }
  1169. /// <summary>
  1170. /// 堆垛机货叉/工位
  1171. /// </summary>
  1172. public enum SrmFork
  1173. {
  1174. 货叉1 = 0,
  1175. 货叉2 = 1,
  1176. }
  1177. /// <summary>
  1178. /// 站台位置信息
  1179. /// </summary>
  1180. public class StationLocation
  1181. {
  1182. /// <summary>
  1183. /// 所有环穿站台的信息
  1184. /// </summary>
  1185. public static List<StationLocation> ALLlocations { get; set; } = new List<StationLocation>();
  1186. static StationLocation()
  1187. {
  1188. ALLlocations.AddRange(new List<StationLocation>() {
  1189. new StationLocation("G1",486326,"RGV3",1567770),
  1190. new StationLocation("G2",693631,"RGV3",1567770),
  1191. new StationLocation("G3",789931,"RGV3",1567770),
  1192. new StationLocation("G4",961595,"RGV3",1567770),
  1193. new StationLocation("G5",1013350,"RGV3",1567770),
  1194. new StationLocation("G6",1069938,"RGV3",1567770),
  1195. new StationLocation("G7",1126338,"RGV3",1567770),
  1196. new StationLocation("G8",1178355,"RGV3",1567770),
  1197. new StationLocation("G9",1256875,"RGV3",1567770),
  1198. new StationLocation("G10",1313239,"RGV3",1567770),
  1199. new StationLocation("G11",1369970,"RGV3",1567770),
  1200. new StationLocation("G12",636770,"RGV1",3719290),
  1201. new StationLocation("G13",749520,"RGV1",3719290),
  1202. new StationLocation("G14",879930,"RGV1",3719290),
  1203. new StationLocation("G15",936310,"RGV1",3719290),
  1204. new StationLocation("G16",988000,"RGV1",3719290),
  1205. new StationLocation("G19",1785000,"RGV1",3719290),
  1206. new StationLocation("G23",2714350,"RGV1",3719290),
  1207. });
  1208. }
  1209. public StationLocation(string station, int location, string plc, int length)
  1210. {
  1211. Station = station;
  1212. Location = location;
  1213. PLC = plc;
  1214. Length = length;
  1215. }
  1216. /// <summary>
  1217. /// 输送机设备组编号
  1218. /// </summary>
  1219. public string Station { get; set; }
  1220. /// <summary>
  1221. /// 输送机在环轨中的位置
  1222. /// </summary>
  1223. public int Location { get; set; }
  1224. /// <summary>
  1225. /// 所属RGV组 PLC名称
  1226. /// </summary>
  1227. public string PLC { get; set; }
  1228. /// <summary>
  1229. /// 所属环穿轨道的长度
  1230. /// </summary>
  1231. public int Length { get; set; }
  1232. }
  1233. /// <summary>
  1234. /// 巷道信息
  1235. /// </summary>
  1236. public class TunnelInfo
  1237. {
  1238. public WCS_DEVICE Tunnel;
  1239. public WCS_DEVICE taskIN;
  1240. public Device<ISRM520, ISRM521, ISRM537> SRM;
  1241. }
  1242. /// <summary>
  1243. /// 设备配置
  1244. /// </summary>
  1245. [Flags]
  1246. public enum DF
  1247. {
  1248. 无 = 0,
  1249. SRM = 1 << 0,
  1250. SRM二级品取货 = 1 << 1,
  1251. SRM涂布取货 = 1 << 2,
  1252. SRM月台放货 = 1 << 3,
  1253. 一楼RGV放货 = 1 << 4,
  1254. 月台 = 1 << 5,
  1255. 涂布RGV = 1 << 6,
  1256. BOPPRGV = 1 << 7,
  1257. 涂布RGV取货设备组 = 1 << 8,
  1258. 涂布RGV放货设备组 = 1 << 9,
  1259. 涂布出库RGV取货站台 = 1 << 10,
  1260. 涂布入库RGV取货站台 = 1 << 11,
  1261. SRM涂布放货 = 1 << 12,
  1262. 涂布RGV取货站台 = 1 << 13,
  1263. BOPPRGV取货设备组 = 1 << 14,
  1264. BOPPRGV放货设备组 = 1 << 15,
  1265. SRMBOPP取货 = 1 << 16,
  1266. }
  1267. }