MainThreadClass.cs 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255
  1. using CCDCount.DLL.AlarmTools;
  2. using CCDCount.DLL.SqlDataClass;
  3. using CCDCount.DLL.Tools;
  4. using CCDCount.MODEL.CameraClass;
  5. using CCDCount.MODEL.ConfigModel;
  6. using CCDCount.MODEL.ResultModel;
  7. using CCDCount.MODEL.ShuLiModel;
  8. using LogClass;
  9. using MvCameraControl;
  10. using System;
  11. using System.Collections.Concurrent;
  12. using System.Collections.Generic;
  13. using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
  14. using System.Diagnostics;
  15. using System.Drawing;
  16. using System.Linq;
  17. using System.Runtime.InteropServices;
  18. using System.Threading;
  19. using System.Threading.Tasks;
  20. namespace CCDCount.DLL
  21. {
  22. public class MainThreadClass
  23. {
  24. #region 变量与实例
  25. Thread SwitchIdentifyImageThread = null;
  26. private bool IsSwitch = false;
  27. public ShuLiClass shuLiClass = null;
  28. CameraClass cameraClass = new CameraClass();
  29. public ShuLiConfigClass shuLiConfig = null;
  30. public CameraConfig cameraConfig = null;
  31. public bool CameraConfigIsChange = false;
  32. public string BatchNumber = "";
  33. public int ThisCamerNo = -1;
  34. public double XCoefficient {
  35. get
  36. {
  37. shuLiClass.GetXYCoefficient(out double XCoefficient, out double YCoefficient);
  38. return XCoefficient;
  39. }
  40. set
  41. {
  42. shuLiClass.SetXCoefficient(value);
  43. }
  44. }
  45. public double YCoefficient {
  46. get
  47. {
  48. shuLiClass.GetXYCoefficient(out double XCoefficient, out double YCoefficient);
  49. return YCoefficient;
  50. }
  51. set
  52. {
  53. shuLiClass.SetYCoefficient(value);
  54. }
  55. }
  56. public bool CameraStatic { get { return _CameraStatic; } }
  57. private bool _CameraStatic = false;
  58. // 返回交换线程状态
  59. public bool CameraRunStatic { get { return IsSwitch; } }
  60. public bool CameraSendMessageStatic { get { return IsSend; } }
  61. public bool IsOpenLoadThread { get { return _IsOpenLoadThread; }}
  62. private bool _IsOpenLoadThread = false;
  63. public int HistoryActiveNum { get { return shuLiClass.GetHistoryActiveNum(); } }
  64. public int OkHistoryNum { get { return shuLiClass.GetOkHistoryNum(); } }
  65. public int NgHistoryNum { get { return shuLiClass.GetNgHistoryNum(); } }
  66. // 数粒信息发送线程
  67. Thread SendBottLogicMessageThread = null;
  68. bool IsSend = false;
  69. // Modbus客户端实例
  70. public ModbusTcpClient modbusTcpClient = new ModbusTcpClient();
  71. // 颗粒结果待发送队列
  72. //ConcurrentQueue<ushort> SendQueue = new ConcurrentQueue<ushort>();
  73. ConcurrentQueue<TestSenMessage> SendQueue = new ConcurrentQueue<TestSenMessage>();
  74. /// <summary>
  75. /// 数粒状态计时器
  76. /// </summary>
  77. Stopwatch stopwatch = Stopwatch.StartNew();
  78. /// <summary>
  79. /// 数粒状态
  80. /// </summary>
  81. private bool _ShuLiState = true;
  82. public bool ShuLiState { get { return _ShuLiState; } }
  83. private bool _IsDebug = false;
  84. public bool IsDebug { get { return _IsDebug; } }
  85. private bool IsConnectModbus = false;
  86. public static ActionMesSqliteDataClass actionMesSqliteDataClass = null;
  87. ConcurrentQueue<IFrameOut> _images = new ConcurrentQueue<IFrameOut>();
  88. public string FormulationName { get; set; } = string.Empty;
  89. public bool IsLoadFormulation = false;
  90. #endregion
  91. #region 公共方法
  92. /// <summary>
  93. /// 设置ModbusTcpClient
  94. /// </summary>
  95. /// <param name="modbusTcpClient"></param>
  96. public void ConnectModbus(string ipAddress)
  97. {
  98. Task.Run(() =>
  99. {
  100. int i = 10;
  101. while(!modbusTcpClient.Connect(ipAddress)&&i>0)
  102. {
  103. IsConnectModbus = false;
  104. //SystemAlarm.AlarmAlert(AlarmMessageList.数粒通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
  105. SystemAlarm.AlarmAlert(AlarmMessageList.数粒通讯连接失败,
  106. $"Number counting communication connection failed, target IP:{ipAddress}",
  107. $"Modbus通讯连接失败,目标IP:{ipAddress}",
  108. "DLL:MainThreadClass-ConnectModbus");
  109. i--;
  110. Task.Delay(1000);
  111. }
  112. SystemAlarm.AlarmCancel(AlarmMessageList.数粒通讯连接失败);
  113. LOG.log("数粒通讯成功", 6);
  114. //FaultLog.RecordLogMessage($"Modbus通讯连接成功,目标IP:{ipAddress}",6);
  115. IsConnectModbus = true;
  116. });
  117. //if (!modbusTcpClient.Connect(ipAddress))
  118. //{
  119. // //FaultLog.RecordErrorMessage($"MianThread{cameraConfig.CamerNo}-Modbus通讯连接失败,目标IP:{ipAddress}");
  120. // SystemAlarm.AlarmAlert(AlarmMessageList.PLC通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
  121. // return;
  122. //}
  123. //IsConnectModbus = true;
  124. //SystemAlarm.AlarmCancel(AlarmMessageList.PLC通讯连接失败);
  125. }
  126. /// <summary>
  127. /// 初始化构造方法
  128. /// </summary>
  129. /// <param name="configClass"></param>
  130. /// <param name="CameraConfig"></param>
  131. public MainThreadClass(ShuLiConfigClass configClass,CameraConfig CameraConfig)
  132. {
  133. shuLiConfig = configClass;
  134. cameraConfig = CameraConfig;
  135. // 数粒配置文件地址
  136. if (configClass!=null)
  137. {
  138. // 创建数粒对象(配置文件)
  139. shuLiClass = new ShuLiClass(shuLiConfig);
  140. }
  141. else
  142. {
  143. // 创建数粒对象(默认)
  144. shuLiClass = new ShuLiClass();
  145. }
  146. _IsOpenLoadThread = cameraConfig.IsOpenLoad;
  147. ThisCamerNo = cameraConfig.CamerNo;
  148. }
  149. /// <summary>
  150. /// 加载相机
  151. /// </summary>
  152. /// <returns></returns>
  153. public bool LoadCamera()
  154. {
  155. bool result = false;
  156. // 相机列表
  157. try
  158. {
  159. // 获取相机列表
  160. cameraClass.GetCameraList(out List<CameraInfoClass> list);
  161. if (list.Count == 0)
  162. {
  163. //FaultLog.RecordErrorMessage(string.Format("{0}:没有相机", "MainThreadClass-StartMianThread"));
  164. SystemAlarm.AlarmAlert(AlarmMessageList.未检测到相机,
  165. "No Camera",
  166. "没有相机",
  167. "DLL:MainThreadClass-LoadCamera");
  168. // 如果没有相机,则退出
  169. return result;
  170. }
  171. SystemAlarm.AlarmCancel(AlarmMessageList.未检测到相机);
  172. // 加载相机
  173. // cameraClass.LoadCamereDevice(list.First().DeviceSN);
  174. if (!cameraClass.LoadCamereDevice(cameraConfig))
  175. {
  176. //FaultLog.RecordErrorMessage(string.Format("{0}:相机加载失败", "MainThreadClass-StartMianThread"));
  177. SystemAlarm.AlarmAlert(AlarmMessageList.相机加载失败,
  178. "No LoadCamera",
  179. "相机加载失败",
  180. "DLL:MainThreadClass-LoadCamera");
  181. return result;
  182. }
  183. SystemAlarm.AlarmCancel(AlarmMessageList.相机加载失败);
  184. CameraConfigIsChange = false;
  185. }
  186. catch(Exception ex)
  187. {
  188. Console.WriteLine($"MainThreadClass-LoadCamera:加载相机失败: {ex.Message}");
  189. }
  190. return result;
  191. }
  192. /// <summary>
  193. /// 重新加载相机配置
  194. /// </summary>
  195. public void ReLoadCameraConfig()
  196. {
  197. if(CameraConfigIsChange)
  198. {
  199. CameraConfig OldcameraConfig = cameraClass.GetConfigValue();
  200. if(OldcameraConfig.CameraSNNum != cameraConfig.CameraSNNum)
  201. {
  202. cameraClass.LoadCamereDevice(cameraConfig);
  203. }
  204. else
  205. {
  206. cameraClass.ReLoadCameraConfig(cameraConfig);
  207. }
  208. }
  209. }
  210. /// <summary>
  211. /// 开始主线程
  212. /// </summary>
  213. public bool StartMianThread()
  214. {
  215. bool result = false;
  216. try
  217. {
  218. if (BatchNumber == "")
  219. {
  220. FaultLog.RecordErrorMessage($"Camera {cameraConfig.CamerNo} does not have a batch number, and it failed to start", "Dll:MainThreadClass-StartMianThread");
  221. return result;
  222. }
  223. //if (!IsConnectModbus)
  224. //{
  225. // FaultLog.RecordErrorMessage($"Camera {cameraConfig.CamerNo} does not have a PLC connection, resulting in startup failure", "Dll:MainThreadClass-StartMianThread");
  226. // return result;
  227. //}
  228. timeBeginPeriod(1); // 设置为1ms精度
  229. actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{cameraConfig.CamerNo}\\ActiveObjectData_{BatchNumber}.db");
  230. //actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{cameraConfig.CamerNo}\\ActiveObjectData20250827.db");
  231. actionMesSqliteDataClass.GetAllActionMinStartMaxEndLine(out int num, out int StartLine, out int EndLine);
  232. shuLiClass.InitCurrentLine(EndLine);
  233. //shuLiClass.InitNum(num);
  234. // 取图开启
  235. if (!cameraClass.StartCamera())
  236. {
  237. return result;
  238. }
  239. _CameraStatic = true;
  240. StartSendBottLogicMessageThread();
  241. // 数据交换线程开启
  242. StartSwitchThread();
  243. // 为数粒算法的识别成功一粒回调函数添加方法
  244. //shuLiClass.WorkCompleted += Worker_OneGrainCompleted;
  245. // 开启识别线程
  246. shuLiClass.StartIdentifyFuntion2(cameraClass.GetCamereImageSize().Width);
  247. result = true;
  248. }
  249. catch(Exception ex)
  250. {
  251. Console.WriteLine($"MainThreadClass-StartMianThread:启动失败: {ex.Message}");
  252. }
  253. return result;
  254. }
  255. /// <summary>
  256. /// 停止主线程
  257. /// </summary>
  258. public bool StopMianThread()
  259. {
  260. bool result = false;
  261. try
  262. {
  263. _CameraStatic = false;
  264. shuLiClass.WorkCompleted -= Worker_OneGrainCompleted;
  265. // 相机取图关闭
  266. cameraClass.StopCamera();
  267. // 数据交换线程关闭
  268. StopSwitchThread();
  269. // 数粒识别线程关闭
  270. shuLiClass.StopIdentifyFuntion();
  271. StopSendBottLogicMessageThread();
  272. result = true;
  273. actionMesSqliteDataClass.Dispose();
  274. }
  275. catch(Exception ex)
  276. {
  277. LOG.error($"MainThreadClass-StopMianThread:停止失败: {ex.Message}");
  278. }
  279. return result;
  280. }
  281. /// <summary>
  282. /// 释放相机资源
  283. /// </summary>
  284. public void DisposeCamera()
  285. {
  286. cameraClass.StopCamera();
  287. cameraClass.DisPoseCamera();
  288. }
  289. /// <summary>
  290. /// 重加载相机
  291. /// </summary>
  292. /// <param name="CameraSN"></param>
  293. /// <returns></returns>
  294. public bool ReLoadCamera(string CameraSN)
  295. {
  296. bool result;
  297. result = cameraClass.ReLoadCameraDevice(CameraSN);
  298. if (result)
  299. {
  300. cameraConfig = cameraClass.GetConfigValue();
  301. }
  302. return result;
  303. }
  304. /// <summary>
  305. /// 获取显示用的图片数据
  306. /// </summary>
  307. /// <param name="ImageHeight"></param>
  308. /// <param name="Data"></param>
  309. public void GetShowImage(int ImageHeight,out Bitmap ImageData)
  310. {
  311. List<RowStartEndCol> RowsShowList = new List<RowStartEndCol>();
  312. ActiveObjectClass NewActive = shuLiClass.GetLastActive();
  313. if (NewActive == null)
  314. {
  315. LOG.log(string.Format("{0}:没有获取到数粒数据", "MainThreadClass-GetShowImage"));
  316. ImageData = GetNullShowImage(4096, ImageHeight);
  317. return;
  318. }
  319. List<ActiveObjectClass> Data = shuLiClass.GetHistoryActive().Where(o => o.LastSeenLine > NewActive.LastSeenLine - ImageHeight).ToList();
  320. Bitmap BitmapImage = new Bitmap(NewActive.ImageWidth, ImageHeight);
  321. if (Data.Count > 0)
  322. {
  323. int ThisImageStartLine = (int)Data.Max(o => o.LastSeenLine) - ImageHeight;
  324. using (Graphics g = Graphics.FromImage(BitmapImage))
  325. {
  326. g.Clear(Color.White);
  327. var redPen = new Pen(Color.Red, 1);
  328. var bluePen = new Pen(Color.FromArgb(0,146,255), 1);
  329. var GreenPen = new Pen(Color.SeaGreen, 5);
  330. using (Pen RedPan = new Pen(Color.Black, 5))
  331. {
  332. for (int i = 0; i < shuLiClass.ChannelsRoi.Count - 1; i++)
  333. {
  334. g.DrawLine(RedPan, new Point(shuLiClass.ChannelsRoi[i], 0), new Point(shuLiClass.ChannelsRoi[i], BitmapImage.Height));
  335. }
  336. }
  337. foreach (var item in Data)
  338. {
  339. int roix = item.MinStartCol - 5;
  340. int roiy = (int)(item.StartLine - ThisImageStartLine) - 5 < 0 ? 0 : (int)(item.StartLine - ThisImageStartLine) - 5;
  341. int roiheight = (int)(item.LastSeenLine - ThisImageStartLine - roiy) + 10;
  342. int roiwidth = item.MaxEndCol - item.MinStartCol + 10;
  343. g.DrawRectangle(GreenPen, new Rectangle(roix, roiy, roiwidth, roiheight));
  344. Font font = new Font("Arial", 20);
  345. g.DrawString(item.Num.ToString(), font, Brushes.Black, new Point(roix - 20*item.Num.ToString().Length, roiy - 20));
  346. foreach (var item1 in item.RowsData)
  347. {
  348. if((int)(item1.RowsCol - ThisImageStartLine)<0) continue;
  349. int yPos = (int)(item1.RowsCol - ThisImageStartLine);
  350. g.DrawLine(item.StateCode == 0 ? bluePen : redPen, new Point(item1.StartCol, yPos), new Point(item1.EndCol, yPos));
  351. }
  352. }
  353. }
  354. }
  355. ImageData = BitmapImage.Clone() as Bitmap;
  356. BitmapImage.Dispose();
  357. //GC.Collect();
  358. }
  359. public void GetShowImage(int ImageHeight, int ImageWidth , out Bitmap ImageData)
  360. {
  361. List<RowStartEndCol> RowsShowList = new List<RowStartEndCol>();
  362. ActiveObjectClass NewActive = shuLiClass.GetLastActive();
  363. if (NewActive == null)
  364. {
  365. LOG.log(string.Format("{0}:没有获取到数粒数据", "MainThreadClass-GetShowImage"));
  366. ImageData = GetNullShowImage(ImageWidth, ImageHeight);
  367. return;
  368. }
  369. List<ActiveObjectClass> Data = shuLiClass.GetHistoryActive().Where(o => o.StartLine > NewActive.StartLine - ImageHeight).ToList();
  370. Bitmap BitmapImage = new Bitmap(NewActive.ImageWidth, ImageHeight);
  371. if (Data.Count > 0)
  372. {
  373. int ThisImageStartLine = (int)Data.Max(o => o.LastSeenLine) - ImageHeight;
  374. using (Graphics g = Graphics.FromImage(BitmapImage))
  375. {
  376. g.Clear(Color.White);
  377. var redPen = new Pen(Color.Red, 1);
  378. var bluePen = new Pen(Color.FromArgb(0, 146, 255), 1);
  379. var GreenPen = new Pen(Color.SeaGreen, 5);
  380. using (Pen RedPan = new Pen(Color.Black, 5))
  381. {
  382. for (int i = 0; i < shuLiClass.ChannelsRoi.Count - 1; i++)
  383. {
  384. g.DrawLine(RedPan, new Point(shuLiClass.ChannelsRoi[i], 0), new Point(shuLiClass.ChannelsRoi[i], BitmapImage.Height));
  385. }
  386. }
  387. foreach (var item in Data)
  388. {
  389. int roix = item.MinStartCol - 5;
  390. int roiy = ImageHeight - (int)(item.LastSeenLine - ThisImageStartLine) - 5 < 0 ? 0 : ImageHeight - (int)(item.LastSeenLine - ThisImageStartLine) - 5;
  391. int roiheight = ImageHeight - (int)(item.StartLine - ThisImageStartLine) - roiy + 10;
  392. int roiwidth = item.MaxEndCol - item.MinStartCol + 10;
  393. g.DrawRectangle(GreenPen, new Rectangle(roix, roiy, roiwidth, roiheight));
  394. Font font = new Font("Arial", 20);
  395. g.DrawString(item.Num.ToString(), font, Brushes.Black, new Point(roix - 20 * item.Num.ToString().Length, roiy - 20));
  396. foreach (var item1 in item.RowsData)
  397. {
  398. if ((int)(item1.RowsCol - ThisImageStartLine) < 0) continue;
  399. int yPos = ImageHeight - (int)(item1.RowsCol - ThisImageStartLine);
  400. g.DrawLine(item.StateCode == 0 ? bluePen : redPen, new Point(item1.StartCol, yPos), new Point(item1.EndCol, yPos));
  401. }
  402. }
  403. }
  404. }
  405. ImageData = BitmapImage.Clone() as Bitmap;
  406. BitmapImage.Dispose();
  407. //GC.Collect();
  408. }
  409. /// <summary>
  410. /// 获取无数据的图片
  411. /// </summary>
  412. /// <param name="ImageWidth"></param>
  413. /// <param name="ImageHeight"></param>
  414. /// <param name="ImageData"></param>
  415. public Bitmap GetNullShowImage(int ImageWidth, int ImageHeight)
  416. {
  417. Bitmap BitmapImage = new Bitmap(ImageWidth, ImageHeight);
  418. using (Graphics g = Graphics.FromImage(BitmapImage))
  419. {
  420. g.Clear(Color.White);
  421. using (Pen RedPan = new Pen(Color.Black, 8))
  422. {
  423. for (int i = 0; i < shuLiClass.ChannelsRoi.Count - 1; i++)
  424. {
  425. g.DrawLine(RedPan, new Point(shuLiClass.ChannelsRoi[i], 0), new Point(shuLiClass.ChannelsRoi[i], BitmapImage.Height));
  426. }
  427. }
  428. }
  429. return BitmapImage;
  430. }
  431. /// <summary>
  432. /// 获取此刻的Config数据
  433. /// </summary>
  434. /// <param name="config"></param>
  435. public void GetAllConfigValue(out CameraConfig Camconfig,out ShuLiConfigClass shuLiConfig)
  436. {
  437. //判断是否加载了相机
  438. if(cameraClass.IsLoadCamera())
  439. {
  440. //获取已经加载的相机的配置
  441. cameraConfig = cameraClass.GetConfigValue();
  442. Camconfig = cameraConfig;
  443. }
  444. else
  445. {
  446. cameraConfig = new CameraConfig();
  447. Camconfig = cameraConfig;
  448. }
  449. //读取视觉配置
  450. shuLiConfig = shuLiClass.GetConfigValue();
  451. this.shuLiConfig = shuLiConfig;
  452. }
  453. /// <summary>
  454. /// 获取相机此刻的Config数据
  455. /// </summary>
  456. /// <param name="CameraConfig"></param>
  457. public CameraConfig GetCameraConfig()
  458. {
  459. CameraConfig result = null;
  460. //判断是否加载了相机
  461. if (cameraClass.IsLoadCamera())
  462. {
  463. //获取已经加载的相机的配置
  464. result = cameraClass.GetConfigValue();
  465. cameraConfig = result;
  466. }
  467. return result;
  468. }
  469. /// <summary>
  470. /// 保存所有Config
  471. /// </summary>
  472. public void SaveAllConfig()
  473. {
  474. shuLiClass.SaveConfig();
  475. }
  476. /// <summary>
  477. /// 获取相机连接状态
  478. /// </summary>
  479. /// <returns></returns>
  480. public bool GetCameraConnectStatic()
  481. {
  482. bool result = false;
  483. if(cameraClass != null)
  484. result = cameraClass.IsConnectCamera();
  485. return result;
  486. }
  487. /// <summary>
  488. /// 清除历史数据
  489. /// </summary>
  490. /// <returns></returns>
  491. public bool ClearHistoryActive()
  492. {
  493. bool result = false;
  494. if(shuLiClass != null)
  495. result = shuLiClass.ClearHistoryActive();
  496. return result;
  497. }
  498. public bool ClearSendQueue()
  499. {
  500. bool result = false;
  501. while(SendQueue.Count>0)
  502. {
  503. SendQueue.TryDequeue(out TestSenMessage testSen);
  504. }
  505. result = true;
  506. return result;
  507. }
  508. /// <summary>
  509. /// 获取过去一秒内颗粒数量
  510. /// </summary>
  511. /// <returns></returns>
  512. public int GetOneSecondActiveNum()
  513. {
  514. int result = 0;
  515. if(shuLiClass != null)
  516. result = shuLiClass.GetHistoryActive().Where(o=>o.EndCheckTime>DateTime.Now-TimeSpan.FromSeconds(1)).Count();
  517. return result;
  518. }
  519. /// <summary>
  520. /// 获取相机原图一张
  521. /// </summary>
  522. /// <returns></returns>
  523. public Bitmap GetCamImageOnce()
  524. {
  525. Bitmap result = null;
  526. if(_images.Count>0)
  527. {
  528. _images.TryDequeue(out IFrameOut image);
  529. if(image != null)
  530. result = image.Image.ToBitmap();
  531. }
  532. return result;
  533. }
  534. public void ParameterTrain(List<ActiveObjectClass> ListValues)
  535. {
  536. if(ListValues.Count() > 0)
  537. {
  538. if(ListValues.Count()>500)
  539. {
  540. ListValues = ListValues.Take(500).OrderByDescending(o=>o.Area).ToList();
  541. }
  542. int MaxArea = ListValues.Max(x => x.Area);
  543. int MinArea = ListValues.Min(x => x.Area);
  544. double MaxLength = ListValues.Max(x => x.MaxLength);
  545. double MinLength = ListValues.Min(x => x.MaxLength);
  546. if(ListValues.Count<2) return;
  547. int ChazhiId = -1;
  548. int MaxChazhi = 0;
  549. for (int i = 1; i < ListValues.Count; i++)
  550. {
  551. int Chazhi = (ListValues[i - 1].Area - ListValues[i].Area);
  552. if (Chazhi > MaxChazhi*10)
  553. {
  554. MaxChazhi = Chazhi;
  555. ChazhiId = i;
  556. }
  557. }
  558. if(ChazhiId == -1)
  559. return;
  560. int yuzhi = (int)Math.Sqrt(2) + 2;
  561. }
  562. }
  563. /// <summary>
  564. /// 运行参数训练
  565. /// </summary>
  566. /// <returns></returns>
  567. public ParaTrainResultClass ParameterTrain()
  568. {
  569. var ListValues = shuLiClass.GetHistoryActive();
  570. ParaTrainResultClass Result = new ParaTrainResultClass();
  571. if (ListValues.Count() > 0)
  572. {
  573. if (ListValues.Count() > 500)
  574. {
  575. ListValues = ListValues.Skip(ListValues.Count - 500).OrderByDescending(o => o.Area).ToList();
  576. }
  577. if (ListValues.Count < 2)
  578. {
  579. Result = null;
  580. return Result;
  581. }
  582. Result.NoiseFilterThreshold = 5;
  583. var LengthListValues = ListValues.OrderBy(o => o.MaxLength).Skip(100).Take(300).ToList();
  584. Result.MaxLength = Convert.ToInt32(ListValues.Max(x => x.MaxLength) * 1.1);
  585. Result.MinLength = Convert.ToInt32(ListValues.Min(x => x.MaxLength) * 0.9);
  586. var AreaListValues = ListValues.OrderBy(o => o.Area).Skip(100).Take(300).ToList();
  587. Result.MaxArea = Convert.ToInt32(ListValues.Max(x => x.Area) * 1.1);
  588. Result.MinArea = Convert.ToInt32(ListValues.Min(x => x.Area) * 0.9);
  589. }
  590. return Result;
  591. }
  592. public ParaTrainResultClass ParameterTrain(int KeliW,int KeliL)
  593. {
  594. var ListValues = shuLiClass.GetHistoryActive();
  595. ParaTrainResultClass Result = new ParaTrainResultClass();
  596. List<ActiveObjectClass> LengthListValues = null;
  597. List<ActiveObjectClass> AreaListValues = null;
  598. if (ListValues.Count() > 0)
  599. {
  600. try
  601. {
  602. LOG.log("参数训练-数据筛选", 6);
  603. if (ListValues.Count() > 1000)
  604. {
  605. ListValues = ListValues.Skip(ListValues.Count - 1000).OrderByDescending(o => o.Num).ToList();
  606. }
  607. if (ListValues.Count < 2)
  608. {
  609. Result = null;
  610. return Result;
  611. }
  612. List<MaxLengthModel> maxLengthModels = new List<MaxLengthModel>();
  613. LOG.log("参数训练-凸包计算-未标定长度检测", 6);
  614. foreach (var item in ListValues)
  615. {
  616. List<Point> points = shuLiClass.ConvexHull(item.RowsData);
  617. var itemLenValue = shuLiClass.CoefficientCalculateMinimumBoundingRectangle(points);
  618. var UseValue = new MaxLengthModel()
  619. {
  620. MaxLength = itemLenValue.Height,
  621. Point1 = itemLenValue.HeightPoints[0],
  622. Point2 = itemLenValue.HeightPoints[1]
  623. };
  624. maxLengthModels.Add(UseValue);
  625. }
  626. LOG.log("参数训练-标定计算", 6);
  627. var ScaleResult = LineScanCameraCalibrator.CalculateLineScanPixelScale(maxLengthModels, KeliL);
  628. shuLiClass.SetXCoefficient(ScaleResult.XScale);
  629. shuLiClass.SetYCoefficient(ScaleResult.YScale);
  630. shuLiConfig.ScaleX = ScaleResult.XScale;
  631. shuLiConfig.ScaleY = ScaleResult.YScale;
  632. Result.ScaleX = ScaleResult.XScale;
  633. Result.ScaleY = ScaleResult.YScale;
  634. Result.NoiseFilterThreshold = 5;
  635. LOG.log("参数训练-计算标定后的长度", 6);
  636. foreach (var item in ListValues)
  637. {
  638. item.MaxLength = shuLiClass.SizeCalculation(item.RowsData).Height;
  639. }
  640. LOG.log("参数训练-长度数据筛选", 6);
  641. if (ListValues.Count >= 1000)
  642. {
  643. LengthListValues = ListValues.OrderBy(o => o.MaxLength).Skip(10).Take(980).ToList();
  644. }
  645. else
  646. {
  647. LengthListValues = ListValues.OrderBy(o => o.MaxLength).Skip(10).Take(ListValues.Count - 20).ToList();
  648. }
  649. LOG.log("参数训练-长度数据计算", 6);
  650. Result.MaxLength = Convert.ToInt32(LengthListValues.Max(x => x.MaxLength)) + 2;
  651. Result.MinLength = Convert.ToInt32(LengthListValues.Min(x => x.MaxLength) - 2);
  652. LOG.log("参数训练-面积数据筛选", 6);
  653. if (ListValues.Count >= 1000)
  654. {
  655. AreaListValues = ListValues.OrderBy(o => o.Area).Skip(10).Take(980).ToList();
  656. }
  657. else
  658. {
  659. AreaListValues = ListValues.OrderBy(o => o.Area).Skip(10).Take(ListValues.Count - 20).ToList();
  660. }
  661. LOG.log("参数训练-面积数据计算", 6);
  662. Result.MaxArea = Convert.ToInt32(AreaListValues.Max(x => x.Area)) + 50;
  663. Result.MinArea = Convert.ToInt32(AreaListValues.Min(x => x.Area)) - 50;
  664. }
  665. catch(Exception ex)
  666. {
  667. LOG.error(ex.Message);
  668. }
  669. }
  670. return Result;
  671. }
  672. #endregion
  673. #region 私有方法
  674. public DateTime FromUnixTimestamp(long timestamp, bool isMilliseconds = false)
  675. {
  676. try
  677. {
  678. // 如果未指定单位,尝试自动检测
  679. if (!isMilliseconds)
  680. {
  681. // 如果时间戳看起来像毫秒级(13位数字)
  682. if (timestamp.ToString().Length > 10)
  683. {
  684. isMilliseconds = true;
  685. }
  686. }
  687. if (isMilliseconds)
  688. {
  689. // 验证毫秒级时间戳范围
  690. const long minMilliTimestamp = -62135596800000; // 0001-01-01 00:00:00 UTC
  691. const long maxMilliTimestamp = 253402300799999; // 9999-12-31 23:59:59 UTC
  692. if (timestamp < minMilliTimestamp || timestamp > maxMilliTimestamp)
  693. {
  694. throw new ArgumentOutOfRangeException(nameof(timestamp),
  695. "毫秒级时间戳超出有效范围");
  696. }
  697. DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(timestamp);
  698. return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
  699. }
  700. else
  701. {
  702. // 验证秒级时间戳范围
  703. const long minTimestamp = -62135596800;
  704. const long maxTimestamp = 253402300799;
  705. if (timestamp < minTimestamp || timestamp > maxTimestamp)
  706. {
  707. throw new ArgumentOutOfRangeException(nameof(timestamp),
  708. "秒级时间戳超出有效范围");
  709. }
  710. DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp);
  711. return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
  712. }
  713. }
  714. catch (ArgumentOutOfRangeException)
  715. {
  716. throw;
  717. }
  718. catch (Exception ex)
  719. {
  720. throw new ArgumentException($"无法转换时间戳 {timestamp}: {ex.Message}", ex);
  721. }
  722. }
  723. //bool IsFill = false;
  724. //bool IsXuanZhuanCloseFaMen = false;
  725. /// <summary>
  726. /// 每数完一粒识别线程执行的事件
  727. /// </summary>
  728. /// <param name="sender"></param>
  729. /// <param name="e"></param>
  730. private void Worker_OneGrainCompleted(object sender, ActiveObjectEventArgsClass e)
  731. {
  732. //LOG.log("有活跃物体转换为了历史物体,回调事件被触发!", 6);
  733. //LOG.log(string.Format("图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum), 6);
  734. if (e.Actives.Where(o => o.StateCode == 7).Count() > 0)
  735. {
  736. stopwatch.Restart();
  737. _ShuLiState = false;
  738. }
  739. else if (stopwatch.ElapsedMilliseconds > 1000)
  740. {
  741. stopwatch.Stop();
  742. _ShuLiState = true;
  743. }
  744. //Int16 result = new Int16();
  745. ushort[] result = new ushort[2];
  746. var Hset = new HashSet<int>();
  747. // 事件处理逻辑
  748. foreach (ActiveObjectClass oneActive in e.Actives)
  749. {
  750. //往数组中计数
  751. var UseTime = (oneActive.EndCheckTime - oneActive.StartCheckTime).TotalMilliseconds;
  752. var QutuYanshiTime = (DateTime.Now - oneActive.PictureEndReadTime).TotalMilliseconds;
  753. if (BatchNumber!="") oneActive.BatchNumber = BatchNumber;
  754. LOG.log(string.Format("输出当前颗粒信息,颗粒编号:{0},开始时间:{1},第一张图像读取时间:{2},\n" +
  755. "识别耗时:{3},尾图到输出的时间:{4},颗粒状态:{5},通道数:{6}",
  756. oneActive.Num, oneActive.StartCheckTime.ToString("O"), oneActive.PictureStartReadTime.ToString("O"),
  757. UseTime.ToString(), QutuYanshiTime.ToString(), oneActive.StateCode, oneActive.ChannelNO), 6);
  758. if (QutuYanshiTime > 20)
  759. {
  760. LOG.error($"识别延时超过了20ms,耗时{QutuYanshiTime}");
  761. }
  762. //switch (oneActive.StateCode)
  763. //{
  764. // case 1:
  765. // SystemAlarm.AlarmAlert(AlarmMessageList.数粒超长粒,
  766. // "Counting extra-long particles",
  767. // "数粒超长粒",
  768. // "MianThreadClass-Worker_OneGrainCompleted");
  769. // break;
  770. // case 2:
  771. // SystemAlarm.AlarmAlert(AlarmMessageList.数粒超短粒,
  772. // "Counting ultra-short particles",
  773. // "数粒超短粒",
  774. // "MianThreadClass-Worker_OneGrainCompleted");
  775. // break;
  776. // case 5:
  777. // SystemAlarm.AlarmAlert(AlarmMessageList.数粒超大粒,
  778. // "Counting extra-large particles",
  779. // "数粒超大粒",
  780. // "MianThreadClass-Worker_OneGrainCompleted");
  781. // break;
  782. // case 6:
  783. // SystemAlarm.AlarmAlert(AlarmMessageList.数粒超小粒,
  784. // "Counting ultra-small particles",
  785. // "数粒超小粒",
  786. // "MianThreadClass-Worker_OneGrainCompleted");
  787. // break;
  788. // case 8:
  789. // SystemAlarm.AlarmAlert(AlarmMessageList.疑似叠粒,
  790. // "Count overlapping particles",
  791. // "疑似叠粒",
  792. // "MianThreadClass-Worker_OneGrainCompleted");
  793. // break;
  794. // case 10:
  795. // SystemAlarm.AlarmAlert(AlarmMessageList.丢帧颗粒,
  796. // "Counting frame loss particles",
  797. // "丢帧颗粒",
  798. // "MianThreadClass-Worker_OneGrainCompleted");
  799. // break;
  800. //}
  801. //ThreadPool.QueueUserWorkItem(_ =>
  802. //{
  803. // if (actionMesSqliteDataClass != null)
  804. // {
  805. // try
  806. // {
  807. // actionMesSqliteDataClass.AddData(oneActive);
  808. // }
  809. // catch (Exception ex)
  810. // {
  811. // LOG.error("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message);
  812. // }
  813. // }
  814. //});
  815. //记录到数据库
  816. //ThreadPool.QueueUserWorkItem(_ =>
  817. //{
  818. // if (actionMesSqliteDataClass != null)
  819. // {
  820. // try
  821. // {
  822. // actionMesSqliteDataClass.InsertActiveObject(oneActive);
  823. // }
  824. // catch (Exception ex)
  825. // {
  826. // LOG.error("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message);
  827. // }
  828. // }
  829. //});
  830. if (!Hset.Add(oneActive.ChannelNO))
  831. {
  832. //有重复的通道的异常记录流程
  833. var EResult = new ushort[2];
  834. //正常运行流程
  835. if (oneActive.ChannelNO == -1)
  836. {
  837. //FaultLog.RecordErrorMessage("颗粒通道判定异常");
  838. FaultLog.RecordErrorMessage("Abnormal particle channel determination");
  839. continue;
  840. }
  841. if (oneActive.StateCode == 0)
  842. {
  843. //单通道合格计数
  844. EResult[0] |= (ushort)(1 << oneActive.ChannelNO);
  845. }
  846. else
  847. {
  848. if (oneActive.StateCode != 7 && oneActive.StateCode != 9)
  849. {
  850. //单通道不合格计数
  851. EResult[1] |= (ushort)(1 << (oneActive.ChannelNO));
  852. }
  853. else if (oneActive.StateCode == 7)
  854. {
  855. //FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机");
  856. FaultLog.RecordErrorMessage("The field of view is obstructed. Please check the camera");
  857. }
  858. }
  859. SendQueue.Enqueue(new TestSenMessage()
  860. {
  861. Message = EResult,
  862. NumMessage = new ushort[] { (ushort)oneActive.Num },
  863. count = 1
  864. });
  865. continue;
  866. }
  867. else
  868. {
  869. //正常运行流程
  870. if (oneActive.ChannelNO == -1)
  871. {
  872. //FaultLog.RecordErrorMessage("颗粒通道判定异常");
  873. FaultLog.RecordErrorMessage("Abnormal particle channel determination");
  874. continue;
  875. }
  876. if (oneActive.StateCode == 0)
  877. {
  878. //单通道合格计数
  879. result[0] |= (ushort)(1 << oneActive.ChannelNO);
  880. }
  881. else
  882. {
  883. if (oneActive.StateCode != 7 && oneActive.StateCode != 9)
  884. {
  885. //单通道不合格计数
  886. result[1] |= (ushort)(1 << (oneActive.ChannelNO));
  887. }
  888. else if (oneActive.StateCode == 7)
  889. {
  890. //FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机");
  891. FaultLog.RecordErrorMessage("The field of view is obstructed. Please check the camera");
  892. }
  893. }
  894. }
  895. }
  896. //LOG.log("当前待发送队列数量:" + SendQueue.Count, 6);
  897. if (IsSend)
  898. {
  899. SendQueue.Enqueue(new TestSenMessage()
  900. {
  901. Message = result,
  902. //NumMessage = new ushort[] { (ushort)oneActive.Num },
  903. NumMessage = e.Actives.Select(o => (ushort)o.Num).ToArray(),
  904. count = (ushort)e.Actives.Count
  905. });
  906. }
  907. }
  908. #endregion
  909. #region 线程方法
  910. /// <summary>
  911. /// 开启交换线程
  912. /// </summary>
  913. private void StartSwitchThread()
  914. {
  915. IsSwitch = true;
  916. shuLiClass.QueueSemaphore = new SemaphoreSlim(0);
  917. SwitchIdentifyImageThread = new Thread(SwitchIdentifyImageProcess)
  918. {
  919. Priority = ThreadPriority.AboveNormal
  920. };
  921. SwitchIdentifyImageThread.Start();
  922. }
  923. /// <summary>
  924. /// 关闭交换线程
  925. /// </summary>
  926. private void StopSwitchThread()
  927. {
  928. try
  929. {
  930. // 标志位设为false
  931. IsSwitch = false;
  932. if (SwitchIdentifyImageThread != null && SwitchIdentifyImageThread.IsAlive)
  933. SwitchIdentifyImageThread.Join();
  934. SystemAlarm.AlarmCancel(AlarmMessageList.数据分配线程停止失败);
  935. }
  936. catch (Exception ex)
  937. {
  938. SystemAlarm.AlarmAlert(AlarmMessageList.数据分配线程停止失败,
  939. "stop thread failed!, " + ex.Message,
  940. "数据分配线程停止失败:" + ex.Message,
  941. "DLL:MainThreadClass-StopSwitchThread");
  942. //FaultLog.RecordErrorMessage("MainThreadClass-StopSwitchThread:Stop thread failed!, " + ex.Message);
  943. Console.WriteLine("MainThreadClass-StopSwitchThread:Stop thread failed!, " + ex.Message);
  944. throw;
  945. }
  946. }
  947. /// <summary>
  948. /// 交换线程
  949. /// </summary>
  950. private void SwitchIdentifyImageProcess()
  951. {
  952. while (IsSwitch)
  953. {
  954. bool result = cameraClass.GetOnceImage(out IFrameOut IFramedata);
  955. if (result)
  956. {
  957. //Debug模式,不进行图像处理
  958. if (_IsDebug)
  959. {
  960. _images.Enqueue(IFramedata);
  961. if (_images.Count > 5)
  962. {
  963. _images.TryDequeue(out IFrameOut image);
  964. image.Dispose();
  965. }
  966. }
  967. if (IFramedata == null)
  968. continue;
  969. shuLiClass.SetOnceIdentifyImageData(IFramedata);
  970. shuLiClass.QueueSemaphore.Release(); // 通知处理线程有新数据
  971. }
  972. else
  973. continue;
  974. IFramedata.Dispose();
  975. Thread.Sleep(1);
  976. }
  977. }
  978. /// <summary>
  979. /// 启动发送消息线程
  980. /// </summary>
  981. private bool StartSendBottLogicMessageThread()
  982. {
  983. bool result = false;
  984. try
  985. {
  986. if(!IsConnectModbus) return result;
  987. IsSend = true;
  988. SendBottLogicMessageThread = new Thread(SendBottLogicMessageProcess);
  989. SendBottLogicMessageThread.Start();
  990. SystemAlarm.AlarmCancel(AlarmMessageList.结果发送线程启动失败);
  991. result = true;
  992. }
  993. catch (Exception ex)
  994. {
  995. //FaultLog.RecordErrorMessage("MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message);
  996. SystemAlarm.AlarmAlert(AlarmMessageList.结果发送线程启动失败,
  997. "MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message,
  998. "结果发送线程启动失败," + ex.Message,
  999. "DLL:MainThreadClass-StartSendBottLogicMessageThread");
  1000. Console.WriteLine("MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message);
  1001. result = false;
  1002. }
  1003. return result;
  1004. }
  1005. /// <summary>
  1006. /// 停止发送消息线程
  1007. /// </summary>
  1008. private void StopSendBottLogicMessageThread()
  1009. {
  1010. try
  1011. {
  1012. // 标志位设为false
  1013. if (SendBottLogicMessageThread != null && SendBottLogicMessageThread.IsAlive)
  1014. {
  1015. try
  1016. {
  1017. IsSend = false;
  1018. SendBottLogicMessageThread.Join();
  1019. //SendBottLogicMessageThread.Interrupt();
  1020. }
  1021. catch (ThreadStateException)
  1022. {
  1023. // 忽略异常
  1024. }
  1025. }
  1026. //if (modbusTcpClient != null) modbusTcpClient.Disconnect();
  1027. SystemAlarm.AlarmCancel(AlarmMessageList.结果发送线程停止失败);
  1028. }
  1029. catch (Exception ex)
  1030. {
  1031. //FaultLog.RecordErrorMessage("Start thread failed!, " + ex.Message);
  1032. SystemAlarm.AlarmAlert(AlarmMessageList.结果发送线程停止失败,
  1033. "MianThread-StopSendBottLogicMessageThread:Stop thread failed!, " + ex.Message,
  1034. "结果发送线程停止失败, " + ex.Message,
  1035. "DLL:MainThreadClass-StopSendBottLogicMessageThread");
  1036. throw;
  1037. }
  1038. }
  1039. /// <summary>
  1040. /// 信息发送线程
  1041. /// </summary>
  1042. private void SendBottLogicMessageProcess1()
  1043. {
  1044. //获取数据
  1045. TestSenMessage sendMessage;
  1046. bool AllowTransfer;
  1047. bool TransferDone;
  1048. Stopwatch sw = Stopwatch.StartNew();
  1049. while (IsSend)
  1050. {
  1051. //LOG.log("进入线程", 6);
  1052. sw.Restart();
  1053. sendMessage = new TestSenMessage();
  1054. //读取装瓶状态
  1055. AllowTransfer = false;
  1056. TransferDone = false;
  1057. bool[] ReturnValue = null;
  1058. modbusTcpClient.ReadCoilsRegister(slaveId: 1, startAddress: 11, numRegisters: 2,out ReturnValue);
  1059. if (ReturnValue == null)
  1060. {
  1061. continue;
  1062. }
  1063. AllowTransfer = ReturnValue[1];
  1064. TransferDone = ReturnValue[0];
  1065. //LOG.log(string.Format("读取值:AllowTransfer[0]:{0},TransferDone[1]:{1}", AllowTransfer, TransferDone), 6);
  1066. //当允许写入且处于未写入的状态时
  1067. if (AllowTransfer && !TransferDone)
  1068. {
  1069. if (SendQueue.Count() > 0)
  1070. {
  1071. if (!SendQueue.TryDequeue(out sendMessage))
  1072. {
  1073. //FaultLog.RecordErrorMessage("MainThreadClass-SendBottLogicMessageProcess-SendQueue.TryDequeue failed!");
  1074. }
  1075. }
  1076. if (sendMessage.Message == null)
  1077. {
  1078. continue;
  1079. }
  1080. if (sendMessage.Message[0] != 0|| sendMessage.Message[1] != 0)
  1081. {
  1082. //写入数据
  1083. modbusTcpClient.WriteMultipleRegisters(slaveId: 1, startAddress: 100, values: sendMessage.Message);
  1084. modbusTcpClient.WriteCoilsRegister(slaveId: 1, CoilsAddress: 11, values: true);
  1085. sw.Stop();
  1086. LOG.log(string.Format("SendMessageOk:{0},SendMessageNg:{1},此次写值耗时:{2},此次写入数据量{3}", sendMessage.Message[0],sendMessage.Message[1], sw.Elapsed,sendMessage.count), 6);
  1087. }
  1088. }
  1089. Thread.Sleep(1);
  1090. }
  1091. }
  1092. /// <summary>
  1093. /// 信息发送线程2
  1094. /// </summary>
  1095. private void SendBottLogicMessageProcess()
  1096. {
  1097. //获取数据
  1098. TestSenMessage sendMessage;
  1099. Stopwatch sw = Stopwatch.StartNew();
  1100. Stopwatch WriteDoneTime = Stopwatch.StartNew();
  1101. ushort writedoneMes = 0;
  1102. writedoneMes |= (ushort)(1 << 0);
  1103. while (IsSend)
  1104. {
  1105. //LOG.log("进入线程", 6);
  1106. sendMessage = new TestSenMessage();
  1107. ushort[] ReturnValue = null;
  1108. ushort[] result = new ushort[20];
  1109. modbusTcpClient.ReadHoldingRegisters(slaveId: 1, startAddress: 0, numRegisters: 1, out ReturnValue);
  1110. if (ReturnValue == null)
  1111. {
  1112. continue;
  1113. }
  1114. if ((ReturnValue[0] & 1) == 0)
  1115. {
  1116. WriteDoneTime.Restart();
  1117. while (SendQueue.Count==0&& IsSend)
  1118. {
  1119. //Thread.Sleep(1);
  1120. }
  1121. sw.Restart();
  1122. int SendMessageCount = SendQueue.Count > 10 ? 10 : SendQueue.Count;
  1123. for (int i = 0; i < SendMessageCount; i++)
  1124. {
  1125. if (!SendQueue.TryDequeue(out sendMessage))
  1126. {
  1127. //FaultLog.RecordErrorMessage("MainThreadClass-SendBottLogicMessageProcess-SendQueue.TryDequeue failed!");
  1128. }
  1129. if (sendMessage.Message == null)
  1130. {
  1131. continue;
  1132. }
  1133. result[i * 2] = sendMessage.Message[0];
  1134. result[i * 2 + 1] = sendMessage.Message[1];
  1135. }
  1136. //写入数据
  1137. modbusTcpClient.WriteMultipleRegisters(slaveId: 1, startAddress: 100, values: result);
  1138. modbusTcpClient.WriteSingleRegister(slaveId: 1, registerAddress: 0, value: writedoneMes);
  1139. if (result[0] > 0 || result[1]>0)
  1140. {
  1141. sw.Stop();
  1142. WriteDoneTime.Stop();
  1143. LOG.log(string.Format("待发送数量{0}", SendQueue.Count), 6);
  1144. LOG.log(string.Format("SendMess1Ok:{0},SendMess1Ng:{1},SendMess2Ok:{2},SendMess2Ng:{3}," +
  1145. "\nSendMessage3Ok:{4},SendMessage3Ng:{5},SendMessage4Ok:{6},SendMessage4Ng:{7}," +
  1146. "\nSendMessage5Ok:{8},SendMessage5Ng:{9},SendMessage6Ok:{10},SendMessage6Ng:{11}," +
  1147. "\nSendMessage7Ok:{12},SendMessage7Ng:{13},SendMessage8Ok:{14},SendMessage8Ng:{15}," +
  1148. "\nSendMessage9Ok:{16},SendMessage9Ng:{17},SendMessage10Ok:{18},SendMessage10Ng:{19}," +
  1149. "\n此次写值耗时:{20},此次WriteDone耗时:{21},此次写入数据量{22}",
  1150. result[0], result[1], result[2], result[3],
  1151. result[4], result[5], result[6], result[7],
  1152. result[8], result[9], result[10], result[11],
  1153. result[12], result[13], result[14], result[15],
  1154. result[16], result[17], result[18], result[19],
  1155. sw.Elapsed,WriteDoneTime.Elapsed,sendMessage.count), 6);
  1156. if (sw.ElapsedMilliseconds > 10)
  1157. LOG.error("通讯超时");
  1158. }
  1159. }
  1160. Thread.Sleep(1);
  1161. }
  1162. }
  1163. #endregion
  1164. #region 外部函数
  1165. [DllImport("winmm.dll")]
  1166. static extern uint timeBeginPeriod(uint period);
  1167. #endregion
  1168. }
  1169. public class TestSenMessage
  1170. {
  1171. public ushort[] Message { get; set; }
  1172. //public Int16 Message { get; set; }
  1173. public ushort[] NumMessage { get; set; }
  1174. public ushort count { get; set; }
  1175. }
  1176. }