334 lines
14 KiB
C#
334 lines
14 KiB
C#
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.View;
|
|
using MathNet.Numerics.Statistics;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Windows.Input;
|
|
using System.Windows.Threading;
|
|
|
|
namespace AuroraPLC.Views
|
|
{
|
|
public class UCPLCDeviceViewModel : NotifyPropertyChangedBase
|
|
{
|
|
private static readonly Logger Log = new Logger("PLC Device View");
|
|
|
|
private int _numBitInput;
|
|
public int NumBitInput { get => _numBitInput; set => SetProperty(ref _numBitInput, value); }
|
|
private int _numBitOutput;
|
|
public int NumBitOutput { get => _numBitOutput; set => SetProperty(ref _numBitOutput, value); }
|
|
|
|
private int _numMBitInput;
|
|
public int NumMBitInput { get => _numMBitInput; set => SetProperty(ref _numMBitInput, value); }
|
|
private int _numMBitOutput;
|
|
public int NumMBitOutput { get => _numMBitOutput; set => SetProperty(ref _numMBitOutput, value); }
|
|
|
|
private int _numWordInput;
|
|
public int NumWordInput { get => _numWordInput; set => SetProperty(ref _numWordInput, value); }
|
|
private int _numWordOutput;
|
|
public int NumWordOutput { get => _numWordOutput; set => SetProperty(ref _numWordOutput, value); }
|
|
|
|
private int _numDWordInput;
|
|
public int NumDWordInput { get => _numDWordInput; set => SetProperty(ref _numDWordInput, value); }
|
|
private int _numDWordOutput;
|
|
public int NumDWordOutput { get => _numDWordOutput; set => SetProperty(ref _numDWordOutput, value); }
|
|
|
|
private double _updateRate;
|
|
public double UpdateRate { get => _updateRate; set => SetProperty(ref _updateRate, value); }
|
|
|
|
private string _networkType;
|
|
public string NetworkType { get => _networkType; set => SetProperty(ref _networkType, value); }
|
|
|
|
private string _operationMode;
|
|
public string OperationMode { get => _operationMode; set => SetProperty(ref _operationMode, value); }
|
|
|
|
private string _elapsedTime;
|
|
public string ElapsedTime { get => _elapsedTime; set => SetProperty(ref _elapsedTime, value); }
|
|
TimeSpan tsElaspedTime;
|
|
|
|
|
|
private int _numReadBitFail;
|
|
public int NumReadBitFail { get => _numReadBitFail; set => SetProperty(ref _numReadBitFail, value); }
|
|
private int _numWriteBitFail;
|
|
public int NumWriteBitFail { get => _numWriteBitFail; set => SetProperty(ref _numWriteBitFail, value); }
|
|
private int _numReadWordFail;
|
|
public int NumReadWordFail { get => _numReadWordFail; set => SetProperty(ref _numReadWordFail, value); }
|
|
private int _numWriteWordFail;
|
|
public int NumWriteWordFail { get => _numWriteWordFail; set => SetProperty(ref _numWriteWordFail, value); }
|
|
|
|
private int _numReconnection;
|
|
public int NumReconnection { get => _numReconnection; set => SetProperty(ref _numReconnection, value); }
|
|
|
|
|
|
// PLC 통신 통계
|
|
|
|
private int _numOfMaxRateSample = 100;
|
|
public int NumOfMaxRateSample { get => _numOfMaxRateSample;
|
|
set {
|
|
if (value != _numOfMaxRateSample) {
|
|
if (value < 10) { // 최소 개수: 10
|
|
value = 10;
|
|
}
|
|
|
|
// 큐 새로 생성..
|
|
RateQueueStatus = rateDataCollecting;
|
|
MonitorTimer.Stop();
|
|
// csv 저장 불가
|
|
IsSaveAsCSVVisible = false;
|
|
rateHzBuffer = new Queue<double>(value);
|
|
MonitorTimer.Start();
|
|
|
|
SetProperty(ref _numOfMaxRateSample, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private double _MeanRateHz;
|
|
public double MeanRateHz { get => _MeanRateHz; set => SetProperty(ref _MeanRateHz, value); }
|
|
|
|
private double _MinRateHz;
|
|
public double MinRateHz { get => _MinRateHz; set => SetProperty(ref _MinRateHz, value); }
|
|
|
|
private double _StdRateHz;
|
|
public double StdRateHz { get => _StdRateHz; set => SetProperty(ref _StdRateHz, value); }
|
|
|
|
private string _RateQueueStatus;
|
|
public string RateQueueStatus { get => _RateQueueStatus; set => SetProperty(ref _RateQueueStatus, value); }
|
|
|
|
const string rateDataCollecting = "Data collecting...";
|
|
const string rateDataReady = "Data is ready";
|
|
|
|
|
|
private bool _isStatPauseChecked = true;
|
|
public bool IsStatPauseChecked { get => _isStatPauseChecked; set => SetProperty(ref _isStatPauseChecked, value); }
|
|
|
|
private Queue<double> rateHzBuffer;
|
|
|
|
private bool _isSaveAsCSVVisible = false;
|
|
public bool IsSaveAsCSVVisible { get => _isSaveAsCSVVisible; set => SetProperty(ref _isSaveAsCSVVisible, value); }
|
|
|
|
public ICommand CMD_SaveAsCSV { get; }
|
|
public ICommand CMD_VisibleChanged { get; }
|
|
|
|
|
|
//private IDeviceEntry DeviceManager;
|
|
private readonly PLCBase _plcBase;
|
|
|
|
private readonly DispatcherTimer MonitorTimer;
|
|
|
|
const double monitorTimerMS = 50.0; // 50 [ms] -> 20 [Hz]
|
|
|
|
#region 생성자
|
|
public UCPLCDeviceViewModel(IAuroraEnvironment aurora, PLCBase plc)
|
|
{
|
|
//DeviceManager = aurora.DeviceManager;
|
|
|
|
CMD_VisibleChanged = new DelegateCommand(VisibleChanged);
|
|
CMD_SaveAsCSV = new DelegateCommand(SaveAsCSV);
|
|
|
|
_plcBase = plc;
|
|
|
|
// 초기화...
|
|
NetworkType = _plcBase.NetworkType.ToString();
|
|
// 운전 모드
|
|
OperationMode = _plcBase.IsSimul ? "Simul" : "Real";
|
|
// 연, 일, 시, 분, 초, 밀리초
|
|
ElapsedTime = String.Format("00Y,000D,00:00:00.00 [s]");
|
|
|
|
NumBitInput = _plcBase.BitInputSize;
|
|
NumBitOutput = _plcBase.BitOutputSize;
|
|
NumMBitInput = _plcBase.MBitInputSize;
|
|
NumMBitOutput = _plcBase.MBitOutputSize;
|
|
|
|
NumWordInput = _plcBase.WordInputWordSize;
|
|
NumWordOutput = _plcBase.WordOutputWordSize;
|
|
NumDWordInput = _plcBase.DWordInputWordSize;
|
|
NumDWordOutput = _plcBase.DWordOutputWordSize;
|
|
|
|
// 통계 기능은 기본적으로는 off 상태
|
|
IsStatPauseChecked = true;
|
|
rateHzBuffer = new Queue<double>(NumOfMaxRateSample);
|
|
RateQueueStatus = rateDataCollecting;
|
|
|
|
// csv 저장 불가
|
|
IsSaveAsCSVVisible = false;
|
|
|
|
MonitorTimer = new DispatcherTimer();
|
|
MonitorTimer.Tick += OnMonitoring;
|
|
MonitorTimer.Interval = TimeSpan.FromMilliseconds(monitorTimerMS); // 50 [ms] => 20 [Hz]
|
|
}
|
|
#endregion
|
|
|
|
private void VisibleChanged(object obj) // 화면 전환 시 동작하는 곳
|
|
{
|
|
bool visible = (bool)obj;
|
|
if (visible) {
|
|
// 초기화...
|
|
NumBitInput = _plcBase.BitInputSize;
|
|
NumBitOutput = _plcBase.BitOutputSize;
|
|
NumMBitInput = _plcBase.MBitInputSize;
|
|
NumMBitOutput = _plcBase.MBitOutputSize;
|
|
|
|
NumWordInput = _plcBase.WordInputWordSize;
|
|
NumWordOutput = _plcBase.WordOutputWordSize;
|
|
NumDWordInput = _plcBase.DWordInputWordSize;
|
|
NumDWordOutput = _plcBase.DWordOutputWordSize;
|
|
|
|
MonitorTimer.Start();
|
|
} else {
|
|
MonitorTimer.Stop();
|
|
|
|
// csv 저장 불가
|
|
IsSaveAsCSVVisible = false;
|
|
|
|
// 통계 기능 Off
|
|
IsStatPauseChecked = true;
|
|
rateHzBuffer.Clear();
|
|
RateQueueStatus = rateDataCollecting;
|
|
}
|
|
}
|
|
|
|
int elapsedYear;
|
|
int elapsedDay;
|
|
StringBuilder sbElaspedTime = new StringBuilder(30);
|
|
private void OnMonitoring(object sender, EventArgs e)
|
|
{
|
|
if (!_plcBase.IsActivated) {
|
|
return;
|
|
}
|
|
|
|
NumReadBitFail = (int)_plcBase.PLCUpdatorInfo.NumberOfRBFail;
|
|
NumWriteBitFail = (int)_plcBase.PLCUpdatorInfo.NumberOfWBFail;
|
|
NumReadWordFail = (int)_plcBase.PLCUpdatorInfo.NumberOfRWFail;
|
|
NumWriteWordFail = (int)_plcBase.PLCUpdatorInfo.NumberOfWWFail;
|
|
|
|
NumReconnection = (int)_plcBase.PLCUpdatorInfo.NumberOfReconnection;
|
|
|
|
|
|
tsElaspedTime = _plcBase._dataUpdateTimer.TotalRunningTime;
|
|
// year 와 day 를 분리하자..
|
|
elapsedYear = tsElaspedTime.Days / 365;
|
|
elapsedDay = tsElaspedTime.Days - (elapsedYear * 365);
|
|
sbElaspedTime.Clear();
|
|
if (elapsedYear != 0) {
|
|
sbElaspedTime.AppendFormat($"{elapsedYear,2:##}Y,{elapsedDay,3:###}D,{tsElaspedTime.Hours:00}:{tsElaspedTime.Minutes:00}:{tsElaspedTime.Seconds:00}.{tsElaspedTime.Milliseconds:00} [s]");
|
|
//ElapsedTime = String.Format("{0,2:##}Y,{1,3:###}D,{2:00}:{3:00}:{4:00}.{5:00} [s]",
|
|
// elapsedYear, elapsedDay, tsElaspedTime.Hours, tsElaspedTime.Minutes, tsElaspedTime.Seconds, tsElaspedTime.Milliseconds);
|
|
} else if (elapsedDay != 0) {
|
|
sbElaspedTime.AppendFormat($"{elapsedDay,3:###}D,{tsElaspedTime.Hours:00}:{tsElaspedTime.Minutes:00}:{tsElaspedTime.Seconds:00}.{tsElaspedTime.Milliseconds:00} [s]");
|
|
//ElapsedTime = String.Format("{0,3:###}D,{1:00}:{2:00}:{3:00}.{4:00} [s]",
|
|
// elapsedDay, tsElaspedTime.Hours, tsElaspedTime.Minutes, tsElaspedTime.Seconds, tsElaspedTime.Milliseconds);
|
|
} else {
|
|
sbElaspedTime.AppendFormat($"{tsElaspedTime.Hours:00}:{tsElaspedTime.Minutes:00}:{tsElaspedTime.Seconds:00}.{tsElaspedTime.Milliseconds:00} [s]");
|
|
//ElapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00} [s]",
|
|
// tsElaspedTime.Hours, tsElaspedTime.Minutes, tsElaspedTime.Seconds, tsElaspedTime.Milliseconds);
|
|
}
|
|
ElapsedTime = sbElaspedTime.ToString();
|
|
|
|
UpdateRate = (1000.0 / _plcBase._dataUpdateTimer.ActualUpdateRateMS); // [Hz]
|
|
|
|
// PLC 통신 속도 통계
|
|
if (IsStatPauseChecked == false) { // 통계 동작 중...
|
|
if (rateHzBuffer.Count < NumOfMaxRateSample) {
|
|
rateHzBuffer.Enqueue(UpdateRate); // 추가
|
|
|
|
RateQueueStatus = $"{rateDataCollecting} ({rateHzBuffer.Count})";
|
|
if (rateHzBuffer.Count == NumOfMaxRateSample) {
|
|
RateQueueStatus = rateDataReady;
|
|
|
|
// csv 저장 가능
|
|
IsSaveAsCSVVisible = true;
|
|
}
|
|
} else {
|
|
// 통계 계산...
|
|
MeanRateHz = rateHzBuffer.Sum() / rateHzBuffer.Count;
|
|
MinRateHz = rateHzBuffer.Min();
|
|
StdRateHz = Statistics.StandardDeviation(rateHzBuffer);
|
|
|
|
// 가장 오래된 것을 제거하고, 새로운 값 추가
|
|
rateHzBuffer.Dequeue();
|
|
rateHzBuffer.Enqueue(UpdateRate);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
// 개발용: PLC method 테스트 : 커밋 반영시에는 주석 처리 필요
|
|
bool needTest = false;
|
|
if (needTest) {
|
|
// Test1: WriteBitBlock by bool array
|
|
//bool[] bit_t1 = { true, true, true };
|
|
//_plcBase.WriteBitBlock(0x30, bit_t1.Length, bit_t1);
|
|
|
|
// Test2: WriteBitBlock by short value
|
|
short bit_short = 0x00FF;
|
|
int bit_size = 5;
|
|
_plcBase.WriteBitBlock(0x30, bit_size, bit_short);
|
|
|
|
// Test3: ReadBitBlock
|
|
int bit_t3_size = 3;
|
|
var bit_t3 = _plcBase.ReadBitBlock(0x30, bit_t3_size);
|
|
|
|
// Test4: ReadBitBlockArr
|
|
var bit_t4 = _plcBase.ReadBitBlockArr(0x30, bit_t3_size);
|
|
|
|
//var test = 100;
|
|
|
|
|
|
//// Test1 : ReadDWord
|
|
//int addrOffset = 0x40;
|
|
//var temp = _plc.ReadDWord(addrOffset, 10000);
|
|
//float tempFloat = (float)temp;
|
|
|
|
////var test1 = 1;
|
|
|
|
//// Test2 : ReadDWord
|
|
//var temp1 = _plc.ReadDWord(addrOffset);
|
|
|
|
//// Test3 : WriteWord
|
|
//short w1 = 100;
|
|
//addrOffset = 0;
|
|
//_plc.WriteWord(addrOffset, w1);
|
|
|
|
//// Test4: WriteDWord
|
|
//var t1 = 1.123;
|
|
//var t2 = 12.345;
|
|
//var t3 = 123.246;
|
|
//_plc.WriteLaserMoveData(t1, t2, t3);
|
|
}
|
|
*/
|
|
//============================
|
|
}
|
|
|
|
int _fileIndex = 0;
|
|
private void SaveAsCSV(object obj)
|
|
{
|
|
// 데이터 저장 정지 !!!
|
|
IsStatPauseChecked = true;
|
|
// 모니터 타이머에서 rate 데이터 갱신이 안되도록 시간 지연 추가
|
|
Thread.Sleep((int)(2.0 * monitorTimerMS));
|
|
|
|
var recipePath = ConfigurationManager.AppSettings["DATA_PATH"];
|
|
string strFilePath = string.Format($"{recipePath}\\External\\PLC\\PLCNetRateData_{_fileIndex++}.csv");
|
|
if (File.Exists(strFilePath)) File.Delete(strFilePath);
|
|
|
|
string strSave = "Net Rate[Hz]";
|
|
foreach (var item in rateHzBuffer) {
|
|
strSave = string.Format("{0}\n{1}", strSave, item.ToString("F3"));
|
|
}
|
|
try { // 저장 경로가 무효할 때의 예외 처리용
|
|
File.AppendAllText(strFilePath, strSave);
|
|
|
|
MessageUtil.ShowSaveSuccessDialog();
|
|
Log.V("Save as CSV is success");
|
|
} catch (Exception ex) {
|
|
Log.E($"Save is failed: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
} |