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(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 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(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}"); } } } }