This commit is contained in:
backuppc
2025-12-17 14:54:33 +09:00
parent eb0e08d290
commit 1f37871336
22 changed files with 1218 additions and 884 deletions

View File

@@ -12,7 +12,7 @@ using AR;
namespace arDev
{
public partial class Narumi : arRS232
public partial class Narumi
{
public bool AGVMoveSet(BunkiData opt)
@@ -236,12 +236,12 @@ namespace arDev
case eAgvCmd.ChargeOf:
cmdString = $"CBT{param}O0003"; ///0003=충전대기시간
retval = AddCommand(cmdString);
RaiseMessage(arRS232.MessageType.Normal, "충전취소전송");
RaiseMessage(NarumiSerialComm.MessageType.Normal, "충전취소전송");
break;
case eAgvCmd.ChargeOn:
cmdString = $"CBT{param}I0003"; ///0003=충전대기시간
retval = AddCommand(cmdString);
RaiseMessage(arRS232.MessageType.Normal, "충전명령전송");
RaiseMessage(NarumiSerialComm.MessageType.Normal, "충전명령전송");
break;
case eAgvCmd.TurnLeft:

View File

@@ -2,7 +2,7 @@
namespace arDev
{
public partial class Narumi
public partial class Narumi
{
public class DataEventArgs : EventArgs
{

View File

@@ -0,0 +1,69 @@
using System;
using System.Linq;
using AR;
namespace arDev
{
public partial class Narumi
{
public class Dataframe
{
public byte STX { get; private set; }
public byte ETX { get; private set; }
public byte[] Data { get; private set; }
public string DataString { get; private set; }
public byte[] checksum { get; private set; } = new byte[2];
public bool Valid { get; private set; } = false;
public string Message { get; private set; } = string.Empty;
public byte[] Buffer { get; private set; }
public string Cmd { get; private set; } = string.Empty;
public bool Parse(byte[] data, int MinRecvLength = 0)
{
if (data == null || data.Any() == false)
{
this.Message = string.Format("수신 데이터가 없습니다");
return false;
}
else if (data.Length < 5)
{
this.Message = $"데이터의 길이가 5보다 작습니다 길이={data.Length}";
return false;
}
else if (MinRecvLength > 0 && data.Length < MinRecvLength)
{
this.Message = $"데이터의 길이가 {MinRecvLength}보다 작습니다 길이={data.Length}";
return false;
}
else if (data[0] != 0x02 || data[data.Length - 1] != 0x03)
{
this.Message = $"STX/ETX Error";
return false;
}
Buffer = new byte[data.Length];
Array.Copy(data, Buffer, data.Length);
STX = data[0];
ETX = data[data.Length - 1];
Array.Copy(data, data.Length - 3, checksum, 0, 2);
Data = new byte[data.Length - 4];
Array.Copy(data, 1, Data, 0, data.Length - 4);
if (data.Length > 2) Cmd = System.Text.Encoding.Default.GetString(Data, 0, 3);
this.DataString = System.Text.Encoding.Default.GetString(Data);
Valid = true;
return true;
}
public Dataframe(byte[] buffer = null, int minlen = 0)
{
if (buffer != null) Parse(buffer);
}
}
}
}

View File

@@ -47,8 +47,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DataEventArgs.cs" />
<Compile Include="Dataframe.cs" />
<Compile Include="EnumData.cs" />
<Compile Include="Command.cs" />
<Compile Include="NarumiSerialComm.cs" />
<Compile Include="Structure\ErrorFlag.cs" />
<Compile Include="Narumi.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@@ -11,15 +11,13 @@ using AR;
namespace arDev
{
public partial class Narumi : arRS232
public partial class Narumi : arDev.NarumiSerialComm
{
Hashtable SystemCheck, ErrorCheck;
private Queue Errlog; // 에러발생시 코드 임시 저장용(쓰레드 동기화용)
public int nBatteryNo { get; set; } = 0;
public Narumi()
{
SystemCheck = new Hashtable();
@@ -124,75 +122,18 @@ namespace arDev
return bComplete;
}
public class Dataframe
{
public byte STX { get; private set; }
public byte ETX { get; private set; }
public byte[] Data { get; private set; }
public string DataString { get; private set; }
public byte[] checksum { get; private set; } = new byte[2];
public bool Valid { get; private set; } = false;
public string Message { get; private set; } = string.Empty;
public byte[] Buffer { get; private set; }
public string Cmd { get; private set; } = string.Empty;
public bool Parse(byte[] data, int MinRecvLength = 0)
{
if (data == null || data.Any() == false)
{
this.Message = string.Format("수신 데이터가 없습니다");
return false;
}
else if (data.Length < 5)
{
this.Message = $"데이터의 길이가 5보다 작습니다 길이={data.Length}";
return false;
}
else if (MinRecvLength > 0 && data.Length < MinRecvLength)
{
this.Message = $"데이터의 길이가 {MinRecvLength}보다 작습니다 길이={data.Length}";
return false;
}
else if (data[0] != 0x02 || data[data.Length - 1] != 0x03)
{
this.Message = $"STX/ETX Error";
return false;
}
Buffer = new byte[data.Length];
Array.Copy(data, Buffer, data.Length);
STX = data[0];
ETX = data[data.Length - 1];
Array.Copy(data, data.Length - 3, checksum, 0, 2);
Data = new byte[data.Length - 4];
Array.Copy(data, 1, Data, 0, data.Length - 4);
if (data.Length > 2) Cmd = System.Text.Encoding.Default.GetString(Data, 0, 3);
this.DataString = System.Text.Encoding.Default.GetString(Data);
Valid = true;
return true;
}
public Dataframe(byte[] buffer = null, int minlen = 0)
{
if (buffer != null) Parse(buffer);
}
}
public override bool ProcessRecvData(byte[] data)
{
//LastReceiveBuffer
var frame = new Dataframe(data, MinRecvLength);
if (frame.Valid == false)
{
RaiseMessage(arRS232.MessageType.Error, frame.Message);
RaiseMessage(MessageType.Error, frame.Message);
return false;
}
else if (frame.DataString.StartsWith("$") == false && CheckSum(data) == false)
{
RaiseMessage(arRS232.MessageType.Error, "Checksum Error MSG=" + frame.DataString);
RaiseMessage(MessageType.Error, "Checksum Error MSG=" + frame.DataString);
return false;
}
@@ -218,7 +159,7 @@ namespace arDev
// $로 시작되는 AGV 상태 표시
//var text_Sts_Etc = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' '); //20210311 김정만 - SmartX FrameWork 사용 안함으로 주석처리
//var sMessageOther = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' ');
RaiseMessage(arRS232.MessageType.Normal, "$메세지수신:" + frame.DataString);
RaiseMessage(MessageType.Normal, "$메세지수신:" + frame.DataString);
}
else
{
@@ -228,7 +169,7 @@ namespace arDev
}
catch (Exception ex)
{
RaiseMessage(arRS232.MessageType.Error, ex.Message);
RaiseMessage(MessageType.Error, ex.Message);
retval = false;
}
}

View File

@@ -0,0 +1,494 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
namespace arDev
{
public abstract class NarumiSerialComm : ISerialComm, IDisposable
{
protected System.IO.Ports.SerialPort _device;
protected ManualResetEvent _mre;
protected byte[] LastReceiveBuffer = new byte[] { };
/// <summary>
/// 최종 전송 메세지
/// </summary>
public byte[] lastSendBuffer = new byte[] { };
//public int ValidCheckTimeMSec { get; set; } = 5000;
protected List<byte> tempBuffer = new List<byte>();
protected Boolean findSTX = false;
public string ErrorMessage { get; set; }
public DateTime LastConnTime { get; set; }
public DateTime LastConnTryTime { get; set; }
public DateTime lastSendTime;
/// <summary>
/// 메세지 수신시 사용하는 내부버퍼
/// </summary>
protected List<byte> _buffer = new List<byte>();
/// <summary>
/// 데이터조회간격(초)
/// </summary>
public float ScanInterval { get; set; }
// public byte[] LastRecvData;
public string LastRecvString
{
get
{
if (LastReceiveBuffer == null) return String.Empty;
else return System.Text.Encoding.Default.GetString(LastReceiveBuffer);
}
}
/// <summary>
/// 마지막으로 데이터를 받은 시간
/// </summary>
public DateTime lastRecvTime;
public int WriteError = 0;
public string WriteErrorMessage = string.Empty;
public int WaitTimeout { get; set; } = 1000;
public int MinRecvLength { get; set; } = 1;
/// <summary>
/// 포트이름
/// </summary>
[Description("시리얼 포트 이름")]
[Category("설정"), DisplayName("Port Name")]
public string PortName
{
get
{
if (_device == null) return string.Empty;
else return _device.PortName;
}
set
{
if (this.IsOpen)
{
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true));
}
else if (String.IsNullOrEmpty(value) == false)
_device.PortName = value;
else
{
Message?.Invoke(this, new MessageEventArgs("No PortName", true));
}
}
}
public int BaudRate
{
get
{
if (_device == null) return 0;
else return _device.BaudRate;
}
set
{
if (this.IsOpen)
{
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true));
}
else if (value != 0)
_device.BaudRate = value;
else Message?.Invoke(this, new MessageEventArgs("No baud rate", true));
}
}
public NarumiSerialComm()
{
_device = new System.IO.Ports.SerialPort();
this.BaudRate = 9600;
ScanInterval = 10;
_device.DataReceived += barcode_DataReceived;
_device.ErrorReceived += this.barcode_ErrorReceived;
_device.WriteTimeout = 3000;
_device.ReadTimeout = 3000;
_device.ReadBufferSize = 8192;
_device.WriteBufferSize = 8192;
//_device.DiscardInBuffer();
//_device.DiscardOutBuffer();
ErrorMessage = string.Empty;
lastRecvTime = DateTime.Parse("1982-11-23");
LastConnTime = DateTime.Parse("1982-11-23");
LastConnTryTime = DateTime.Parse("1982-11-23");
lastRecvTime = DateTime.Parse("1982-11-23");
this._mre = new ManualResetEvent(true);
}
~NarumiSerialComm()
{
Dispose(false);
}
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
// Free any other managed objects here.
//
}
_device.DataReceived -= barcode_DataReceived;
_device.ErrorReceived -= this.barcode_ErrorReceived;
// Free any unmanaged objects here.
//
disposed = true;
}
public Boolean Open()
{
try
{
_device.Open();
return IsOpen;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
Message.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
}
public string GetHexString(Byte[] input)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (byte b in input)
sb.Append(" " + b.ToString("X2"));
return sb.ToString();
}
/// <summary>
/// 포트가 열려있는지 확인
/// </summary>
[Description("현재 시리얼포트가 열려있는지 확인합니다")]
[Category("정보"), DisplayName("Port Open")]
public Boolean IsOpen
{
get
{
if (_device == null) return false;
return _device.IsOpen;
}
}
public virtual bool Close()
{
if (_device != null && _device.IsOpen)
{
_device.DiscardInBuffer();
_device.DiscardOutBuffer();
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
return true;
}
else return false;
}
protected Boolean RaiseRecvData()
{
return RaiseRecvData(LastReceiveBuffer.ToArray(), false);
}
/// <summary>
/// 수신받은 메세지를 발생 시킵니다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual Boolean RaiseRecvData(byte[] Data, bool udpatelastbuffer)
{
//181206 - 최종수신 메세지 기록
lastRecvTime = DateTime.Now;
if (udpatelastbuffer && Data != null)
{
if (LastReceiveBuffer == null || LastReceiveBuffer.Length != Data.Length)
{
LastReceiveBuffer = new byte[Data.Length];
Array.Copy(Data, LastReceiveBuffer, Data.Length);
}
}
try
{
Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
if (ProcessRecvData(Data) == false)
{
//Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
Message?.Invoke(this, new MessageEventArgs(this.ErrorMessage, true)); //errormessage
return false;
}
else
{
return true;
}
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
}
/// <summary>
/// 수신받은 자료를 처리한다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public abstract bool ProcessRecvData(byte[] data);
#region "Internal Events"
void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
{
Message?.Invoke(this, new MessageEventArgs(e.ToString(), true));
}
byte[] buffer = new byte[] { };
void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
try
{
int ReadCount = _device.BytesToRead;
buffer = new byte[ReadCount];
_device.Read(buffer, 0, buffer.Length);
System.Text.StringBuilder LogMsg = new StringBuilder();
byte[] remainBuffer;
Repeat:
if (CustomParser(buffer, out remainBuffer))
{
//분석완료이므로 받은 데이터를 버퍼에 기록한다
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
tempBuffer.Clear();
//수신메세지발생
RaiseRecvData();
if (remainBuffer != null && remainBuffer.Length > 0)
{
//버퍼를 변경해서 다시 전송을 해준다.
Array.Resize(ref buffer, remainBuffer.Length);
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
}
}
}
catch (Exception ex)
{
//if (IsOpen)
//{
// //_device.DiscardInBuffer();
// //_device.DiscardOutBuffer();
//}
ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
}
}
#endregion
#region "External Events"
/// <summary>
/// 오류 및 기타 일반 메세지
/// </summary>
public event EventHandler<MessageEventArgs> Message;
#endregion
#region "Event Args"
/// <summary>
/// 데이터를 수신할떄 사용함(RAW 포함)
/// </summary>
public class ReceiveDataEventArgs : EventArgs
{
private byte[] _buffer = null;
/// <summary>
/// 바이트배열의 버퍼값
/// </summary>
public byte[] Value { get { return _buffer; } }
/// <summary>
/// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
/// </summary>
public string StrValue
{
get
{
//return string.Empty;
if (_buffer == null || _buffer.Length < 1) return string.Empty;
else return System.Text.Encoding.Default.GetString(_buffer);
}
}
public ReceiveDataEventArgs(byte[] buffer)
{
_buffer = buffer;
}
}
/// <summary>
/// 메세지를 강제 발생
/// </summary>
/// <param name="mt"></param>
/// <param name="message"></param>
protected virtual void RaiseMessage(MessageType mt, string message)
{
this.Message?.Invoke(this, new MessageEventArgs(mt, message));
}
public enum MessageType
{
Normal,
Error,
Send,
Recv,
}
public class MessageEventArgs : EventArgs
{
public MessageType MsgType { get; set; }
private string _message = string.Empty;
/// <summary>
/// Recv,Send,Normal,Error 모두 지원
/// </summary>
public string Message { get { return _message; } }
private byte[] _data = null;
/// <summary>
/// Recv,Send에서만 값이 존재 합니다
/// </summary>
public byte[] Data { get { return _data; } }
public MessageEventArgs(string Message, bool isError = false)
{
if (isError) MsgType = MessageType.Error;
else MsgType = MessageType.Normal;
_message = Message;
}
public MessageEventArgs(MessageType msgtype, string Message)
{
MsgType = msgtype;
_message = Message;
_data = System.Text.Encoding.Default.GetBytes(Message);
}
public MessageEventArgs(byte[] buffer, bool isRecv = true)
{
if (isRecv) MsgType = MessageType.Recv;
else MsgType = MessageType.Send;
_data = new byte[buffer.Length];
Array.Copy(buffer, _data, Data.Length);
_message = System.Text.Encoding.Default.GetString(_data);
}
}
#endregion
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
/// <summary>
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다
/// </summary>
public Boolean IsValid
{
get
{
if (IsOpen == false) return false;
if (lastRecvTime.Year == 1982) return false;
var ts = DateTime.Now - lastRecvTime;
if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false;
return true;
}
}
protected bool WriteData(string cmd)
{
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
}
/// <summary>
/// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
/// </summary>
protected Boolean WriteData(byte[] data)
{
Boolean bRet = false;
//171205 : 타임아웃시간추가
if (!_mre.WaitOne(WaitTimeout))
{
ErrorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
this.Message?.Invoke(this, new MessageEventArgs(ErrorMessage, true));
return false;
}
_mre.Reset();
//Array.Resize(ref data, data.Length + 2);
try
{
lastSendTime = DateTime.Now;
if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113
else Array.Resize(ref lastSendBuffer, data.Length);
Array.Copy(data, lastSendBuffer, data.Length);
for (int i = 0; i < data.Length; i++)
_device.Write(data, i, 1);
//_device.Write(data, 0, data.Length);
//171113
this.Message?.Invoke(this, new MessageEventArgs(data, false));
bRet = true;
WriteError = 0;
WriteErrorMessage = string.Empty;
}
catch (Exception ex)
{
// this.isinit = false;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
bRet = false;
WriteError += 1; //연속쓰기오류횟수
WriteErrorMessage = ex.Message;
}
finally
{
_mre.Set();
}
return bRet;
}
}
}

View File

@@ -44,6 +44,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Flag.cs" />
<Compile Include="ISerialComm.cs" />
<Compile Include="RS232.cs" />
<Compile Include="Var.cs" />
<Compile Include="Enum.cs" />

View File

@@ -0,0 +1,18 @@
using System;
namespace arDev
{
/// <summary>
/// 시리얼통신모듈의기본형태정의
/// </summary>
public interface ISerialComm
{
string PortName { get; set; }
int BaudRate { get; set; }
bool IsOpen { get; }
string ErrorMessage { get; set; }
Boolean Open();
Boolean Close();
}
}

View File

@@ -7,7 +7,8 @@ using System.Threading;
namespace arDev
{
public abstract partial class arRS232 : IDisposable
public abstract partial class arRS232 : ISerialComm, IDisposable
{
protected System.IO.Ports.SerialPort _device;
protected ManualResetEvent _mre;
@@ -19,7 +20,7 @@ namespace arDev
//public int ValidCheckTimeMSec { get; set; } = 5000;
protected List<byte> tempBuffer = new List<byte>();
protected Boolean findSTX = false;
public string errorMessage { get; set; }
public string ErrorMessage { get; set; }
public DateTime LastConnTime { get; set; }
public DateTime LastConnTryTime { get; set; }
public DateTime lastSendTime;
@@ -111,7 +112,7 @@ namespace arDev
_device.ReadBufferSize = 8192;
_device.WriteBufferSize = 8192;
errorMessage = string.Empty;
ErrorMessage = string.Empty;
lastRecvTime = DateTime.Parse("1982-11-23");
LastConnTime = DateTime.Parse("1982-11-23");
LastConnTryTime = DateTime.Parse("1982-11-23");
@@ -164,7 +165,7 @@ namespace arDev
}
catch (Exception ex)
{
errorMessage = ex.Message;
ErrorMessage = ex.Message;
Message.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
@@ -191,14 +192,16 @@ namespace arDev
}
}
public virtual void Close(Boolean PortClose = true)
public virtual bool Close()
{
if (_device != null && _device.IsOpen)
{
_device.DiscardInBuffer();
_device.DiscardOutBuffer();
if (PortClose) _device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
return true;
}
else return false;
}
protected Boolean RaiseRecvData()
{
@@ -228,7 +231,7 @@ namespace arDev
if (ProcessRecvData(Data) == false)
{
//Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
Message?.Invoke(this, new MessageEventArgs(this.errorMessage, true)); //errormessage
Message?.Invoke(this, new MessageEventArgs(this.ErrorMessage, true)); //errormessage
return false;
}
else
@@ -239,7 +242,7 @@ namespace arDev
}
catch (Exception ex)
{
this.errorMessage = ex.Message;
this.ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
@@ -301,7 +304,7 @@ namespace arDev
// //_device.DiscardInBuffer();
// //_device.DiscardOutBuffer();
//}
errorMessage = ex.Message;
ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
}
@@ -419,7 +422,7 @@ namespace arDev
/// <summary>
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다
/// </summary>
public Boolean IsValid
public Boolean IsValid
{
get
{
@@ -444,8 +447,8 @@ namespace arDev
//171205 : 타임아웃시간추가
if (!_mre.WaitOne(WaitTimeout))
{
errorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
this.Message?.Invoke(this, new MessageEventArgs(errorMessage, true));
ErrorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
this.Message?.Invoke(this, new MessageEventArgs(ErrorMessage, true));
return false;
}

Submodule Cs_HMI/SubProject/EnigProtocol updated: 2a5bb77dab...283910459e