using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.ComponentModel; using System.Windows.Forms; using System.Drawing; using System.Drawing.Design; using System.Reflection; using System.Runtime; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace arDev { public partial class MasterK : IDisposable { #region "Variable" public bool bRunMonitor; //쓰레드작동시그널 public Boolean bAIMonitor=false; //배경쓰레드에서 아날로그값을 모니터링 합니다.\ public Boolean bDIOMonitor=true; //배경쓰레드에서 dIO를 모니터링 할것인가? public string errorMessage = string.Empty; System.Diagnostics.Stopwatch aiMonWatch = new System.Diagnostics.Stopwatch(); public long LoopDelay; protected byte wSTX = 0x05; protected byte wETX = 0x04; protected byte rSTX = 0x06; protected byte rETX = 0x03; protected int interchar_delay; protected Boolean bSyncRecvOK = false;// 메세지를 수신할 경우 설정됩니다. protected Boolean _isInit = false;// 보드가 초기화가 되었는지 확인합니다. public Boolean disposed = false;// Dipose 되었는가? protected byte _CommandCode = 0x00;// recv command code protected Boolean _bDlc = false; protected int _dlc = 0; protected Boolean _bStx = false; //stx를 찾앗는지? protected Boolean _bEtx = false; //etx를 찾앗는지? protected Boolean[] _valueI; //입력접점 protected Boolean[] _valueO; //출력접점 protected Boolean[] OldvalueI; protected Boolean[] OldvalueO; protected Thread thMonitor; //배경쓰레드(상태체크 및 이벤트 발생) protected ManualResetEvent _mre; protected string _mreowner = string.Empty; protected System.Diagnostics.Stopwatch wat = new System.Diagnostics.Stopwatch(); protected System.IO.Ports.SerialPort _device; public int DOCount { get; protected set; } public int DICount { get; protected set; } public double ThreadSpeed = 0; public sRecvMessage LastRecvMessage; public sSendMessage lastSendMessage; public DateTime lastSendTime; public byte[] lastSendBuffer; /// /// device index /// public byte IDX = 0; #endregion public MasterK() { //serial port _device = new System.IO.Ports.SerialPort(); _device.BaudRate = 9600; _device.DtrEnable = false; _device.WriteTimeout = 2000; _device.ReadTimeout = 2000; _device.DiscardNull = true; //autoreset events _mre = new System.Threading.ManualResetEvent(true); bRunMonitor = false; LoopDelay = 150; //모든ls산전은 논리적으로 64개가 지정되어있다. this.DICount = 64; this.DOCount = 64; //변수초기화 this._valueI = new bool[DICount]; this._valueO = new bool[DOCount]; this.OldvalueI = new bool[_valueI.Length]; this.OldvalueO = new bool[_valueO.Length]; //초기값설정(인풋) for (int i = 0; i < _valueI.Length; i++) { this._valueI[i] = false; this.OldvalueI[i] = false; } //초기값설정(아웃풋) for (int i = 0; i < _valueO.Length; i++) { this._valueO[i] = false; this.OldvalueO[i] = false; } } ~MasterK() { Dispose(false); } public void Close() { if (_device.IsOpen) { if (_mre.WaitOne(3000)) { _mre.Reset(); _mreowner = "Close"; _device.Close(); _mre.Set(); } else { RaiseMessage(true, "close error timeout"); } } } /// /// 개체를 해제합니다. /// [Description("")] public virtual void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // Protected implementation of Dispose pattern. protected virtual void Dispose(bool disposing) { if (disposed) return; if (disposing) { // Dispose managed resources. } StopMonitor(); // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. // Note disposing has been done. disposed = true; } public string PortName { get { return _device.PortName; } set { _device.PortName = value; } } public Boolean Init(string port,int baud = 19200) { Close(); errorMessage = string.Empty; _device.PortName = port; _device.BaudRate = baud; try { _device.Open(); _device.DiscardInBuffer(); _device.DiscardOutBuffer(); _isInit = true; sw_ch = new System.Diagnostics.Stopwatch(); interchar_delay = GetIntercharDelay(); interframe_delay = GetInterframeDelay(); } catch (Exception ex) { _isInit = false; errorMessage = ex.Message; RaiseMessage(true, ex.Message); } return _isInit; } #region "Properties" /// /// 입력핀 상태값을 확인합니다. /// /// 0부터 시작하는 인덱스값 /// [Description("")] public Boolean INPUT(int arridx) { if (arridx < 0 || arridx > DICount - 1) { if (IOErrorMessage != null) IOErrorMessage(this, new MessageEventArgs(true, "INPUT Command : ArrayIndex Error Value=" + arridx.ToString())); return false; } return this._valueI[arridx]; } /// /// 입력핀 상태를 모두 반환합니다. /// [Description("")] public Boolean[] getInputArray { get { return (Boolean[])this._valueI.Clone(); } } public byte getInputValueToByte(int startindex) { System.Collections.BitArray ba = new System.Collections.BitArray(this._valueI); byte[] value = new byte[8]; ba.CopyTo(value, startindex); return value[0]; } public Int64 getInputValue { get { System.Collections.BitArray ba = new System.Collections.BitArray(this._valueI); byte[] value = new byte[8]; ba.CopyTo(value, 0); return BitConverter.ToInt64(value, 0); } } /// /// 출력핀 상태를 모두 반환합니다. /// [Description("")] public Boolean[] getOutputArray { get { return (Boolean[])this._valueO.Clone(); } } /// /// 출력핀 상태 값을 확인합니다. /// /// 0부터 시작하는 인덱스값 /// [Description("")] public Boolean OUTPUT(int arridx) { // get { return (Boolean[])this._valueO.Clone(); } if (arridx < 0 || arridx > DOCount - 1) { if (IOErrorMessage != null) IOErrorMessage(this, new MessageEventArgs(true, "OUTPUT Command : ArrayIndex Error Value=" + arridx.ToString())); return false; } return this._valueO[arridx]; } /// /// read digital input port state /// /// [Description("")] public string GetInputState() { System.Text.StringBuilder sb = new StringBuilder(); for (int i = 0; i < OldvalueI.Length; i++) sb.Append(OldvalueI[i] ? "1" : "0"); return sb.ToString(); } /// /// read digital output port state /// /// [Description("")] public string GetOutputState() { System.Text.StringBuilder sb = new StringBuilder(); for (int i = 0; i < OldvalueO.Length; i++) sb.Append(OldvalueO[i] ? "1" : "0"); return sb.ToString(); } #endregion #region "Method" /// /// 오류메세지 반환 /// /// [Description("")] public string GetErrorMessage() { return this.errorMessage; } #region "raise Message" /// /// 일반메세지륿 라생시킵니다. /// /// /// [Description("")] public void RaiseMessage(Boolean isError, string message) { if (isError) errorMessage = message; //170920 if (Message != null) Message(this, new MessageEventArgs(isError, message)); } /// /// 지정한 데이터로 바코드가 수신된것처럼 발생시킵니다.(Parser를 사용함) /// /// [Description("")] public void RaiseDataMessage(eMsgType dir, byte[] Data) { try { if (dir == eMsgType.Rx) Parse_RecvBuffer(Data); if (DataMessage != null) DataMessage(this, new DataEventArgs(dir,Data)); } catch (Exception ex) { errorMessage = ex.Message; RaiseMessage(true, "RaiseDataMessage:" + ex.Message); } } #endregion /// /// Input / Output 핀 상태를 지속적으로 읽는 쓰레드작업을 시작합니다. /// [Description("")] public void RunMonitor(int loopDelay_ = 150) { this.LoopDelay = loopDelay_; if(!this.bRunMonitor) { this.bRunMonitor = true; this.thMonitor = new Thread(new ThreadStart(this.bwMonitor)); this.thMonitor.IsBackground = true; this.thMonitor.Start(); } } /// /// INPUT/OUPUT 핀 상태를 모니터링하는 쓰레드를 종료시킵니다. /// /// [Description("")] public bool StopMonitor() { try { this.bRunMonitor = false; RaiseMessage(false, "Stop Monitor"); return true; } catch (Exception ex) { RaiseMessage(true, ex.Message); return false; } } /// /// 지정한 출력포트의 값을 반전 시킵니다. /// /// /// [Description("")] public Boolean SetToggle(int arrIndex) { if (!IsInit) return false; if (arrIndex < 0 || (arrIndex + 1) > DOCount) throw new Exception(string.Format("IDX Range Errror({0}~{1})", 0, DOCount-1)); Boolean currentValue = OUTPUT(arrIndex); return SetOutput(arrIndex, !currentValue); } /// /// 출력을 설정합니다. /// /// /// /// /// [Description("")] public Boolean SetOutput(int arrIndex, Boolean Value, int Duration = 0) { errorMessage = string.Empty; if (!IsInit) { //RaiseMessage(true, "SetOutput:No Init"); return false; } //RaiseMessage(false, "Set Output Index=" + arrIndex.ToString() + ",Value=" + Value.ToString()); if (arrIndex < 0 || (arrIndex + 1) > DOCount) { RaiseMessage(true, "SetOutput:ArrIndex Error = " + arrIndex.ToString()); return false; } if (!_mre.WaitOne(1000)) { RaiseMessage(true, "SetOutput Wait on timeout"); return false; } _mre.Reset(); if (Duration < 0) Duration = 0; int Port = 0x40 + arrIndex; //출력포트는 64번부터 시작한다. string cmd = "00WSS0107%PX00" + Port.ToString("X2") + (Value ? "01":"00"); //해당라인에 그값을 쓴다. Boolean bRet = true; try { var buffer = WriteSync(cmd); string strBuffer = System.Text.Encoding.Default.GetString(buffer); if (buffer == null) { RaiseMessage(true, "SetOutput:No Response"); bRet= false; } else { if(buffer[0] == 0x06) { if (Duration != 0) { var task = new Task(() => { Thread.Sleep(Duration); string cmd2 = "00WSS0107%PX00" + Port.ToString("X2") + (Value ? "00" : "01"); var buffer2 = WriteSync(cmd2); if (buffer2 == null) { RaiseMessage(true, "SetOutput:Duration toggle Error:No Reponse"); } }); task.Start(); //비동기로 실행한다. } } else { RaiseMessage(true, "SetOutput:Nak response"); bRet = false; } } } catch (Exception ex) { RaiseMessage(true, "SetOutput:"+ex.Message); bRet = false; } _mre.Set(); return bRet; } /// /// 이벤트를 강제 생성합니다. /// /// /// /// [Description("")] public void RaiseEvent(eIOPINDIR Direction, int ArrIDX, Boolean newvalue) { if (IOValueChanged != null) { //mre.WaitOne(); //mre.Reset(); //171226 if (Direction == eIOPINDIR.INPUT) { _valueI[ArrIDX] = newvalue; OldvalueI[ArrIDX] = newvalue; } else { _valueO[ArrIDX] = newvalue; OldvalueO[ArrIDX] = newvalue; } try { if (IOValueChanged != null) IOValueChanged(this, new IOValueEventArgs(Direction, ArrIDX, !newvalue, newvalue)); } catch (Exception ex) { if (IOErrorMessage != null) this.IOErrorMessage(this, new MessageEventArgs(true , string.Format( "{0},{1}:{2}:{3}",ex.Message,Direction,ArrIDX,newvalue))); } //mre.Set(); } } public virtual void UserMonitorCode() { } private byte loopIndex = 0; /// /// I/O상태를 모니터링 하여 내부 변수에 값을 저장합니다. /// [Description("")] protected void bwMonitor() { DateTime lastaitime = DateTime.Now; DateTime lastmottime = DateTime.Now; System.Diagnostics.Stopwatch wat = new System.Diagnostics.Stopwatch(); while (this.bRunMonitor) { wat.Restart(); //170905 if (!IsInit) { aiMonWatch.Stop(); System.Threading.Thread.Sleep(1000); continue; } else { if (!aiMonWatch.IsRunning) aiMonWatch.Reset(); } //read dio status if (loopIndex == 0) { //RaiseMessage(false,"Run IOMonitor"); if (bDIOMonitor) Read_RelayPort(); loopIndex += 1; } else { //RaiseMessage(false, "Run AIMonitor"); UserMonitorCode(); loopIndex = 0; } wat.Stop(); ThreadSpeed = wat.ElapsedMilliseconds; if(wat.ElapsedMilliseconds < LoopDelay) { Thread.Sleep((int)(LoopDelay- wat.ElapsedMilliseconds)); } } wat.Stop(); } #endregion #region "Send Method" //public Boolean Read_AIPort() //{ // if (!_mre.WaitOne(1000)) // { // RaiseMessage(true, "Read_AIPort Wait on timeout"); // return false; // } // //RaiseMessage(false, "Read_AIPort"); // _mre.Reset(); // System.Threading.Thread.Sleep(25); // _mre.Set(); // return true; //} public Boolean isValid { get { var ts = DateTime.Now - lastrecvTime; if (ts.TotalSeconds > 5) return false; else return true; } } private DateTime lastrecvTime = DateTime.Now; public Boolean Read_RelayPort() { if(!_mre.WaitOne(1000)) { RaiseMessage(true, "Read_RelayPort Wait on timeout"); return false; } // RaiseMessage(false, "Read_RelayPort"); _mre.Reset(); var packet = MakePacket(0, 'R', "SB","%PW000", 8); //"00RSB06%PW00008" var buffer = WriteSync(packet); if(buffer != null) { var frame = Parse_RecvBuffer(buffer); if(!frame.isError) { this.Run_RelayPortData(ref frame); lastrecvTime = DateTime.Now; } LastRecvMessage = frame; } _mre.Set(); return buffer != null; } /// /// /// /// 0~9 /// R,W /// SB,SW ?? /// %PW000 /// 1 /// HexString /// public string MakePacket(byte Station,char Command,string CmdType,string Device,byte NumberofData,string data = "") { string buffer = Station.ToString("X2").PadLeft(2, '0') + Command + CmdType + Device.Length.ToString("X2").PadLeft(2, '0') + Device + NumberofData.ToString("X2").PadLeft(2, '0'); if (data != "") buffer += data; return buffer; } #endregion } }