using DevExpress.XtraCharts; using DevExpress.XtraEditors; using GCAS.Code; using GCAS.Dto; using GCAS.Localization; using GCAS.Model; using HslCommunication; using HslCommunication.Core; using HslCommunication.ModBus; using System; using System.Collections.Generic; using System.Configuration; using System.Drawing; using System.Linq; using System.Media; using System.Speech.Synthesis; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace GCAS { public partial class RealTimeOneCarForm : XtraForm { private ValueDataModel viewData; private bool isThreadRun = false; private Thread clientAThread = null; private Thread teamThread = null; private ModbusTcpNet busTcpClient = null; private DeviceRepository deviceRepository; private ReverseWordTrans reverseWord; private ConfigRepository configRepository; private RecordRepository recordRepository; private TeamRepository teamRepository; public OperatorModel currentUser; private DeviceModel device; private List teams; private int interval = 50;//读取寄存器间隔ms毫秒 private Series series1; private ConfigDto config; private TeamModel currentTeam; private List dataPick; private List L; private ModbusTcpServer busTcpServer; private EntranceModel entrance; private EntranceRepository entranceRepository; private bool isBindChart = false; private int frontTimes = 100; private int effectCount = 6; private WeightDataDto weightData = new WeightDataDto { Records = new List { } }; private SpeechSynthesizer speech; private SoundPlayer playerIn; private SoundPlayer playerOut; private string startModbus = "false"; public RealTimeOneCarForm() { InitializeComponent(); playerIn = new SoundPlayer { SoundLocation = AppDomain.CurrentDomain.BaseDirectory + @"\Resources\in.wav" }; Task.Run(() => playerIn.LoadAsync()); //同步加载声音 playerOut = new SoundPlayer { SoundLocation = AppDomain.CurrentDomain.BaseDirectory + @"\Resources\out.wav" }; Task.Run(() => playerOut.LoadAsync()); //同步加载声音 speech = new SpeechSynthesizer(); DoubleBuffered = true; L = LocalizationHelper.GetSource(Thread.CurrentThread.CurrentUICulture); deviceRepository = new DeviceRepository(); device = deviceRepository.GetList().FirstOrDefault(); dataPick = new List(); configRepository = new ConfigRepository(); recordRepository = new RecordRepository(); teamRepository = new TeamRepository(); entranceRepository = new EntranceRepository(); entrance = entranceRepository.GetList().FirstOrDefault(); teams = teamRepository.GetList(); reverseWord = new ReverseWordTrans(DataFormat.CDAB) { IsStringReverse = true }; config = configRepository.Get(); InitTeam(); startModbus = ConfigurationManager.AppSettings["startModbus"]; if (bool.TryParse(startModbus, out bool isStartModbus) && isStartModbus) { busTcpServer = ModbusServerHelper.StartModbusServer(); if (busTcpServer != null) { Task.Run(() => RefreshModbusServerAsync()); } } series1 = chartControl1.Series[0]; ConnectModbusTcp(); interval = int.Parse(ConfigurationManager.AppSettings["readInterval"]); frontTimes = int.Parse(ConfigurationManager.AppSettings["frontTime"]); effectCount = int.Parse(ConfigurationManager.AppSettings["effectCount"]); BindTable(); } private void ConnectModbusTcp() { busTcpClient = new ModbusTcpNet(device.Ip, device.Port, 1) { ConnectTimeOut = 1000, ReceiveTimeOut = 1000, ConnectionId = device.Id.ToString(), }; var result = busTcpClient.ConnectServer(); MainThreadRun(); } public void ChangeDeviceInfo() { device = deviceRepository.GetList().FirstOrDefault(); viewData.DeviceName = device.Name; busTcpClient.ConnectClose(); busTcpClient.Port = device.Port; busTcpClient.IpAddress = device.Ip; busTcpClient.ConnectServer(); } public void ChangeTeamInfo() { teams = teamRepository.GetList(); InitTeam(); } public void ChangeConfigInfo() { config = configRepository.Get(); } private void MainThreadRun() { if (!isThreadRun) { if (viewData == null) { viewData = new ValueDataModel(); } isThreadRun = true; clientAThread = new Thread(ThreadAReadServer) { IsBackground = true }; clientAThread.Start(); teamThread = new Thread(ThreadTeamChange) { IsBackground = true }; teamThread.Start(); } else { isThreadRun = false; } } private void ThreadTeamChange() { InitTeam(); while (isThreadRun) { Thread.Sleep(1000 * 60); if (IsHandleCreated) { Invoke(new EventHandler(delegate { InitTeam(); })); } } } private void InitTeam() { DateTime now = DateTime.Now; TeamModel nextTeam = new TeamModel(); var query = from team in teams let sp1 = TimeSpan.Parse(team.StartTime) let sp2 = TimeSpan.Parse(team.EndTime) let start = sp1 > sp2 ? (now.TimeOfDay > sp1 ? now.Date + sp1 : now.AddDays(-1).Date + sp1) : now.Date + sp1 let end = sp1 > sp2 ? (now.TimeOfDay > sp1 ? now.AddDays(1).Date + sp2 : now.Date + sp2) : now.Date + sp2 where now >= start && now < end select team; var nextTeams = query.ToList(); ////获取符合时间的班组 //var nextTeams = teams.Where(c => now > DateTime.Parse(now.ToShortDateString() + " " + c.StartTime) && now < DateTime.Parse(now.AddDays(c.NextDay).ToShortDateString() + " " + c.EndTime)).ToList(); nextTeam = nextTeams[0]; if (nextTeam != null) { var span = DateTime.Parse(now.ToShortDateString() + " " + nextTeam.EndTime) - now; if (span.TotalMinutes < 10) { lbl_changeTeam1.Visible = true; lbl_changeTeam1.Text = string.Format(L.GetString("changeTeam"), (int)span.TotalMinutes); } else { lbl_changeTeam1.Visible = false; } lbl_team_1.Text = nextTeam.Name; } else { lbl_changeTeam1.Visible = false; lbl_team_1.Text = L.GetString("leisure"); } if (currentTeam != nextTeam) { currentTeam = nextTeam; BindTable(); } } private void ThreadAReadServer() { while (true) { if (isThreadRun) { Thread.Sleep(interval); ReadValueDataAsync(); } else { break; } // 再次检测连接是否关闭 if (!isThreadRun) { Thread.Sleep(50); break; } } } private void ReadValueDataAsync() { OperateResult modbusRead; int weight = 0; string recordWeight = string.Empty, effectWeight = string.Empty; int maxTakeWeightCount = frontTimes / interval * 20; //判读是否存在 if (viewData == null) { viewData = new ValueDataModel(device.Id, device.Name); } viewData.DeviceName = device?.Name; if (dataPick == null) { dataPick = new List(); } modbusRead = busTcpClient.Read("x=4;0000", 7); if (modbusRead.IsSuccess) { viewData.IsOnline = true; var stateBytes = modbusRead.Content.Skip(0).Take(2).ToArray(); var stateChars = Convert.ToString(reverseWord.TransInt16(stateBytes, 0), 2).PadLeft(8, '0').ToCharArray().Select(c => c == '1').Reverse().ToArray(); viewData.YJ = stateChars[0]; viewData.BJ = stateChars[1]; viewData.LW = stateChars[4]; viewData.WD = stateChars[5]; var statusBytes = modbusRead.Content.Skip(2).Take(2).ToArray(); var statuChars = Convert.ToString(reverseWord.TransInt16(statusBytes, 0), 2).PadLeft(8, '0').Select(c => c == '1').Reverse().ToArray(); viewData.DCR = statuChars[0]; viewData.XCR = statuChars[1]; viewData.OP = statuChars[2]; var netBytes = modbusRead.Content.Skip(4).Take(4).ToArray(); viewData.NetWeight = reverseWord.TransInt32(netBytes, 0); var tareBytes = modbusRead.Content.Skip(8).Take(4).ToArray(); viewData.TareWeight = reverseWord.TransInt32(tareBytes, 0); var ratedBytes = modbusRead.Content.Skip(12).Take(2).ToArray(); viewData.RatedWeight = (ushort)(reverseWord.TransUInt16(ratedBytes, 0) * 10); #region 重量计算 dataPick.Add(new WeightDto { Weight = viewData.NetWeight, Time = DateTime.Now }); if (dataPick.Count > 100) { dataPick.RemoveAt(0); } //就位料口 if (viewData.DCR && viewData.XCR) { if (!weightData.IsReady) { Task.Run(() => playerIn.PlaySync()); //启用新线程播放 weightData.IsReady = true; } // 抓斗打开 且上次抓斗状态为闭合 即为投料 if (viewData.OP && !weightData.OpenState) { Measure(dataPick, weightData); } } else { if (weightData.IsReady) { Task.Run(() => playerOut.PlaySync()); //启用新线程播放 weightData.IsReady = false; } if (weightData.Records.Any()) { Measure(dataPick, weightData); var firstRecord = weightData.Records.First(); var lastRecord = weightData.Records.Count >= 2 ? weightData.Records.Last() : null; weight = firstRecord.Weight - (lastRecord?.Weight ?? 0); if (weight < 0 || weight > config.PutThresholdMin) { recordRepository.Insert(new RecordModel { Device = device.Id, Entrance = entrance.Id, GrossWeight = weight + viewData.TareWeight, TareWeight = viewData.TareWeight, NetWeight = weight, Time = DateTime.Now, Team = currentTeam?.Id ?? 0, RecordData = firstRecord.RecordWeight, EffectData = firstRecord.EffectWeight }); Task.Run(() => speech.SpeakAsync($"加料 {weight / 1000f}吨")); if (IsHandleCreated) { Invoke(new EventHandler(delegate { series1.Points.Add(new SeriesPoint(DateTime.Now, (weight / 1000f).ToString("0.00"))); BindTable(); if (bool.TryParse(startModbus, out bool isStartModbus) && isStartModbus) { Task.Run(() => RefreshModbusServerAsync()); } })); } } //清空开斗数据记录 weightData.Records = new List(); } } weightData.OpenState = viewData.OP; #endregion 重量计算 } else { if (viewData != null) { viewData.IsOnline = false; } } if (isThreadRun) { if (IsHandleCreated) { Invoke(new EventHandler(delegate { InitView(); })); } } } private void InitView() { if (!isBindChart) { BindChart(); } if (viewData == null) { return; } lbl_devcieName_1.Text = viewData.DeviceName; var totalWeight = viewData.NetWeight + viewData.TareWeight; var axisY1 = ((XYDiagram)chartControl1.Diagram).AxisY; if (!axisY1.ConstantLines.Any(c => c.Value == viewData.RatedWeight / 1000 * 1.05f)) { axisY1.ConstantLines.Clear(); ConstantLine line = new ConstantLine { Color = Color.Red, AxisValue = viewData.RatedWeight / 1000f * 1.05f, Name = L.GetString("overload") }; line.LineStyle.DashStyle = DashStyle.Dash; axisY1.ConstantLines.Add(line); } if (viewData.BJ) { lbl_netWeight_1.ForeColor = Color.FromArgb(234, 21, 122); } else { lbl_netWeight_1.ForeColor = viewData.YJ ? Color.FromArgb(254, 184, 10) : Color.FromArgb(127, 209, 59); } lbl_netWeight_1.Text = (viewData.NetWeight / 1000F).ToString("0.00"); if (viewData.IsOnline) { lbl_isOnline_1.Text = L.GetString("normal"); pic_isOnline_1.Image = Properties.Resources.green; } else { lbl_isOnline_1.Text = L.GetString("abnormal"); pic_isOnline_1.Image = Properties.Resources.red; } pic_lw_1.Image = viewData.LW ? Properties.Resources.green : Properties.Resources.gray; pic_wd_1.Image = viewData.WD ? Properties.Resources.green : Properties.Resources.gray; if (viewData.BJ) { pic_yj_1.Image = Properties.Resources.red; lbl_yj_1.Text = L.GetString("overload"); } else { pic_yj_1.Image = viewData.YJ ? Properties.Resources.yellow : Properties.Resources.gray; lbl_yj_1.Text = L.GetString("warning"); } busTcpServer?.Write("1100", new int[] { viewData.NetWeight, viewData.TareWeight, viewData.RatedWeight }); //A车 if (viewData.DCR && viewData.XCR) { if (!pic_entrance_1_1.Enabled) { pic_entrance_1_1.Enabled = true; } } else { if (pic_entrance_1_1.Enabled) { pic_entrance_1_1.Enabled = false; } } } private async Task RefreshModbusServerAsync() { var source = await recordRepository.TotalStatistics(); busTcpServer?.Write("1000", new int[] { source.Daily, source.Weekly, source.Monthly, source.AnnualTotal }); } public void BindTable() { var source1 = Task.Run(() => recordRepository.DailyStatistics(device.Id)).Result; if (source1.Any()) { gridControl.DataSource = source1; lbl_total_1.Text = ((source1.FirstOrDefault(c => c.Id == currentTeam?.Id)?.DailyTotal ?? 0)).ToString("0.00"); } } public void BindChart() { series1.Points.Clear(); if (currentTeam != null) { var soruce1 = Task.Run(() => recordRepository.GetDailyRealTimeData(device.Id, currentTeam.Id)).Result; foreach (var point in soruce1) { series1.Points.Add(new SeriesPoint(point.Time, point.Weight.ToString("0.00"))); } isBindChart = true; } } private void RealTimeForm_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason == CloseReason.MdiFormClosing)//判断点击的是父窗体还是子窗体 { isThreadRun = false; } else if (e.CloseReason == CloseReason.UserClosing) { e.Cancel = true; } } private void RealTimeForm_FormClosed(object sender, FormClosedEventArgs e) { isThreadRun = false; } private void panelControl10_Resize(object sender, EventArgs e) { lbl_devcieName_1.Location = new Point(Convert.ToInt32(panelControl10.Width - lbl_devcieName_1.Width) / 2, Convert.ToInt32(panelControl10.Height - lbl_devcieName_1.Height) / 4); } public void Measure(List dataPick, WeightDataDto weightData) { //是否有称重数据 if (dataPick.Any()) { var lastDataTime = dataPick.Last().Time; var firstDataTime = dataPick.First().Time; dataPick.Reverse(); //截取有效值 即 松斗信号之前x毫秒数据内n条重量记录 var effectWeights = dataPick.Where(c => c.Time < lastDataTime.AddMilliseconds(-frontTimes)).Take(effectCount); //有效值数量是否满足 if (effectWeights.Count() == effectCount) { //获得准确重量 var weight = (int)effectWeights.OrderBy(c => c.Weight).Skip(1).Take(effectCount - 2).Average(c => c.Weight); var effectWeight = effectWeights.ToJsonString(); var recordWeight = dataPick.Take(40).ToJsonString(); //记录松斗记录(有可能一次投料 松斗合斗好几次 造成多次累积 故记录多次 离开或者归零时取首次) weightData.Records.Add(new WeightDataItem { Time = DateTime.Now, EffectWeight = effectWeight, RecordWeight = recordWeight, Weight = weight }); } dataPick = new List(); } } } }