Files
vms2016_kadisp/Sub/arMasterK/Project/MasterK.cs

687 lines
21 KiB
C#

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;
/// <summary>
/// device index
/// </summary>
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");
}
}
}
/// <summary>
/// 개체를 해제합니다.
/// </summary>
[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"
/// <summary>
/// 입력핀 상태값을 확인합니다.
/// </summary>
/// <param name="arridx">0부터 시작하는 인덱스값</param>
/// <returns></returns>
[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];
}
/// <summary>
/// 입력핀 상태를 모두 반환합니다.
/// </summary>
[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);
}
}
/// <summary>
/// 출력핀 상태를 모두 반환합니다.
/// </summary>
[Description("")]
public Boolean[] getOutputArray
{
get
{
return (Boolean[])this._valueO.Clone();
}
}
/// <summary>
/// 출력핀 상태 값을 확인합니다.
/// </summary>
/// <param name="arridx">0부터 시작하는 인덱스값</param>
/// <returns></returns>
[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];
}
/// <summary>
/// read digital input port state
/// </summary>
/// <returns></returns>
[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();
}
/// <summary>
/// read digital output port state
/// </summary>
/// <returns></returns>
[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"
/// <summary>
/// 오류메세지 반환
/// </summary>
/// <returns></returns>
[Description("")]
public string GetErrorMessage()
{
return this.errorMessage;
}
#region "raise Message"
/// <summary>
/// 일반메세지륿 라생시킵니다.
/// </summary>
/// <param name="isError"></param>
/// <param name="message"></param>
[Description("")]
public void RaiseMessage(Boolean isError, string message)
{
if (isError) errorMessage = message; //170920
if (Message != null) Message(this, new MessageEventArgs(isError, message));
}
/// <summary>
/// 지정한 데이터로 바코드가 수신된것처럼 발생시킵니다.(Parser를 사용함)
/// </summary>
/// <param name="b"></param>
[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
/// <summary>
/// Input / Output 핀 상태를 지속적으로 읽는 쓰레드작업을 시작합니다.
/// </summary>
[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();
}
}
/// <summary>
/// INPUT/OUPUT 핀 상태를 모니터링하는 쓰레드를 종료시킵니다.
/// </summary>
/// <returns></returns>
[Description("")]
public bool StopMonitor()
{
try
{
this.bRunMonitor = false;
RaiseMessage(false, "Stop Monitor");
return true;
}
catch (Exception ex)
{
RaiseMessage(true, ex.Message);
return false;
}
}
/// <summary>
/// 지정한 출력포트의 값을 반전 시킵니다.
/// </summary>
/// <param name="arrIndex"></param>
/// <returns></returns>
[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);
}
/// <summary>
/// 출력을 설정합니다.
/// </summary>
/// <param name="arrIndex"></param>
/// <param name="Value"></param>
/// <param name="Duration"></param>
/// <returns></returns>
[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;
}
/// <summary>
/// 이벤트를 강제 생성합니다.
/// </summary>
/// <param name="Direction"></param>
/// <param name="ArrIDX"></param>
/// <param name="newvalue"></param>
[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;
/// <summary>
/// I/O상태를 모니터링 하여 내부 변수에 값을 저장합니다.
/// </summary>
[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;
}
/// <summary>
///
/// </summary>
/// <param name="Station">0~9</param>
/// <param name="Command">R,W</param>
/// <param name="CmdType">SB,SW ??</param>
/// <param name="Device">%PW000</param>
/// <param name="NumberofData">1</param>
/// <param name="data">HexString</param>
/// <returns></returns>
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
}
}