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,
}
}