using Bro.Common.Helper; using Bro.Common.Interface; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.CommandWpf; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Configuration; using System.IO; namespace Bro.UI.ViewModel { public class InformationViewModel : ViewModelBase { #region Properties private IProcess processControl = null; public IProcess ProcessControl { get => processControl; set { if (processControl?.ProductionCode != value.ProductionCode) { processControl = value; ResetProcessControl(); } } } private void ResetProcessControl() { } 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; private int idealUPH = 10000; public int Qty_OK { get => qty_OK; set { Set(ref qty_OK, value); 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 { Set(ref qty_NG, value); 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 => Set(ref qty_All, value); } public float Qty_OK_Rate { get => qty_ok_rate; set => Set(ref qty_ok_rate, value); } public float Qty_NG_Rate { get => qty_ng_rate; set => Set(ref qty_ng_rate, value); } public float CT { get => ct; set => Set(ref ct, value); } public int UPH { get => uph; set => Set(ref uph, value); } public DateTime? StartTime { get => startTime; set => Set(ref startTime, value); } public TimeSpan RunTime { get => runTime; set { Set(ref runTime, value); //GetAvailableTime(); } } public TimeSpan IdleTime { get => idleTime; set { Set(ref idleTime, value); GetRunTime(); } } public TimeSpan DownTime { get => downTime; set { Set(ref downTime, value); GetRunTime(); } } private void GetRunTime() { //AvailableTime = RunTime - IdleTime - DownTime; RunTime = AvailableTime + IdleTime + DownTime; } public TimeSpan AvailableTime { get => availableTime; set { Set(ref availableTime, value); GetRunTime(); } } public int Qty_OEE { get => qty_OEE; set => Set(ref qty_OEE, value); } public int Qty_OEE_OK { get => qty_OEE_OK; set => Set(ref qty_OEE_OK, value); } public float OEE { get => oee; set => Set(ref oee, value); } public int IdealUPH { get => idealUPH; set => Set(ref idealUPH, value); } #endregion #region Command public RelayCommand Cmmd_ClearQty { get; set; } public RelayCommand Cmmd_RefreshOEE { get; set; } public RelayCommand Cmmd_ClearOEE { get; set; } #endregion 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; } } } } int _idleThreshold = 30; public InformationViewModel(IProcess control) { string idealUPHStr = ConfigurationManager.AppSettings["IdealUPH"]; if (!string.IsNullOrWhiteSpace(idealUPHStr) && int.TryParse(idealUPHStr, out int idealUPHSetting)) { IdealUPH = idealUPHSetting; } string idleThresholdStr = ConfigurationManager.AppSettings["IdleThreshold"]; if (!string.IsNullOrWhiteSpace(idleThresholdStr) && int.TryParse(idleThresholdStr, out int idleTTemp)) { _idleThreshold = idleTTemp; } else { _idleThreshold = 30; } ProcessControl = control; ProcessControl.OnProcessStateChanged -= OnProcessStateChanged; ProcessControl.OnProcessStateChanged += OnProcessStateChanged; ProcessControl.OnUpdateResult = UpdateResultOK; ProcessControl.OnUpdateCT = UpdateCT; Cmmd_ClearQty = new RelayCommand(OnClearQty); Cmmd_RefreshOEE = new RelayCommand(RefreshOEE); Cmmd_ClearOEE = new RelayCommand(ClearOEE); _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); ReadNumRecords(); } /// /// 每分钟计算OEE,平均UPH /// /// private void Calculation(object state) { if (AvailableTime.TotalHours != 0) { UPH = (int)(Qty_OEE / AvailableTime.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() { AvailableTime = IdleTime = DownTime = new TimeSpan(); Qty_OEE_OK = Qty_OEE = 0; if (ProcessControl.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 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 dt, int result) { CurrentState = RunState.Running; _checkIdleTimer.Change(_idleThreshold * 1000, -1); //限定时间内没有上传Load认为进入空闲时间 if (result == 1) { Qty_OK++; Qty_OEE_OK++; //if (_lastCTTime != null) //{ // CT = (float)(dt - _lastCTTime.Value).TotalSeconds; //} //_lastCTTime = dt; } else { Qty_NG++; } Qty_OEE++; } //DateTime? _lastCTTime = null; private void UpdateCT(float ctTime) { //if (_lastCTTime != null) //{ // CT = (float)(dt - _lastCTTime.Value).TotalSeconds; //} //_lastCTTime = dt; CT = ctTime; } private void RefreshOEE() { Calculation(null); } 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"); } } } } } public override void Cleanup() { SaveNumRecord(); base.Cleanup(); } } public enum RunState { Stop, Running, Idle, Down, } }