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();
|
}
|
|
/// <summary>
|
/// 每分钟计算OEE,平均UPH
|
/// </summary>
|
/// <param name="state"></param>
|
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<JObject>(dataStr);
|
|
if (data != null)
|
{
|
Qty_OK = data.Value<int>("Qty_OK");
|
Qty_NG = data.Value<int>("Qty_NG");
|
}
|
}
|
}
|
}
|
}
|
|
public override void Cleanup()
|
{
|
SaveNumRecord();
|
base.Cleanup();
|
}
|
}
|
|
public enum RunState
|
{
|
Stop,
|
Running,
|
Idle,
|
Down,
|
}
|
}
|