using Bro.Common.Helper; using Bro.UI.Model.Winform; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Configuration; using System.IO; using WeifenLuo.WinFormsUI.Docking; using static Bro.Common.Helper.EnumHelper; namespace Bro.UI.Config.MenuForms { [DockOption(DockState.DockRight, 230, 0)] [MenuNode("Statistic", "生产信息", 2, "View1", true)] public partial class FrmStatistic : MenuFrmBase { #region Properties private int qty_OK = 0; private int qty_NG = 0; private int qty_All = 0; private float qty_ok_rate = 0; private float qty_ng_rate = 0; private float ct = 0; private int uph = 0; private DateTime? startTime = null; private TimeSpan runTime = new TimeSpan(); private TimeSpan idleTime = new TimeSpan(); private TimeSpan downTime = new TimeSpan(); private TimeSpan availableTime = new TimeSpan(); private int qty_OEE = 0; private int qty_OEE_OK = 0; private float oee = 0; public int Qty_OK { get => qty_OK; set { qty_OK = value; this.Invoke(new Action(() => { lblOKNum.Text = qty_OK.ToString(); })); Qty_All = Qty_OK + Qty_NG; Qty_OK_Rate = Qty_All == 0 ? 0 : (float)Qty_OK / (float)Qty_All; Qty_NG_Rate = Qty_All == 0 ? 0 : (float)Qty_NG / (float)Qty_All; } } public int Qty_NG { get => qty_NG; set { qty_NG = value; this.Invoke(new Action(() => { lblNGNum.Text = qty_NG.ToString(); })); Qty_All = Qty_OK + Qty_NG; Qty_OK_Rate = Qty_All == 0 ? 0 : (float)Qty_OK / (float)Qty_All; Qty_NG_Rate = Qty_All == 0 ? 0 : (float)Qty_NG / (float)Qty_All; } } public int Qty_All { get => qty_All; set { qty_All = value; this.Invoke(new Action(() => { lblTotalNum.Text = qty_All.ToString(); })); } } public float Qty_OK_Rate { get => qty_ok_rate; set { qty_ok_rate = value; this.Invoke(new Action(() => { lblOKRate.Text = $"{(qty_ok_rate * 100).ToString("f2")}%"; })); } } public float Qty_NG_Rate { get => qty_ng_rate; set { qty_ng_rate = value; this.Invoke(new Action(() => { lblNGRate.Text = $"{(qty_ng_rate * 100).ToString("f2")}%"; })); } } public float CT { get => ct; set { ct = value; this.Invoke(new Action(() => { lblCT.Text = $"{ct.ToString("f3")} s"; })); } } public int UPH { get => uph; set { uph = value; this.Invoke(new Action(() => { lblUPH.Text = $"{uph} pcs/hr"; })); } } public DateTime? StartTime { get => startTime; set { startTime = value; this.Invoke(new Action(() => { lblStartTime.Text = (startTime != null ? startTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : ""); })); } } public TimeSpan RunTime { get => runTime; set { runTime = value; this.Invoke(new Action(() => { lblRunTime.Text = runTime.ToString(); })); } } public TimeSpan IdleTime { get => idleTime; set { idleTime = value; this.Invoke(new Action(() => { lblIdleTime.Text = idleTime.ToString(); })); GetRunTime(); } } public TimeSpan DownTime { get => downTime; set { downTime = value; this.Invoke(new Action(() => { lblDownTime.Text = downTime.ToString(); })); GetRunTime(); } } private void GetRunTime() { RunTime = AvailableTime + IdleTime + DownTime; } public TimeSpan AvailableTime { get => availableTime; set { availableTime = value; this.Invoke(new Action(() => { lblAvailableTime.Text = availableTime.ToString(); })); GetRunTime(); } } public int Qty_OEE { get => qty_OEE; set { qty_OEE = value; this.Invoke(new Action(() => { lblOEE_Total.Text = qty_OEE.ToString(); })); } } public int Qty_OEE_OK { get => qty_OEE_OK; set { qty_OEE_OK = value; this.Invoke(new Action(() => { lblOEE_OK.Text = qty_OEE_OK.ToString(); })); } } public float OEE { get => oee; set { oee = value; this.Invoke(new Action(() => { lblOEE_Rate.Text = $"{(oee * 100).ToString("f2")}%"; })); } } public int IdealUPH { get => (int)nudDemandUPH.Value; set { this.Invoke(new Action(() => { nudDemandUPH.Value = value; })); } } #endregion #region Timer System.Threading.Timer _availableTimer = null; System.Threading.Timer _idleTimer = null; System.Threading.Timer _downTimer = null; System.Threading.Timer _checkIdleTimer = null; System.Threading.Timer _calculateTimer = null; private RunState? currentState = null; public RunState? CurrentState { get => currentState; set { if (value != null) { if (currentState != value.Value) { switch (value.Value) { case RunState.Stop: _availableTimer.Change(-1, -1); _idleTimer.Change(-1, -1); _downTimer.Change(-1, -1); break; case RunState.Running: _idleTimer.Change(-1, -1); _downTimer.Change(-1, -1); _availableTimer.Change(0, 1000); break; case RunState.Idle: _availableTimer.Change(-1, -1); _downTimer.Change(-1, -1); _idleTimer.Change(0, 1000); break; case RunState.Down: _idleTimer.Change(-1, -1); _availableTimer.Change(-1, -1); _downTimer.Change(0, 1000); break; } currentState = value; } } } } #endregion int _idleThreshold = 30; public FrmStatistic() { InitializeComponent(); this.Load += (s, e) => { ReadNumRecords(); string idealUPHStr = ConfigurationManager.AppSettings["IdealUPH"]; if (!string.IsNullOrWhiteSpace(idealUPHStr) && int.TryParse(idealUPHStr, out int idealUPHSetting)) { IdealUPH = idealUPHSetting; } else { IdealUPH = 5000; } }; string idleThresholdStr = ConfigurationManager.AppSettings["IdleThreshold"]; if (!string.IsNullOrWhiteSpace(idleThresholdStr) && int.TryParse(idleThresholdStr, out int idleTTemp)) { _idleThreshold = idleTTemp; } else { _idleThreshold = 30; } } public override void OnProcessUpdated() { Process.OnProcessStateChanged -= Process_OnProcessStateChanged; Process.OnProcessStateChanged += Process_OnProcessStateChanged; Process.OnAlarmUpdate -= Process_OnAlarmUpdate; Process.OnAlarmUpdate += Process_OnAlarmUpdate; Process.OnUpdateResult = UpdateResultOK; Process.OnUpdateCT = UpdateCT; _availableTimer = new System.Threading.Timer(AvailableTimerCallBack, null, -1, -1); _idleTimer = new System.Threading.Timer(IdleTimerCallBack, null, -1, -1); _downTimer = new System.Threading.Timer(DownTimerCallBack, null, -1, -1); _checkIdleTimer = new System.Threading.Timer(CheckIdle, null, -1, -1); _calculateTimer = new System.Threading.Timer(Calculation, null, 1000 * 60, 1000 * 60); } private void Process_OnAlarmUpdate(string alarmMsg) { if (string.IsNullOrWhiteSpace(alarmMsg)) { if (CurrentState == RunState.Down) { CurrentState = RunState.Idle; } } else { if (CurrentState == RunState.Idle || CurrentState == RunState.Running) { CurrentState = RunState.Down; } } } private void Process_OnProcessStateChanged(EnumHelper.DeviceState state) { if (state == EnumHelper.DeviceState.DSOpen) { StartTime = DateTime.Now; CurrentState = RunState.Idle; ; } else { CurrentState = RunState.Stop; } if (state == EnumHelper.DeviceState.DSClose) { SaveNumRecord(); } } private void CheckIdle(object state) { var idleBack = new TimeSpan(0, 0, _idleThreshold); IdleTime = IdleTime.Add(idleBack); AvailableTime = AvailableTime.Subtract(idleBack); CurrentState = RunState.Idle; } private void UpdateResultOK(DateTime updateTime, int result) { CurrentState = RunState.Running; _checkIdleTimer.Change(_idleThreshold * 1000, -1); //1分钟没有上传Load认为进入空闲时间 if (result == 1) { Qty_OK++; Qty_OEE_OK++; } else { Qty_NG++; } Qty_OEE++; } private void UpdateCT(float ctTime) { CT = ctTime; } private void RefreshOEE() { Calculation(null); } /// /// 每分钟计算OEE,平均UPH /// /// private void Calculation(object state) { if (RunTime.TotalHours == 0) { UPH = 0; } else { UPH = (int)(Qty_OEE / RunTime.TotalHours); } if (IdealUPH == 0 || RunTime.TotalSeconds == 0 || Qty_OEE == 0) { OEE = 0; } else { OEE = (float)(AvailableTime.TotalSeconds / RunTime.TotalSeconds) * ((float)UPH / (float)IdealUPH) * ((float)Qty_OEE_OK / (float)Qty_OEE); } } private void ClearOEE() { RunTime = IdleTime = DownTime = new TimeSpan(); Qty_OEE_OK = Qty_OEE = 0; if (Process.ProcessState == EnumHelper.DeviceState.DSOpen) { StartTime = DateTime.Now; } else { StartTime = null; } } private void DownTimerCallBack(object state) { DownTime = DownTime.Add(new TimeSpan(0, 0, 1)); } private void IdleTimerCallBack(object state) { IdleTime = IdleTime.Add(new TimeSpan(0, 0, 1)); } private void AvailableTimerCallBack(object state) { AvailableTime = AvailableTime.Add(new TimeSpan(0, 0, 1)); } private void OnClearQty() { Qty_OK = Qty_NG = 0; } string numRecordFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Statistic.json"); static object numLock = new object(); private void SaveNumRecord() { lock (numLock) { using (StreamWriter writer = new StreamWriter(numRecordFile, false, System.Text.Encoding.UTF8)) { string dataStr = JsonConvert.SerializeObject(new { Qty_OK, Qty_NG }); writer.Write(dataStr); writer.Flush(); } } } private void ReadNumRecords() { if (File.Exists(numRecordFile)) { lock (numLock) { using (StreamReader reader = new StreamReader(numRecordFile, System.Text.Encoding.UTF8)) { string dataStr = reader.ReadToEnd(); JObject data = JsonConvert.DeserializeObject(dataStr); if (data != null) { Qty_OK = data.Value("Qty_OK"); Qty_NG = data.Value("Qty_NG"); } } } } } private void btnClearStatistic_Click(object sender, EventArgs e) { Qty_OK = Qty_NG = 0; } } }