초기 커밋.
This commit is contained in:
607
LFP_Manager/Threads/CsSnmpThread.cs
Normal file
607
LFP_Manager/Threads/CsSnmpThread.cs
Normal file
@@ -0,0 +1,607 @@
|
||||
using LFP_Manager.DataStructure;
|
||||
using LFP_Manager.Function;
|
||||
using LFP_Manager.Utils;
|
||||
using SnmpSharpNet;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LFP_Manager.Threads
|
||||
{
|
||||
// 콜백 델리게이트
|
||||
public delegate void SetResult(string result, bool error);
|
||||
public delegate void DataUpdate(object sender, ref CsDeviceData.DeviceModuleData rData);
|
||||
|
||||
class CsSnmpThread
|
||||
{
|
||||
#region VARIABLES (상태)
|
||||
private CsDeviceData.DeviceModuleData ModuleData;
|
||||
private CommConfig Config;
|
||||
|
||||
// 스레드/토큰 기반 (Abort 제거)
|
||||
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
|
||||
private Task _snmpTask;
|
||||
|
||||
// 상태 보호용 락
|
||||
private readonly object _stateLock = new object();
|
||||
|
||||
// 실행 상태
|
||||
private bool snmpThreadHold = true; // true = 정지, false = 동작
|
||||
private string targetIP = "";
|
||||
private bool AutoSnmpTx = true;
|
||||
|
||||
// OID 순회
|
||||
private int OidIndex = 0;
|
||||
private int OidMax = 0;
|
||||
private string[] OidList = Array.Empty<string>();
|
||||
|
||||
// 통신 타임아웃 카운터
|
||||
private int CommTimeOut = 0;
|
||||
|
||||
// 결과 메시지 (레거시 호환)
|
||||
private bool snmpDataRecv = false;
|
||||
private string snmpResult = "";
|
||||
|
||||
// 대기중 SET 명령 (race 방지 위해 락으로 보호)
|
||||
private int _pendingModeInt = 0;
|
||||
private int _pendingModeStr = 0;
|
||||
private UInt32 _pendingValue = 0;
|
||||
private string _pendingString = null;
|
||||
|
||||
private long _lastUpdateTicks = 0;
|
||||
private const int UpdateMinIntervalMs = 120;
|
||||
|
||||
// 이벤트
|
||||
public event DataUpdate OnUpdate = null;
|
||||
public event SetResult OnSetResult = null;
|
||||
#endregion
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public CsSnmpThread(CommConfig aConfig, ref CsDeviceData.DeviceModuleData mData)
|
||||
{
|
||||
Config = aConfig;
|
||||
ModuleData = mData;
|
||||
|
||||
OidList = CsSnmpConstData.SnmpOidInfo ?? Array.Empty<string>();
|
||||
OidMax = OidList.Length;
|
||||
OidIndex = 0;
|
||||
|
||||
_snmpTask = Task.Factory.StartNew(
|
||||
() => snmpThreadFuncA(_cts.Token),
|
||||
_cts.Token,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PUBLIC API
|
||||
/// <summary>
|
||||
/// 안전 종료 (Abort 제거)
|
||||
/// </summary>
|
||||
public void disposeThread()
|
||||
{
|
||||
Stop();
|
||||
_cts.Cancel();
|
||||
try { _snmpTask?.Wait(1500); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 스레드 실행 시작/재시작
|
||||
/// </summary>
|
||||
public void Start(string IP, bool autoTx)
|
||||
{
|
||||
lock (_stateLock)
|
||||
{
|
||||
targetIP = IP ?? "";
|
||||
AutoSnmpTx = autoTx;
|
||||
snmpThreadHold = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 스레드 일시정지
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
lock (_stateLock)
|
||||
{
|
||||
snmpThreadHold = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true면 정지 상태
|
||||
/// </summary>
|
||||
public bool GetStatus()
|
||||
{
|
||||
lock (_stateLock) { return snmpThreadHold; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 레거시: 마지막 결과 문자열 반환 후 플래그 리셋
|
||||
/// </summary>
|
||||
public string GetResult()
|
||||
{
|
||||
lock (_stateLock)
|
||||
{
|
||||
if (snmpDataRecv)
|
||||
{
|
||||
snmpDataRecv = false;
|
||||
return snmpResult;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void SetAutoTx(bool autoTx)
|
||||
{
|
||||
lock (_stateLock) { AutoSnmpTx = autoTx; }
|
||||
}
|
||||
|
||||
public string GetSnmpOidInfo(int index)
|
||||
{
|
||||
if (index < 0 || index >= OidMax) return null;
|
||||
return OidList[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 정수형 SET 명령 예약
|
||||
/// </summary>
|
||||
public void SetDataBySnmp(int mode, UInt32 value)
|
||||
{
|
||||
lock (_stateLock)
|
||||
{
|
||||
_pendingModeInt = mode;
|
||||
_pendingValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 문자열 SET 명령 예약
|
||||
/// </summary>
|
||||
public void SetDataBySnmp(int mode, string value)
|
||||
{
|
||||
lock (_stateLock)
|
||||
{
|
||||
_pendingModeStr = mode;
|
||||
_pendingString = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region COMM. THREAD
|
||||
|
||||
private void snmpThreadFuncA(CancellationToken ct)
|
||||
{
|
||||
var wait = new ManualResetEventSlim(false);
|
||||
int backoffMs = 50; // 정상 루프 기본 지연
|
||||
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
bool hold;
|
||||
string ip;
|
||||
bool autoTx;
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
hold = snmpThreadHold;
|
||||
ip = targetIP;
|
||||
autoTx = AutoSnmpTx;
|
||||
}
|
||||
|
||||
if (hold || string.IsNullOrWhiteSpace(ip))
|
||||
{
|
||||
// 대기
|
||||
wait.Wait(TimeSpan.FromMilliseconds(500), ct);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Target 생성 및 재사용 범위
|
||||
if (!TryParseIp(ip, out var addr))
|
||||
{
|
||||
OnSetResultSafe($"Invalid IP address: {ip}", true);
|
||||
wait.Wait(TimeSpan.FromMilliseconds(1000), ct);
|
||||
continue;
|
||||
}
|
||||
|
||||
var param = CreateAgentParams(); // 커뮤니티/버전 통일
|
||||
|
||||
using (var target = new UdpTarget(addr, 161, 1000, 1))
|
||||
{
|
||||
var pdu = new Pdu(PduType.Get);
|
||||
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
lock (_stateLock)
|
||||
{
|
||||
if (snmpThreadHold) break;
|
||||
}
|
||||
|
||||
// 1) 대기중 SET 우선 처리
|
||||
int modeInt, modeStr;
|
||||
UInt32 setVal;
|
||||
string setStr;
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
modeInt = _pendingModeInt;
|
||||
modeStr = _pendingModeStr;
|
||||
setVal = _pendingValue;
|
||||
setStr = _pendingString;
|
||||
|
||||
_pendingModeInt = 0;
|
||||
_pendingModeStr = 0;
|
||||
_pendingString = null;
|
||||
}
|
||||
|
||||
if (modeInt != 0 || modeStr != 0)
|
||||
{
|
||||
int mode = (modeInt != 0) ? modeInt : modeStr;
|
||||
bool err = SetCmdbySnmp(target, mode, setVal, setStr);
|
||||
backoffMs = err ? Math.Min(backoffMs * 2, 2000) : 100; // 에러 시 백오프
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2) GET 폴링
|
||||
autoTx = AutoSnmpTx;
|
||||
if (!autoTx)
|
||||
{
|
||||
wait.Wait(TimeSpan.FromMilliseconds(200), ct);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OidMax == 0)
|
||||
{
|
||||
OnSetResultSafe("SNMP OID list is empty.", true);
|
||||
wait.Wait(TimeSpan.FromMilliseconds(1000), ct);
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = NextOidIndex();
|
||||
string oid = GetSnmpOidInfo(idx);
|
||||
if (string.IsNullOrEmpty(oid))
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
pdu.VbList.Clear();
|
||||
pdu.VbList.Add(oid);
|
||||
|
||||
try
|
||||
{
|
||||
target.Timeout = 500;
|
||||
|
||||
OnSetResultSafe(csLog.trx_msg_print(
|
||||
$"SEND {target.Address}: OID [{pdu.VbList[0].Oid}]", 0), false);
|
||||
|
||||
var packet = (SnmpV1Packet)target.Request(pdu, param);
|
||||
if (packet == null)
|
||||
{
|
||||
HandleNoResponse(ref backoffMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packet.Pdu.ErrorStatus != 0)
|
||||
{
|
||||
string oidTxt = (packet.Pdu.VbList.Count > 0)
|
||||
? packet.Pdu.VbList[0].Oid.ToString()
|
||||
: "(no vb)";
|
||||
|
||||
OnSetResultSafe(csLog.trx_msg_print(
|
||||
$"Error in SNMP reply. Error {packet.Pdu.ErrorStatus} index {packet.Pdu.ErrorIndex} - {oidTxt}", 1), true);
|
||||
|
||||
HandleCommFailStep(ref backoffMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packet.Pdu.VbList.Count == 0)
|
||||
{
|
||||
OnSetResultSafe("SNMP reply has empty VbList.", true);
|
||||
HandleCommFailStep(ref backoffMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
var vb = packet.Pdu.VbList[0];
|
||||
|
||||
OnSetResultSafe(csLog.trx_msg_print(
|
||||
$"RECV {target.Address}: OID [{vb.Oid}]: [{vb.Value}]", 1), false);
|
||||
|
||||
// 특정 트리만 반영 (1.3.6.1.2.1.15.*) → 필요에 맞게 수정하세요.
|
||||
if (TryParseShelfOid(vb, out uint leaf))
|
||||
{
|
||||
if (vb.Value.Type == AsnType.OCTETSTRING)
|
||||
{
|
||||
try
|
||||
{
|
||||
csMakeDataFunction.SetSnmpData((int)leaf, vb.Value.ToString(), ref ModuleData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnSetResultSafe($"{NowPrefix()} {ex.Message}", true);
|
||||
}
|
||||
}
|
||||
|
||||
CalcAvgTemperatureModule(ref ModuleData, (uint)ModuleData.tempQty);
|
||||
CheckShelfCommFail(ref ModuleData);
|
||||
|
||||
var nowTicks = Environment.TickCount;
|
||||
if (nowTicks - _lastUpdateTicks >= UpdateMinIntervalMs)
|
||||
{
|
||||
OnUpdate?.Invoke(this, ref ModuleData);
|
||||
_lastUpdateTicks = nowTicks;
|
||||
}
|
||||
}
|
||||
|
||||
CommTimeOut = 0;
|
||||
ModuleData.CommFail = false;
|
||||
backoffMs = 50;
|
||||
|
||||
Thread.Sleep(backoffMs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string curOid = pdu.VbList.Count > 0 ? pdu.VbList[0].Oid.ToString() : "(no oid)";
|
||||
OnSetResultSafe(csLog.trx_msg_print(
|
||||
$"Exception in SNMP reply. OID {curOid}: {ex.Message}", 1), true);
|
||||
|
||||
HandleCommFailStep(ref backoffMs);
|
||||
}
|
||||
} // inner while
|
||||
} // using target
|
||||
|
||||
Thread.Sleep(500);
|
||||
} // outer while
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SNMP HELPERS
|
||||
private AgentParameters CreateAgentParams()
|
||||
{
|
||||
// 커뮤니티/버전 통일: v1 + "public" (필요시 Ver2로 변경)
|
||||
// Ver2가 가능하면: new AgentParameters(SnmpVersion.Ver2, new OctetString("public"));
|
||||
return new AgentParameters(SnmpVersion.Ver1, new OctetString("admin"));
|
||||
}
|
||||
|
||||
private bool SetCmdbySnmp(UdpTarget sTarget, int mode, UInt32 value, string svalue)
|
||||
{
|
||||
bool resultError = false;
|
||||
var SetPdu = new Pdu(PduType.Set);
|
||||
SetPdu.VbList.Clear();
|
||||
|
||||
// 모드별 OID 매핑 (기존 코드 유지)
|
||||
switch (mode)
|
||||
{
|
||||
case 1: // Reset Command
|
||||
SetPdu.VbList.Add(new Oid(".1.3.6.1.4.1.33.13.1.0"), new Integer32(Convert.ToInt32(value)));
|
||||
break;
|
||||
case 2: // Module Quantity Set
|
||||
SetPdu.VbList.Add(new Oid(".1.3.6.1.4.1.33.2.4.0"), new Integer32(Convert.ToInt32(value)));
|
||||
break;
|
||||
case 3: // Alarm Output Mode
|
||||
SetPdu.VbList.Add(new Oid(".1.3.6.1.4.1.33.2.5.0"), new Integer32(Convert.ToInt32(value)));
|
||||
break;
|
||||
case 4: // Manufacture Date
|
||||
SetPdu.VbList.Add(new Oid(".1.3.6.1.4.1.33.2.6.0"), new Integer32(Convert.ToInt32(value)));
|
||||
break;
|
||||
case 5: // BMS Serial Number
|
||||
SetPdu.VbList.Add(new Oid(".1.3.6.1.4.1.33.2.7.0"), new OctetString(svalue ?? ""));
|
||||
break;
|
||||
default:
|
||||
OnSetResultSafe($"Unsupported SET mode: {mode}", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var aparam = CreateAgentParams();
|
||||
var result = sTarget.Request(SetPdu, aparam) as SnmpV1Packet;
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
OnSetResultSafe($"{NowPrefix()} No response received from SNMP agent.", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result.Pdu.ErrorStatus != 0)
|
||||
{
|
||||
string oidTxt = (result.Pdu.VbList.Count > 0)
|
||||
? result.Pdu.VbList[0].Oid.ToString()
|
||||
: "(no vb)";
|
||||
|
||||
OnSetResultSafe($"Error in SNMP SET. Error {result.Pdu.ErrorStatus} index {result.Pdu.ErrorIndex} - {oidTxt}", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
var vbOk = result.Pdu.VbList.Count > 0 ? result.Pdu.VbList[0] : null;
|
||||
var val = vbOk != null ? vbOk.Value.ToString() : "(no vb)";
|
||||
OnSetResultSafe($"SNMP SET OK. OID [{vbOk?.Oid}] -> [{val}]", false);
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnSetResultSafe($"{NowPrefix()} Exception: {ex.Message}", true);
|
||||
resultError = true;
|
||||
}
|
||||
|
||||
return resultError;
|
||||
}
|
||||
|
||||
private static bool TryParseIp(string ip, out IPAddress addr)
|
||||
{
|
||||
if (IPAddress.TryParse(ip, out addr)) return true;
|
||||
try
|
||||
{
|
||||
var host = Dns.GetHostEntry(ip);
|
||||
addr = host?.AddressList?.FirstOrDefault(a => a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
|
||||
return addr != null;
|
||||
}
|
||||
catch { addr = null; return false; }
|
||||
}
|
||||
|
||||
private int NextOidIndex()
|
||||
{
|
||||
// 간단한 라운드로빈
|
||||
int next = Interlocked.Increment(ref OidIndex);
|
||||
if (OidMax == 0) return 0;
|
||||
next %= OidMax;
|
||||
if (next < 0) next += OidMax;
|
||||
return next;
|
||||
}
|
||||
|
||||
private static bool TryParseShelfOid(Vb vb, out uint leaf)
|
||||
{
|
||||
leaf = 0;
|
||||
if (vb?.Oid == null) return false;
|
||||
|
||||
// OID를 문자열로 받아 숫자 배열로 파싱
|
||||
// 예: ".1.3.6.1.2.1.15.7.0" 또는 "1.3.6.1.2.1.15.7.0"
|
||||
string oidStr = vb.Oid.ToString();
|
||||
if (string.IsNullOrWhiteSpace(oidStr)) return false;
|
||||
|
||||
// 선행 '.' 제거 후 분해
|
||||
var parts = oidStr.Trim('.').Split('.');
|
||||
if (parts.Length < 8) return false;
|
||||
|
||||
// 숫자 배열로 변환 (변환 실패 시 false)
|
||||
uint[] ids = new uint[parts.Length];
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
if (!uint.TryParse(parts[i], out ids[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 예시 트리: 1.3.6.1.2.1.15.x.*
|
||||
if (ids[0] == 1 && ids[1] == 3 && ids[2] == 6 &&
|
||||
ids[3] == 1 && ids[4] == 2 && ids[5] == 1 &&
|
||||
ids[6] == 15)
|
||||
{
|
||||
leaf = ids[7];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void HandleNoResponse(ref int backoffMs)
|
||||
{
|
||||
lock (_stateLock)
|
||||
{
|
||||
if (!snmpDataRecv)
|
||||
{
|
||||
snmpResult = $"{NowPrefix()} No response received from SNMP agent.";
|
||||
snmpDataRecv = true;
|
||||
OnSetResult?.Invoke(snmpResult, true);
|
||||
}
|
||||
}
|
||||
|
||||
CommTimeOut++;
|
||||
if (CommTimeOut >= 10)
|
||||
{
|
||||
ModuleData.Reset();
|
||||
ModuleData.CommFail = true;
|
||||
|
||||
var nowTicks = Environment.TickCount;
|
||||
if (nowTicks - _lastUpdateTicks >= UpdateMinIntervalMs)
|
||||
{
|
||||
OnUpdate?.Invoke(this, ref ModuleData);
|
||||
_lastUpdateTicks = nowTicks;
|
||||
}
|
||||
|
||||
CommTimeOut = 10;
|
||||
}
|
||||
Thread.Sleep(backoffMs = Math.Min(backoffMs * 2, 2000));
|
||||
}
|
||||
|
||||
private void HandleCommFailStep(ref int backoffMs)
|
||||
{
|
||||
CommTimeOut++;
|
||||
if (CommTimeOut >= 10)
|
||||
{
|
||||
ModuleData.Reset();
|
||||
ModuleData.CommFail = true;
|
||||
|
||||
var nowTicks = Environment.TickCount;
|
||||
if (nowTicks - _lastUpdateTicks >= UpdateMinIntervalMs)
|
||||
{
|
||||
OnUpdate?.Invoke(this, ref ModuleData);
|
||||
_lastUpdateTicks = nowTicks;
|
||||
}
|
||||
|
||||
CommTimeOut = 10;
|
||||
}
|
||||
Thread.Sleep(backoffMs = Math.Min(backoffMs * 2, 2000));
|
||||
}
|
||||
|
||||
private void OnSetResultSafe(string msg, bool error)
|
||||
{
|
||||
try { OnSetResult?.Invoke(msg, error); } catch { /* swallow */ }
|
||||
}
|
||||
|
||||
private static string NowPrefix() => DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ");
|
||||
#endregion
|
||||
|
||||
#region DOMAIN HELPERS (기존 함수 보강/유지)
|
||||
private void CheckShelfCommFail(ref CsDeviceData.DeviceModuleData mData)
|
||||
{
|
||||
// 상태코드 0x0003 → ShelfCommFail=true (기존 가정 유지)
|
||||
if (mData.StatusData.batteryStatus == 0x0003) { mData.ShelfCommFail = true; }
|
||||
else { mData.ShelfCommFail = false; }
|
||||
}
|
||||
|
||||
// 기존 코드 유지 (평균/최대/최소 계산)
|
||||
static void CalcAvgTemperatureModule(ref CsDeviceData.DeviceModuleData mData, UInt32 tSize)
|
||||
{
|
||||
short i, j;
|
||||
int sum;
|
||||
TMinMax min, max;
|
||||
|
||||
min = new TMinMax();
|
||||
max = new TMinMax();
|
||||
|
||||
sum = 0;
|
||||
max.value = 0;
|
||||
max.num = 0;
|
||||
min.value = 0;
|
||||
min.num = 0;
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < tSize; i++)
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
max.value = mData.ValueData.CellTemperature[i];
|
||||
max.num = i;
|
||||
min.value = mData.ValueData.CellTemperature[i];
|
||||
min.num = i;
|
||||
}
|
||||
|
||||
if (max.value < mData.ValueData.CellTemperature[i])
|
||||
{
|
||||
max.value = mData.ValueData.CellTemperature[i];
|
||||
max.num = i;
|
||||
}
|
||||
if (min.value > mData.ValueData.CellTemperature[i])
|
||||
{
|
||||
min.value = mData.ValueData.CellTemperature[i];
|
||||
min.num = i;
|
||||
}
|
||||
|
||||
sum += mData.ValueData.CellTemperature[i];
|
||||
j++;
|
||||
}
|
||||
|
||||
if (j == 0) { mData.AvgData.avgTemp = 0; }
|
||||
else { mData.AvgData.avgTemp = (short)(sum / j); }
|
||||
|
||||
mData.AvgData.maxTemp = max.value;
|
||||
mData.AvgData.maxTempNum = max.num;
|
||||
mData.AvgData.minTemp = min.value;
|
||||
mData.AvgData.minTempNum = min.num;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
644
LFP_Manager/Threads/CsUartThreadSB.cs
Normal file
644
LFP_Manager/Threads/CsUartThreadSB.cs
Normal file
@@ -0,0 +1,644 @@
|
||||
using System;
|
||||
|
||||
using System.Threading;
|
||||
using System.IO.Ports;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using LFP_Manager.DataStructure;
|
||||
using LFP_Manager.Function;
|
||||
using LFP_Manager.Utils;
|
||||
using DevExpress.XtraGauges.Core.Model;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Drawing.Printing;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace LFP_Manager.Threads
|
||||
{
|
||||
class CsUartThreadSB
|
||||
{
|
||||
#region DELEGATE
|
||||
// 표준 이벤트 패턴
|
||||
public sealed class ModuleDataEventArgs : EventArgs
|
||||
{
|
||||
public CsDeviceData.DeviceModuleData[] Modules { get; }
|
||||
public ModuleDataEventArgs(CsDeviceData.DeviceModuleData[] modules) => Modules = modules;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CONSTANTS
|
||||
private const int RX_PACKET_BUFFER_SIZE = 512;
|
||||
#endregion
|
||||
|
||||
#region VARIABLES
|
||||
|
||||
private readonly CommConfig Config;
|
||||
private CsDeviceData.DeviceModuleData[] ModuleData;
|
||||
private Thread _serialComm = null;
|
||||
private SerialPort sPort = null;
|
||||
private CancellationTokenSource _cts;
|
||||
private volatile bool _serialPortThreadEnd = true;
|
||||
|
||||
private readonly object _startStopLock = new object();
|
||||
|
||||
private readonly ISynchronizeInvoke _ui; // 보통 Form 또는 Control
|
||||
|
||||
public int ModuleID = 1;
|
||||
private bool UartPolling = false;
|
||||
private bool AutoUartTx = true;
|
||||
|
||||
private int addr_ch = 0;
|
||||
private ushort RequestRegAddr = 0x0000; //Byul Init 0x0000
|
||||
private ushort RequestRegLen = 50;
|
||||
private bool rFlag = false;
|
||||
private int wFlag = 0;
|
||||
private int rFlag2 = 0;
|
||||
|
||||
private ushort ExtReqRegAddr = 0x0000;
|
||||
|
||||
private ushort WriteRegAddr = 0x0000; //Byul Init 0x0000
|
||||
private short WriteCoilRegData = 0;
|
||||
private byte[] WriteRegData;
|
||||
private short WriteParamRegData;
|
||||
|
||||
private readonly ConcurrentQueue<byte> _rxQ = new ConcurrentQueue<byte>();
|
||||
|
||||
private TUartTxBuff UartTxBuff;
|
||||
|
||||
// 이벤트
|
||||
public event EventHandler<ModuleDataEventArgs> OnUpdate;
|
||||
public event EventHandler<DataRecvEventArgs> OnDataRecv;
|
||||
public event EventHandler<PrintEventArgs> OnPrint;
|
||||
|
||||
#endregion
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public CsUartThreadSB(int mId,
|
||||
CommConfig aConfig,
|
||||
ISynchronizeInvoke uiInvoker,
|
||||
CsDeviceData.DeviceModuleData[] aModuleData)
|
||||
{
|
||||
ModuleID = mId;
|
||||
Config = aConfig;
|
||||
ModuleData = aModuleData;
|
||||
|
||||
UartTxBuff = new TUartTxBuff();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
public bool Start(ref CommConfig aConfig, int mID, bool aPolling)
|
||||
{
|
||||
if (aConfig == null) throw new ArgumentNullException(nameof(aConfig));
|
||||
|
||||
string modelname = csConstData.UART_MODEL[aConfig.UartModelIndex];
|
||||
int mQty = aConfig.ModuleQty;
|
||||
|
||||
if (ModuleData == null || ModuleData.Length < mQty)
|
||||
throw new InvalidOperationException($"ModuleData length({ModuleData?.Length ?? 0}) < ModuleQty({mQty})");
|
||||
|
||||
for (int i = 0; i < mQty; i++)
|
||||
{
|
||||
ModuleData[i].mNo = i + 1;
|
||||
//ModuleData[i].Information.ModelName = modelname;
|
||||
}
|
||||
|
||||
ModuleID = mID;
|
||||
UartPolling = aPolling;
|
||||
|
||||
if (!Open(aConfig.UartPort, aConfig.UartBaudrate))
|
||||
return false;
|
||||
|
||||
// 이중 시작 방지
|
||||
if (_serialComm != null && _serialComm.IsAlive)
|
||||
return true;
|
||||
|
||||
_serialPortThreadEnd = false;
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
_serialComm = new Thread(() => uartCommThreadSB(_cts.Token))
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "UartCommThreadDelta"
|
||||
};
|
||||
_serialComm.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_serialPortThreadEnd = true;
|
||||
var cts = _cts;
|
||||
_cts = null;
|
||||
if (cts != null && !cts.IsCancellationRequested)
|
||||
cts.Cancel();
|
||||
|
||||
var t = _serialComm;
|
||||
_serialComm = null;
|
||||
if (t != null && t.IsAlive)
|
||||
{
|
||||
try { t.Join(500); } catch { /* timeout 시 로그만 */ }
|
||||
}
|
||||
|
||||
Close(); // SerialPort 닫기 및 핸들러 해제
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SAFETY RAISE HELPER
|
||||
private void RaiseOnUpdate(CsDeviceData.DeviceModuleData[] snapshot)
|
||||
{
|
||||
var handler = OnUpdate;
|
||||
if (handler == null) return;
|
||||
|
||||
var args = new ModuleDataEventArgs(snapshot);
|
||||
|
||||
if (_ui != null && _ui.InvokeRequired)
|
||||
{
|
||||
try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); }
|
||||
catch { /* 폼 종료 중 등 예외 무시(로그만) */ }
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseOnDataRecv(byte[] data)
|
||||
{
|
||||
var handler = OnDataRecv;
|
||||
if (handler == null) return;
|
||||
|
||||
var args = new DataRecvEventArgs(data);
|
||||
|
||||
if (_ui != null && _ui.InvokeRequired)
|
||||
{
|
||||
try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); }
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseOnPrint(int id, string msg)
|
||||
{
|
||||
var handler = OnPrint;
|
||||
if (handler == null) return;
|
||||
|
||||
var args = new PrintEventArgs(id, msg);
|
||||
|
||||
if (_ui != null && _ui.InvokeRequired)
|
||||
{
|
||||
try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); }
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, args);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region COMMPORT CONTROLS
|
||||
|
||||
private void sDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sp = (SerialPort)sender;
|
||||
int available = sp.BytesToRead;
|
||||
if (available <= 0) return;
|
||||
|
||||
byte[] tmp = new byte[available];
|
||||
int read = sp.Read(tmp, 0, tmp.Length);
|
||||
for (int i = 0; i < read; i++)
|
||||
_rxQ.Enqueue(tmp[i]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
/* 필요시 로그 */
|
||||
}
|
||||
}
|
||||
|
||||
private void sErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
|
||||
{
|
||||
//
|
||||
//csMakeDataFunction.SetData()
|
||||
}
|
||||
|
||||
private void sPinChanged(object sender, System.IO.Ports.SerialPinChangedEventArgs e)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
private bool Open(string cPort, int cBaudrate)
|
||||
{
|
||||
sPort = new SerialPort();
|
||||
sPort.PortName = cPort;
|
||||
sPort.BaudRate = cBaudrate;
|
||||
sPort.DataReceived += sDataReceived;
|
||||
sPort.ErrorReceived += sErrorReceived;
|
||||
sPort.PinChanged += sPinChanged;
|
||||
|
||||
try
|
||||
{
|
||||
sPort.Open();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Error Open - " + ex.Message);
|
||||
}
|
||||
|
||||
return sPort.IsOpen;
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
if (sPort != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
sPort.DataReceived -= sDataReceived;
|
||||
sPort.ErrorReceived -= sErrorReceived;
|
||||
sPort.PinChanged -= sPinChanged;
|
||||
if (sPort.IsOpen) sPort.Close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
sPort.Dispose();
|
||||
sPort = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PUBLIC FUNCTION
|
||||
|
||||
public void SetPolling(bool flag, int mID, ref CsDeviceData.DeviceModuleData[] aModuleData)
|
||||
{
|
||||
ModuleID = mID;
|
||||
UartPolling = flag;
|
||||
ModuleData = aModuleData;
|
||||
ModuleData[0].mNo = ModuleID;
|
||||
}
|
||||
|
||||
public void SetAutoTx(bool autoTx)
|
||||
{
|
||||
AutoUartTx = autoTx;
|
||||
}
|
||||
|
||||
public void SetWriteReg(ushort WriteAddr, byte[] WriteData, bool ReplyFlag, int type)
|
||||
{
|
||||
WriteRegAddr = WriteAddr;
|
||||
|
||||
TUartTRxData uartTRxData = new TUartTRxData();
|
||||
uartTRxData.type = type;
|
||||
uartTRxData.data = WriteData;
|
||||
uartTRxData.length = uartTRxData.data.Length;
|
||||
|
||||
UartTxBuff?.PutBuff(uartTRxData);
|
||||
}
|
||||
|
||||
public void SetParam(ushort WriteAddr, short WriteData)
|
||||
{
|
||||
short[] wData = new short[1];
|
||||
wData[0] = WriteData;
|
||||
|
||||
byte[] sData = CsSerialCommFunctionDelta.MakeWriteRegisterData((ushort)ModuleID, WriteAddr, wData);
|
||||
|
||||
if (sData != null)
|
||||
{
|
||||
TUartTRxData aData = new TUartTRxData();
|
||||
aData.length = sData.Length;
|
||||
aData.data = sData;
|
||||
|
||||
UartTxBuff.PutBuff(aData);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetReadWriteParam(ushort WriteAddr, short WriteData)
|
||||
{
|
||||
byte[] sData = CsSerialCommFunctionDelta.MakeReadWriteRegisterData((ushort)ModuleID, WriteAddr, WriteData);
|
||||
|
||||
if (sData != null)
|
||||
{
|
||||
TUartTRxData aData = new TUartTRxData();
|
||||
aData.length = sData.Length;
|
||||
aData.data = sData;
|
||||
|
||||
ExtReqRegAddr = WriteAddr;
|
||||
|
||||
UartTxBuff.PutBuff(aData);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RX BUFFERING
|
||||
|
||||
private int GetBuff()
|
||||
{
|
||||
return _rxQ.TryDequeue(out var b) ? (0x0100 | b) : 0;
|
||||
}
|
||||
|
||||
private void FlushBuff()
|
||||
{
|
||||
while (_rxQ.TryDequeue(out _)) { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TX BUFFERING
|
||||
|
||||
private byte[] MakeWriteRegData(ushort DevID, ushort WriteAddr, ushort WriteLen, ref ushort[] WriteData)
|
||||
{
|
||||
byte[] wData = null;
|
||||
|
||||
if (WriteLen > 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return wData;
|
||||
}
|
||||
|
||||
private string MakeCheckSum(byte[] sData, int len)
|
||||
{
|
||||
string result = "";
|
||||
int checksum = 0;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
checksum += sData[i + 1];
|
||||
}
|
||||
|
||||
checksum = ~checksum + 1;
|
||||
|
||||
result = String.Format("{0:X2}{1:X2}", (byte)(checksum >> 8), (byte)checksum);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] MakeTxDataDelta(bool wData)
|
||||
{
|
||||
byte[] sData = null;
|
||||
|
||||
if ((UartTxBuff != null) && (UartTxBuff.CheckBuff()))
|
||||
{
|
||||
TUartTRxData sBuff = UartTxBuff.GetBuff();
|
||||
if (sBuff != null)
|
||||
{
|
||||
sData = sBuff.data;
|
||||
wData = true;
|
||||
rFlag = true;
|
||||
rFlag2 = sBuff.type;
|
||||
|
||||
RequestRegAddr = ExtReqRegAddr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AutoUartTx && UartPolling)
|
||||
{
|
||||
ushort sCmd;
|
||||
switch (addr_ch)
|
||||
{
|
||||
case 0: // Battery Status Data
|
||||
addr_ch = 3;
|
||||
RequestRegAddr = 0x0FFF; //Byul Init 0x0000
|
||||
RequestRegLen = 0x27;
|
||||
sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; // Command 0x04
|
||||
rFlag = true;
|
||||
break;
|
||||
case 1: // Gyroscope Data
|
||||
addr_ch = 2;
|
||||
RequestRegAddr = 0x4000;
|
||||
RequestRegLen = 7;
|
||||
sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; // Command 0x17
|
||||
rFlag = true;
|
||||
break;
|
||||
case 2:
|
||||
addr_ch = 3;
|
||||
RequestRegAddr = 0x3000;
|
||||
RequestRegLen = 3;
|
||||
sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; // Command 0x19
|
||||
rFlag = true;
|
||||
break;
|
||||
case 3:
|
||||
addr_ch = 4;
|
||||
RequestRegAddr = 0x0000;
|
||||
RequestRegLen = 1;
|
||||
sCmd = CsSerialCommFunctionDelta.READ_DEV_ID; // Command 0x2B
|
||||
rFlag = true;
|
||||
break;
|
||||
case 4:
|
||||
if (Config.ModuleQty > 1)
|
||||
{
|
||||
ModuleID++;
|
||||
if (ModuleID > Config.ModuleQty)
|
||||
{
|
||||
ModuleID = 1;
|
||||
}
|
||||
}
|
||||
addr_ch = 0;
|
||||
sCmd = CsSerialCommFunctionDelta.NO_CMD;
|
||||
rFlag = false;
|
||||
break;
|
||||
default:
|
||||
addr_ch = 0;
|
||||
RequestRegAddr = 0x0FFF; //Byul Init 0x0000
|
||||
RequestRegLen = 27;
|
||||
sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; // Command 0x04
|
||||
rFlag = true;
|
||||
break;
|
||||
}
|
||||
if (sCmd == CsSerialCommFunctionDelta.NO_CMD)
|
||||
{
|
||||
sData = null;
|
||||
}
|
||||
else if (sCmd == CsSerialCommFunctionDelta.READ_DEV_ID)
|
||||
{
|
||||
sData = CsSerialCommFunctionDelta.MakeReadDevIdRegReqData((ushort)ModuleID, sCmd, RequestRegAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
sData = CsSerialCommFunctionDelta.MakeReadRegisterData((ushort)ModuleID, sCmd, RequestRegAddr, RequestRegLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region COMM THREAD
|
||||
|
||||
private readonly byte[] ReadBuf = new byte[RX_PACKET_BUFFER_SIZE];
|
||||
ushort rPosition = 0;
|
||||
bool BuffStart = false;
|
||||
|
||||
private void RecvPacketInit()
|
||||
{
|
||||
BuffStart = false;
|
||||
rPosition = 0;
|
||||
}
|
||||
|
||||
private void uartCommThreadSB(CancellationToken token)
|
||||
{
|
||||
int RecvTimeout = Config.RecvWaitTime;
|
||||
int COMM_TIMEOUT_MS = Config.CommFailTime; // 5초 동안 수신 없으면 Fail
|
||||
DateTime[] LastReceiveTime = new DateTime[csConstData.SystemInfo.MAX_MODULE_SIZE];
|
||||
|
||||
// ★ 마지막 수신 시간 초기화 (현재 시각 기준)
|
||||
var now = DateTime.UtcNow;
|
||||
for (int i = 0; i < LastReceiveTime.Length; i++)
|
||||
LastReceiveTime[i] = now;
|
||||
|
||||
StartSend:
|
||||
while (!token.IsCancellationRequested && !_serialPortThreadEnd)
|
||||
{
|
||||
if (sPort == null || !sPort.IsOpen) { Thread.Sleep(50); continue; }
|
||||
|
||||
FlushBuff();
|
||||
|
||||
byte[] txData = MakeTxDataDelta(false);
|
||||
if (txData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
sPort.Write(txData, 0, txData.Length);
|
||||
RaiseOnPrint(txData[0], csLog.trx_data_print(txData, txData.Length, 0));
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* 로그 */
|
||||
RaiseOnPrint(ModuleID, "UART Thread - TX Buff Error !");
|
||||
}
|
||||
}
|
||||
|
||||
if (rFlag)
|
||||
{
|
||||
var deadline = DateTime.UtcNow.AddMilliseconds(RecvTimeout);
|
||||
RecvPacketInit();
|
||||
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
int gd = GetBuff();
|
||||
if ((gd & 0x0100) == 0) { Thread.Sleep(1); continue; }
|
||||
|
||||
byte cData = (byte)(gd & 0xFF);
|
||||
|
||||
if (!BuffStart)
|
||||
{
|
||||
if (cData == (byte)ModuleID || cData == 0x7F)
|
||||
{
|
||||
rPosition = 0;
|
||||
ReadBuf[rPosition++] = cData;
|
||||
BuffStart = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rPosition >= ReadBuf.Length) { RecvPacketInit(); continue; }
|
||||
ReadBuf[rPosition++] = cData;
|
||||
|
||||
int chk = CsSerialCommFunctionDelta.ModbusPacketFromSlaveCheck(ReadBuf, rPosition);
|
||||
if (chk == 0) continue;
|
||||
|
||||
RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1));
|
||||
|
||||
if (chk == 1)
|
||||
{
|
||||
int mID = ReadBuf[0];
|
||||
if (mID > 0) mID--;
|
||||
if (mID >= 0 && mID < ModuleData.Length)
|
||||
{
|
||||
// 마지막 정상 업데이트 시간 기록
|
||||
LastReceiveTime[mID] = DateTime.UtcNow;
|
||||
|
||||
ModuleData[mID].CommFail = false;
|
||||
ModuleData[mID].ShelfCommFail = false;
|
||||
var rc = CsSerialCommFunctionDelta.SerialRxProcess(
|
||||
ReadBuf, RequestRegAddr, (ushort)rPosition, ref ModuleData[mID]);
|
||||
}
|
||||
|
||||
var snapshot1 = ModuleData; // 필요 시 DeepCopy
|
||||
RaiseOnUpdate(snapshot1);
|
||||
Thread.Sleep(5);
|
||||
goto StartSend;
|
||||
}
|
||||
else if (chk == 2)
|
||||
{
|
||||
rFlag = false;
|
||||
RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1));
|
||||
|
||||
byte[] adata = new byte[rPosition + 1];
|
||||
for (int j = 0; j < adata.Length; j++)
|
||||
{
|
||||
adata[j] = ReadBuf[j];
|
||||
}
|
||||
RaiseOnDataRecv(adata);
|
||||
|
||||
goto StartSend;
|
||||
}
|
||||
else // -1
|
||||
{
|
||||
RecvPacketInit();
|
||||
Thread.Sleep(50);
|
||||
goto StartSend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// timeout
|
||||
if (rPosition > 0)
|
||||
{
|
||||
RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
int idx = csUtils.MathEx.Clamp(ModuleID - 1, 0, csConstData.SystemInfo.MAX_MODULE_SIZE - 1);
|
||||
|
||||
// 아직 한 번도 정상 수신한 적 없는 경우 MinValue일 수 있으므로,
|
||||
// 첫 타임아웃에서는 기준 시간을 현재로 초기화해 준다.
|
||||
if (LastReceiveTime[idx] == DateTime.MinValue)
|
||||
LastReceiveTime[idx] = DateTime.UtcNow;
|
||||
|
||||
// 시간 차이 계산
|
||||
var elapsed = (DateTime.Now - LastReceiveTime[idx]).TotalMilliseconds;
|
||||
|
||||
if (elapsed >= COMM_TIMEOUT_MS)
|
||||
{
|
||||
// Fail 처리
|
||||
if (!ModuleData[idx].ShelfCommFail)
|
||||
{
|
||||
ModuleData[idx].Reset();
|
||||
ModuleData[idx].ShelfCommFail = true;
|
||||
}
|
||||
|
||||
ModuleData[idx].CommFail = true;
|
||||
ModuleData[idx].ShelfCommFail = true;
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
var snapshot2 = ModuleData; // 필요 시 DeepCopy
|
||||
RaiseOnUpdate(snapshot2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
} /* if (rFlag == true) */
|
||||
rPosition = 0;
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1014
LFP_Manager/Threads/csDbThread.cs
Normal file
1014
LFP_Manager/Threads/csDbThread.cs
Normal file
File diff suppressed because it is too large
Load Diff
780
LFP_Manager/Threads/csUartThread.cs
Normal file
780
LFP_Manager/Threads/csUartThread.cs
Normal file
@@ -0,0 +1,780 @@
|
||||
using System;
|
||||
|
||||
using System.Threading;
|
||||
using System.IO.Ports;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using LFP_Manager.DataStructure;
|
||||
using LFP_Manager.Function;
|
||||
using LFP_Manager.Utils;
|
||||
using DevExpress.XtraGauges.Core.Model;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace LFP_Manager.Threads
|
||||
{
|
||||
#region DELEGATE
|
||||
// 표준 이벤트 패턴
|
||||
public sealed class DataRecvEventArgs : EventArgs
|
||||
{
|
||||
public byte[] Data { get; }
|
||||
public DataRecvEventArgs(byte[] data) => Data = data;
|
||||
}
|
||||
|
||||
public sealed class PrintEventArgs : EventArgs
|
||||
{
|
||||
public int Id { get; }
|
||||
public string Message { get; }
|
||||
public PrintEventArgs(int id, string message)
|
||||
=> (Id, Message) = (id, message);
|
||||
}
|
||||
#endregion
|
||||
|
||||
class csUartThread
|
||||
{
|
||||
#region DELEGATE
|
||||
// 표준 이벤트 패턴
|
||||
public sealed class ModuleDataEventArgs : EventArgs
|
||||
{
|
||||
public CsDeviceData.DeviceModuleData Module { get; }
|
||||
public ModuleDataEventArgs(CsDeviceData.DeviceModuleData module) => Module = module;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CONSTANTS
|
||||
private const int RX_PACKET_BUFFER_SIZE = 512;
|
||||
#endregion
|
||||
|
||||
#region VARIABLES
|
||||
|
||||
private readonly CommConfig Config;
|
||||
private CsDeviceData.DeviceModuleData ModuleData;
|
||||
private Thread _serialComm = null;
|
||||
private SerialPort sPort = null;
|
||||
private CancellationTokenSource _cts;
|
||||
private volatile bool _serialPortThreadEnd = true;
|
||||
|
||||
private readonly ISynchronizeInvoke _ui; // 보통 Form 또는 Control
|
||||
|
||||
public int ModuleId = 0;
|
||||
private bool UartPolling = false;
|
||||
private bool AutoUartTx = true;
|
||||
|
||||
private int addr_ch = 0;
|
||||
private ushort RequestRegAddr = 0x0000; //Byul Init 0x0000
|
||||
private ushort RequestRegLen = 50;
|
||||
private bool rFlag = false;
|
||||
private int rFlag2 = 0;
|
||||
|
||||
private ushort ReadRegAddr = 0x0000; //Byul Init 0x0000
|
||||
private ushort ReadRegLen = 0x0000; //Byul Init 0x0000
|
||||
|
||||
private ushort WriteRegAddr = 0x0000; //Byul Init 0x0000
|
||||
private short WriteCoilRegData = 0;
|
||||
private byte[] WriteRegData;
|
||||
|
||||
private readonly ConcurrentQueue<byte> _rxQ = new ConcurrentQueue<byte>();
|
||||
|
||||
private TUartTxBuff UartTxBuff;
|
||||
|
||||
// 이벤트
|
||||
public event EventHandler<ModuleDataEventArgs> OnUpdate;
|
||||
public event EventHandler<DataRecvEventArgs> OnDataRecv;
|
||||
public event EventHandler<PrintEventArgs> OnPrint;
|
||||
|
||||
#endregion
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public csUartThread(int mId,
|
||||
CommConfig aConfig,
|
||||
ISynchronizeInvoke uiInvoker, // ← 추가
|
||||
CsDeviceData.DeviceModuleData aModuleData)
|
||||
{
|
||||
ModuleId = mId;
|
||||
Config = aConfig;
|
||||
ModuleData = aModuleData;
|
||||
_ui = uiInvoker; // Form(this) 또는 어떤 Control
|
||||
|
||||
UartTxBuff = new TUartTxBuff();
|
||||
}
|
||||
|
||||
public bool Start(ref CommConfig aConfig, int sId, bool aPolling)
|
||||
{
|
||||
ModuleId = sId;
|
||||
UartPolling = aPolling;
|
||||
|
||||
//if (!Open(aConfig.UartPort, aConfig.UartBaudrate))
|
||||
if (!Open(aConfig.UartPort, 115200))
|
||||
return false;
|
||||
|
||||
// 이중 시작 방지
|
||||
if (_serialComm != null && _serialComm.IsAlive)
|
||||
return true;
|
||||
|
||||
_serialPortThreadEnd = false;
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
_serialComm = new Thread(() => uartCommThread(_cts.Token))
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "UartCommThread"
|
||||
};
|
||||
_serialComm.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
_serialPortThreadEnd = true;
|
||||
var cts = _cts;
|
||||
_cts = null;
|
||||
if (cts != null && !cts.IsCancellationRequested)
|
||||
cts.Cancel();
|
||||
|
||||
var t = _serialComm;
|
||||
_serialComm = null;
|
||||
if (t != null && t.IsAlive)
|
||||
{
|
||||
try { t.Join(500); } catch { /* timeout 시 로그만 */ }
|
||||
}
|
||||
|
||||
Close(); // SerialPort 닫기 및 핸들러 해제
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region COMMPORT CONTROLS
|
||||
|
||||
private void sDataReceived(object sender, SerialDataReceivedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sp = (SerialPort)sender;
|
||||
int available = sp.BytesToRead;
|
||||
if (available <= 0) return;
|
||||
|
||||
byte[] tmp = new byte[available];
|
||||
int read = sp.Read(tmp, 0, tmp.Length);
|
||||
for (int i = 0; i < read; i++)
|
||||
_rxQ.Enqueue(tmp[i]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
/* 필요시 로그 */
|
||||
}
|
||||
}
|
||||
|
||||
private void sErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
|
||||
{
|
||||
//
|
||||
//csMakeDataFunction.SetData()
|
||||
}
|
||||
|
||||
private void sPinChanged(object sender, System.IO.Ports.SerialPinChangedEventArgs e)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
private bool Open(string cPort, int cBaudrate)
|
||||
{
|
||||
sPort = new SerialPort();
|
||||
sPort.PortName = cPort;
|
||||
sPort.BaudRate = cBaudrate;
|
||||
sPort.DataReceived += sDataReceived;
|
||||
sPort.ErrorReceived += sErrorReceived;
|
||||
sPort.PinChanged += sPinChanged;
|
||||
|
||||
try
|
||||
{
|
||||
sPort.Open();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Error Open - " + ex.Message);
|
||||
}
|
||||
|
||||
return sPort.IsOpen;
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
if (sPort != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
sPort.DataReceived -= sDataReceived;
|
||||
sPort.ErrorReceived -= sErrorReceived;
|
||||
sPort.PinChanged -= sPinChanged;
|
||||
if (sPort.IsOpen) sPort.Close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
sPort.Dispose();
|
||||
sPort = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PUBLIC FUNCTION
|
||||
|
||||
public void SetPolling(bool flag, int sId, ref CsDeviceData.DeviceModuleData aSystemData)
|
||||
{
|
||||
ModuleId = sId;
|
||||
UartPolling = flag;
|
||||
ModuleData = aSystemData;
|
||||
ModuleData.mNo = ModuleId;
|
||||
}
|
||||
|
||||
public void SetAutoTx(bool autoTx)
|
||||
{
|
||||
AutoUartTx = autoTx;
|
||||
}
|
||||
|
||||
public void SetWriteReg(ushort WriteAddr, byte[] WriteData, bool ReplyFlag, int type)
|
||||
{
|
||||
WriteRegAddr = WriteAddr;
|
||||
|
||||
TUartTRxData uartTRxData = new TUartTRxData();
|
||||
uartTRxData.type = type;
|
||||
uartTRxData.data = WriteData;
|
||||
uartTRxData.length = uartTRxData.data.Length;
|
||||
|
||||
UartTxBuff?.PutBuff(uartTRxData);
|
||||
}
|
||||
|
||||
public void SetParam(ushort WriteAddr, short[] WriteData)
|
||||
{
|
||||
short[] wData = new short[1];
|
||||
wData[0] = WriteData[0];
|
||||
|
||||
byte[] sData = CsModbusFunction.MakeWriteRegisterData((ushort)ModuleId, WriteAddr, wData);
|
||||
|
||||
if (sData != null)
|
||||
{
|
||||
TUartTRxData aData = new TUartTRxData();
|
||||
aData.length = sData.Length;
|
||||
aData.data = sData;
|
||||
|
||||
UartTxBuff.PutBuff(aData);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendProcessFromApp(int sId, int mode, int index, int flag
|
||||
, ref CsDeviceData.DeviceModuleData.DeviceParamData aParam, ref CsDeviceData.DeviceModuleData.DeviceCalibration aCalib)
|
||||
{
|
||||
short[] data = null;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case csConstData.UART_PARAM_LIST.CELL_UNDER_VOLTAGE: // Cell Under Volatge Parameter
|
||||
data = new short[1];
|
||||
data[0] = aParam.CellUnderVoltageWarning; SetParam((ushort)(mode + 0), data);
|
||||
data[0] = aParam.CellUnderVoltageTrip; SetParam((ushort)(mode + 1), data);
|
||||
data[0] = aParam.CellUnderVoltageRelease; SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.CELL_OVER_VOLTAGE: // Cell Over Volatge Parameter
|
||||
data = new short[1];
|
||||
data[0] = aParam.CellOverVoltageWarning; SetParam((ushort)(mode + 0), data);
|
||||
data[0] = aParam.CellOverVoltageTrip; SetParam((ushort)(mode + 1), data);
|
||||
data[0] = aParam.CellOverVoltageRelease; SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.PACK_UNDER_VOLTAGE: // Pack Under Volatge Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.SysUnderVoltageWarning * 10); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.SysUnderVoltageTrip * 10); SetParam((ushort)(mode + 1), data);
|
||||
data[0] = (short)(aParam.SysUnderVoltageRelease * 10); SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.PACK_OVER_VOLTAGE: // Pack Over Volatge Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.SysOverVoltageWarning * 10); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.SysOverVoltageTrip * 10); SetParam((ushort)(mode + 1), data);
|
||||
data[0] = (short)(aParam.SysOverVoltageRelease * 10); SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.CHG_LOW_TEMPERATURE: // Charge Low Temperature Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.ChaLowTempWarning + 50); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.ChaLowTempTrip + 50); SetParam((ushort)(mode + 1), data);
|
||||
data[0] = (short)(aParam.ChaLowTempRelease + 50); SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.CHG_HIGH_TEMPERATURE: // Pack High Temperature Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.ChaHighTempWarning + 50); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.ChaHighTempTrip + 50); SetParam((ushort)(mode + 1), data);
|
||||
data[0] = (short)(aParam.ChaHighTempRelease + 50); SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.DCH_LOW_TEMPERATURE: // Charge Low Temperature Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.DchLowTempWarning + 50); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.DchLowTempTrip + 50); SetParam((ushort)(mode + 1), data);
|
||||
data[0] = (short)(aParam.DchLowTempRelease + 50); SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.DCH_HIGH_TEMPERATURE: // Pack High Temperature Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.DchHighTempWarning + 50); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.DchHighTempTrip + 50); SetParam((ushort)(mode + 1), data);
|
||||
data[0] = (short)(aParam.DchHighTempRelease + 50); SetParam((ushort)(mode + 2), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.CHG_OVER_CURRENT: // Charge Over Current Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.ChaOverCurrentReleaseTime * 1); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.ChaOverCurrentTrip1 * 10); SetParam((ushort)(mode + 2), data);
|
||||
data[0] = (short)(aParam.ChaOverCurrentTrip2 * 10); SetParam((ushort)(mode + 5), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.DCH_OVER_CURRENT: // Discharge Over Current Parameter
|
||||
data = new short[1];
|
||||
data[0] = (short)(aParam.DchOverCurrentReleaseTime * 1); SetParam((ushort)(mode + 0), data);
|
||||
data[0] = (short)(aParam.DchOverCurrentTrip1 * 10); SetParam((ushort)(mode + 2), data);
|
||||
data[0] = (short)(aParam.DchOverCurrentTrip2 * 10); SetParam((ushort)(mode + 5), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.LOW_CAPACITY:
|
||||
data = new short[1];
|
||||
data[0] = aParam.LowSocWarning; SetParam((ushort)(mode + 0), data);
|
||||
break;
|
||||
case csConstData.UART_PARAM_LIST.DEFAULT_PARAM: // Default Parameter
|
||||
int i = 0;
|
||||
|
||||
Forms.fmxWait WaitForm = new Forms.fmxWait();
|
||||
WaitForm.StartPosition = FormStartPosition.CenterScreen;
|
||||
|
||||
try
|
||||
{
|
||||
//WaitForm.Show();
|
||||
data = new short[1];
|
||||
|
||||
i = csConstData.UART_PARAM_LIST.CELL_UNDER_VOLTAGE;
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Cell_UV; SetParam((ushort)(i + 0), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Cell_UV; SetParam((ushort)(i + 1), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Release_Cell_UV; SetParam((ushort)(i + 2), data);
|
||||
i = csConstData.UART_PARAM_LIST.PACK_UNDER_VOLTAGE;
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Pack_UV; SetParam((ushort)(i + 0), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Pack_UV; SetParam((ushort)(i + 1), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Release_Pack_UV; SetParam((ushort)(i + 2), data);
|
||||
i = csConstData.UART_PARAM_LIST.CELL_OVER_VOLTAGE;
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Cell_OV; SetParam((ushort)(i + 0), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Cell_OV; SetParam((ushort)(i + 1), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Release_Cell_OV; SetParam((ushort)(i + 2), data);
|
||||
i = csConstData.UART_PARAM_LIST.PACK_OVER_VOLTAGE;
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Pack_OV; SetParam((ushort)(i + 0), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Pack_OV; SetParam((ushort)(i + 1), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Release_Pack_OV; SetParam((ushort)(i + 2), data);
|
||||
|
||||
i = csConstData.UART_PARAM_LIST.CHG_OVER_CURRENT;
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Times_Charge_OC; SetParam((ushort)(i - 2), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.ReleaseTime_Charge_OC; SetParam((ushort)(i + 0), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Charge_OC1; SetParam((ushort)(i + 2), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Charge_OC2; SetParam((ushort)(i + 5), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Charge_OC1; SetParam((ushort)(i + 8), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Charge_OC2; SetParam((ushort)(i + 9), data);
|
||||
|
||||
i = csConstData.UART_PARAM_LIST.DCH_OVER_CURRENT;
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Times_Discharge_OC; SetParam((ushort)(i - 2), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.ReleaseTime_Discharge_OC; SetParam((ushort)(i + 0), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Discharge_OC1; SetParam((ushort)(i + 2), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Discharge_OC2; SetParam((ushort)(i + 5), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Discharge_OC1; SetParam((ushort)(i + 8), data);
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Discharge_OC2; SetParam((ushort)(i + 9), data);
|
||||
|
||||
i = csConstData.UART_PARAM_LIST.CHG_LOW_TEMPERATURE;
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Charge_UT + 50); SetParam((ushort)(i + 0), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Charge_UT + 50); SetParam((ushort)(i + 1), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Charge_UT + 50); SetParam((ushort)(i + 2), data);
|
||||
i = csConstData.UART_PARAM_LIST.CHG_HIGH_TEMPERATURE;
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Charge_OT + 50); SetParam((ushort)(i + 0), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Charge_OT + 50); SetParam((ushort)(i + 1), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Charge_OT + 50); SetParam((ushort)(i + 2), data);
|
||||
i = csConstData.UART_PARAM_LIST.DCH_LOW_TEMPERATURE;
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Discharge_UT + 50); SetParam((ushort)(i + 0), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Discharge_UT + 50); SetParam((ushort)(i + 1), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Discharge_UT + 50); SetParam((ushort)(i + 2), data);
|
||||
i = csConstData.UART_PARAM_LIST.DCH_HIGH_TEMPERATURE;
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Discharge_OT + 50); SetParam((ushort)(i + 0), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Discharge_OT + 50); SetParam((ushort)(i + 1), data);
|
||||
data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Discharge_OT + 50); SetParam((ushort)(i + 2), data);
|
||||
|
||||
i = csConstData.UART_PARAM_LIST.LOW_CAPACITY;
|
||||
data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Low_Capacity + 0; SetParam((ushort)(i + 0), data);
|
||||
|
||||
i = csConstData.UART_PARAM_LIST.ENV_LOW_TEMPERATURE;
|
||||
data[0] = 0; SetParam((ushort)(i + 0), data);
|
||||
data[0] = 0; SetParam((ushort)(i + 1), data);
|
||||
data[0] = 0; SetParam((ushort)(i + 2), data);
|
||||
i = csConstData.UART_PARAM_LIST.ENV_HIGH_TEMPERATURE;
|
||||
data[0] = 0; SetParam((ushort)(i + 0), data);
|
||||
data[0] = 0; SetParam((ushort)(i + 1), data);
|
||||
data[0] = 0; SetParam((ushort)(i + 2), data);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
//WaitForm?.Close();
|
||||
}
|
||||
break;
|
||||
case 15: // Capcity change
|
||||
data = new short[1];
|
||||
i = csConstData.UART_PARAM_LIST.DESIGN_CAPACITY;
|
||||
data[0] = (short)aCalib.CapCalib.DesignCapacity; SetParam((ushort)(i + 0), data);
|
||||
i = csConstData.UART_PARAM_LIST.SOC_VALUE;
|
||||
data[0] = (short)aCalib.CapCalib.SocValue; SetParam((ushort)(i + 0), data);
|
||||
break;
|
||||
case 16: // Charge Mode
|
||||
data = new short[1];
|
||||
i = csConstData.UART_CALIB_ADDR_LIST.CHAGE_MODE;
|
||||
data[0] = (short)aCalib.ChaMode.Mode; SetParam((ushort)(i + 0), data);
|
||||
data[0] = (short)aCalib.ChaMode.Value; SetParam((ushort)(i + 1), data);
|
||||
break;
|
||||
case 17: // BMS DateTime Set
|
||||
data = new short[2];
|
||||
i = csConstData.UART_CALIB_ADDR_LIST.BMS_DATETIME;
|
||||
data[0] = (short)aCalib.BmsDateTime.sValue[0];
|
||||
data[1] = (short)aCalib.BmsDateTime.sValue[1]; SetParam((ushort)(i + 0), data);
|
||||
break;
|
||||
case 18:
|
||||
data = new short[1];
|
||||
i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT_GYRO;
|
||||
data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 0), data);
|
||||
break;
|
||||
case 19: // Clear Protect
|
||||
data = new short[1];
|
||||
i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT_GYRO;
|
||||
data[0] = (short)0; SetParam((ushort)(i + 0), data);
|
||||
data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 0), data);
|
||||
break;
|
||||
case 20:
|
||||
data = new short[1];
|
||||
i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT_COMM;
|
||||
data[0] = (short)aCalib.AntiTheft.Comm; SetParam((ushort)(i + 0), data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RX BUFFERING
|
||||
|
||||
private int GetBuff()
|
||||
{
|
||||
return _rxQ.TryDequeue(out var b) ? (0x0100 | b) : 0;
|
||||
}
|
||||
|
||||
private void FlushBuff()
|
||||
{
|
||||
while (_rxQ.TryDequeue(out _)) { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TX BUFFERING
|
||||
|
||||
private byte[] MakeWriteRegData(ushort DevID, ushort WriteAddr, ushort WriteLen, ref ushort[] WriteData)
|
||||
{
|
||||
byte[] wData = null;
|
||||
|
||||
if (WriteLen > 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return wData;
|
||||
}
|
||||
|
||||
private string MakeCheckSum(byte[] sData, int len)
|
||||
{
|
||||
string result = "";
|
||||
int checksum = 0;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
checksum += sData[i + 1];
|
||||
}
|
||||
|
||||
checksum = ~checksum + 1;
|
||||
|
||||
result = String.Format("{0:X2}{1:X2}", (byte)(checksum >> 8), (byte)checksum);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] MakeTxDataUart()
|
||||
{
|
||||
byte[] sData = null;
|
||||
|
||||
rFlag2 = 0;
|
||||
|
||||
if ((UartTxBuff != null) && (UartTxBuff.CheckBuff()))
|
||||
{
|
||||
TUartTRxData sBuff = UartTxBuff.GetBuff();
|
||||
if (sBuff != null)
|
||||
{
|
||||
sData = sBuff.data;
|
||||
rFlag = true;
|
||||
rFlag2 = sBuff.type;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UartPolling && AutoUartTx)
|
||||
{
|
||||
ushort sCmd;
|
||||
switch (addr_ch)
|
||||
{
|
||||
case 0:
|
||||
addr_ch = 1;
|
||||
RequestRegAddr = 0x0000; //Byul Init 0x0000
|
||||
RequestRegLen = 0x0040;
|
||||
sCmd = csSerialCommFunction.READ_HOLDING_REG;
|
||||
rFlag = true;
|
||||
break;
|
||||
case 1:
|
||||
addr_ch = 2;
|
||||
RequestRegAddr = 0x0040;
|
||||
RequestRegLen = 0x0040;
|
||||
sCmd = csSerialCommFunction.READ_HOLDING_REG;
|
||||
rFlag = true;
|
||||
break;
|
||||
case 2:
|
||||
addr_ch = 3;
|
||||
RequestRegAddr = 0x0080;
|
||||
RequestRegLen = 0x0020 + 7;
|
||||
sCmd = csSerialCommFunction.READ_HOLDING_REG;
|
||||
rFlag = true;
|
||||
break;
|
||||
case 3:
|
||||
if (Config.ModuleQty > 1)
|
||||
{
|
||||
ModuleId++;
|
||||
if (ModuleId > Config.ModuleQty)
|
||||
{
|
||||
ModuleId = 1;
|
||||
}
|
||||
}
|
||||
addr_ch = 0;
|
||||
sCmd = csSerialCommFunction.NO_CMD;
|
||||
rFlag = false;
|
||||
break;
|
||||
default:
|
||||
addr_ch = 0;
|
||||
RequestRegAddr = 0x0000; //Byul Init 0x0000
|
||||
RequestRegLen = 100;
|
||||
sCmd = csSerialCommFunction.READ_HOLDING_REG;
|
||||
break;
|
||||
}
|
||||
if (sCmd == csSerialCommFunction.NO_CMD)
|
||||
{
|
||||
sData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
sData = csSerialCommFunction.MakeReadRegisterData((ushort)ModuleId, sCmd, RequestRegAddr, RequestRegLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region COMM THREAD
|
||||
|
||||
private readonly byte[] ReadBuf = new byte[RX_PACKET_BUFFER_SIZE];
|
||||
ushort rPosition = 0;
|
||||
bool BuffStart = false;
|
||||
|
||||
private void RecvPacketInit()
|
||||
{
|
||||
BuffStart = false;
|
||||
rPosition = 0;
|
||||
}
|
||||
|
||||
private void uartCommThread(CancellationToken token)
|
||||
{
|
||||
int RecvTimeout = Config.RecvWaitTime;
|
||||
int TimeOutCount = 0;
|
||||
|
||||
StartSend:
|
||||
while (!token.IsCancellationRequested && !_serialPortThreadEnd)
|
||||
{
|
||||
if (sPort == null || !sPort.IsOpen) { Thread.Sleep(50); continue; }
|
||||
|
||||
FlushBuff();
|
||||
|
||||
byte[] txData = MakeTxDataUart();
|
||||
if (txData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
sPort.Write(txData, 0, txData.Length);
|
||||
RaiseOnPrint(txData[0], csLog.trx_data_print(txData, txData.Length, 0));
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* 로그 */
|
||||
RaiseOnPrint(ModuleId, "UART-232 Thread - TX Buff Error !");
|
||||
}
|
||||
}
|
||||
|
||||
if (rFlag == true)
|
||||
{
|
||||
var deadline = DateTime.UtcNow.AddMilliseconds(RecvTimeout);
|
||||
RecvPacketInit();
|
||||
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
int gd = GetBuff();
|
||||
if ((gd & 0x0100) == 0) { Thread.Sleep(1); continue; }
|
||||
|
||||
byte cData = (byte)(gd & 0xFF);
|
||||
|
||||
if (!BuffStart)
|
||||
{
|
||||
if (cData == (byte)ModuleId || cData == 0x7F)
|
||||
{
|
||||
rPosition = 0;
|
||||
ReadBuf[rPosition++] = cData;
|
||||
BuffStart = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rPosition >= ReadBuf.Length) { RecvPacketInit(); continue; }
|
||||
ReadBuf[rPosition++] = cData;
|
||||
|
||||
int chk = CsSerialCommFunctionDelta.ModbusPacketFromSlaveCheck(ReadBuf, rPosition);
|
||||
if (chk == 0) continue;
|
||||
|
||||
RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1));
|
||||
|
||||
if (chk == 1)
|
||||
{
|
||||
int mID = ReadBuf[0];
|
||||
if (mID > 0) mID--;
|
||||
ModuleData.CommFail = false;
|
||||
ModuleData.ShelfCommFail = false;
|
||||
var rc = csSerialCommFunction.SerialRxProcess(
|
||||
ReadBuf, RequestRegAddr, (ushort)rPosition, ref ModuleData);
|
||||
|
||||
var snapshot1 = ModuleData; // 필요 시 DeepCopy
|
||||
RaiseOnUpdate(snapshot1);
|
||||
Thread.Sleep(5);
|
||||
goto StartSend;
|
||||
}
|
||||
else if (chk == 2)
|
||||
{
|
||||
rFlag = false;
|
||||
RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1));
|
||||
|
||||
goto StartSend;
|
||||
}
|
||||
else // -1
|
||||
{
|
||||
RecvPacketInit();
|
||||
Thread.Sleep(50);
|
||||
goto StartSend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// timeout
|
||||
if (rPosition > 0)
|
||||
{
|
||||
RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteRegAddr = 0;
|
||||
TimeOutCount++;
|
||||
if (TimeOutCount >= 5)
|
||||
{
|
||||
TimeOutCount = 5;
|
||||
//SystemData[0].CommFail = true;
|
||||
if (ModuleData.ShelfCommFail == false)
|
||||
{
|
||||
ModuleData.Reset();
|
||||
ModuleData.ShelfCommFail = true;
|
||||
}
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
var snapshot2 = ModuleData; // 필요 시 DeepCopy
|
||||
RaiseOnUpdate(snapshot2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
} /* if (rFlag == true) */
|
||||
rPosition = 0;
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SAFETY RAISE HELPER
|
||||
private void RaiseOnUpdate(CsDeviceData.DeviceModuleData snapshot)
|
||||
{
|
||||
var handler = OnUpdate;
|
||||
if (handler == null) return;
|
||||
|
||||
var args = new ModuleDataEventArgs(snapshot);
|
||||
|
||||
if (_ui != null && _ui.InvokeRequired)
|
||||
{
|
||||
try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); }
|
||||
catch { /* 폼 종료 중 등 예외 무시(로그만) */ }
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseOnDataRecv(byte[] data)
|
||||
{
|
||||
var handler = OnDataRecv;
|
||||
if (handler == null) return;
|
||||
|
||||
var args = new DataRecvEventArgs(data);
|
||||
|
||||
if (_ui != null && _ui.InvokeRequired)
|
||||
{
|
||||
try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); }
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseOnPrint(int id, string msg)
|
||||
{
|
||||
var handler = OnPrint;
|
||||
if (handler == null) return;
|
||||
|
||||
var args = new PrintEventArgs(id, msg);
|
||||
|
||||
if (_ui != null && _ui.InvokeRequired)
|
||||
{
|
||||
try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); }
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
handler(this, args);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user