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 System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Forms;
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;
}
this.FormClosing += (s, e) =>
{
_checkIdleTimer?.Change(-1, -1);
_checkIdleTimer?.Dispose();
_checkIdleTimer = null;
_idleTimer?.Change(-1, -1);
_idleTimer?.Dispose();
_idleTimer = null;
_downTimer?.Change(-1, -1);
_downTimer?.Dispose();
_downTimer = null;
_availableTimer?.Change(-1, -1);
_availableTimer?.Dispose();
_availableTimer = null;
};
}
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;
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++;
Task.Run(() =>
{
SaveNumRecord(Qty_OK, Qty_NG);
});
}
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();
[MethodImpl(MethodImplOptions.Synchronized)]
private void SaveNumRecord(int okNum, int ngNum)
{
lock (numLock)
{
try
{
//using (StreamWriter writer = new StreamWriter(numRecordFile, false, System.Text.Encoding.UTF8))
//{
// if (okNum % 5 == 0)
// {
// Application.Exit();
// throw new AccessViolationException();
// }
// string dataStr = JsonConvert.SerializeObject(new StatisticModel() { Qty_OK = okNum, Qty_NG = ngNum });
// if (!string.IsNullOrWhiteSpace(dataStr))
// {
// writer.Write(dataStr);
// writer.Flush();
// }
//}
using (FileStream fs = new FileStream(numRecordFile, FileMode.OpenOrCreate, FileAccess.Write))
{
fs.Seek(0, SeekOrigin.Begin);
string dataStr = JsonConvert.SerializeObject(new StatisticModel() { Qty_OK = okNum, Qty_NG = ngNum });
byte[] dataBuf = System.Text.Encoding.UTF8.GetBytes(dataStr);
fs.Write(dataBuf, 0, dataBuf.Length);
fs.SetLength(dataBuf.Length);
fs.Flush();
fs.Close();
}
}
catch (Exception ex)
{
LogAsync(DateTime.Now, $"保存统计数据异常,{ex.GetExceptionMessage()}");
}
}
}
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");
//}
StatisticModel model = JsonConvert.DeserializeObject(dataStr);
if (model != null)
{
Qty_OK = model.Qty_OK;
Qty_NG = model.Qty_NG;
}
else
{
//LogAsync(DateTime.Now, $"载入统计数据错误", "");
MessageBox.Show($"载入统计数据错误");
}
}
}
}
else
{
//LogAsync(DateTime.Now, $"统计数据文件不存在", "");
MessageBox.Show($"统计数据文件不存在");
}
}
private void btnClearStatistic_Click(object sender, EventArgs e)
{
//Qty_OK = Qty_NG = 0;
}
private void tsmiClearQty_Click(object sender, EventArgs e)
{
Qty_OK = Qty_NG = 0;
}
}
public class StatisticModel
{
public int Qty_OK { get; set; }
public int Qty_NG { get; set; }
}
}