초기 커밋.

This commit is contained in:
2025-12-19 13:59:34 +09:00
parent 1c0b03f88c
commit 79fea6964b
184 changed files with 94471 additions and 0 deletions

View 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
}
}

View 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
}
}

File diff suppressed because it is too large Load Diff

View 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
}
}