From f56051fa3102feb35ea60650ebda80f49e62d025 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期二, 05 八月 2025 14:54:24 +0800
Subject: [PATCH] 1. SGMeasurement实现规划的地址 2. 优化读取位时输入的地址不是8的倍数的问题 3. 添加读写心跳的功能

---
 SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp |  331 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 275 insertions(+), 56 deletions(-)

diff --git a/SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp b/SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp
index b946b61..a3f8234 100644
--- a/SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp
+++ b/SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp
@@ -8,6 +8,7 @@
 #include "SGMeasurementDlg.h"
 #include "afxdialogex.h"
 #include "SGIF.h"
+#include "Logger.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -33,6 +34,19 @@
 // 瀹氭椂鍣ㄧ浉鍏冲畯瀹氫箟
 #define TIMER_INTERVAL_MS		500
 #define TIMER_ID_OUTPUT_UPDATE	1
+
+// 璁℃椂瀹忓畾涔�
+#define MEASURE_FUNC_START() \
+	clock_t __startClock = clock();
+
+#define MEASURE_FUNC_END() \
+	do { \
+		clock_t __endClock = clock(); \
+		double __elapsedMs = 1000.0 * (__endClock - __startClock) / CLOCKS_PER_SEC; \
+		CString __strElapsed; \
+		__strElapsed.Format(_T("%s 鎵ц鑰楁椂锛�%.1f ms"), _T(__FUNCTION__), __elapsedMs); \
+		AppendLogLineRichStyled(__strElapsed, LOG_COLOR_SUCCESS); \
+	} while (0)
 
 class CAboutDlg : public CDialogEx
 {
@@ -71,7 +85,7 @@
 	, m_dOutValues{ 0.0, 0.0, 0.0, 0.0 }
 	, m_nUseTrigger(0)
 	, m_nSavePointCount(100000)
-	, m_fJumpThreshold(1.0f)
+	, m_fJumpThreshold(0.2f)
 	, m_nJumpWindow(3)
 	, m_nValleyMargin(0)
 	, m_nMinGlass1Count(10)
@@ -115,6 +129,8 @@
 		Shell_NotifyIcon(NIM_DELETE, &m_trayIconData);
 		m_bTrayIconCreated = FALSE;
 	}
+
+	m_plcListener.Stop();
 
 	DestroyWindow();
 	CDialogEx::OnClose();
@@ -175,7 +191,7 @@
 	m_editLog.ReplaceSel(_T(""));   // 鍒犻櫎
 }
 
-void CSGMeasurementDlg::AppendLogLineRichStyled(const CString& content, COLORREF color /*= RGB(0, 0, 0)*/)
+void CSGMeasurementDlg::AppendLogLineRichStyled(const CString& strContent, COLORREF color /*= RGB(0, 0, 0)*/)
 {
 	if (!::IsWindow(GetSafeHwnd()) || !::IsWindow(m_editLog.GetSafeHwnd())) {
 		return;
@@ -197,16 +213,41 @@
 	m_editLog.SetSelectionCharFormat(cfTime);
 	m_editLog.ReplaceSel(strTimestamp);
 
+	// 鐢熸垚鏃ュ織绾у埆鏍囩
+	CString strLevel;
+	if (color == LOG_COLOR_WARNING) {
+		strLevel = _T("[璀﹀憡]");
+	}
+	else if (color == LOG_COLOR_ERROR) {
+		strLevel = _T("[閿欒]");
+	}
+	else if (color == LOG_COLOR_NORMAL) {
+		strLevel = _T("[淇℃伅]");
+	}
+	else if (color == LOG_COLOR_SUCCESS) {
+		strLevel = _T("[鎴愬姛]");
+	}
+	else {
+		strLevel = _T("[鏈煡]");
+	}
+
 	// 鎻掑叆鏃ュ織姝f枃锛堜紶鍏ラ鑹诧級
 	CHARFORMAT2 cfMsg = {};
 	cfMsg.cbSize = sizeof(cfMsg);
 	cfMsg.dwMask = CFM_COLOR;
 	cfMsg.crTextColor = color;
 	m_editLog.SetSelectionCharFormat(cfMsg);
-	m_editLog.ReplaceSel(content + _T("\r\n"));
+	m_editLog.ReplaceSel(strLevel + strContent + _T("\r\n"));
 
 	// 闄愬埗鏈�澶ц鏁�
 	TrimRichEditLineLimit(100);
+
+	// 鎷兼帴瀹屾暣鏃ュ織琛�
+	CString strFullLogLine;
+	strFullLogLine.Format(_T("%s %s"), strLevel, strContent);
+
+	// 鍐欏叆鏃ュ織鏂囦欢
+	// LOG_LINE(strFullLogLine);
 }
 
 void CSGMeasurementDlg::HighlightAllMatches(const CString& strSearch, COLORREF clrHighlight/* = RGB(255, 165, 0)*/)
@@ -311,32 +352,38 @@
 		return false;
 	}
 
-	RC nRet = SGIF_CloseDevice(DeviceID);
-	if (nRet == RC_OK) {
-		if (m_bSaving) {
-			m_bSaving = FALSE;
-			nRet = SGIF_DataStorageStop(DeviceID);
-			if (nRet == RC_OK) {
-				AppendLogLineRichStyled(_T("鏁版嵁瀛樺偍宸插仠姝€��"), LOG_COLOR_SUCCESS);
-			}
-			else {
-				CString strError;
-				strError.Format(_T("鍋滄鏁版嵁瀛樺偍澶辫触锛岄敊璇爜锛�%#X"), nRet);
-				AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
-			}
-		}
+	// 鍋滄瀹氭椂鍣紝閬垮厤鍦ㄦ柇寮�鍚庝粛灏濊瘯鏇存柊杈撳嚭鏁版嵁
+	KillTimer(TIMER_ID_OUTPUT_UPDATE);
 
+	// 鍏堝仠姝㈡暟鎹瓨鍌紙濡傛灉姝e湪杩涜锛�
+	RC nRet;
+	if (m_bConnected && m_bSaving) {
+		nRet = SGIF_DataStorageStop(DeviceID);
+		if (nRet == RC_OK) {
+			m_bSaving = FALSE;
+			AppendLogLineRichStyled(_T("鏁版嵁瀛樺偍宸插仠姝€��"), LOG_COLOR_SUCCESS);
+		}
+		else {
+			CString strError;
+			strError.Format(_T("鍋滄鏁版嵁瀛樺偍澶辫触锛岄敊璇爜锛�%#X"), nRet);
+			AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
+		}
+	}
+
+	// 鏂紑璁惧杩炴帴
+	nRet = SGIF_CloseDevice(DeviceID);
+	if (nRet == RC_OK) {
 		AppendLogLineRichStyled(_T("鏂紑杩炴帴鎴愬姛锛�"), LOG_COLOR_SUCCESS);
 
 		m_bConnected = FALSE;
 		UpdateControlStatus(FALSE, FALSE);
-		KillTimer(TIMER_ID_OUTPUT_UPDATE);
 		return true;
 	}
 	else {
 		CString strError;
 		strError.Format(_T("鏂紑杩炴帴澶辫触锛岄敊璇爜锛�%#X"), nRet);
 		AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
+		AppendLogLineRichStyled(_T("淇濇寔褰撳墠鐘舵�侊紝鏂紑澶辫触銆�"), LOG_COLOR_ERROR);
 
 		UpdateControlStatus(m_bConnected, m_bSaving);
 		return false;
@@ -345,6 +392,8 @@
 
 void CSGMeasurementDlg::CleanInvalidValuesInPlace(int nOutNo, std::vector<float>& vecData, float fInvalid/* = -999.0f*/)
 {
+	MEASURE_FUNC_START();
+
 	// 鎵惧埌绗竴涓湁鏁堝��
 	auto itStart = std::find_if(vecData.begin(), vecData.end(), [=](float v) { return v > fInvalid; });
 
@@ -380,62 +429,87 @@
 	else {
 		vecData.clear();
 	}
+
+	MEASURE_FUNC_END();
 }
 
-bool CSGMeasurementDlg::SplitGlassSegments(int nOutNo, const std::vector<float>& vecData, std::vector<float>& vecGlass1, std::vector<float>& vecGlass2, float fJumpThreshold /*= 0.2f*/, int nWindow /*= 3*/, int nValleyMargin /*= 0*/, int nMinGlass1Count /*= 10*/)
+bool CSGMeasurementDlg::SplitGlassSegments(int nOutNo, const std::vector<float>& vecData, std::vector<float>& vecGlass1, std::vector<float>& vecGlass2, float fJumpThreshold /*= 0.2f*/, int nWindow /*= 3*/, int nValleyMargin /*= 0*/, int nMinGlassCount /*= 10*/)
 {
-	const int n = static_cast<int>(vecData.size());
-	if (n < 2 * nWindow + 1 + nMinGlass1Count) {
-		CString strError;
-		strError.Format(_T("OUT%d: 鏁版嵁閲忎笉瓒筹紝鑷冲皯闇�瑕� %d 涓偣鎵嶈兘杩涜鍒囧壊銆�"), nOutNo, 2 * nWindow + 1 + nMinGlass1Count);
-		AppendLogLineRichStyled(strError, LOG_COLOR_WARNING);
+	MEASURE_FUNC_START();
+
+	CString strLog;
+	const int nTotal = static_cast<int>(vecData.size());
+
+	if (nTotal < 2 * nWindow + 1 + nMinGlassCount) {
+		strLog.Format(_T("OUT%d: 鏁版嵁閲忎笉瓒筹紝鑷冲皯闇�瑕� %d 涓偣鎵嶈兘鍒囧壊銆�"), nOutNo, 2 * nWindow + 1 + nMinGlassCount);
+		AppendLogLineRichStyled(strLog, LOG_COLOR_WARNING);
 		return false;
 	}
 
-	int nValleyIdx = -1;
-	for (int i = nWindow; i < n - nWindow - nMinGlass1Count; ++i) {
-		float fDelta = std::fabs(vecData[i + nWindow] - vecData[i - nWindow]);
-		if (fDelta > fJumpThreshold) {
-			// 鎵� valley 鐐癸紙绋嶅井寰�鍚庤烦鍑犳浠ラ伩寮�杈圭紭鎶栧姩锛�
-			int nSearchStart = i + nWindow + 2;
-			int nSearchEnd = min(nSearchStart + 10, n);
+	// 浠庝腑闂村悜涓よ竟鏌ユ壘 valley 鍊欓�夌偣
+	int nMid = nTotal / 2;
+	int nValleyLeft = -1, nValleyRight = -1;
 
-			auto itValley = std::min_element(vecData.begin() + nSearchStart, vecData.begin() + nSearchEnd,
-				[](float a, float b) {
-					return (a > -900.0f && b > -900.0f) ? a < b : false;
-				});
-
-			if (itValley != vecData.begin() + nSearchEnd) {
-				nValleyIdx = static_cast<int>(std::distance(vecData.begin(), itValley));
-				break;
-			}
+	// 鍚戝乏鏌ユ壘 valley
+	for (int i = nMid; i >= nWindow; --i) {
+		float fDelta = std::fabs(vecData[i] - vecData[i - nWindow]);
+		if (fDelta > fJumpThreshold &&
+			vecData[i] < vecData[i - 1] &&
+			vecData[i] < vecData[i + 1]) {
+			nValleyLeft = i;
+			break;
 		}
 	}
 
-	if (nValleyIdx < 0 || nValleyIdx < nMinGlass1Count) {
-		AppendLogLineRichStyled(_T("鏈壘鍒板悎閫� valley 鐐癸紝鎴� valley 澶潬鍓嶃��"), LOG_COLOR_WARNING);
-		return false;
+	// 鍚戝彸鏌ユ壘 valley
+	for (int i = nMid; i <= nTotal - nWindow - 2; ++i) {
+		float fDelta = std::fabs(vecData[i + nWindow] - vecData[i]);
+		if (fDelta > fJumpThreshold &&
+			vecData[i] < vecData[i - 1] &&
+			vecData[i] < vecData[i + 1]) {
+			nValleyRight = i;
+			break;
+		}
 	}
 
-	// 浠� valley 澶勫紑濮嬪垏鍓诧紝鎴栬�呭線鍚庡亸绉�
-	int nCutStart = min(nValleyIdx + nValleyMargin, n);
-	vecGlass1.assign(vecData.begin(), vecData.begin() + nCutStart);
-	vecGlass2.assign(vecData.begin() + nCutStart, vecData.end());
+	// 閫夊畾 valley 鐐�
+	int nValleyIdx = -1;
+	if (nValleyLeft > 0 && nValleyRight > 0) {
+		nValleyIdx = (vecData[nValleyLeft] < vecData[nValleyRight]) ? nValleyLeft : nValleyRight;
+	}
+	else if (nValleyLeft > 0) {
+		nValleyIdx = nValleyLeft;
+	}
+	else if (nValleyRight > 0) {
+		nValleyIdx = nValleyRight;
+	}
+
+	// fallback: valley 鏈壘鍒帮紝浣跨敤涓棿鍒囧壊娉�
+	if (nValleyIdx < 0) {
+		AppendLogLineRichStyled(_T("鏈壘鍒� valley 璺冲彉鐐癸紝浣跨敤涓棿浣嶇疆鍒囧壊銆�"), LOG_COLOR_WARNING);
+		nValleyIdx = nMid;
+	}
+
+	// 搴旂敤鍒囧壊浣嶇疆锛岄檺鍒惰竟鐣�
+	int nCutPos = max(1, min(nTotal - 1, nValleyIdx + nValleyMargin));
+
+	vecGlass1.assign(vecData.begin(), vecData.begin() + nCutPos);
+	vecGlass2.assign(vecData.begin() + nCutPos, vecData.end());
 
 	int nGlass1Count = static_cast<int>(vecGlass1.size());
 	int nGlass2Count = static_cast<int>(vecGlass2.size());
 
 	// 鏃ュ織杈撳嚭
-	CString strLog;
-	strLog.Format(_T("OUT%d: 鍒囧壊鎴愬姛锛岀涓�鐗囩幓鐠冩暟鎹噺 %d锛岀浜岀墖鐜荤拑鏁版嵁閲� %d銆�"), nOutNo, nGlass1Count, nGlass2Count);
+	strLog.Format(_T("OUT%d: 鍒囧壊鎴愬姛锛岀涓�鐗囩幓鐠� %d 鐐癸紝绗簩鐗囩幓鐠� %d 鐐广��"), nOutNo, nGlass1Count, nGlass2Count);
 	AppendLogLineRichStyled(strLog, LOG_COLOR_SUCCESS);
 
-	if (nGlass1Count < nMinGlass1Count) {
-		strLog.Format(_T("OUT%d: 绗竴鐗囩幓鐠冩暟鎹噺杩囧皯锛屽彲鑳藉奖鍝嶅悗缁鐞嗐��"), nOutNo);
+	if (nGlass1Count < nMinGlassCount) {
+		strLog.Format(_T("OUT%d: 绗竴鐗囩幓鐠冩暟鎹噺灏戜簬 %d 鐐癸紝鍙兘褰卞搷璁$畻銆�"), nOutNo, nMinGlassCount);
 		AppendLogLineRichStyled(strLog, LOG_COLOR_WARNING);
 	}
-	if (nGlass2Count < nMinGlass1Count) {
-		strLog.Format(_T("OUT%d: 绗簩鐗囩幓鐠冩暟鎹噺杩囧皯锛屽彲鑳藉奖鍝嶅悗缁鐞嗐��"), nOutNo);
+
+	if (nGlass2Count < nMinGlassCount) {
+		strLog.Format(_T("OUT%d: 绗簩鐗囩幓鐠冩暟鎹噺灏戜簬 %d 鐐癸紝鍙兘褰卞搷璁$畻銆�"), nOutNo, nMinGlassCount);
 		AppendLogLineRichStyled(strLog, LOG_COLOR_WARNING);
 	}
 
@@ -445,11 +519,61 @@
 	AppendLogLineRichStyled(_T("绗簩鐗囩幓鐠冩暟鎹細"), LOG_COLOR_NORMAL);
 	PrintSampleData(nOutNo, vecGlass2);
 
-	return !vecGlass1.empty() && !vecGlass2.empty();
+	MEASURE_FUNC_END();
+
+	return true;
+}
+
+bool CSGMeasurementDlg::FilterDominantGroup(int nOutNo, const std::vector<float>& vecInput, std::vector<float>& vecOutput)
+{
+	MEASURE_FUNC_START();
+
+	if (vecInput.empty()) {
+		AppendLogLineRichStyled(_T("杈撳叆鏁版嵁涓虹┖锛屾棤娉曡繘琛屽垎缁勭瓫閫夈��"), LOG_COLOR_WARNING);
+		return false;
+	}
+
+	// 鍒嗙粍锛歮ap<int鏁存暟閮ㄥ垎, vector<float>>
+	std::map<int, std::vector<float>> mapGroup;
+	for (float fVal : vecInput) {
+		int nKey = static_cast<int>(fVal);
+		mapGroup[nKey].push_back(fVal);
+	}
+
+	// 鎵惧嚭鏁伴噺鏈�澶氱殑閭g粍
+	size_t nMaxCount = 0;
+	auto itMaxGroup = mapGroup.begin();
+	for (auto it = mapGroup.begin(); it != mapGroup.end(); ++it) {
+		if (it->second.size() > nMaxCount) {
+			nMaxCount = it->second.size();
+			itMaxGroup = it;
+		}
+	}
+
+	if (nMaxCount == 0) {
+		AppendLogLineRichStyled(_T("鎵�鏈夋暟鎹兘琚繃婊ゆ垨涓虹┖锛屾棤娉曠瓫閫夊嚭涓昏鍒嗙粍銆�"), LOG_COLOR_WARNING);
+		return false;
+	}
+
+	vecOutput = itMaxGroup->second;
+
+	CString strLog;
+	strLog.Format(_T("鎴愬姛浠� %d 涓粍涓瓫閫夊嚭涓昏鍒嗙粍锛氬�肩害涓� %d锛屽叡 %zu 涓偣銆�"), static_cast<int>(mapGroup.size()), itMaxGroup->first, nMaxCount);
+	AppendLogLineRichStyled(strLog, LOG_COLOR_SUCCESS);
+
+	// 鎵撳嵃绛涢�夊悗鐨勬暟鎹�
+	AppendLogLineRichStyled(_T("绛涢�夊悗鐨勪富瑕佸垎缁勬暟鎹細"), LOG_COLOR_NORMAL);
+	PrintSampleData(nOutNo, vecOutput);
+
+	MEASURE_FUNC_END();
+
+	return true;
 }
 
 bool CSGMeasurementDlg::ExtractStableRegionFixed(int nOutNo, const std::vector<float>& vecIn, std::vector<float>& vecOut, int nFixedCount/* = 5*/, float fMaxDelta/* = 0.04f*/)
 {
+	MEASURE_FUNC_START();
+
 	const int n = static_cast<int>(vecIn.size());
 	if (n < nFixedCount) {
 		CString strError;
@@ -494,11 +618,15 @@
 	AppendLogLineRichStyled(_T("鎻愬彇鐨勭ǔ瀹氬尯鏁版嵁锛�"), LOG_COLOR_NORMAL);
 	PrintSampleData(nOutNo, vecOut);
 
+	MEASURE_FUNC_END();
+
 	return true;
 }
 
 bool CSGMeasurementDlg::CalcGlassOffset(const std::vector<float>& vecGlass1, const std::vector<float>& vecGlass2, float& fAvg1, float& fAvg2, float& fOffset)
 {
+	MEASURE_FUNC_START();
+
 	if (vecGlass1.empty() || vecGlass2.empty()) {
 		AppendLogLineRichStyled(_T("绋冲畾鍖烘暟鎹负绌猴紝鏃犳硶璁$畻骞冲潎鍊煎拰鍋忕Щ銆�"), LOG_COLOR_WARNING);
 		return false;
@@ -516,6 +644,8 @@
 	CString strLog;
 	strLog.Format(_T("绗竴鐗囩幓鐠冨钩鍧囧��: %.3f锛岀浜岀墖鐜荤拑骞冲潎鍊�: %.3f锛屽亸绉婚噺: %.3f"), fAvg1, fAvg2, fOffset);
 	AppendLogLineRichStyled(strLog, LOG_COLOR_SUCCESS);
+
+	MEASURE_FUNC_END();
 
 	return true;
 }
@@ -586,6 +716,8 @@
 
 float CSGMeasurementDlg::AnalyzeStoredData(int nOutNo)
 {
+	MEASURE_FUNC_START();
+
 	UpdateData(TRUE);
 
 	if (m_nUseTrigger) {
@@ -634,9 +766,29 @@
 		return -1.0f;
 	}
 
+	std::vector<float> vecGlass1Filtered, vecGlass2Filtered;
+	bool bGlass1Filtered = FilterDominantGroup(nOutNo, vecGlass1, vecGlass1Filtered);
+	bool bGlass2Filtered = FilterDominantGroup(nOutNo, vecGlass2, vecGlass2Filtered);
+
+	if (!bGlass1Filtered) {
+		AppendLogLineRichStyled(_T("Glass1 鍒嗘涓湭鑳借瘑鍒嚭涓绘暟鎹粍锛屼娇鐢ㄥ師濮嬫暟鎹��"), LOG_COLOR_WARNING);
+		vecGlass1Filtered = vecGlass1;
+	}
+	else {
+		AppendLogLineRichStyled(_T("Glass1 涓绘暟鎹粍宸叉彁鍙栥��"), LOG_COLOR_SUCCESS);
+	}
+
+	if (!bGlass2Filtered) {
+		AppendLogLineRichStyled(_T("Glass2 鍒嗘涓湭鑳借瘑鍒嚭涓绘暟鎹粍锛屼娇鐢ㄥ師濮嬫暟鎹��"), LOG_COLOR_WARNING);
+		vecGlass2Filtered = vecGlass2;
+	}
+	else {
+		AppendLogLineRichStyled(_T("Glass2 涓绘暟鎹粍宸叉彁鍙栥��"), LOG_COLOR_SUCCESS);
+	}
+
 	std::vector<float> vecStable1, vecStable2;
-	bool bStable1 = ExtractStableRegionFixed(nOutNo, vecGlass1, vecStable1, m_nFixedCount, m_fMaxDelta);
-	bool bStable2 = ExtractStableRegionFixed(nOutNo, vecGlass2, vecStable2, m_nFixedCount, m_fMaxDelta);
+	bool bStable1 = ExtractStableRegionFixed(nOutNo, vecGlass1Filtered, vecStable1, m_nFixedCount, m_fMaxDelta);
+	bool bStable2 = ExtractStableRegionFixed(nOutNo, vecGlass2Filtered, vecStable2, m_nFixedCount, m_fMaxDelta);
 
 	float fAvg1 = 0.0f, fAvg2 = 0.0f, fOffset = 0.0f;
 	if (bStable1 && bStable2) {
@@ -647,6 +799,8 @@
 		AppendLogLineRichStyled(_T("鏈兘鎻愬彇鍒扮ǔ瀹氬尯鏁版嵁锛屽皾璇曚娇鐢ㄥ師濮嬪垎娈垫暟鎹绠楀亸绉汇��"), LOG_COLOR_WARNING);
 		CalcGlassOffset(vecGlass1, vecGlass2, fAvg1, fAvg2, fOffset);
 	}
+
+	MEASURE_FUNC_END();
 
 	return fOffset;
 }
@@ -728,6 +882,66 @@
 	// 鍒濆鍖朓P鍦板潃鎺т欢锛岃缃负榛樿IP鍦板潃
 	((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS))->SetAddress(192, 168, 0, 10);
 
+	// 璁剧疆 PLC 鐩戝惉鍣ㄧ殑鏃ュ織鍥炶皟鍑芥暟
+	m_plcListener.SetLogCallback([this](const CString& msg, int type) {
+		if (type == -1) {
+			AppendLogLineRichStyled(msg, LOG_COLOR_ERROR);
+		}
+		else if (type == 0) {
+			AppendLogLineRichStyled(msg, LOG_COLOR_SUCCESS);
+		}
+		else if (type == 1) {
+			AppendLogLineRichStyled(msg, LOG_COLOR_WARNING);
+		}
+		else if (type == 2) {
+			AppendLogLineRichStyled(msg, LOG_COLOR_NORMAL);
+		}
+	});
+
+	// 鍒濆鍖� PLC 鐩戝惉鍣�
+	m_plcListener.Initialize(StationIdentifier(0, 255), 200);
+
+	// 璁剧疆 PLC 鐩戝惉鍣ㄧ殑寮�濮嬮噰闆嗗洖璋冨嚱鏁�
+	m_plcListener.SetStartCallback([this]() {
+		if (!m_bConnected) {
+			ConnectToDevice();
+		}
+
+		if (InitDataStorage()) {
+			StartDataStorage();
+			UpdateControlStatus(m_bConnected, m_bSaving);
+		}
+	});
+
+	// 璁剧疆 PLC 鐩戝惉鍣ㄧ殑鍋滄閲囬泦鍥炶皟鍑芥暟
+	m_plcListener.SetStopCallback([this]() { 
+		StopDataStorage();
+		UpdateControlStatus(m_bConnected, m_bSaving);
+	});
+
+	// 璁剧疆 PLC 鐩戝惉鍣ㄧ殑鍒嗘瀽鍥炶皟鍑芥暟
+	m_plcListener.SetAnalyzeCallback([this]() {
+		if (!m_bConnected) {
+			AppendLogLineRichStyled(_T("璁惧鏈繛鎺ワ紝璇峰厛杩炴帴璁惧銆�"), LOG_COLOR_WARNING);
+			return std::array<double, 4>{ -1.0, -1.0, -1.0, -1.0 };
+		}
+
+		std::array<double, 4> result;
+		for (int i = 0; i < 4; ++i) {
+			result[i] = AnalyzeStoredData(i + 1); // OUT1 ~ OUT4
+		}
+
+		if (std::any_of(result.begin(), result.end(), [](double v) { return v < 0; })) {
+			AppendLogLineRichStyled(_T("鍒嗘瀽澶辫触锛屾煇浜涜緭鍑虹鍙f暟鎹棤鏁堛��"), LOG_COLOR_ERROR);
+			return std::array<double, 4>{ -1.0, -1.0, -1.0, -1.0 };
+		}
+
+		CString strLog;
+		strLog.Format(_T("鍒嗘瀽缁撴灉锛歄UT1: %.3f, OUT2: %.3f, OUT3: %.3f, OUT4: %.3f"), result[0], result[1], result[2], result[3]);
+		return result;
+	});
+	m_plcListener.Start();
+
 	// 鍒濆鍖栨棩蹇楁
 	AppendLogLineRichStyled(_T("鍑嗗灏辩华..."), LOG_COLOR_SUCCESS);
 
@@ -794,6 +1008,11 @@
 			CString strError;
 			strError.Format(_T("鑾峰彇娴嬮噺鍊煎け璐ワ紝閿欒鐮侊細%#X"), nRet);
 			AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
+
+			// 鏂紑杩炴帴
+			if (m_bConnected) {
+				DisconnectFromDevice();
+			}
 		}
 	}
 

--
Gitblit v1.9.3