파일정리

This commit is contained in:
ChiKyun Kim
2026-01-29 14:03:17 +09:00
parent 00cc0ef5b7
commit 58ca67150d
440 changed files with 47236 additions and 99165 deletions

605
HMI/Project/Device/BMS.cs Normal file
View File

@@ -0,0 +1,605 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.CodeDom;
namespace arDev
{
public class BMSCellInformation
{
public float[] Voltage = new float[8];
public float Average
{
get
{
if (Voltage.Any() == false) return 0f;
return Voltage.Average();
}
}
public float Delta
{
get
{
if (Voltage.Any() == false) return 0f;
var value = Math.Abs(Voltage.Max() - Voltage.Min());
return value * 1000f;
}
}
}
public class BMSBasicInformation
{
public float packVoltage { get; set; }
public float current { get; set; }
public float remainingCapacity { get; set; }
public float fullCapacity { get; set; }
public UInt16 cycleCount { get; set; }
public DateTime productionDate { get; set; }
public UInt32 fullBalance { get; set; }
public BMSProtectionStatus protectionStatus { get; set; }
public byte version { get; set; }
public byte rsoc { get; set; }
public BMSMosfetStatus mosfetStatus { get; set; }
public byte ntcCount { get; set; }
public float[] ntcTemp { get; set; }
public ushort raw_protection { get; set; }
public float watt
{
get
{
return (this.current * packVoltage);
}
}
/// <summary>
/// Remain / Full * 100
/// </summary>
public float RawLevel
{
get
{
if (fullCapacity < 1) return 0f;
return (remainingCapacity / fullCapacity) * 100f;
}
}
public BMSBasicInformation()
{
protectionStatus = new BMSProtectionStatus();
mosfetStatus = new BMSMosfetStatus();
productionDate = new DateTime();
ntcTemp = new float[] { 0f, 0f };
}
public void Clear()
{
packVoltage = 0;
current = 0;
remainingCapacity = 0;
fullBalance = 0;
fullCapacity = 0;
cycleCount = 0;
productionDate = new DateTime();
protectionStatus = new BMSProtectionStatus();
}
public override string ToString()
{
return $"packVoltage:{packVoltage}\n" +
$"current:{current}\n" +
$"remainingCapacity:{remainingCapacity}\n" +
$"fullCapacity:{fullCapacity}\n" +
$"cycleCount:{cycleCount}\n" +
$"productionDate:{productionDate}\n" +
$"fullBalance:{fullBalance}\n" +
$"protectionStatus:{protectionStatus}\n" +
$"version:{version}\n" +
$"rsoc:{rsoc}\n" +
$"mosfetStatus:{mosfetStatus}\n" +
$"ntcCount:{ntcCount}\n" +
$"ntcTemp:{ntcTemp}\n" +
$"watt:{watt}\n" +
$"RawLevel:{RawLevel}";
}
}
public class BMSProtectionStatus
{
public bool covp { get; set; }// Cell Over Voltage Protection
public bool cuvp { get; set; } // Cell Under Voltage Protection
public bool povp { get; set; } // Pack Over Voltage Protection
public bool puvp { get; set; } // Pack Under Voltage Protection
public bool chgot { get; set; }// Charge Over Temp
public bool chgut { get; set; } // Charge Under Temp
public bool dsgot { get; set; } // Discharge Over Temp
public bool dsgut { get; set; } // Discharge Under Temp
public bool chgoc { get; set; } // Charge Over Current
public bool dsgoc { get; set; } // Discharge Over Current
public bool sc { get; set; } // Short Circuit
public bool afe { get; set; } // AFE Error
public override string ToString()
{
return "BMSProtectionStatus\n" +
$"covp={covp}\n" +// { get; set; }// Cell Over Voltage Protection
$"cuvp={cuvp}\n" +// { get; set; } // Cell Under Voltage Protection
$"povp={povp}\n" +// { get; set; } // Pack Over Voltage Protection
$"puvp={puvp}\n" +// { get; set; } // Pack Under Voltage Protection
$"chgot={chgot}\n" +// { get; set; }// Charge Over Temp
$"chgut={chgut}\n" +// { get; set; } // Charge Under Temp
$"dsgot={dsgot}\n" +// { get; set; } // Discharge Over Temp
$"dsgut={dsgut}\n" +// { get; set; } // Discharge Under Temp
$"chgoc={chgoc}\n" +// { get; set; } // Charge Over Current
$"dsgoc={dsgoc}\n" +// { get; set; } // Discharge Over Current
$"sc={sc}\n" +// { get; set; } // Short Circuit
$"afe={afe}";// +// { get; set; } // AFE Error
}
}
public class BMSMosfetStatus
{
public bool charge { get; set; }
public bool discharge { get; set; }
public override string ToString()
{
return $"charge:{charge}\n" +
$"discharge:{discharge}";
}
}
public class BMS : BMSSerialComm
{
public BMS()
{
}
/// <summary>
/// 시리얼포트의 핀 상태값이 변경될 때 발생합니다.
/// </summary>
public event EventHandler<BMSInformationEventArgs> BMSDataReceive;
public event EventHandler<BMSCelvoltageEventArgs> BMSCellDataReceive;
protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
{
List<byte> remain = new List<byte>();
//데이터는 총 34byt 이며 , DD~77로 구성됨
Boolean bComplete = false;
for (int i = 0; i < buf.Length; i++)
{
var incomByte = buf[i];
if (bComplete == true)
{
remain.Add(incomByte);
}
else
{
if (findSTX == false)
{
if (incomByte != 0xDD)
{
//버리는데이터
}
else
{
findSTX = true;
tempBuffer.Clear();
tempBuffer.Add(incomByte);
}
}
else
{
tempBuffer.Add(incomByte);
if (tempBuffer.Count > 7)
{
byte len = tempBuffer[3];
if (tempBuffer.Count >= 4 + len + 3) // Start+Reg+Status+Len + Data + Chk(2) + End
{
if (tempBuffer.Last() == 0x77)
{
//데이터가 맞게 수신됨
LastReceiveBuffer = tempBuffer.ToArray();
bComplete = true;
}
else
{
//종단기호가 맞지 않다. 이자료는 폐기한다.
var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
RaiseMessage(MessageType.Error, $"discard : {hexstr}");
tempBuffer.Clear();
}
findSTX = false;
}
}
// [22 - 12 - 27 14:32:49] open: True
//[22 - 12 - 27 14:32:49] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:50] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:51] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:51] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
//var queylen = QueryIndex == 0 ? 34 : 23;
//if (tempBuffer.Count == queylen)
//{
// if (incomByte != 0x77)
// {
// //종단기호가 맞지 않다. 이자료는 폐기한다.
// var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
// RaiseMessage(MessageType.Error, $"discard : {hexstr}");
// tempBuffer.Clear();
// }
// else
// {
// //데이터가 맞게 수신됨
// LastReceiveBuffer = tempBuffer.ToArray();
// bComplete = true;
// }
// findSTX = false;
//}
//else
//{
// //아직 모자르므로 대기한다
//}
}
}
}
remainBuffer = remain.ToArray();
return bComplete;
}
bool Recv0 = false;
bool Recv1 = false;
public event EventHandler<ChargetDetectArgs> ChargeDetect;
public override bool ProcessRecvData(byte[] data)
{
//LastReceiveBuffer
if (data == null)
{
RaiseMessage(MessageType.Error, "수신 데이터가 없습니다");
return false;
}
var datalne = QueryIndex == 0 ? 34 : 23;
if (data.Length != datalne)
{
RaiseMessage(MessageType.Error, $"데이터의 길이가 {MinRecvLength}가 아닙니다 길이={data.Length}");
return false;
}
else
{
var rxstr = string.Join(" ", data.Select(t => t.ToString("X2")));
RaiseMessage(MessageType.Recv, rxstr);
}
if (QueryIndex == 0)
{
return ParseBMSInfo();
}
else
{
return ParseBMSCellVoltage();
}
}
bool ParseBMSCellVoltage()
{
//var i = 0;
var BatteryCell_Checksum = 0xffff;// - (LastReceiveBuffer[i + 3] + LastReceiveBuffer[i + 4] + LastReceiveBuffer[i + 5] + LastReceiveBuffer[i + 6] + LastReceiveBuffer[i + 7] + LastReceiveBuffer[i + 8] + LastReceiveBuffer[i + 9] + LastReceiveBuffer[i + 10] + LastReceiveBuffer[i + 11] + LastReceiveBuffer[i + 12] + LastReceiveBuffer[i + 13] + LastReceiveBuffer[i + 14] + LastReceiveBuffer[i + 15] + LastReceiveBuffer[i + 16] + LastReceiveBuffer[i + 17] + LastReceiveBuffer[i + 18] + LastReceiveBuffer[i + 19])) + 1;
for (int i = 3; i < 20; i++)
{
BatteryCell_Checksum -= LastReceiveBuffer[i];// + LastReceiveBuffer[i + 4] + LastReceiveBuffer[i + 5] + LastReceiveBuffer[i + 6] + LastReceiveBuffer[i + 7] + LastReceiveBuffer[i + 8] + LastReceiveBuffer[i + 9] + LastReceiveBuffer[i + 10] + LastReceiveBuffer[i + 11] + LastReceiveBuffer[i + 12] + LastReceiveBuffer[i + 13] + LastReceiveBuffer[i + 14] + LastReceiveBuffer[i + 15] + LastReceiveBuffer[i + 16] + LastReceiveBuffer[i + 17] + LastReceiveBuffer[i + 18] + LastReceiveBuffer[i + 19])) + 1;
}
BatteryCell_Checksum += 1;
var recvchecksum = BitConverter.ToUInt16(LastReceiveBuffer.Skip(20).Take(2).Reverse().ToArray(), 0);
if (recvchecksum == BatteryCell_Checksum)
{
var v1 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(4).Take(2).Reverse().ToArray(), 0) / 1000f;
var v2 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(6).Take(2).Reverse().ToArray(), 0) / 1000f;
var v3 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(8).Take(2).Reverse().ToArray(), 0) / 1000f;
var v4 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(10).Take(2).Reverse().ToArray(), 0) / 1000f;
var v5 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(12).Take(2).Reverse().ToArray(), 0) / 1000f;
var v6 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(14).Take(2).Reverse().ToArray(), 0) / 1000f;
var v7 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(16).Take(2).Reverse().ToArray(), 0) / 1000f;
var v8 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(18).Take(2).Reverse().ToArray(), 0) / 1000f;
var idx = 0;
BMSCellVoltage.Voltage[idx++] = v1;
BMSCellVoltage.Voltage[idx++] = v2;
BMSCellVoltage.Voltage[idx++] = v3;
BMSCellVoltage.Voltage[idx++] = v4;
BMSCellVoltage.Voltage[idx++] = v5;
BMSCellVoltage.Voltage[idx++] = v6;
BMSCellVoltage.Voltage[idx++] = v7;
BMSCellVoltage.Voltage[idx++] = v8;
Recv1 = true;
try
{
BMSCellDataReceive?.Invoke(this, new BMSCelvoltageEventArgs(v1, v2, v3, v4, v5, v6, v7, v8));
Current_CellTime = DateTime.Now;
return true;
}
catch (Exception ex)
{
RaiseMessage(MessageType.Error, ex.Message);
return false;
}
}
else return false;
}
bool ParseBMSInfo()
{
var newinfo = new BMSBasicInformation();
//전압확인
var offset = 4;
UInt16 batH = (UInt16)LastReceiveBuffer[offset + 0];
UInt16 batL = (UInt16)LastReceiveBuffer[offset + 1];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
newinfo.packVoltage = (float)(batH / 100.0);
//충방전전류
Int16 batHi = (Int16)LastReceiveBuffer[offset + 2];
Int16 batLi = (Int16)LastReceiveBuffer[offset + 3];
batHi = (Int16)(batHi << 8);
batHi = (Int16)(batHi | batLi);
newinfo.current = (float)(batHi / 100.0);
//잔량확인
batH = (UInt16)LastReceiveBuffer[offset + 4];
batL = (UInt16)LastReceiveBuffer[offset + 5];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
newinfo.remainingCapacity = (float)(batH / 100.0);
//총량확인
batH = (UInt16)LastReceiveBuffer[offset + 6];
batL = (UInt16)LastReceiveBuffer[offset + 7];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
newinfo.fullCapacity = (float)(batH / 100.0);
//cycle
batH = (UInt16)LastReceiveBuffer[offset + 8];
batL = (UInt16)LastReceiveBuffer[offset + 9];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
newinfo.cycleCount = batH;
//productiondate
batH = (UInt16)LastReceiveBuffer[offset + 10];
batL = (UInt16)LastReceiveBuffer[offset + 11];
batH = (UInt16)(batH << 8);
batH = (UInt16)(batH | batL);
var info_productiondateint = batH;
var date_year = (info_productiondateint >> 9) + 2000;
var date_month = (info_productiondateint >> 5) & 0x0F;
var date_day = info_productiondateint & 0x1F;
newinfo.productionDate = new DateTime(date_year, date_month, date_day);
//balnace status
batH = (UInt16)LastReceiveBuffer[offset + 12];
batL = (UInt16)LastReceiveBuffer[offset + 13];
batH = (UInt16)(batH << 8);
var balanceStatus = (UInt16)(batH | batL);
//balnace status(HIGH)
batH = (UInt16)LastReceiveBuffer[offset + 14];
batL = (UInt16)LastReceiveBuffer[offset + 15];
batH = (UInt16)(batH << 8);
var balanceStatusHigh = (UInt16)(batH | batL);
newinfo.fullBalance = (UInt32)(balanceStatus | (balanceStatusHigh << 16));
//protectionStatusRaw
batH = (UInt16)LastReceiveBuffer[offset + 16];
batL = (UInt16)LastReceiveBuffer[offset + 17];
batH = (UInt16)(batH << 8);
newinfo.raw_protection = batH;// view.getUint16(16, false);
var protectionStatusRaw = newinfo.raw_protection;
newinfo.protectionStatus.covp = (protectionStatusRaw & 1) > 0;
newinfo.protectionStatus.cuvp = ((protectionStatusRaw >> 1) & 1) > 0;
newinfo.protectionStatus.povp = ((protectionStatusRaw >> 2) & 1) > 0;
newinfo.protectionStatus.puvp = ((protectionStatusRaw >> 3) & 1) > 0;
newinfo.protectionStatus.chgot = ((protectionStatusRaw >> 4) & 1) > 0;
newinfo.protectionStatus.chgut = ((protectionStatusRaw >> 5) & 1) > 0;
newinfo.protectionStatus.dsgot = ((protectionStatusRaw >> 6) & 1) > 0;
newinfo.protectionStatus.dsgut = ((protectionStatusRaw >> 7) & 1) > 0;
newinfo.protectionStatus.chgoc = ((protectionStatusRaw >> 8) & 1) > 0;
newinfo.protectionStatus.dsgoc = ((protectionStatusRaw >> 9) & 1) > 0;
newinfo.protectionStatus.sc = ((protectionStatusRaw >> 10) & 1) > 0;
newinfo.protectionStatus.afe = ((protectionStatusRaw >> 11) & 1) > 0;
//version
newinfo.version = LastReceiveBuffer[offset + 18];
newinfo.rsoc = LastReceiveBuffer[offset + 19];
var mosfetRaw = LastReceiveBuffer[offset + 20];
newinfo.mosfetStatus.charge = (mosfetRaw & 1) == 1;
newinfo.mosfetStatus.discharge = ((mosfetRaw >> 1) & 1) == 1;
//250620 jwlee 추가
newinfo.ntcCount = LastReceiveBuffer[offset + 22]; //센서갯수
int temp1 = (LastReceiveBuffer[offset + 23] << 8) | LastReceiveBuffer[offset + 24];
int temp2 = (LastReceiveBuffer[offset + 25] << 8) | LastReceiveBuffer[offset + 26];
var Current_temp1 = (temp1 - 2731) / 10f;
var Current_temp2 = (temp2 - 2731) / 10f;
newinfo.ntcTemp = new float[] { (temp1 - 2731) / 10f, (temp2 - 2731) / 10f };
CheckManualCharge(newinfo);
Recv0 = true;
try
{
this.BMSInformation = newinfo;
BMSDataReceive?.Invoke(this, new BMSInformationEventArgs(BMSInformation));
Current_DataTime = DateTime.Now;
return true;
}
catch (Exception ex)
{
RaiseMessage(MessageType.Error, ex.Message);
return false;
}
}
public BMSBasicInformation BMSInformation = new BMSBasicInformation();
public BMSCellInformation BMSCellVoltage = new BMSCellInformation();
/// <summary>
/// 현재 충전중인지?
/// </summary>
public bool IsCharging { get; private set; }
DateTime ChargeStart = DateTime.Now;
DateTime ChargeEnd = DateTime.Now;
void CheckManualCharge(BMSBasicInformation info)
{
//충방전전력이 1보다 크면 충전으로 한다.
if (this.BMSInformation.current > 0.1)
{
//기존에 충전상태가 OFF였다면 충전중으로 알려준다
if (IsCharging == false)
{
IsCharging = true;
ChargeStart = DateTime.Now;
ChargeEnd = new DateTime(1982, 11, 23);
try
{
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeStart, true, info.RawLevel));
}
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
}
else
{
//충전상태가 유지되고 있다.
}
}
else
{
//충전이해제되었다.. 단 바로 해제하지않고 1초정도 텀을 주고 OFF한다.
if (IsCharging)
{
if (ChargeEnd.Year == 1982)
{
ChargeEnd = DateTime.Now;
}
else
{
var ts = DateTime.Now - ChargeEnd;
if (ts.TotalSeconds > 2) //충전종료시그널후 2초후에 충전off를 알린다.
{
ChargeEnd = DateTime.Now;
IsCharging = false;
try
{
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeEnd, false, info.RawLevel));
}
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
}
}
}
else
{
//방전상태가 유지되고 있다.
}
}
}
public DateTime chk_times { get; set; } = new DateTime(1982, 11, 23);
public DateTime chk_timee { get; set; } = new DateTime(1982, 11, 23);
public float chk_values { get; set; } = 0f;
public float chk_valuee { get; set; } = 0f;
public DateTime Current_DataTime = DateTime.Parse("1982-11-23");
public DateTime Current_CellTime = DateTime.Parse("1982-11-23");
public int QueryIndex { get; set; } = 0;
/// <summary>
/// 상태읽기와 전압읽기명령을 반복합니다
/// </summary>
/// <returns></returns>
public Boolean SendQuery()
{
if (QueryIndex == 0)
{
if (Recv0 == true)
{
QueryIndex = 1;
Recv1 = false;
return true;
}
else
{
return SendQuery_ReadStatue();
}
}
else
{
if (Recv1 == true)
{
QueryIndex = 0;
Recv0 = false;
return true;
}
else
{
return SendQuery_ReadCellvoltage();
}
}
}
public Boolean SendQuery_ReadStatue()
{
Recv0 = false;
var cmd = new List<byte>();
cmd.Add(0xDD);
cmd.Add(0xA5);
cmd.Add(0x03);
cmd.Add(0x00);
cmd.Add(0xFF);
cmd.Add(0xFD);
cmd.Add(0x77);
//cmd.Add(0x0D);
//_device.DiscardInBuffer();
return WriteData(cmd.ToArray());
}
public Boolean SendQuery_ReadCellvoltage()
{
Recv1 = false;
var cmd = new List<byte>();
cmd.Add(0xDD);
cmd.Add(0xA5);
cmd.Add(0x04);
cmd.Add(0x00);
cmd.Add(0xFF);
cmd.Add(0xFC);
cmd.Add(0x77);
//cmd.Add(0x0D);
//_device.DiscardInBuffer();
return WriteData(cmd.ToArray());
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Linq;
namespace arDev
{
public class ChargetDetectArgs : EventArgs
{
public DateTime time { get; set; }
public float level { get; set; }
public bool Detected { get; set; }
public ChargetDetectArgs(DateTime times, bool detected, float values)
{
this.time = times;
this.level = values;
this.Detected = detected;
}
}
public class BMSInformationEventArgs : EventArgs
{
public BMSBasicInformation Data { get; set; }
public BMSInformationEventArgs(BMSBasicInformation info)
{
this.Data = info;
}
}
public class BMSCelvoltageEventArgs : EventArgs
{
public double[] voltage;
public BMSCelvoltageEventArgs(double v1, double v2, double v3, double v4, double v5, double v6, double v7, double v8)
{
voltage = new double[] { v1, v2, v3, v4, v5, v6, v7, v8 };
}
public override string ToString()
{
return string.Join(" ", voltage.Select(t => t.ToString()));
}
}
}

View File

@@ -0,0 +1,607 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
namespace arDev
{
public abstract class BMSSerialComm : 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;
// Polling Thread related
protected Thread _recvThread;
protected volatile bool _isReading = false;
/// <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 BMSSerialComm()
{
_device = new System.IO.Ports.SerialPort();
this.BaudRate = 9600;
ScanInterval = 10;
// _device.DataReceived += barcode_DataReceived; // Removed event handler
_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);
}
~BMSSerialComm()
{
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.
//
}
// Stop reading thread
_isReading = false;
// _device.DataReceived -= barcode_DataReceived; // Removed event handler
_device.ErrorReceived -= this.barcode_ErrorReceived;
if (_recvThread != null && _recvThread.IsAlive)
{
_recvThread.Join(500);
}
if (_device != null)
{
if (_device.IsOpen) _device.Close();
_device.Dispose();
}
// Free any unmanaged objects here.
//
disposed = true;
}
public Boolean Open()
{
try
{
if (_device.IsOpen == false)
{
_device.Open();
}
if (_device.IsOpen)
{
// Start polling thread
if (_isReading == false)
{
_isReading = true;
_recvThread = new Thread(ReadPort);
_recvThread.IsBackground = true;
_recvThread.Start();
}
return true;
}
return false;
}
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()
{
try
{
_isReading = false; // Stop thread loop
if (_recvThread != null && _recvThread.IsAlive)
{
if (!_recvThread.Join(500)) // Wait for thread to finish
{
// _recvThread.Abort(); // Avoid Abort if possible
}
}
if (_device != null && _device.IsOpen)
{
_device.DiscardInBuffer();
_device.DiscardOutBuffer();
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
return true;
}
else return false;
}
catch (Exception)
{
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
{
// UI update might need Invoke if this event handler updates UI directly,
// but usually the subscriber handles Invoke.
// Since we are running on a background thread now, subscribers must be aware.
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[] { };
// Replaced with ReadPort Loop
/*
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));
}
}
*/
void ReadPort()
{
while (_isReading)
{
try
{
if (_device == null || !_device.IsOpen)
{
Thread.Sleep(100);
continue;
}
int readCount = _device.BytesToRead;
if (readCount > 0)
{
byte[] buffer = new byte[readCount];
_device.Read(buffer, 0, buffer.Length);
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)
{
//버퍼를 변경해서 다시 전송을 해준다.
buffer = new byte[remainBuffer.Length]; // Reallocate buffer for remaining data
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
}
}
}
else
{
Thread.Sleep(20); // Data 없음, 대기
}
}
catch (Exception ex)
{
// Thread 상에서 Exception 발생 시 로그 남기고 계속 진행 여부 결정
// 여기서는 에러 메시지 발생시키고 Sleep
ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
Thread.Sleep(1000);
}
}
}
#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

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Project.Device
{
public class CFlag : COMM.Flag
{
public CFlag() : base(64)
{
}
public bool get(EFlag flag)
{
return Get((int)flag);
}
public void set(EFlag flag, bool value)
{
var idx = (int)flag;
Set(idx, value);
}
public void Toggle(EFlag flag)
{
int idx = (int)flag;
var curValue = Get(idx);
Set(idx, !curValue);
}
public void Toggle(int idx)
{
var curValue = Get(idx);
Set(idx, !curValue);
}
}
}

View File

@@ -0,0 +1,427 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Project.Device
{
public class Socket
{
public enum eType
{
REQUEST,
REPLY,
}
public enum eSType
{
STATUS,
GEN,
SPL
}
public struct Message
{
public Boolean isError;
public string ErrorMessage;
public string Asset;
public eType Type;
public eSType SType;
public string Body;
public string timeStamp;
}
private Winsock_Orcas.Winsock wsListen;
private Winsock_Orcas.Winsock wsData;
private char sepMsg = (char)0x07;
private char sepBody = (char)0x09;
public int ListenPort { get; set; }
public string errorMessage { get; set; }
public event EventHandler<SocketMessageEventArgs> RecvMessage;
public event EventHandler<SocketMessageEventArgs> SendMessage;
public event EventHandler<MesasgeEventArgs> GetMessage;
private void RaiseMessage(string msg, Boolean isErr = false)
{
if (GetMessage != null) GetMessage(this, new MesasgeEventArgs(msg, isErr));
}
private void RaiseSendMessage(Message msg, string raw)
{
if (SendMessage != null)
{
try
{
SendMessage(this, new SocketMessageEventArgs(msg, raw));
}
catch (Exception ex)
{
this.errorMessage = ex.Message;
}
}
}
private void RaiseRecvMessage(Message msg, string raw)
{
if (RecvMessage != null)
{
try
{
RecvMessage(this, new SocketMessageEventArgs(msg, raw));
}
catch (Exception ex)
{
this.errorMessage = ex.Message;
}
}
}
public Socket()
{
wsListen = new Winsock_Orcas.Winsock();
wsData = new Winsock_Orcas.Winsock();
wsListen.LegacySupport = true;
wsData.LegacySupport = true;
wsData.Connected += wsData_Connected;
wsData.Disconnected += wsData_Disconnected;
wsData.DataArrival += wsData_DataArrival;
wsData.ErrorReceived += wsData_ErrorReceived;
wsListen.ConnectionRequest += wsListen_ConnectionRequest;
wsListen.ErrorReceived += wsListen_ErrorReceived;
}
void wsListen_ErrorReceived(object sender, Winsock_Orcas.WinsockErrorReceivedEventArgs e)
{
RaiseMessage("LTERR:" + e.Details, true);
}
void wsData_ErrorReceived(object sender, Winsock_Orcas.WinsockErrorReceivedEventArgs e)
{
RaiseMessage("RXERR:"+e.Details, true);
}
void wsData_Disconnected(object sender, EventArgs e)
{
RaiseMessage("Disconnected");
}
void wsData_Connected(object sender, Winsock_Orcas.WinsockConnectedEventArgs e)
{
RaiseMessage("Conncted");
}
void wsListen_ConnectionRequest(object sender, Winsock_Orcas.WinsockConnectionRequestEventArgs e)
{
if (wsData.State != Winsock_Orcas.WinsockStates.Closed)
{
wsData.Close();
System.Threading.Thread.Sleep(500);
}
RaiseMessage("Connection Request " + e.ClientIP);
try
{
wsData.Accept(e.Client);
}catch (Exception ex)
{
RaiseMessage("Connection Request\n"+ex.Message, true);
}
}
~Socket()
{
if (wsData != null)
{
wsData.DataArrival -= wsData_DataArrival;
wsData.Connected += wsData_Connected;
wsData.Disconnected += wsData_Disconnected;
}
if (wsListen != null)
{
wsListen.ConnectionRequest += wsListen_ConnectionRequest;
}
if (wsListen != null && wsListen.State != Winsock_Orcas.WinsockStates.Closed)
wsListen.Close();
if (wsData != null && wsData.State != Winsock_Orcas.WinsockStates.Closed)
wsData.Close();
wsData.Dispose();
wsListen.Dispose();
}
public Boolean Listen(int port)
{
try
{
ListenPort = port;
wsListen.Listen(this.ListenPort);
this.errorMessage = string.Empty;
return true;
}
catch (Exception ex)
{
this.errorMessage = ex.Message;
return false;
}
}
public Boolean isListen
{
get
{
return wsListen.State == Winsock_Orcas.WinsockStates.Listening;
}
}
public Boolean isConn
{
get
{
return wsData.State == Winsock_Orcas.WinsockStates.Connected;
}
}
public Message LastSendMessage = new Message();
private Message makeMessage(string Asset, eType type, eSType subtype, string body)
{
Message retval = new Message();
retval.Asset = Asset;
retval.Type = type;
retval.SType = subtype;
retval.Body = body;
retval.isError = true;
retval.ErrorMessage = string.Empty;
retval.timeStamp = DateTime.Now.ToString("yyyyMMddHHmmss");
return retval;
}
/// <summary>
/// Body문자열을 구분자를 기준으로 한 배열로 반환 함
/// </summary>
/// <param name="bodyMessage"></param>
/// <returns></returns>
public string[] getBodyArray(string bodyMessage)
{
return bodyMessage.Split(sepBody);
}
/// <summary>
/// 패킷의 내용을 메세지로 분리한다.
/// </summary>
/// <param name="PacketMessage"></param>
/// <returns></returns>
private Message ParseMessage(string PacketMessage)
{
Message retval = new Message();
retval.isError = false;
retval.ErrorMessage = string.Empty;
var buffer = PacketMessage.Split(sepMsg);
if (buffer.Length != 5)
{
retval.isError = true;
retval.ErrorMessage = "Packet Size Error : Expect 5 Array";
return retval;
}
else
{
retval.Asset = buffer[0];
if (!getType(buffer[1], out retval.Type))
{
retval.isError = true;
retval.ErrorMessage = "지정된 Type 이 아닙니다(" + buffer[1] + ")";
return retval;
}
if (!getSType(buffer[2], out retval.SType))
{
retval.isError = true;
retval.ErrorMessage = "지정된 SubType 이 아닙니다(" + buffer[2] + ")";
return retval;
}
retval.timeStamp = buffer[3];
//DateTime timeStamp;
//if (!DateTime.TryParse(retval.timeStamp, out timeStamp))
//{
// retval.isError = true;
// retval.ErrorMessage = "timeStamp 해석 오류(" + retval.timeStamp + ")";
// return retval;
//}
retval.Body = buffer[4];
}
return retval;
}
private Boolean getType(string strType, out eType type)
{
type = eType.REPLY;
var list = Enum.GetNames(typeof(eType));
int value = -1;
for (int i = 0; i < list.Length; i++)
{
if (list[i].ToUpper() == strType.ToUpper())
{
value = i;
break;
}
}
if (value == -1) return false;
type = (eType)value;
return true;
}
private Boolean getSType(string strSType, out eSType stype)
{
stype = eSType.GEN;
var list = Enum.GetNames(typeof(eSType));
int value = -1;
for (int i = 0; i < list.Length; i++)
{
if (list[i].ToUpper() == strSType.ToUpper())
{
value = i;
break;
}
}
if (value == -1) return false;
stype = (eSType)value;
return true;
}
public string makePacket(Message msg)
{
//프레임구조 asset chr(7) + type + chr(7) + stype + char(7) + timeStamp + char(7) + body"
//timestamp = yyyymmddhhmmss
//sepChar = (char)0x07;
System.Text.StringBuilder buffer = new StringBuilder();
buffer.Append(msg.Asset);
buffer.Append(sepMsg);
buffer.Append(msg.Type.ToString());
buffer.Append(sepMsg);
buffer.Append(msg.SType.ToString());
buffer.Append(sepMsg);
buffer.Append(msg.timeStamp);
buffer.Append(sepMsg);
buffer.Append(msg.Body);
return buffer.ToString();
}
public string makeLotSplitPacket( string fcst, string tcst, string slot, string wafer)
{
System.Text.StringBuilder buffer = new StringBuilder();
buffer.Append(fcst);
buffer.Append(sepBody);
buffer.Append(tcst);
buffer.Append(sepBody);
buffer.Append(slot);
buffer.Append(sepBody);
buffer.Append(wafer);
return buffer.ToString();
}
public string makeLotGenPacket(string Lot, string cst, string slot, string wafer)
{
System.Text.StringBuilder buffer = new StringBuilder();
buffer.Append(Lot);
buffer.Append(sepBody);
buffer.Append(cst);
buffer.Append(sepBody);
buffer.Append(slot);
buffer.Append(sepBody);
buffer.Append(wafer);
return buffer.ToString();
}
public enum eStatus
{
RUN =0,
IDLE ,
ALARM,
}
public string makeReplyStatus(eStatus Status)
{
System.Text.StringBuilder buffer = new StringBuilder();
buffer.Append(((int)Status).ToString());
return buffer.ToString();
}
public Boolean Send(string Asset, eType type, eSType subtype, string body)
{
var msg = makeMessage(Asset, type, subtype, body);
var isStatus = subtype == eSType.STATUS;
return Send(msg, isStatus);
}
public Boolean isReplyRecv = false;
public Boolean Send(Message msg,Boolean isStatus)
{
var packet = makePacket(msg);
if (wsData.State != Winsock_Orcas.WinsockStates.Connected)
{
errorMessage = "Not Connected";
return false;
}
try
{
if(isStatus==false) isReplyRecv = false; //190129
wsData.Send(packet + "\r\n");
errorMessage = string.Empty;
LastSendMessage = msg;
RaiseSendMessage(msg, packet);
return true;
}
catch (Exception ex)
{
errorMessage = ex.Message;
return false;
}
}
public Message LastReplyMessage = new Message();
public Message LastRecvMessage = new Message();
void wsData_DataArrival(object sender, Winsock_Orcas.WinsockDataArrivalEventArgs e)
{
var sock = sender as Winsock_Orcas.Winsock;
var data = sock.Get<String>();
if(PUB.setting.LOg_SocketRecv)
{
if (data.IndexOf("REQUEST") != -1 && data.IndexOf("STATUS") != -1)
{
//핑 명령
}
else PUB.log.AddE("Socket Recv : " + data); //190129
}
LastRecvMessage = ParseMessage(data);
if (LastRecvMessage.isError) PUB.log.AddE("Socket Message error" + LastRecvMessage.ErrorMessage);
else
{
//190129 - reply만 별도 처리함
if (LastRecvMessage.Type == eType.REPLY)
{
LastReplyMessage = ParseMessage(data);
isReplyRecv = true;
}
}
RaiseRecvMessage(LastRecvMessage, data);
}
public class SocketMessageEventArgs : EventArgs
{
public string rawData { get; set; }
public Device.Socket.Message Message { get; set; }
public SocketMessageEventArgs(Device.Socket.Message msg, string raw)
{
this.Message = msg;
this.rawData = raw;
}
}
public class MesasgeEventArgs : EventArgs
{
public string Message { get; set; }
public Boolean isError { get; set; }
public MesasgeEventArgs(string msg, Boolean iserr)
{
this.Message = msg;
this.isError = iserr;
}
}
}
}

330
HMI/Project/Device/Xbee.cs Normal file
View File

@@ -0,0 +1,330 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using COMM;
using ENIG;
using System.Security.Cryptography;
using AR;
using System.IO.Ports;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;
namespace Project.Device
{
public enum eDocStep : byte
{
NotSet = 0,
/// <summary>
/// 투입준비됨
/// </summary>
ReadyForEnter = 10,
/// <summary>
/// 투입완료
/// </summary>
EnterComplete = 11,
/// <summary>
/// 투입(진행중)
/// </summary>
EnterIng = 12,
/// <summary>
/// 진출완료
/// </summary>
ExitComplete = 21,
/// <summary>
/// 진출중
/// </summary>
ExitIng = 22,
}
public class Xbee : SerialPort, arDev.ISerialComm
{
public string buffer = string.Empty;
public System.Text.StringBuilder newbuffer = new StringBuilder();
public string ErrorMessage { get; set; } = string.Empty;
public DateTime LastStatusSendTime { get; set; } = DateTime.Now;
private EEProtocol proto;
public class MessageArgs : EventArgs
{
public bool IsError { get; set; }
public string Message { get; set; }
public MessageArgs(bool iserr, string m)
{
this.IsError = iserr;
this.Message = m;
}
}
public event EventHandler<MessageArgs> MessageReceived;
public event EventHandler<EEProtocol.DataEventArgs> ProtocReceived;
public Xbee()
{
this.WriteTimeout = 500;
this.ReadTimeout = 500;
this.DataReceived += Xbee_DataReceived;
proto = new EEProtocol();
proto.OnDataReceived += Proto_OnDataReceived;
proto.OnMessage += Proto_OnMessage;
}
~Xbee()
{
this.DataReceived -= Xbee_DataReceived;
proto.OnDataReceived -= Proto_OnDataReceived;
proto.OnMessage -= Proto_OnMessage;
}
/// <summary>
/// 지그비장치에 데이터를 전송합니다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public bool Send(byte[] data)
{
try
{
this.Write(data, 0, data.Length);
return true;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
return false;
}
}
public new bool Close()
{
try
{
base.Close();
return true;
}
catch
{
return false;
}
}
public new bool Open()
{
try
{
base.Open();
return IsOpen;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
PUB.logxbee.AddE(ErrorMessage);
return false;
}
}
private void Proto_OnDataReceived(object sender, EEProtocol.DataEventArgs e)
{
var hexstrRaw = e.ReceivedPacket.RawData.HexString();
var hexstr = e.ReceivedPacket.Data.HexString();
var cmd = e.ReceivedPacket.Command.ToString("X2");
var id = e.ReceivedPacket.ID.ToString("X2");
PUB.logxbee.Add("RX", $"{hexstrRaw}\nID:{id},CMD:{cmd},DATA:{hexstr}");
ProtocReceived?.Invoke(this, e);
}
private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e)
{
MessageReceived?.Invoke(this, new MessageArgs(e.IsError, e.Message));
}
private void Xbee_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
var dev = sender as System.IO.Ports.SerialPort;
var buffer = new byte[dev.BytesToRead];
dev.Read(buffer, 0, buffer.Length);
proto.ProcessReceivedData(buffer);
}
/// <summary>
/// 이동완료 신호 전송
/// </summary>
/// <param name="tag">목적지태그값</param>
public void SendMoveComplete(string tag)
{
var id = PUB.setting.XBE_ID;
byte cmd = (byte)ENIGProtocol.AGVCommandEH.Arrived;
var data = System.Text.Encoding.Default.GetBytes(tag);
var packet = proto.CreatePacket(id, cmd, data);
Send(packet);
}
/// <summary>
/// 신규 RFID태그값이 읽혔다면 이명령을 통해서 전송한다
/// </summary>
public void SendRFIDTag(string tag)
{
var id = PUB.setting.XBE_ID;
byte cmd = (byte)ENIGProtocol.AGVCommandEH.ReadRFID;
var data = System.Text.Encoding.Default.GetBytes(tag);
var packet = proto.CreatePacket(id, cmd, data);
Send(packet);
}
/// <summary>
/// 오류코드를 호스트에 전송합니다
/// </summary>
/// <param name="errcode"></param>
public void SendError(ENIGProtocol.AGVErrorCode errcode, string errormessage)
{
// Update global error state so it persists in Status messages
PUB.Result.RunStepErrorCode = errcode;
PUB.Result.ResultMessage = errormessage;
var id = PUB.setting.XBE_ID;
byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error;
if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29);
var data = new List<byte>();
data.Add((byte)errcode);
var datamsg = System.Text.Encoding.Default.GetBytes(errormessage);
data.AddRange(datamsg);
var packet = proto.CreatePacket(id, cmd, data.ToArray());
Send(packet);
}
public eDocStep StepMC { get; set; } = eDocStep.NotSet;
ManualResetEvent sendlock = new ManualResetEvent(true);
/// <summary>
/// AGV상태를 Xbee 로 전송한다
/// </summary>
public void SendStatus()
{
if (this.IsOpen == false) return;
if (sendlock.WaitOne() == false) return;
sendlock.Reset();
/*
Mode[1] : 0=manual, 1=auto
RunSt[1] : 0=stop, 1=run, 2=error
RunStep[1] : (byte)PUB.sm.RunStep
RunStepSeq[1] : (byte)StepMC
MotorDir[1] : 0=F(Forward), 1=B(Backward)
MagnetDir[1] : 0=S(Straight), 1=L(Left), 2=R(Right)
ChargeSt[1] : 0=off, 1=on
CartSt[1] : 0=off, 1=on, 2=unknown
LiftSt[1] : 0=down , 1=up, 2=unknown
ErrorCode[1] : (byte)PUB.Result.ResultErrorCode
LastTag[4] : "0000"
*/
try
{
List<byte> data = new List<byte>();
byte value = 0;
// Autoron Mode
value = (byte)(VAR.BOOL[eVarBool.FLAG_AUTORUN] ? 1 : 0);
data.Add(value);
// RunSt
if (PUB.AGV.error.Emergency)
value = 2; // error
else if (PUB.AGV.system1.agv_run)
value = 1; // run
else
value = 0; // stop
data.Add(value);
//runstep
value = (byte)PUB.sm.RunStep;
data.Add(value);
//runstepseq
value = (byte)StepMC;
data.Add(value);
// Motor Direction
if (PUB.AGV.data.Direction == 'F')
value = 0;
else if (PUB.AGV.data.Direction == 'B')
value = 1;
else
value = 0xff; //unknown
data.Add(value);
// Magnet Direction
if (PUB.AGV.data.Sts == 'L')
value = 1; // left
else if (PUB.AGV.data.Sts == 'R')
value = 2; // right
else if (PUB.AGV.data.Sts == 'S')
value = 0; // straight
else
value = 0xFF; //unknown
data.Add(value);
// ChargeSt
value = (byte)((VAR.BOOL[eVarBool.FLAG_CHARGEONA] || VAR.BOOL[eVarBool.FLAG_CHARGEONM]) ? 1 : 0);
data.Add(value);
// CartSt
if (PUB.AGV.signal2.cart_detect1 && PUB.AGV.signal2.cart_detect2)
value = 1; // 센서두개가 모두 감지되는 경우
else if (PUB.AGV.signal2.cart_detect1 == false && PUB.AGV.signal2.cart_detect2 == false)
value = 0; // 센서두개가 모두 감지되지 않는 경우
else
value = 2; // 센서하나만 감지되는 경우
data.Add(value);
// LiftSt
if (PUB.AGV.signal1.lift_up)
value = 1; // 위로 올라가는 경우
else if (PUB.AGV.signal1.lift_down)
value = 0; // 아래로 내려가는 경우
else
value = 2; // unknown (기본값)
data.Add(value);
// ErrorCode [New RunStepErrorCode]
value = (byte)PUB.Result.RunStepErrorCode;
data.Add(value);
// LastTag
string lastTag = PUB.AGV.data.TagNo.ToString("0000") ?? "0000";
byte[] tagBytes = Encoding.ASCII.GetBytes(lastTag.PadRight(4, '0'));
data.AddRange(tagBytes);
// 데이터 전송
var cmd = (byte)ENIGProtocol.AGVCommandEH.Status;
var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data.ToArray());
if (Send(packet))
PUB.logxbee.AddI($"Send status [O] : {packet.Length} {packet.HexString()}");
else
PUB.logxbee.AddE($"Send status [X] : {packet.Length} {packet.HexString()}");
LastStatusSendTime = DateTime.Now;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
PUB.logxbee.AddE(ErrorMessage);
}
finally
{
sendlock.Set();
}
}
}
}