687 lines
21 KiB
C#
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
|
|
|
|
}
|
|
|
|
}
|