RealTimeOneCarForm.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. using DevExpress.XtraCharts;
  2. using DevExpress.XtraEditors;
  3. using GCAS.Code;
  4. using GCAS.Dto;
  5. using GCAS.Localization;
  6. using GCAS.Model;
  7. using HslCommunication;
  8. using HslCommunication.Core;
  9. using HslCommunication.ModBus;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Configuration;
  13. using System.Drawing;
  14. using System.Linq;
  15. using System.Media;
  16. using System.Speech.Synthesis;
  17. using System.Threading;
  18. using System.Threading.Tasks;
  19. using System.Windows.Forms;
  20. namespace GCAS
  21. {
  22. public partial class RealTimeOneCarForm : XtraForm
  23. {
  24. private ValueDataModel viewData;
  25. private bool isThreadRun = false;
  26. private Thread clientAThread = null;
  27. private Thread teamThread = null;
  28. private ModbusTcpNet busTcpClient = null;
  29. private DeviceRepository deviceRepository;
  30. private ReverseWordTrans reverseWord;
  31. private ConfigRepository configRepository;
  32. private RecordRepository recordRepository;
  33. private TeamRepository teamRepository;
  34. public OperatorModel currentUser;
  35. private DeviceModel device;
  36. private List<TeamModel> teams;
  37. private int interval = 50;//读取寄存器间隔ms毫秒
  38. private Series series1;
  39. private ConfigDto config;
  40. private TeamModel currentTeam;
  41. private List<WeightDto> dataPick;
  42. private List<NameValue> L;
  43. private ModbusTcpServer busTcpServer;
  44. private EntranceModel entrance;
  45. private EntranceRepository entranceRepository;
  46. private bool isBindChart = false;
  47. private int frontTimes = 100;
  48. private int effectCount = 6;
  49. private WeightDataDto weightData = new WeightDataDto { Records = new List<WeightDataItem> { } };
  50. private SpeechSynthesizer speech;
  51. private SoundPlayer playerIn;
  52. private SoundPlayer playerOut;
  53. private string startModbus = "false";
  54. public RealTimeOneCarForm()
  55. {
  56. InitializeComponent();
  57. playerIn = new SoundPlayer
  58. {
  59. SoundLocation = AppDomain.CurrentDomain.BaseDirectory + @"\Resources\in.wav"
  60. };
  61. Task.Run(() => playerIn.LoadAsync()); //同步加载声音
  62. playerOut = new SoundPlayer
  63. {
  64. SoundLocation = AppDomain.CurrentDomain.BaseDirectory + @"\Resources\out.wav"
  65. };
  66. Task.Run(() => playerOut.LoadAsync()); //同步加载声音
  67. speech = new SpeechSynthesizer();
  68. DoubleBuffered = true;
  69. L = LocalizationHelper.GetSource(Thread.CurrentThread.CurrentUICulture);
  70. deviceRepository = new DeviceRepository();
  71. device = deviceRepository.GetList().FirstOrDefault();
  72. dataPick = new List<WeightDto>();
  73. configRepository = new ConfigRepository();
  74. recordRepository = new RecordRepository();
  75. teamRepository = new TeamRepository();
  76. entranceRepository = new EntranceRepository();
  77. entrance = entranceRepository.GetList().FirstOrDefault();
  78. teams = teamRepository.GetList();
  79. reverseWord = new ReverseWordTrans(DataFormat.CDAB)
  80. {
  81. IsStringReverse = true
  82. };
  83. config = configRepository.Get();
  84. InitTeam();
  85. startModbus = ConfigurationManager.AppSettings["startModbus"];
  86. if (bool.TryParse(startModbus, out bool isStartModbus) && isStartModbus)
  87. {
  88. busTcpServer = ModbusServerHelper.StartModbusServer();
  89. if (busTcpServer != null)
  90. {
  91. Task.Run(() => RefreshModbusServerAsync());
  92. }
  93. }
  94. series1 = chartControl1.Series[0];
  95. ConnectModbusTcp();
  96. interval = int.Parse(ConfigurationManager.AppSettings["readInterval"]);
  97. frontTimes = int.Parse(ConfigurationManager.AppSettings["frontTime"]);
  98. effectCount = int.Parse(ConfigurationManager.AppSettings["effectCount"]);
  99. BindTable();
  100. }
  101. private void ConnectModbusTcp()
  102. {
  103. busTcpClient = new ModbusTcpNet(device.Ip, device.Port, 1)
  104. {
  105. ConnectTimeOut = 1000,
  106. ReceiveTimeOut = 1000,
  107. ConnectionId = device.Id.ToString(),
  108. };
  109. var result = busTcpClient.ConnectServer();
  110. MainThreadRun();
  111. }
  112. public void ChangeDeviceInfo()
  113. {
  114. device = deviceRepository.GetList().FirstOrDefault();
  115. viewData.DeviceName = device.Name;
  116. busTcpClient.ConnectClose();
  117. busTcpClient.Port = device.Port;
  118. busTcpClient.IpAddress = device.Ip;
  119. busTcpClient.ConnectServer();
  120. }
  121. public void ChangeTeamInfo()
  122. {
  123. teams = teamRepository.GetList();
  124. InitTeam();
  125. }
  126. public void ChangeConfigInfo()
  127. {
  128. config = configRepository.Get();
  129. }
  130. private void MainThreadRun()
  131. {
  132. if (!isThreadRun)
  133. {
  134. if (viewData == null)
  135. {
  136. viewData = new ValueDataModel();
  137. }
  138. isThreadRun = true;
  139. clientAThread = new Thread(ThreadAReadServer)
  140. {
  141. IsBackground = true
  142. };
  143. clientAThread.Start();
  144. teamThread = new Thread(ThreadTeamChange)
  145. {
  146. IsBackground = true
  147. };
  148. teamThread.Start();
  149. }
  150. else
  151. {
  152. isThreadRun = false;
  153. }
  154. }
  155. private void ThreadTeamChange()
  156. {
  157. InitTeam();
  158. while (isThreadRun)
  159. {
  160. Thread.Sleep(1000 * 60);
  161. if (IsHandleCreated)
  162. {
  163. Invoke(new EventHandler(delegate
  164. {
  165. InitTeam();
  166. }));
  167. }
  168. }
  169. }
  170. private void InitTeam()
  171. {
  172. DateTime now = DateTime.Now;
  173. TeamModel nextTeam = new TeamModel();
  174. var query = from team in teams
  175. let sp1 = TimeSpan.Parse(team.StartTime)
  176. let sp2 = TimeSpan.Parse(team.EndTime)
  177. let start = sp1 > sp2 ? (now.TimeOfDay > sp1 ? now.Date + sp1 : now.AddDays(-1).Date + sp1) : now.Date + sp1
  178. let end = sp1 > sp2 ? (now.TimeOfDay > sp1 ? now.AddDays(1).Date + sp2 : now.Date + sp2) : now.Date + sp2
  179. where now >= start && now < end
  180. select team;
  181. var nextTeams = query.ToList();
  182. ////获取符合时间的班组
  183. //var nextTeams = teams.Where(c => now > DateTime.Parse(now.ToShortDateString() + " " + c.StartTime) && now < DateTime.Parse(now.AddDays(c.NextDay).ToShortDateString() + " " + c.EndTime)).ToList();
  184. nextTeam = nextTeams[0];
  185. if (nextTeam != null)
  186. {
  187. var span = DateTime.Parse(now.ToShortDateString() + " " + nextTeam.EndTime) - now;
  188. if (span.TotalMinutes < 10)
  189. {
  190. lbl_changeTeam1.Visible = true;
  191. lbl_changeTeam1.Text = string.Format(L.GetString("changeTeam"), (int)span.TotalMinutes);
  192. }
  193. else
  194. {
  195. lbl_changeTeam1.Visible = false;
  196. }
  197. lbl_team_1.Text = nextTeam.Name;
  198. }
  199. else
  200. {
  201. lbl_changeTeam1.Visible = false;
  202. lbl_team_1.Text = L.GetString("leisure");
  203. }
  204. if (currentTeam != nextTeam)
  205. {
  206. currentTeam = nextTeam;
  207. BindTable();
  208. }
  209. }
  210. private void ThreadAReadServer()
  211. {
  212. while (true)
  213. {
  214. if (isThreadRun)
  215. {
  216. Thread.Sleep(interval);
  217. ReadValueDataAsync();
  218. }
  219. else
  220. {
  221. break;
  222. }
  223. // 再次检测连接是否关闭
  224. if (!isThreadRun) { Thread.Sleep(50); break; }
  225. }
  226. }
  227. private void ReadValueDataAsync()
  228. {
  229. OperateResult<byte[]> modbusRead;
  230. int weight = 0;
  231. string recordWeight = string.Empty, effectWeight = string.Empty;
  232. int maxTakeWeightCount = frontTimes / interval * 20;
  233. //判读是否存在
  234. if (viewData == null)
  235. {
  236. viewData = new ValueDataModel(device.Id, device.Name);
  237. }
  238. viewData.DeviceName = device?.Name;
  239. if (dataPick == null)
  240. {
  241. dataPick = new List<WeightDto>();
  242. }
  243. modbusRead = busTcpClient.Read("x=4;0000", 7);
  244. if (modbusRead.IsSuccess)
  245. {
  246. viewData.IsOnline = true;
  247. var stateBytes = modbusRead.Content.Skip(0).Take(2).ToArray();
  248. var stateChars = Convert.ToString(reverseWord.TransInt16(stateBytes, 0), 2).PadLeft(8, '0').ToCharArray().Select(c => c == '1').Reverse().ToArray();
  249. viewData.YJ = stateChars[0];
  250. viewData.BJ = stateChars[1];
  251. viewData.LW = stateChars[4];
  252. viewData.WD = stateChars[5];
  253. var statusBytes = modbusRead.Content.Skip(2).Take(2).ToArray();
  254. var statuChars = Convert.ToString(reverseWord.TransInt16(statusBytes, 0), 2).PadLeft(8, '0').Select(c => c == '1').Reverse().ToArray();
  255. viewData.DCR = statuChars[0];
  256. viewData.XCR = statuChars[1];
  257. viewData.OP = statuChars[2];
  258. var netBytes = modbusRead.Content.Skip(4).Take(4).ToArray();
  259. viewData.NetWeight = reverseWord.TransInt32(netBytes, 0);
  260. var tareBytes = modbusRead.Content.Skip(8).Take(4).ToArray();
  261. viewData.TareWeight = reverseWord.TransInt32(tareBytes, 0);
  262. var ratedBytes = modbusRead.Content.Skip(12).Take(2).ToArray();
  263. viewData.RatedWeight = (ushort)(reverseWord.TransUInt16(ratedBytes, 0) * 10);
  264. #region 重量计算
  265. dataPick.Add(new WeightDto { Weight = viewData.NetWeight, Time = DateTime.Now });
  266. if (dataPick.Count > 100)
  267. {
  268. dataPick.RemoveAt(0);
  269. }
  270. //就位料口
  271. if (viewData.DCR && viewData.XCR)
  272. {
  273. if (!weightData.IsReady)
  274. {
  275. Task.Run(() => playerIn.PlaySync()); //启用新线程播放
  276. weightData.IsReady = true;
  277. }
  278. // 抓斗打开 且上次抓斗状态为闭合 即为投料
  279. if (viewData.OP && !weightData.OpenState)
  280. {
  281. Measure(dataPick, weightData);
  282. }
  283. }
  284. else
  285. {
  286. if (weightData.IsReady)
  287. {
  288. Task.Run(() => playerOut.PlaySync()); //启用新线程播放
  289. weightData.IsReady = false;
  290. }
  291. if (weightData.Records.Any())
  292. {
  293. Measure(dataPick, weightData);
  294. var firstRecord = weightData.Records.First();
  295. var lastRecord = weightData.Records.Count >= 2 ? weightData.Records.Last() : null;
  296. weight = firstRecord.Weight - (lastRecord?.Weight ?? 0);
  297. if (weight < 0 || weight > config.PutThresholdMin)
  298. {
  299. recordRepository.Insert(new RecordModel
  300. {
  301. Device = device.Id,
  302. Entrance = entrance.Id,
  303. GrossWeight = weight + viewData.TareWeight,
  304. TareWeight = viewData.TareWeight,
  305. NetWeight = weight,
  306. Time = DateTime.Now,
  307. Team = currentTeam?.Id ?? 0,
  308. RecordData = firstRecord.RecordWeight,
  309. EffectData = firstRecord.EffectWeight
  310. });
  311. Task.Run(() => speech.SpeakAsync($"加料 {weight / 1000f}吨"));
  312. if (IsHandleCreated)
  313. {
  314. Invoke(new EventHandler(delegate
  315. {
  316. series1.Points.Add(new SeriesPoint(DateTime.Now, (weight / 1000f).ToString("0.00")));
  317. BindTable();
  318. if (bool.TryParse(startModbus, out bool isStartModbus) && isStartModbus)
  319. {
  320. Task.Run(() => RefreshModbusServerAsync());
  321. }
  322. }));
  323. }
  324. }
  325. //清空开斗数据记录
  326. weightData.Records = new List<WeightDataItem>();
  327. }
  328. }
  329. weightData.OpenState = viewData.OP;
  330. #endregion 重量计算
  331. }
  332. else
  333. {
  334. if (viewData != null)
  335. {
  336. viewData.IsOnline = false;
  337. }
  338. }
  339. if (isThreadRun)
  340. {
  341. if (IsHandleCreated)
  342. {
  343. Invoke(new EventHandler(delegate
  344. {
  345. InitView();
  346. }));
  347. }
  348. }
  349. }
  350. private void InitView()
  351. {
  352. if (!isBindChart)
  353. {
  354. BindChart();
  355. }
  356. if (viewData == null)
  357. {
  358. return;
  359. }
  360. lbl_devcieName_1.Text = viewData.DeviceName;
  361. var totalWeight = viewData.NetWeight + viewData.TareWeight;
  362. var axisY1 = ((XYDiagram)chartControl1.Diagram).AxisY;
  363. if (!axisY1.ConstantLines.Any(c => c.Value == viewData.RatedWeight / 1000 * 1.05f))
  364. {
  365. axisY1.ConstantLines.Clear();
  366. ConstantLine line = new ConstantLine { Color = Color.Red, AxisValue = viewData.RatedWeight / 1000f * 1.05f, Name = L.GetString("overload") };
  367. line.LineStyle.DashStyle = DashStyle.Dash;
  368. axisY1.ConstantLines.Add(line);
  369. }
  370. if (viewData.BJ)
  371. {
  372. lbl_netWeight_1.ForeColor = Color.FromArgb(234, 21, 122);
  373. }
  374. else
  375. {
  376. lbl_netWeight_1.ForeColor = viewData.YJ ? Color.FromArgb(254, 184, 10) : Color.FromArgb(127, 209, 59);
  377. }
  378. lbl_netWeight_1.Text = (viewData.NetWeight / 1000F).ToString("0.00");
  379. if (viewData.IsOnline)
  380. {
  381. lbl_isOnline_1.Text = L.GetString("normal");
  382. pic_isOnline_1.Image = Properties.Resources.green;
  383. }
  384. else
  385. {
  386. lbl_isOnline_1.Text = L.GetString("abnormal");
  387. pic_isOnline_1.Image = Properties.Resources.red;
  388. }
  389. pic_lw_1.Image = viewData.LW ? Properties.Resources.green : Properties.Resources.gray;
  390. pic_wd_1.Image = viewData.WD ? Properties.Resources.green : Properties.Resources.gray;
  391. if (viewData.BJ)
  392. {
  393. pic_yj_1.Image = Properties.Resources.red;
  394. lbl_yj_1.Text = L.GetString("overload");
  395. }
  396. else
  397. {
  398. pic_yj_1.Image = viewData.YJ ? Properties.Resources.yellow : Properties.Resources.gray;
  399. lbl_yj_1.Text = L.GetString("warning");
  400. }
  401. busTcpServer?.Write("1100", new int[] { viewData.NetWeight, viewData.TareWeight, viewData.RatedWeight });
  402. //A车
  403. if (viewData.DCR && viewData.XCR)
  404. {
  405. if (!pic_entrance_1_1.Enabled)
  406. {
  407. pic_entrance_1_1.Enabled = true;
  408. }
  409. }
  410. else
  411. {
  412. if (pic_entrance_1_1.Enabled)
  413. {
  414. pic_entrance_1_1.Enabled = false;
  415. }
  416. }
  417. }
  418. private async Task RefreshModbusServerAsync()
  419. {
  420. var source = await recordRepository.TotalStatistics();
  421. busTcpServer?.Write("1000", new int[] { source.Daily, source.Weekly, source.Monthly, source.AnnualTotal });
  422. }
  423. public void BindTable()
  424. {
  425. var source1 = Task.Run(() => recordRepository.DailyStatistics(device.Id)).Result;
  426. if (source1.Any())
  427. {
  428. gridControl.DataSource = source1;
  429. lbl_total_1.Text = ((source1.FirstOrDefault(c => c.Id == currentTeam?.Id)?.DailyTotal ?? 0)).ToString("0.00");
  430. }
  431. }
  432. public void BindChart()
  433. {
  434. series1.Points.Clear();
  435. if (currentTeam != null)
  436. {
  437. var soruce1 = Task.Run(() => recordRepository.GetDailyRealTimeData(device.Id, currentTeam.Id)).Result;
  438. foreach (var point in soruce1)
  439. {
  440. series1.Points.Add(new SeriesPoint(point.Time, point.Weight.ToString("0.00")));
  441. }
  442. isBindChart = true;
  443. }
  444. }
  445. private void RealTimeForm_FormClosing(object sender, FormClosingEventArgs e)
  446. {
  447. if (e.CloseReason == CloseReason.MdiFormClosing)//判断点击的是父窗体还是子窗体
  448. {
  449. isThreadRun = false;
  450. }
  451. else if (e.CloseReason == CloseReason.UserClosing)
  452. {
  453. e.Cancel = true;
  454. }
  455. }
  456. private void RealTimeForm_FormClosed(object sender, FormClosedEventArgs e)
  457. {
  458. isThreadRun = false;
  459. }
  460. private void panelControl10_Resize(object sender, EventArgs e)
  461. {
  462. lbl_devcieName_1.Location = new Point(Convert.ToInt32(panelControl10.Width - lbl_devcieName_1.Width) / 2, Convert.ToInt32(panelControl10.Height - lbl_devcieName_1.Height) / 4);
  463. }
  464. public void Measure(List<WeightDto> dataPick, WeightDataDto weightData)
  465. {
  466. //是否有称重数据
  467. if (dataPick.Any())
  468. {
  469. var lastDataTime = dataPick.Last().Time;
  470. var firstDataTime = dataPick.First().Time;
  471. dataPick.Reverse();
  472. //截取有效值 即 松斗信号之前x毫秒数据内n条重量记录
  473. var effectWeights = dataPick.Where(c => c.Time < lastDataTime.AddMilliseconds(-frontTimes)).Take(effectCount);
  474. //有效值数量是否满足
  475. if (effectWeights.Count() == effectCount)
  476. {
  477. //获得准确重量
  478. var weight = (int)effectWeights.OrderBy(c => c.Weight).Skip(1).Take(effectCount - 2).Average(c => c.Weight);
  479. var effectWeight = effectWeights.ToJsonString();
  480. var recordWeight = dataPick.Take(40).ToJsonString();
  481. //记录松斗记录(有可能一次投料 松斗合斗好几次 造成多次累积 故记录多次 离开或者归零时取首次)
  482. weightData.Records.Add(new WeightDataItem
  483. {
  484. Time = DateTime.Now,
  485. EffectWeight = effectWeight,
  486. RecordWeight = recordWeight,
  487. Weight = weight
  488. });
  489. }
  490. dataPick = new List<WeightDto>();
  491. }
  492. }
  493. }
  494. }