From 02c39baf5375f1588e9c20dc06d89a0605077672 Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期一, 10 二月 2025 10:41:44 +0800
Subject: [PATCH] Merge branch 'liuyang' into clh

---
 SourceCode/Bond/Servo/Servo.vcxproj                     |    4 
 SourceCode/Bond/x64/Debug/Res/Servo002.bmp              |    0 
 SourceCode/Bond/Servo/AlarmManager.cpp                  |  289 +++++++++++++
 SourceCode/Bond/BoounionPLC/BoounionPLC.rc              |    0 
 SourceCode/Bond/BoounionPLC/AddPLCInfo.cpp              |  124 +++++
 SourceCode/Bond/BoounionPLC/Resource.h                  |    0 
 SourceCode/Bond/Servo/Servo.cpp                         |   44 ++
 SourceCode/Bond/Servo/stdafx.h                          |   24 +
 SourceCode/Bond/BoounionPLC/AxisSettingsDlg.cpp         |  205 ++++++--
 SourceCode/Bond/BoounionPLC/AxisSettingsDlg.h           |   26 
 SourceCode/Bond/Servo/ServoDlg.cpp                      |  126 +++++
 SourceCode/Bond/x64/Debug/Res/Robot001.bmp              |    0 
 /dev/null                                               |   74 ---
 SourceCode/Bond/BoounionPLC/AxisDetailSettingsDlg.cpp   |   10 
 SourceCode/Bond/Servo/Servo.vcxproj.filters             |    2 
 SourceCode/Bond/BoounionPLC/BoounionPLCDlg.cpp          |   20 
 SourceCode/Bond/Servo/ServoGraph.cpp                    |   92 +++-
 SourceCode/Bond/BoounionPLC/AddPLCInfo.h                |   43 +
 SourceCode/Bond/Servo/AlarmManager.h                    |  163 +++++++
 SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj.filters |    4 
 SourceCode/Bond/Servo/Servo.h                           |    2 
 SourceCode/Bond/Servo/ServoGraph.h                      |    7 
 SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj         |    4 
 SourceCode/Bond/Servo/ServoDlg.h                        |    8 
 24 files changed, 1,079 insertions(+), 192 deletions(-)

diff --git a/SourceCode/Bond/BoounionPLC/AddPLCInfo.cpp b/SourceCode/Bond/BoounionPLC/AddPLCInfo.cpp
new file mode 100644
index 0000000..c9a8095
--- /dev/null
+++ b/SourceCode/Bond/BoounionPLC/AddPLCInfo.cpp
@@ -0,0 +1,124 @@
+锘�// AddPLCInfo.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "BoounionPLC.h"
+#include "afxdialogex.h"
+#include "AddPLCInfo.h"
+
+
+// CAddPLCInfo 瀵硅瘽妗�
+
+IMPLEMENT_DYNAMIC(CAddPLCInfo, CDialogEx)
+
+CAddPLCInfo::CAddPLCInfo(CWnd* pParent /*=nullptr*/)
+	: CDialogEx(IDD_DIALOG_ADD_PLC_INFO, pParent)
+{
+
+}
+
+CAddPLCInfo::~CAddPLCInfo()
+{
+}
+
+CString CAddPLCInfo::GetPLCName() { return m_strPLCName; }
+
+CString CAddPLCInfo::GetIP() { return m_strIP; }
+
+CString CAddPLCInfo::GetPort() { return m_strPort; }
+
+bool CAddPLCInfo::IsValidPort(const CString& strPort)
+{
+	// 鍒ゆ柇鏄惁涓烘暟瀛�
+	for (int i = 0; i < strPort.GetLength(); ++i) {
+		if (!_istdigit(strPort[i])) {
+			return false; // 闈炴暟瀛�
+		}
+	}
+
+	// 杞崲涓烘暣鏁板苟鍒ゆ柇鑼冨洿
+	int nPort = _ttoi(strPort);
+	return nPort >= 1 && nPort <= 65535;
+}
+
+bool CAddPLCInfo::IsValidIPAddress(const CString& strIP1, const CString& strIP2, const CString& strIP3, const CString& strIP4)
+{
+	// 鏍¢獙姣忎竴娈垫槸鍚︽槸鏁板瓧骞跺湪鑼冨洿鍐�
+	return IsValidIPSegment(strIP1) &&
+		IsValidIPSegment(strIP2) &&
+		IsValidIPSegment(strIP3) &&
+		IsValidIPSegment(strIP4);
+}
+
+bool CAddPLCInfo::IsValidIPSegment(const CString& strSegment)
+{
+	// 鍒ゆ柇鏄惁涓烘暟瀛�
+	for (int i = 0; i < strSegment.GetLength(); ++i) {
+		if (!_istdigit(strSegment[i])) {
+			return false; // 闈炴暟瀛�
+		}
+	}
+
+	// 杞崲涓烘暣鏁板苟鍒ゆ柇鑼冨洿
+	int nSegment = _ttoi(strSegment);
+	return nSegment >= 0 && nSegment <= 255;
+}
+
+void CAddPLCInfo::DoDataExchange(CDataExchange* pDX)
+{
+	CDialogEx::DoDataExchange(pDX);
+	DDX_Control(pDX, IDC_EDIT_PLC_NAME, m_editPLCName);
+	DDX_Control(pDX, IDC_EDIT_PORT, m_editPort);
+}
+
+
+BEGIN_MESSAGE_MAP(CAddPLCInfo, CDialogEx)
+	ON_BN_CLICKED(IDOK, &CAddPLCInfo::OnBnClickedOk)
+END_MESSAGE_MAP()
+
+
+// CAddPLCInfo 娑堟伅澶勭悊绋嬪簭
+
+
+void CAddPLCInfo::OnBnClickedOk()
+{
+	// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+	CString strIPAddr1;
+	CString strIPAddr2;
+	CString strIPAddr3;
+	CString strIPAddr4;
+
+	m_editPLCName.GetWindowText(m_strPLCName);
+	if (m_strPLCName.IsEmpty()) {
+		AfxMessageBox(_T("PLC 鍚嶇О涓嶈兘涓虹┖锛�"));
+		return;
+	}
+
+	// 鏍¢獙绔彛鍙�
+	m_editPort.GetWindowText(m_strPort);
+	if (!IsValidPort(m_strPort)) {
+		AfxMessageBox(_T("绔彛鍙蜂笉鍚堟硶锛佽杈撳叆 1 鍒� 65535 涔嬮棿鐨勬暟瀛椼��"));
+		return;
+	}
+
+	// 鏍¢獙IP鍦板潃
+	GetDlgItem(IDC_EDIT_IP_ADDR1)->GetWindowText(strIPAddr1);
+	GetDlgItem(IDC_EDIT_IP_ADDR2)->GetWindowText(strIPAddr2);
+	GetDlgItem(IDC_EDIT_IP_ADDR3)->GetWindowText(strIPAddr3);
+	GetDlgItem(IDC_EDIT_IP_ADDR4)->GetWindowText(strIPAddr4);
+
+	if (strIPAddr1.IsEmpty() || strIPAddr2.IsEmpty() || strIPAddr3.IsEmpty() || strIPAddr4.IsEmpty()) {
+		AfxMessageBox(_T("IP 鍦板潃涓嶅悎娉曪紒姣忔涓嶈兘涓虹┖銆�"));
+		return;
+	}
+
+	if (!IsValidIPAddress(strIPAddr1, strIPAddr2, strIPAddr3, strIPAddr4)) {
+		AfxMessageBox(_T("IP 鍦板潃涓嶅悎娉曪紒姣忔蹇呴』鏄� 0 鍒� 255 鐨勬暟瀛椼��"));
+		return;
+	}
+
+	// 鎷兼帴IP鍦板潃
+	m_strIP.Format(_T("%s.%s.%s.%s"), strIPAddr1, strIPAddr2, strIPAddr3, strIPAddr4);
+
+	CDialogEx::OnOK();
+}
diff --git a/SourceCode/Bond/BoounionPLC/AddPLCInfo.h b/SourceCode/Bond/BoounionPLC/AddPLCInfo.h
new file mode 100644
index 0000000..89ff176
--- /dev/null
+++ b/SourceCode/Bond/BoounionPLC/AddPLCInfo.h
@@ -0,0 +1,43 @@
+锘�#pragma once
+#include "afxdialogex.h"
+
+
+// CAddPLCInfo 瀵硅瘽妗�
+
+class CAddPLCInfo : public CDialogEx
+{
+	DECLARE_DYNAMIC(CAddPLCInfo)
+
+public:
+	CAddPLCInfo(CWnd* pParent = nullptr);   // 鏍囧噯鏋勯�犲嚱鏁�
+	virtual ~CAddPLCInfo();
+
+	// 鑾峰彇plc鍚嶇О\ip\绔彛
+	CString GetPLCName();
+	CString GetIP();
+	CString GetPort();
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+	enum { IDD = IDD_DIALOG_ADD_PLC_INFO };
+#endif
+
+protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 鏀寔
+	afx_msg void OnBnClickedOk();
+	DECLARE_MESSAGE_MAP()
+
+private:
+	bool IsValidPort(const CString& strPort);
+	bool IsValidIPAddress(const CString& strIP1, const CString& strIP2, const CString& strIP3, const CString& strIP4);
+	bool IsValidIPSegment(const CString& strSegment);
+
+	// 鏁版嵁
+	CString m_strPLCName;
+	CString m_strIP;
+	CString m_strPort;
+
+	// 鎺т欢
+	CEdit m_editPLCName;
+	CEdit m_editPort;
+};
diff --git a/SourceCode/Bond/BoounionPLC/AxisDetailSettingsDlg.cpp b/SourceCode/Bond/BoounionPLC/AxisDetailSettingsDlg.cpp
index 82f070b..3ac128e 100644
--- a/SourceCode/Bond/BoounionPLC/AxisDetailSettingsDlg.cpp
+++ b/SourceCode/Bond/BoounionPLC/AxisDetailSettingsDlg.cpp
@@ -460,6 +460,16 @@
 	strTitle.Format(_T("杞寸粏閮ㄨ瀹�(PLC: %s)"), m_pPLC->getName().c_str());
 	SetWindowText(strTitle);
 
+	// 涓嶅彲缂栬緫
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_POS_MIN)->EnableWindow(FALSE);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_AUTO_SPEED_MIN)->EnableWindow(FALSE);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_ACCE_TIME_MIN)->EnableWindow(FALSE);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_ACCE_TIME_MAX)->EnableWindow(FALSE);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_DECE_TIME_MIN)->EnableWindow(FALSE);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_DECE_TIME_MAX)->EnableWindow(FALSE);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_MICROMENTUM_MIN)->EnableWindow(FALSE);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_MICROMENTUM_MAX)->EnableWindow(FALSE);
+
 	// 鑾峰彇杞存暟鎹�
 	auto axisDetails = m_pAxisManager->GetAxis(m_nAxisNO);
 	if (axisDetails.id == -1) {
diff --git a/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.cpp b/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.cpp
index d98b9b8..2d66d4c 100644
--- a/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.cpp
+++ b/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.cpp
@@ -8,6 +8,7 @@
 #include "AxisDetailSettingsDlg.h"
 #include "ToolUnits.h"
 #include <cctype>
+#include <regex>
 
 
 #define COLOR_GREEN_ON    RGB(0, 255, 0)  // 姝e父鐘舵�佺豢鑹�
@@ -53,10 +54,6 @@
 		m_pBlBtns[i] = new CBlButton();
 	}
 
-	for (int i = 0; i < EDIT_MAX; i++) {
-		m_pRegexEdit[i] = new CRegexEdit();
-	}
-
 	for (int i = 0; i < LABEL_MAX; i++) {
 		m_pBlLabels[i] = new CBLLabel();
 	}
@@ -73,10 +70,6 @@
 {
 	for (int i = 0; i < BTN_MAX; i++) {
 		delete m_pBlBtns[i];
-	}
-
-	for (int i = 0; i < EDIT_MAX; i++) {
-		delete m_pRegexEdit[i];
 	}
 
 	for (int i = 0; i < LABEL_MAX; i++) {
@@ -179,24 +172,6 @@
 	SetLabelColorBasedOnState(*m_pBlLabels[LABEL_ERR], m_bErr, COLOR_RED, COLOR_GREEN_OFF);
 }
 
-void CAxisSettingsDlg::UpdateRegexEdit(CRegexEdit* pRegexEdit, const ValueRange& range, const CString& title)
-{
-	auto formatDouble = [](double value) -> CString {
-		CString str;
-		str.Format(_T("%.3f"), value);
-		return str;
-	};
-
-	pRegexEdit->SetWindowText(formatDouble(range.currentValue));
-	pRegexEdit->SetRegexType(RegexType::Decimal);
-	pRegexEdit->SetValueRange(range.minValue, range.maxValue);
-	pRegexEdit->SetInvalidInputCallback([title, range]() {
-		CString strError;
-		strError.Format(_T("%s鐨勫�煎繀椤诲湪 %.3f 鍜� %.3f 涔嬮棿锛�"), title, range.minValue, range.maxValue);
-		AfxMessageBox(strError);
-	});
-}
-
 void CAxisSettingsDlg::UpdatePageButtonStates()
 {
 	for (int i = 0; i < AXIS_PAGE_SIZE; ++i) {
@@ -271,11 +246,11 @@
 	m_staticAxisDescription.SetWindowText(CString(axisDetails.description.c_str()));	// 杞存弿杩�
 	m_staticStartAddress.SetWindowText(CString(axisDetails.startAddress.c_str()));		// 璧峰鍦板潃
 
-	UpdateRegexEdit(m_pRegexEdit[EDIT_MICROMENTUM], axisDetails.jogDistance, _T("寰姩閲�"));
-	UpdateRegexEdit(m_pRegexEdit[EDIT_MANUAL_SPEED], axisDetails.manualSpeed, _T("鎵嬪姩閫熷害"));
-	UpdateRegexEdit(m_pRegexEdit[EDIT_AUTO_SPEED], axisDetails.autoSpeed, _T("鑷姩閫熷害"));
-	UpdateRegexEdit(m_pRegexEdit[EDIT_ACCE_TIME], axisDetails.accelerationTime, _T("鍔犻�熸椂闂�"));
-	UpdateRegexEdit(m_pRegexEdit[EDIT_DECE_TIME], axisDetails.decelerationTime, _T("鍑忛�熸椂闂�"));
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_MANUAL_SPEED)->SetWindowText(formatDouble(axisDetails.manualSpeed.currentValue));
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_AUTO_SPEED)->SetWindowText(formatDouble(axisDetails.autoSpeed.currentValue));
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_ACCE_TIME)->SetWindowText(formatDouble(axisDetails.accelerationTime.currentValue));
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_DECE_TIME)->SetWindowText(formatDouble(axisDetails.decelerationTime.currentValue));
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_MICROMENTUM)->SetWindowText(formatDouble(axisDetails.jogDistance.currentValue));
 }
 
 void CAxisSettingsDlg::RefreshPositionDetails(int nAxisId, int pageNumber)
@@ -309,13 +284,16 @@
 			CString value;
 			value.Format(_T("%.3f"), position.range.currentValue);
 			if (pPositionCtrl) {
-				pPositionCtrl->SetWindowText(value);
 				pPositionCtrl->EnableWindow(position.isEnable);
-
 				if (position.isEnable) {
-					CString strError;
-					strError.Format(_T("瀹氫綅鐐�%d"), i + 1);
-					UpdateRegexEdit((CRegexEdit*)pPositionCtrl, position.range, strError);
+					if (!IsFloatInRange((LPTSTR)(LPCTSTR)value, position.range.minValue, position.range.maxValue)) {
+						CString strError;
+						strError.Format(_T("瀹氫綅鐐�%d鍊煎繀椤诲湪 %.3f 鍜� %.3f 涔嬮棿锛�"), i + 1, position.range.minValue, position.range.maxValue);
+						ShowTipText(strError, 5);
+					}
+					else {
+						pPositionCtrl->SetWindowText(value);
+					}
 				}
 			}
 		}
@@ -364,8 +342,8 @@
 	// 鍒ゆ柇杈撳叆鐨勫�兼槸鍚﹀湪鍚堟硶鑼冨洿鍐�
 	if (enteredValue < minValue || enteredValue > maxValue) {
 		CString strError;
-		strError.Format(_T("瀹氫綅鐐圭殑鍊煎繀椤诲湪 %f 鍜� %f 涔嬮棿锛�"), minValue, maxValue);
-		pDescriptionCtrl->SetWindowText(strError);
+		strError.Format(_T("瀹氫綅鐐�%d鐨勫�煎繀椤诲湪 %f 鍜� %f 涔嬮棿锛�"), currentIndex + 1, minValue, maxValue);
+		ShowTipText(strError, 5);
 	}
 	else {
 		position.range.currentValue = enteredValue;
@@ -403,19 +381,19 @@
 
 	// 鑾峰彇鐣岄潰涓婄殑淇敼鍙傛暟
 	CString text;
-	m_pRegexEdit[EDIT_MANUAL_SPEED]->GetWindowText(text);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_MANUAL_SPEED)->GetWindowText(text);
 	axisData.manualSpeed.currentValue = _ttof(text);
 
-	m_pRegexEdit[EDIT_AUTO_SPEED]->GetWindowText(text);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_AUTO_SPEED)->GetWindowText(text);
 	axisData.autoSpeed.currentValue = _ttof(text);
 
-	m_pRegexEdit[EDIT_ACCE_TIME]->GetWindowText(text);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_ACCE_TIME)->GetWindowText(text);
 	axisData.accelerationTime.currentValue = _ttof(text);
 
-	m_pRegexEdit[EDIT_DECE_TIME]->GetWindowText(text);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_DECE_TIME)->GetWindowText(text);
 	axisData.decelerationTime.currentValue = _ttof(text);
 
-	m_pRegexEdit[EDIT_MICROMENTUM]->GetWindowText(text);
+	GetDlgItem(IDC_EDIT_AXIS_MODITFY_MICROMENTUM)->GetWindowText(text);
 	axisData.jogDistance.currentValue = _ttof(text);
 
 	// 鏇存柊瀹氫綅鐐规暟鎹�
@@ -774,11 +752,15 @@
 	ON_BN_CLICKED(IDC_BUTTON_AXIS_UPDATE_UI, &CAxisSettingsDlg::OnBnClickedButtonAxisUpdateUi)
 	ON_BN_CLICKED(IDC_BUTTON_AXIS_DETAIL_SETTINGS, &CAxisSettingsDlg::OnBnClickedButtonAxisDetailSettings)
 	ON_MESSAGE(ID_MSG_UPDATA_DATA_TO_UI, &CAxisSettingsDlg::OnUpdateDataToUI)
+	ON_EN_KILLFOCUS(IDC_EDIT_AXIS_MODITFY_MANUAL_SPEED, &CAxisSettingsDlg::OnKillfocusEditAxisModitfyManualSpeed)
+	ON_EN_KILLFOCUS(IDC_EDIT_AXIS_MODITFY_AUTO_SPEED, &CAxisSettingsDlg::OnKillfocusEditAxisModitfyAutoSpeed)
+	ON_EN_KILLFOCUS(IDC_EDIT_AXIS_MODITFY_DECE_TIME, &CAxisSettingsDlg::OnKillfocusEditAxisModitfyDeceTime)
+	ON_EN_KILLFOCUS(IDC_EDIT_AXIS_MODITFY_ACCE_TIME, &CAxisSettingsDlg::OnKillfocusEditAxisModitfyAcceTime)
+	ON_EN_KILLFOCUS(IDC_EDIT_AXIS_MODITFY_MICROMENTUM, &CAxisSettingsDlg::OnEnKillfocusEditAxisModitfyMicromentum)
 	ON_WM_SIZE()
 	ON_WM_TIMER()
 	ON_WM_CLOSE()
 	ON_WM_CTLCOLOR()
-	ON_EN_KILLFOCUS(IDC_EDIT_AXIS_MODITFY_MICROMENTUM, &CAxisSettingsDlg::OnEnKillfocusEditAxisModitfyMicromentum)
 END_MESSAGE_MAP()
 
 
@@ -826,18 +808,6 @@
 
 		AdjustLabelFont(*pLabel);
 	}
-	
-	// 鍒濆鍖栫紪杈戞
-	m_pRegexEdit[EDIT_MANUAL_SPEED]->SubclassDlgItem(IDC_EDIT_AXIS_MODITFY_MANUAL_SPEED, this);
-	m_pRegexEdit[EDIT_AUTO_SPEED]->SubclassDlgItem(IDC_EDIT_AXIS_MODITFY_AUTO_SPEED, this);
-	m_pRegexEdit[EDIT_ACCE_TIME]->SubclassDlgItem(IDC_EDIT_AXIS_MODITFY_ACCE_TIME, this);
-	m_pRegexEdit[EDIT_DECE_TIME]->SubclassDlgItem(IDC_EDIT_AXIS_MODITFY_DECE_TIME, this);
-	m_pRegexEdit[EDIT_MICROMENTUM]->SubclassDlgItem(IDC_EDIT_AXIS_MODITFY_MICROMENTUM, this);
-	m_pRegexEdit[EDIT_ANCHOR_POINT1]->SubclassDlgItem(IDC_EDIT_AXIS_ANCHOR_POINT1, this);
-	m_pRegexEdit[EDIT_ANCHOR_POINT2]->SubclassDlgItem(IDC_EDIT_AXIS_ANCHOR_POINT2, this);
-	m_pRegexEdit[EDIT_ANCHOR_POINT3]->SubclassDlgItem(IDC_EDIT_AXIS_ANCHOR_POINT3, this);
-	m_pRegexEdit[EDIT_ANCHOR_POINT4]->SubclassDlgItem(IDC_EDIT_AXIS_ANCHOR_POINT4, this);
-	m_pRegexEdit[EDIT_ANCHOR_POINT5]->SubclassDlgItem(IDC_EDIT_AXIS_ANCHOR_POINT5, this);
 
 	// 鎸夐挳鍒濆鍖�
 	m_pBlBtns[BTN_PAGE1]->SubclassDlgItem(IDC_BUTTON_AXIS_ANCHOR_POINT_GROUP1, this);
@@ -1385,6 +1355,110 @@
 	return 0;
 }
 
+void CAxisSettingsDlg::OnKillfocusEditAxisModitfyManualSpeed()
+{
+	// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+	int nAxisId = GetCurrentSelectedAxisID();
+	if (nAxisId == -1) {
+		AfxMessageBox(_T("璇烽�夋嫨涓�涓湁鏁堢殑杞寸紪鍙凤紒"));
+		return;
+	}
+	auto axisDetails = m_pAxisManager->GetAxis(nAxisId);
+	auto dMinValue = axisDetails.manualSpeed.minValue;
+	auto dMaxValue = axisDetails.manualSpeed.maxValue;
+
+	CString strText;
+	GetDlgItemText(IDC_EDIT_AXIS_MODITFY_MANUAL_SPEED, strText);
+	if (!IsFloatInRange((LPTSTR)(LPCTSTR)strText, dMinValue, dMaxValue)) {
+		CString strError;
+		strError.Format(_T("鎵嬪姩閫熷害鍊煎繀椤诲湪 %.3f 鍜� %.3f 涔嬮棿锛�"), dMinValue, dMaxValue);
+		ShowTipText(strError, 5);
+	}
+}
+
+void CAxisSettingsDlg::OnKillfocusEditAxisModitfyAutoSpeed()
+{
+	// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+	int nAxisId = GetCurrentSelectedAxisID();
+	if (nAxisId == -1) {
+		AfxMessageBox(_T("璇烽�夋嫨涓�涓湁鏁堢殑杞寸紪鍙凤紒"));
+		return;
+	}
+	auto axisDetails = m_pAxisManager->GetAxis(nAxisId);
+	auto dMinValue = axisDetails.autoSpeed.minValue;
+	auto dMaxValue = axisDetails.autoSpeed.maxValue;
+
+	CString strText;
+	GetDlgItemText(IDC_EDIT_AXIS_MODITFY_AUTO_SPEED, strText);
+	if (!IsFloatInRange((LPTSTR)(LPCTSTR)strText, dMinValue, dMaxValue)) {
+		CString strError;
+		strError.Format(_T("鑷姩閫熷害鍊煎繀椤诲湪 %.3f 鍜� %.3f 涔嬮棿锛�"), dMinValue, dMaxValue);
+		ShowTipText(strError, 5);
+	}
+}
+
+void CAxisSettingsDlg::OnKillfocusEditAxisModitfyAcceTime()
+{
+	// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+	int nAxisId = GetCurrentSelectedAxisID();
+	if (nAxisId == -1) {
+		AfxMessageBox(_T("璇烽�夋嫨涓�涓湁鏁堢殑杞寸紪鍙凤紒"));
+		return;
+	}
+	auto axisDetails = m_pAxisManager->GetAxis(nAxisId);
+	auto dMinValue = axisDetails.accelerationTime.minValue;
+	auto dMaxValue = axisDetails.accelerationTime.maxValue;
+
+	CString strText;
+	GetDlgItemText(IDC_EDIT_AXIS_MODITFY_ACCE_TIME, strText);
+	if (!IsFloatInRange((LPTSTR)(LPCTSTR)strText, dMinValue, dMaxValue)) {
+		CString strError;
+		strError.Format(_T("鍔犻�熸椂闂村�煎繀椤诲湪 %.3f 鍜� %.3f 涔嬮棿锛�"), dMinValue, dMaxValue);
+		ShowTipText(strError, 5);
+	}
+}
+
+void CAxisSettingsDlg::OnKillfocusEditAxisModitfyDeceTime()
+{
+	// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+	int nAxisId = GetCurrentSelectedAxisID();
+	if (nAxisId == -1) {
+		AfxMessageBox(_T("璇烽�夋嫨涓�涓湁鏁堢殑杞寸紪鍙凤紒"));
+		return;
+	}
+	auto axisDetails = m_pAxisManager->GetAxis(nAxisId);
+	auto dMinValue = axisDetails.decelerationTime.minValue;
+	auto dMaxValue = axisDetails.decelerationTime.maxValue;
+
+	CString strText;
+	GetDlgItemText(IDC_EDIT_AXIS_MODITFY_DECE_TIME, strText);
+	if (!IsFloatInRange((LPTSTR)(LPCTSTR)strText, dMinValue, dMaxValue)) {
+		CString strError;
+		strError.Format(_T("鍑忛�熸椂闂村�煎繀椤诲湪 %.3f 鍜� %.3f 涔嬮棿锛�"), dMinValue, dMaxValue);
+		ShowTipText(strError, 5);
+	}
+}
+
+void CAxisSettingsDlg::OnEnKillfocusEditAxisModitfyMicromentum()
+{
+	int nAxisId = GetCurrentSelectedAxisID();
+	if (nAxisId == -1) {
+		AfxMessageBox(_T("璇烽�夋嫨涓�涓湁鏁堢殑杞寸紪鍙凤紒"));
+		return;
+	}
+	auto axisDetails = m_pAxisManager->GetAxis(nAxisId);
+	auto dMinValue = axisDetails.jogDistance.minValue;
+	auto dMaxValue = axisDetails.jogDistance.maxValue;
+
+	CString strText;
+	GetDlgItemText(IDC_EDIT_AXIS_MODITFY_MICROMENTUM, strText);
+	if (!IsFloatInRange((LPTSTR)(LPCTSTR)strText, dMinValue, dMaxValue)) {
+		CString strError;
+		strError.Format(_T("寰姩閲忓�煎繀椤诲湪 %.3f 鍜� %.3f 涔嬮棿锛�"), dMinValue, dMaxValue);
+		ShowTipText(strError, 5);
+	}
+}
+
 void CAxisSettingsDlg::OnTimer(UINT_PTR nIDEvent)
 {
 	if (TIMER_READ_PLC_DATA == nIDEvent) {
@@ -1455,12 +1529,19 @@
 	GetDlgItem(IDC_LABEL_TIP)->ShowWindow(SW_HIDE);
 }
 
-void CAxisSettingsDlg::OnEnKillfocusEditAxisModitfyMicromentum()
+bool CAxisSettingsDlg::IsFloatInRange(const std::string& strInput, double fLowerBound, double fUpperBound)
 {
-	CString strText;
-	GetDlgItemText(IDC_EDIT_AXIS_MODITFY_MICROMENTUM, strText);
-	double value = atof(strText);
-	if (value < 0.0 || value > 0.1) {
-		ShowTipText("寰姩閲忓�煎繀椤诲湪0.0 ~ 0.1涔嬮棿", 5);
+	// 鏄惁涓哄悎娉曟诞鐐规暟
+	std::regex floatRegex(R"([-+]?\d*\.?\d+([eE][-+]?\d+)?)");
+	if (!std::regex_match(strInput, floatRegex)) {
+		return false;
+	}
+
+	try {
+		double value = std::stod(strInput);
+		return value >= fLowerBound && value <= fUpperBound;
+	}
+	catch (const std::exception&) {
+		return false;
 	}
 }
diff --git a/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.h b/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.h
index 240812f..565a385 100644
--- a/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.h
+++ b/SourceCode/Bond/BoounionPLC/AxisSettingsDlg.h
@@ -1,7 +1,6 @@
 锘�#pragma once
 #include "BlButton.h"
 #include "BLLabel.h"
-#include "RegexEdit.h"
 #include "CBaseDlg.h"
 #include "PLC.h"
 #include "AxisManager.h"
@@ -17,19 +16,6 @@
 #define LABEL_READY		3
 #define LABEL_BUSY		4
 #define LABEL_ERR		5
-
-// RegexEdit鎺т欢
-#define EDIT_MAX			10
-#define EDIT_MANUAL_SPEED	0
-#define EDIT_AUTO_SPEED		1
-#define EDIT_ACCE_TIME		2
-#define EDIT_DECE_TIME		3
-#define EDIT_MICROMENTUM	4
-#define EDIT_ANCHOR_POINT1	5
-#define EDIT_ANCHOR_POINT2	6
-#define EDIT_ANCHOR_POINT3	7
-#define EDIT_ANCHOR_POINT4	8
-#define EDIT_ANCHOR_POINT5	9
 
 // BUTTON鎺т欢
 #define BTN_MAX			15
@@ -88,7 +74,6 @@
 	void SetLabelColor(CBLLabel& label, COLORREF color);
 	void SetLabelColorBasedOnState(CBLLabel& label, BOOL bState, COLORREF colorTrue, COLORREF colorFalse);
 	void UpdateLabels();
-	void UpdateRegexEdit(CRegexEdit* pRegexEdit, const ValueRange& range, const CString& title);
 	void UpdatePageButtonStates();
 	void HideEditCursor(int nCtrlID);
 	int GetCurrentSelectedAxisID();
@@ -105,6 +90,7 @@
 	void ReadPLCDataToUI(int nAxisId);
 	void ShowTipText(const char* pszText, float sec);
 	void HideTip();
+	bool IsFloatInRange(const std::string& strInput, double fLowerBound, double fUpperBound);
 
 private:
 	CPLC* m_pPLC;
@@ -122,7 +108,6 @@
 	// 鎺т欢
 	CBlButton* m_pBlBtns[BTN_MAX];
 	CBLLabel* m_pBlLabels[LABEL_MAX];
-	CRegexEdit* m_pRegexEdit[EDIT_MAX];
 	CComboBox m_comboAxisNO;
 	CStatic m_staticAxisNO, m_staticAxisDescription, m_staticStartAddress;
 
@@ -169,10 +154,13 @@
 	afx_msg void OnBnClickedButtonAxisUpdateUi();
 	afx_msg void OnBnClickedButtonAxisDetailSettings();
 	afx_msg LRESULT OnUpdateDataToUI(WPARAM wParam, LPARAM lParam);
+	afx_msg void OnKillfocusEditAxisModitfyManualSpeed();
+	afx_msg void OnKillfocusEditAxisModitfyAutoSpeed();
+	afx_msg void OnKillfocusEditAxisModitfyDeceTime();
+	afx_msg void OnKillfocusEditAxisModitfyAcceTime();
+	afx_msg void OnEnKillfocusEditAxisModitfyMicromentum();
 	afx_msg void OnTimer(UINT_PTR nIDEvent);
 	afx_msg void OnClose();
-	DECLARE_MESSAGE_MAP()
-public:
 	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
-	afx_msg void OnEnKillfocusEditAxisModitfyMicromentum();
+	DECLARE_MESSAGE_MAP()
 };
diff --git a/SourceCode/Bond/BoounionPLC/BoounionPLC.rc b/SourceCode/Bond/BoounionPLC/BoounionPLC.rc
index e661b4b..66d1463 100644
--- a/SourceCode/Bond/BoounionPLC/BoounionPLC.rc
+++ b/SourceCode/Bond/BoounionPLC/BoounionPLC.rc
Binary files differ
diff --git a/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj b/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj
index c4d1ecc..edc7445 100644
--- a/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj
+++ b/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj
@@ -193,6 +193,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\BondEq\HorizontalLine.h" />
+    <ClInclude Include="AddPLCInfo.h" />
     <ClInclude Include="Alarm.h" />
     <ClInclude Include="AlarmMonitor.h" />
     <ClInclude Include="AlarmPopupDlg.h" />
@@ -230,7 +231,6 @@
     <ClInclude Include="PagePlcList.h" />
     <ClInclude Include="PLC.h" />
     <ClInclude Include="PlcView.h" />
-    <ClInclude Include="RegexEdit.h" />
     <ClInclude Include="Resource.h" />
     <ClInclude Include="stdafx.h" />
     <ClInclude Include="targetver.h" />
@@ -240,6 +240,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\BondEq\HorizontalLine.cpp" />
+    <ClCompile Include="AddPLCInfo.cpp" />
     <ClCompile Include="Alarm.cpp" />
     <ClCompile Include="AlarmMonitor.cpp" />
     <ClCompile Include="AlarmPopupDlg.cpp" />
@@ -280,7 +281,6 @@
     <ClCompile Include="PagePlcList.cpp" />
     <ClCompile Include="PLC.cpp" />
     <ClCompile Include="PlcView.cpp" />
-    <ClCompile Include="RegexEdit.cpp" />
     <ClCompile Include="stdafx.cpp">
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
diff --git a/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj.filters b/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj.filters
index d3f05ab..fc03e00 100644
--- a/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj.filters
+++ b/SourceCode/Bond/BoounionPLC/BoounionPLC.vcxproj.filters
@@ -156,7 +156,7 @@
     <ClInclude Include="CBaseDlg.h">
       <Filter>澶存枃浠�</Filter>
     </ClInclude>
-    <ClInclude Include="RegexEdit.h">
+    <ClInclude Include="AddPLCInfo.h">
       <Filter>澶存枃浠�</Filter>
     </ClInclude>
   </ItemGroup>
@@ -281,7 +281,7 @@
     <ClCompile Include="CBaseDlg.cpp">
       <Filter>婧愭枃浠�</Filter>
     </ClCompile>
-    <ClCompile Include="RegexEdit.cpp">
+    <ClCompile Include="AddPLCInfo.cpp">
       <Filter>婧愭枃浠�</Filter>
     </ClCompile>
   </ItemGroup>
diff --git a/SourceCode/Bond/BoounionPLC/BoounionPLCDlg.cpp b/SourceCode/Bond/BoounionPLC/BoounionPLCDlg.cpp
index b5276ea..d859bee 100644
--- a/SourceCode/Bond/BoounionPLC/BoounionPLCDlg.cpp
+++ b/SourceCode/Bond/BoounionPLC/BoounionPLCDlg.cpp
@@ -10,6 +10,7 @@
 #include "PlcView.h"
 #include "Log.h"
 #include "InputDialog.h"
+#include "AddPLCInfo.h"
 #include "AxisManager.h"
 #include "IOManager.h"
 
@@ -505,24 +506,25 @@
 {
 	int id = (int)lParam;
 	if (id == IDC_BUTTON_ADD) {
-		CInputDialog inputDialog(_T("新建设备"), _T("请输入设备名称:"));
-		if (inputDialog.DoModal() != IDOK) {
-			AfxMessageBox(_T("取消验证!"));
+		CAddPLCInfo dlgAddPLCInfo;
+		if (dlgAddPLCInfo.DoModal() != IDOK) {
 			return 0;
 		}
 
-		CString strName;
-		strName = inputDialog.GetInputText();
-		if (!strName.IsEmpty()) {
-			theApp.m_model.addPlc((LPTSTR)(LPCTSTR)strName, "192.168.1.188", 1001);
+		CString strPLCName = dlgAddPLCInfo.GetPLCName();
+		CString strIP = dlgAddPLCInfo.GetIP();
+		CString strPort = dlgAddPLCInfo.GetPort();
+
+		if (!strPLCName.IsEmpty() && !strIP.IsEmpty() && !strPort.IsEmpty()) {
+			theApp.m_model.addPlc((LPTSTR)(LPCTSTR)strPLCName, (LPTSTR)(LPCTSTR)strIP, std::stoi((LPTSTR)(LPCTSTR)strPort));
 
 			// 新建轴文件
 			AxisManager axisManager;
-			axisManager.SaveAxis((LPTSTR)(LPCTSTR)strName);
+			axisManager.SaveAxis((LPTSTR)(LPCTSTR)strPLCName);
 
 			// 新建IO文件
 			IOManager ioManager;
-			ioManager.SaveToFile((LPTSTR)(LPCTSTR)strName);
+			ioManager.SaveToFile((LPTSTR)(LPCTSTR)strPLCName);
 		}
 	}
 	else if (id == IDC_BUTTON_DELETE) {
diff --git a/SourceCode/Bond/BoounionPLC/RegexEdit.cpp b/SourceCode/Bond/BoounionPLC/RegexEdit.cpp
deleted file mode 100644
index b7d4e2c..0000000
--- a/SourceCode/Bond/BoounionPLC/RegexEdit.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "stdafx.h"
-#include "RegexEdit.h"
-#include <stdexcept>
-
-IMPLEMENT_DYNAMIC(CRegexEdit, CEdit)
-
-CRegexEdit::CRegexEdit()
-{
-    m_enRegexType = RegexType::Alphanumeric;
-	m_dMinValue = LDBL_MIN;
-	m_dMaxValue = LDBL_MAX;
-}
-
-CRegexEdit::~CRegexEdit()
-{
-}
-
-void CRegexEdit::SetRegexType(RegexType enType)
-{
-    m_enRegexType = enType;
-}
-
-void CRegexEdit::SetCustomRegex(const std::string& strCustomRegex)
-{
-    m_strCustomRegex = strCustomRegex;
-}
-
-void CRegexEdit::SetValueRange(long double dMinValue, long double dMaxValue)
-{
-    m_dMinValue = dMinValue;
-    m_dMaxValue = dMaxValue;
-}
-
-void CRegexEdit::SetCustomComparator(std::function<bool(const std::string&)> comparator)
-{
-	m_customComparator = comparator;
-}
-
-void CRegexEdit::SetInvalidInputCallback(std::function<void()> callback)
-{
-    m_invalidInputCallback = callback;
-}
-
-bool CRegexEdit::FindMatch(const std::string& pattern, std::string& foundText)
-{
-    CString currentText;
-    GetWindowText(currentText);
-
-    std::string text(CT2A(currentText.GetString()));
-    std::regex regexPattern(pattern);
-    std::smatch match;
-
-    if (std::regex_search(text, match, regexPattern)) {
-        foundText = match.str();
-        return true;
-    }
-    return false;
-}
-
-void CRegexEdit::ReplaceMatch(const std::string& pattern, const std::string& replacement)
-{
-    CString currentText;
-    GetWindowText(currentText);
-
-    std::string text(CT2A(currentText.GetString()));
-    std::regex regexPattern(pattern);
-    std::string result = std::regex_replace(text, regexPattern, replacement);
-
-    SetWindowText(CString(result.c_str()));
-}
-
-std::regex CRegexEdit::GetCurrentRegex() const
-{
-    switch (m_enRegexType)
-    {
-    case RegexType::Alphanumeric:
-        return std::regex("^[a-zA-Z0-9]*$");        // 字母和数字
-    case RegexType::Letters:
-        return std::regex("^[a-zA-Z]*$");           // 只允许字母
-    case RegexType::Digits:
-        return std::regex("^\\d*$");                // 只允许数字
-    case RegexType::Decimal:
-        return std::regex("^[-+]?\\d+(\\.\\d+)?$"); // 允许小数和整数
-    case RegexType::Custom:
-        return std::regex(m_strCustomRegex);        // 自定义正则
-    default:
-        return std::regex(".*");                    // 默认允许输入任何内容
-    }
-}
-
-bool CRegexEdit::IsValueInRange(const std::string& strText)
-{
-    try {
-        if (m_enRegexType == RegexType::Digits || m_enRegexType == RegexType::Decimal) {
-            if (strText.find('.') == std::string::npos) {
-                int nValue = std::stoi(strText);
-                return nValue >= static_cast<int>(m_dMinValue) && nValue <= static_cast<int>(m_dMaxValue);
-            }
-            else {
-                double dValue = std::stod(strText);
-                return dValue >= m_dMinValue && dValue <= m_dMaxValue;
-            }
-        }
-    }
-    catch (const std::invalid_argument&) {
-        return false;
-    }
-
-    return true;
-}
-
-BEGIN_MESSAGE_MAP(CRegexEdit, CEdit)
-    ON_WM_CHAR()
-END_MESSAGE_MAP()
-
-void CRegexEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
-{
-    CEdit::OnChar(nChar, nRepCnt, nFlags);
-    return;
-
-    // 处理删除键和退格键
-    if (nChar == VK_BACK || nChar == VK_DELETE) {
-        CEdit::OnChar(nChar, nRepCnt, nFlags);
-        return;
-    }
-
-    CString strCurrent;
-    GetWindowText(strCurrent);
-
-    // 获取光标当前位置
-    int nStartChar, nEndChar;
-    GetSel(nStartChar, nEndChar); // 获取当前选区的起始和结束位置
-
-    std::string strText(CT2A(strCurrent.GetBuffer()));
-    strText.insert(strText.begin() + nStartChar, (char)nChar);
-
-    bool bValid = m_customComparator ? m_customComparator(strText) : IsValueInRange(strText);
-    if (std::regex_match(strText, GetCurrentRegex()) && bValid) {
-        CEdit::OnChar(nChar, nRepCnt, nFlags);
-    }
-    else {
-        if (m_invalidInputCallback) {
-            m_invalidInputCallback();
-        }
-    }
-}
\ No newline at end of file
diff --git a/SourceCode/Bond/BoounionPLC/RegexEdit.h b/SourceCode/Bond/BoounionPLC/RegexEdit.h
deleted file mode 100644
index 0632d00..0000000
--- a/SourceCode/Bond/BoounionPLC/RegexEdit.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#if !defined(AFX_REGEXEDIT_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)
-#define AFX_REGEXEDIT_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_
-
-#if _MSC_VER >= 1000
-#pragma once
-#endif // _MSC_VER >= 1000
-
-#include <afxwin.h>
-#include <regex>
-#include <functional>
-#include <limits>
-
-// 枚举类型:正则表达式类型
-enum class RegexType
-{
-    Alphanumeric,   // 允许字母和数字
-    Letters,        // 只允许字母
-    Digits,         // 只允许数字
-    Decimal,        // 允许小数和整数
-    Custom          // 自定义正则
-};
-
-class CRegexEdit : public CEdit
-{
-    DECLARE_DYNAMIC(CRegexEdit)
-
-public:
-    // 构造与析构
-    CRegexEdit();
-    virtual ~CRegexEdit();
-
-    // 设置正则类型
-    void SetRegexType(RegexType enType);
-
-    // 设置自定义正则表达式
-    void SetCustomRegex(const std::string& strCustomRegex);
-
-    // 设置数值范围(整数或浮点)
-    void SetValueRange(long double dMinValue, long double dMaxValue);
-
-    // 设置自定义比较函数
-    void SetCustomComparator(std::function<bool(const std::string&)> comparator);
-
-	// 设置输入不合法函数
-    void SetInvalidInputCallback(std::function<void()> callback);
-
-    // 查找匹配内容
-    bool FindMatch(const std::string& pattern, std::string& foundText);
-
-    // 替换匹配内容
-    void ReplaceMatch(const std::string& pattern, const std::string& replacement);
-
-protected:
-    // 根据枚举值返回对应的正则表达式
-    std::regex GetCurrentRegex() const;
-
-    // 校验输入是否在指定范围内
-    bool IsValueInRange(const std::string& strText);
-
-protected:
-    RegexType m_enRegexType;        // 当前选中的正则类型
-    std::string m_strCustomRegex;   // 自定义正则表达式
-	long double m_dMinValue;        // 最小值
-	long double m_dMaxValue;		// 最大值
-
-	std::function<bool(const std::string&)> m_customComparator; // 自定义比较函数
-    std::function<void()> m_invalidInputCallback;               // 不合法输入的回调函数
-
-protected:
-    void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
-    DECLARE_MESSAGE_MAP()
-};
-
-#endif // !defined(AFX_REGEXEDIT_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)
\ No newline at end of file
diff --git a/SourceCode/Bond/BoounionPLC/Resource.h b/SourceCode/Bond/BoounionPLC/Resource.h
index 9dfbd5d..d3ec31a 100644
--- a/SourceCode/Bond/BoounionPLC/Resource.h
+++ b/SourceCode/Bond/BoounionPLC/Resource.h
Binary files differ
diff --git a/SourceCode/Bond/Servo/AlarmManager.cpp b/SourceCode/Bond/Servo/AlarmManager.cpp
new file mode 100644
index 0000000..7864235
--- /dev/null
+++ b/SourceCode/Bond/Servo/AlarmManager.cpp
@@ -0,0 +1,289 @@
+#include "stdafx.h"
+#include "AlarmManager.h"
+#include <sstream>
+#include <iostream>
+#include <stdexcept>
+#include <ctime>
+
+// 常量
+const std::string DATABASE_FILE = R"(AlarmManager.db)";
+
+// 静态成员初始化
+std::mutex AlarmManager::m_mutex;
+
+// 获取单例实例
+AlarmManager& AlarmManager::getInstance() {
+    static AlarmManager instance;
+    return instance;
+}
+
+// 构造函数
+AlarmManager::AlarmManager() {
+	m_pDB = new BL::SQLiteDatabase();
+}
+
+// 析构函数
+AlarmManager::~AlarmManager() {
+    if (m_pDB != nullptr) {
+        delete m_pDB;
+        m_pDB = nullptr;
+    }
+}
+
+// 设置数据库连接
+void AlarmManager::setDatabase(BL::Database* db) {
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_pDB = db;
+}
+
+// 初始化报警表
+bool AlarmManager::initAlarmTable() {
+    char path[MAX_PATH];
+    GetModuleFileName(NULL, path, MAX_PATH);
+    std::string exePath(path);
+    std::string dbFileDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\DB";
+	if (!CreateDirectory(dbFileDir.c_str(), NULL) && ERROR_ALREADY_EXISTS != GetLastError()) {
+		throw std::runtime_error("Failed to create database directory.");
+	}
+
+    std::string dbFilePath = dbFileDir + "\\" + DATABASE_FILE;
+    if (!m_pDB->connect(dbFilePath, true)) {
+        throw std::runtime_error("Failed to connect to database.");
+    }
+
+    const std::string createTableQuery = R"(
+        CREATE TABLE IF NOT EXISTS alarms (
+            id TEXT NOT NULL,
+            device_name TEXT NOT NULL,
+            description TEXT NOT NULL,
+            start_time DATETIME NOT NULL,
+            end_time DATETIME NOT NULL
+        )
+    )";
+
+    return m_pDB->executeQuery(createTableQuery);
+}
+
+// 销毁报警表
+void AlarmManager::termAlarmTable() {
+    if (m_pDB != nullptr) {
+		m_pDB->disconnect();
+    }
+}
+
+// 销毁报警表
+bool AlarmManager::destroyAlarmTable() {
+	if (!m_pDB) {
+		throw std::runtime_error("Database connection is not set.");
+	}
+	const std::string dropTableQuery = "DROP TABLE IF EXISTS alarms";
+	return m_pDB->executeQuery(dropTableQuery);
+}
+
+// 添加报警
+bool AlarmManager::addAlarm(const std::string& id, const std::string& deviceName, const std::string& description, const std::string& startTime, const std::string& endTime) {
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "INSERT INTO alarms (id, device_name, description, start_time, end_time) VALUES ("
+        << "'" << id << "', "
+		<< "'" << deviceName << "', "
+        << "'" << description << "', "
+        << "'" << startTime << "', "
+        << "'" << endTime << "')";
+
+    std::lock_guard<std::mutex> lock(m_mutex);
+    return m_pDB->executeQuery(query.str());
+}
+
+// 查询所有报警数据
+std::vector<std::vector<std::string>> AlarmManager::getAllAlarms() {
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    const std::string query = "SELECT id, device_name, description, start_time, end_time FROM alarms";
+    return m_pDB->fetchResults(query);
+}
+
+// 根据报警ID查询报警
+std::vector<std::vector<std::string>> AlarmManager::getAlarmsById(const std::string& id) {
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "SELECT id, device_name, description, start_time, end_time FROM alarms WHERE id = '" << id << "'";
+    return m_pDB->fetchResults(query.str());
+}
+
+// 根据描述查询报警
+std::vector<std::vector<std::string>> AlarmManager::getAlarmsByDescription(const std::string& description) {
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "SELECT id, device_name, description, start_time, end_time FROM alarms WHERE description LIKE '%" << description << "%'";
+    return m_pDB->fetchResults(query.str());
+}
+
+// 根据时间范围查询报警
+std::vector<std::vector<std::string>> AlarmManager::getAlarmsByTimeRange(
+    const std::string& startTime, const std::string& endTime) {
+
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "SELECT id, device_name, description, start_time, end_time FROM alarms WHERE 1=1";
+
+    if (!startTime.empty()) {
+        query << " AND start_time >= '" << startTime << "'";
+    }
+    if (!endTime.empty()) {
+        query << " AND end_time <= '" << endTime << "'";
+    }
+
+    return m_pDB->fetchResults(query.str());
+}
+
+// 根据ID、开始时间和结束时间查询报警
+std::vector<std::vector<std::string>> AlarmManager::getAlarmsByIdAndTimeRange(
+    const std::string& id, const std::string& startTime, const std::string& endTime) {
+
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "SELECT id, device_name, description, start_time, end_time FROM alarms WHERE id = '" << id << "'";
+
+    if (!startTime.empty()) {
+        query << " AND start_time >= '" << startTime << "'";
+    }
+    if (!endTime.empty()) {
+        query << " AND end_time <= '" << endTime << "'";
+    }
+
+    return m_pDB->fetchResults(query.str());
+}
+
+// 分页查询报警数据
+std::vector<std::vector<std::string>> AlarmManager::getAlarms(int startPosition, int count) {
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "SELECT id, device_name, description, start_time, end_time FROM alarms LIMIT " << count << " OFFSET " << startPosition;
+    return m_pDB->fetchResults(query.str());
+}
+
+// 筛选报警数据
+std::vector<std::vector<std::string>> AlarmManager::getFilteredAlarms(
+    const std::string& id,
+    const std::string& deviceName,
+    const std::string& description,
+    const std::string& startTime,
+    const std::string& endTime,
+    int pageNumber,
+    int pageSize) {
+
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "SELECT id, device_name, description, start_time, end_time FROM alarms WHERE 1=1";
+
+	if (!id.empty()) {
+		query << " AND id = '" << id << "'";
+	}
+	if (!deviceName.empty()) {
+		query << " AND device_name LIKE '%" << deviceName << "%'";
+	}
+    if (!description.empty()) {
+        query << " AND description LIKE '%" << description << "%'";
+    }
+    if (!startTime.empty()) {
+        query << " AND start_time >= '" << startTime << "'";
+    }
+    if (!endTime.empty()) {
+        query << " AND end_time <= '" << endTime << "'";
+    }
+
+    int offset = (pageNumber - 1) * pageSize;
+    query << " ORDER BY start_time DESC LIMIT " << pageSize << " OFFSET " << offset;
+
+    return m_pDB->fetchResults(query.str());
+}
+
+// 获取符合条件的报警总数
+int AlarmManager::getTotalAlarmCount(
+    const std::string& id,
+    const std::string& deviceName,
+    const std::string& description,
+    const std::string& startTime,
+    const std::string& endTime) {
+
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "SELECT COUNT(*) FROM alarms WHERE 1=1";
+
+    if (!id.empty()) {
+		query << " AND id = '" << id << "'";
+    }
+    if (!deviceName.empty()) {
+		query << " AND device_name LIKE '%" << deviceName << "%'";
+    }
+    if (!description.empty()) {
+        query << " AND description LIKE '%" << description << "%'";
+    }
+    if (!startTime.empty()) {
+        query << " AND start_time >= '" << startTime << "'";
+    }
+    if (!endTime.empty()) {
+        query << " AND end_time <= '" << endTime << "'";
+    }
+
+    auto results = m_pDB->fetchResults(query.str());
+    return (!results.empty() && !results[0].empty()) ? std::stoi(results[0][0]) : 0;
+}
+
+// 更新报警的结束时间
+bool AlarmManager::updateAlarmEndTime(const std::string& id, const std::string& deviceName, const std::string& description, const std::string& startTime, const std::string& newEndTime) {
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "UPDATE alarms SET end_time = '" << newEndTime << "'"
+        << " WHERE id = '" << id << "'"
+		<< " AND device_name = '" << deviceName << "'"
+        << " AND description = '" << description << "'"
+        << " AND start_time = '" << startTime << "'";
+
+    return m_pDB->executeQuery(query.str());
+}
+
+// 清理旧报警数据
+void AlarmManager::cleanOldAlarms(int daysToKeep, const std::string& deviceName) {
+    if (!m_pDB) {
+        throw std::runtime_error("Database connection is not set.");
+    }
+
+    std::ostringstream query;
+    query << "DELETE FROM alarms WHERE end_time < datetime('now', '-" << daysToKeep << " days')";
+
+    if (!deviceName.empty()) {
+        query << " AND device_name = '" << deviceName << "'";
+    }
+    m_pDB->executeQuery(query.str());
+}
diff --git a/SourceCode/Bond/Servo/AlarmManager.h b/SourceCode/Bond/Servo/AlarmManager.h
new file mode 100644
index 0000000..28bea98
--- /dev/null
+++ b/SourceCode/Bond/Servo/AlarmManager.h
@@ -0,0 +1,163 @@
+#ifndef ALARM_MANAGER_H
+#define ALARM_MANAGER_H
+
+#include <string>
+#include <vector>
+#include <mutex>
+#include "Database.h"
+
+class AlarmManager {
+public:
+    /**
+     * 获取单例实例
+     * @return AlarmManager实例的引用
+     */
+    static AlarmManager& getInstance();
+
+    /**
+     * 设置数据库连接
+     * @param db 数据库连接的指针
+     */
+    void setDatabase(BL::Database* db);
+
+    /**
+     * 初始化报警表
+     * @return 成功返回true,失败返回false
+     */
+    bool initAlarmTable();
+
+	/**
+	 * 销毁报警表
+	 */
+    void termAlarmTable();
+
+	/**
+	 * 销毁报警表
+	 * @return 成功返回true,失败返回false
+	 */
+    bool destroyAlarmTable();
+
+    /**
+     * 添加报警
+     * @param id 报警ID
+	 * @param deviceName 设备名称
+     * @param description 报警描述
+     * @param startTime 报警开始时间
+     * @param endTime 报警结束时间
+     * @return 成功返回true,失败返回false
+     */
+    bool addAlarm(const std::string& id, const std::string& deviceName, const std::string& description, const std::string& startTime, const std::string& endTime);
+
+    /**
+     * 查询所有报警数据
+     * @return 包含所有报警数据的二维字符串向量
+     */
+    std::vector<std::vector<std::string>> getAllAlarms();
+
+    /**
+     * 根据报警ID查询报警
+     * @param id 报警ID
+     * @return 包含筛选后报警数据的二维字符串向量
+     */
+    std::vector<std::vector<std::string>> getAlarmsById(const std::string& id);
+
+    /**
+     * 根据描述查询报警
+     * @param description 报警描述的筛选条件
+     * @return 包含筛选后报警数据的二维字符串向量
+     */
+    std::vector<std::vector<std::string>> getAlarmsByDescription(const std::string& description);
+
+    /**
+     * 根据时间范围查询报警
+     * @param startTime 起始时间
+     * @param endTime 结束时间
+     * @return 包含查询结果的二维字符串向量
+     */
+    std::vector<std::vector<std::string>> getAlarmsByTimeRange(
+        const std::string& startTime, const std::string& endTime);
+
+	/**
+	* 根据ID和时间范围查询报警
+	 * @param id 报警ID
+	 * @param startTime 起始时间
+	 * @param endTime 结束时间
+	 * @return 包含查询结果的二维字符串向量
+	 */
+    std::vector<std::vector<std::string>> getAlarmsByIdAndTimeRange(
+        const std::string& id, const std::string& startTime, const std::string& endTime);
+
+    /**
+     * 获取报警数据
+     * @param startPosition 起始位置
+     * @param count 获取的记录数量
+     * @return 包含报警数据的二维字符串向量
+     */
+    std::vector<std::vector<std::string>> getAlarms(int startPosition, int count);
+
+    /**
+     * 获取筛选后的报警数据
+	 * @param id 报警ID的筛选条件
+	 * @param deviceName 设备名称的筛选条件
+     * @param description 报警描述的筛选条件
+     * @param startTime 起始时间筛选条件
+     * @param endTime 结束时间筛选条件
+     * @param pageNumber 页码
+     * @param pageSize 每页的记录数
+     * @return 包含筛选后报警数据的二维字符串向量
+     */
+    std::vector<std::vector<std::string>> getFilteredAlarms(
+        const std::string& id,
+        const std::string& deviceName,
+        const std::string& description,
+        const std::string& startTime,
+        const std::string& endTime,
+        int pageNumber,
+        int pageSize);
+
+    /**
+     * 获取符合条件的报警总数
+     * @param id 报警ID的筛选条件
+	 * @param deviceName 设备名称的筛选条件
+     * @param description 报警描述的筛选条件
+     * @param startTime 起始时间筛选条件
+     * @param endTime 结束时间筛选条件
+     * @return 符合条件的报警总数
+     */
+    int getTotalAlarmCount(
+        const std::string& id,
+        const std::string& deviceName,
+        const std::string& description,
+        const std::string& startTime,
+        const std::string& endTime);
+
+	/**
+	 * 更新报警结束时间
+	 * @param id 报警ID
+	 * @param description 报警描述
+	 * @param startTime 报警开始时间
+	 * @param newEndTime 新的报警结束时间
+	 * @return 成功返回true,失败返回false
+	 */
+    bool updateAlarmEndTime(const std::string& id, const std::string& deviceName, const std::string& description, const std::string& startTime, const std::string& newEndTime);
+
+    /**
+     * 清理旧报警
+     * @param daysToKeep 保留的天数
+	 * @param deviceName 设备名称
+     */
+    void cleanOldAlarms(int daysToKeep = 30, const std::string& deviceName = "");
+
+private:
+    AlarmManager();
+    ~AlarmManager();
+
+    // 禁止拷贝和赋值
+    AlarmManager(const AlarmManager&) = delete;
+    AlarmManager& operator=(const AlarmManager&) = delete;
+
+    BL::Database* m_pDB;
+    static std::mutex m_mutex;
+};
+
+#endif // ALARM_MANAGER_H
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/Servo.cpp b/SourceCode/Bond/Servo/Servo.cpp
index b422966..495b5e7 100644
--- a/SourceCode/Bond/Servo/Servo.cpp
+++ b/SourceCode/Bond/Servo/Servo.cpp
@@ -6,6 +6,11 @@
 #include "Servo.h"
 #include "ServoDlg.h"
 #include "ServoGraph.h"
+#include "AlarmManager.h"
+
+// 声明全局变量,用于管理 GDI+ 初始化
+ULONG_PTR g_diplusToken;
+GdiplusStartupInput g_diplusStartupInput;
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -93,6 +98,25 @@
 	HSMS_Initialize();
 
 
+	// 初始化 GDI+
+	InitGDIPlus();
+
+
+	// 初始化报警管理器
+	try {
+		if (!AlarmManager::getInstance().initAlarmTable()) {
+			AfxMessageBox("初始化报警管理器失败!");
+			return FALSE;
+		}
+	}
+	catch (const std::exception& ex) {
+		CString errorMsg;
+		errorMsg.Format(_T("初始化报警管理器失败:%s"), CString(ex.what()));
+		AfxMessageBox(errorMsg, MB_ICONERROR);
+		return FALSE;
+	}
+
+
 	CServoDlg dlg;
 	m_pMainWnd = &dlg;
 	INT_PTR nResponse = dlg.DoModal();
@@ -129,5 +153,25 @@
 	HSMS_Term();
 	RX_Term();
 
+	// 清理 GDI+
+	TermGDIPlus();
+
+	// 销毁报警表
+	AlarmManager::getInstance().termAlarmTable();
+
 	return CWinApp::ExitInstance();
 }
+
+// 初始化 GDI+
+void CServoApp::InitGDIPlus()
+{
+	// 初始化 GDI+ 图形库
+	GdiplusStartup(&g_diplusToken, &g_diplusStartupInput, NULL);
+}
+
+// 清理 GDI+
+void CServoApp::TermGDIPlus()
+{
+	// 清理 GDI+ 图形库
+	GdiplusShutdown(g_diplusToken);
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/Servo.h b/SourceCode/Bond/Servo/Servo.h
index bb306cd..04d5fb2 100644
--- a/SourceCode/Bond/Servo/Servo.h
+++ b/SourceCode/Bond/Servo/Servo.h
@@ -20,6 +20,8 @@
 {
 public:
 	CServoApp();
+	void InitGDIPlus();
+	void TermGDIPlus();
 
 
 public:
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index e049e84..769631b 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -115,6 +115,7 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>.;..;..\DatabaseSDK\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -168,6 +169,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>_WINDOWS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>.;..;..\DatabaseSDK\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -190,6 +192,7 @@
     <Text Include="ReadMe.txt" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="AlarmManager.h" />
     <ClInclude Include="BlButton.h" />
     <ClInclude Include="Common.h" />
     <ClInclude Include="Configuration.h" />
@@ -210,6 +213,7 @@
     <ClInclude Include="TerminalDisplayDlg.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="AlarmManager.cpp" />
     <ClCompile Include="BlButton.cpp" />
     <ClCompile Include="Configuration.cpp" />
     <ClCompile Include="Context.cpp" />
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index 9d77c23..55fdacc 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -70,6 +70,7 @@
       <Filter>澶存枃浠�</Filter>
     </ClInclude>
     <ClInclude Include="TerminalDisplayDlg.h">
+    <ClInclude Include="AlarmManager.h">
       <Filter>澶存枃浠�</Filter>
     </ClInclude>
   </ItemGroup>
@@ -117,6 +118,7 @@
       <Filter>婧愭枃浠�</Filter>
     </ClCompile>
     <ClCompile Include="TerminalDisplayDlg.cpp">
+    <ClCompile Include="AlarmManager.cpp">
       <Filter>婧愭枃浠�</Filter>
     </ClCompile>
   </ItemGroup>
diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index bea9cf1..a88bb3d 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/SourceCode/Bond/Servo/ServoDlg.cpp
@@ -9,12 +9,18 @@
 #include "Common.h"
 #include "Log.h"
 #include "SecsTestDlg.h"
+#include "AlarmManager.h"
+#include <chrono>
+#include <thread>
+#include <cmath>
 
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #endif
 
+// Image
+#define IMAGE_ROBOT				2
 
 #define INDICATE_BONDER1		1
 #define INDICATE_BONDER2		2
@@ -79,6 +85,7 @@
 	m_crBkgnd = APPDLG_BACKGROUND_COLOR;
 	m_hbrBkgnd = nullptr;
 	m_bShowLogWnd = FALSE;
+	m_bIsRobotMoving = FALSE;
 	m_pLogDlg = nullptr;
 	m_pTerminalDisplayDlg = nullptr;
 	m_pObserver = nullptr;
@@ -115,6 +122,7 @@
 	ON_COMMAND(ID_MENU_HELP_ABOUT, &CServoDlg::OnMenuHelpAbout)
 	ON_WM_INITMENUPOPUP()
 	ON_WM_TIMER()
+	ON_WM_ERASEBKGND()
 END_MESSAGE_MAP()
 
 
@@ -193,6 +201,8 @@
 	strPath.Format(_T("%s\\res\\Servo001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
 	m_pGraph->AddImage(1, (LPTSTR)(LPCTSTR)strPath, 0, 0);
 
+	strPath.Format(_T("%s\\res\\Robot001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+	m_pGraph->AddImage(IMAGE_ROBOT, (LPTSTR)(LPCTSTR)strPath, 170, 270);
 
 	// 添加指示器
 	// Bonder
@@ -241,10 +251,10 @@
 
 
 	// Robot
-	m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 620, 294, 48, RGB(22, 22, 22),
+	m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 190, 294, 48, RGB(22, 22, 22),
 		RGB(255, 127, 39), RGB(0, 176, 80));
 	m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "5", "Robot");
-	m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 673, 294, 48, RGB(22, 22, 22),
+	m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 243, 294, 48, RGB(22, 22, 22),
 		RGB(255, 127, 39), RGB(0, 176, 80));
 	m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "6", "Robot");
 
@@ -536,6 +546,106 @@
 	m_btnLog.Invalidate();
 }
 
+void CServoDlg::UpdateRobotPosition(float percentage)
+{
+	// 限制百分比范围在 [0, 1] 之间
+	if (percentage < 0.0f) percentage = 0.0f;
+	if (percentage > 1.0f) percentage = 1.0f;
+
+	// 根据百分比计算目标 X 坐标
+	int startX = m_pGraph->GetImage(IMAGE_ROBOT)->x;
+	int endX = static_cast<int>(170 + percentage * (700 - 170));
+
+	int arm1Offset = 20;  // 从图片到ARM1的偏移
+	int arm2Offset = 73;  // 从图片到ARM2的偏移
+
+	// 计算移动所需的时间
+	int distance = abs(endX - startX);
+	int duration = static_cast<int>((distance / 100.0) * 1000);
+
+	auto startTime = std::chrono::steady_clock::now();
+	auto endTime = startTime + std::chrono::milliseconds(duration);
+
+	// 开始移动,设置标记
+	m_bIsRobotMoving = TRUE;
+
+	// 开始平滑移动
+	while (std::chrono::steady_clock::now() < endTime) {
+		auto currentTime = std::chrono::steady_clock::now();
+		float progress = std::chrono::duration<float, std::milli>(currentTime - startTime).count() / duration;
+		progress = min(progress, 1.0f);
+
+		// 根据进度计算当前位置
+		int currentX = static_cast<int>(startX + progress * (endX - startX));
+		m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, currentX, 270);
+		m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, currentX + arm1Offset, 294);
+		m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, currentX + arm2Offset, 294);
+
+		// 刷新界面
+		Invalidate();
+		UpdateWindow();
+
+		// 控制帧率约为 60 FPS
+		std::this_thread::sleep_for(std::chrono::milliseconds(16));
+	}
+
+	// 确保最后位置精确到目标位置
+	m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, endX, 270);
+	m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, endX + arm1Offset, 294);
+	m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, endX + arm2Offset, 294);
+
+	// 界面重绘
+	Invalidate();
+
+	// 动画结束,设置标记
+	m_bIsRobotMoving = FALSE;
+}
+
+void CServoDlg::RotateRobot(float angleInDegrees)
+{
+	// 将角度转换为弧度
+	float angleInRadians = static_cast<float>(std::acos(-1)) / 180.0f * angleInDegrees;
+
+	// 获取机器人图片的当前坐标和中心
+	auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
+	if (!pImage) return;
+
+	// 更新 Rotate 图片的角度,确保角度保持在 [0, 360) 范围内
+	m_pGraph->UpdateImageAngle(IMAGE_ROBOT, static_cast<float>(fmod(pImage->angle + angleInDegrees + 360, 360)));
+
+	int cx = pImage->x + pImage->bmWidth / 2;  // 图片中心 X
+	int cy = pImage->y + pImage->bmHeight / 2; // 图片中心 Y
+
+	// 旋转指示框的坐标
+	auto* pRobot1 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM1);
+	auto* pRobot2 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM2);
+
+	if (pRobot1 && pRobot2) {
+		int newArmX1 = pImage->x + 20;
+		int newArmY1 = 294;
+
+		int newArmX2 = pImage->x + 73;
+		int newArmY2 = 294;
+
+		if (angleInDegrees != 0.0f) {
+			// 计算指示框1的新坐标
+			newArmX1 = static_cast<int>(cx + (pRobot1->x - cx) * cos(angleInRadians) - (pRobot1->y - cy) * sin(angleInRadians));
+			newArmY1 = static_cast<int>(cy + (pRobot1->x - cx) * sin(angleInRadians) + (pRobot1->y - cy) * cos(angleInRadians));
+
+			// 计算指示框2的新坐标
+			newArmX2 = static_cast<int>(cx + (pRobot2->x - cx) * cos(angleInRadians) - (pRobot2->y - cy) * sin(angleInRadians));
+			newArmY2 = static_cast<int>(cy + (pRobot2->x - cx) * sin(angleInRadians) + (pRobot2->y - cy) * cos(angleInRadians));
+		}
+
+		// 更新指示框的位置
+		m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, newArmX1, newArmY1);
+		m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, newArmX2, newArmY2);
+	}
+
+	// 强制重绘界面
+	Invalidate();
+}
+
 void CServoDlg::OnSize(UINT nType, int cx, int cy)
 {
 	CDialogEx::OnSize(nType, cx, cy);
@@ -615,3 +725,15 @@
 
 	CDialogEx::OnTimer(nIDEvent);
 }
+
+
+BOOL CServoDlg::OnEraseBkgnd(CDC* pDC)
+{
+	// TODO: 在此添加消息处理程序代码和/或调用默认值
+	if (m_bIsRobotMoving) {
+		// 禁止刷新背景,避免闪烁
+		return TRUE;
+	}
+	
+	return CDialogEx::OnEraseBkgnd(pDC);
+}
diff --git a/SourceCode/Bond/Servo/ServoDlg.h b/SourceCode/Bond/Servo/ServoDlg.h
index 2681c75..596cf77 100644
--- a/SourceCode/Bond/Servo/ServoDlg.h
+++ b/SourceCode/Bond/Servo/ServoDlg.h
@@ -24,6 +24,8 @@
 	void InitRxWindows();
 	void Resize();
 	void UpdateLogBtn();
+	void UpdateRobotPosition(float percentage);
+	void RotateRobot(float angleInDegrees);
 
 
 private:
@@ -41,6 +43,11 @@
 	protected:
 	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持
 
+
+private:
+	BOOL m_bIsRobotMoving;
+	BOOL m_bShowLogWnd;
+	CLogDlg* m_pLogDlg;
 
 // 实现
 protected:
@@ -79,4 +86,5 @@
 	afx_msg void OnUpdateMenuFileExit(CCmdUI* pCmdUI);
 	afx_msg void OnMenuHelpAbout();
 	afx_msg void OnTimer(UINT_PTR nIDEvent);
+	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 };
diff --git a/SourceCode/Bond/Servo/ServoGraph.cpp b/SourceCode/Bond/Servo/ServoGraph.cpp
index 718661c..3e815c8 100644
--- a/SourceCode/Bond/Servo/ServoGraph.cpp
+++ b/SourceCode/Bond/Servo/ServoGraph.cpp
@@ -306,29 +306,9 @@
 
 
 	// 画IMAGE
-	HDC hDCTemp = ::CreateCompatibleDC(hMemDC);
 	for (auto& item : m_images) {
-		// 载入BMP
-		if (item.hBitmap == nullptr) {
-			item.hBitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(),
-				item.szPath, IMAGE_BITMAP, 0, 0,
-				LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
-			if (item.hBitmap != nullptr) {
-				BITMAP bitmap;
-				::GetObject(item.hBitmap, sizeof(BITMAP), &bitmap);
-				item.bmWidth = bitmap.bmWidth;
-				item.bmHeight = bitmap.bmHeight;
-			}
-
-		}
-
-		if (item.hBitmap != nullptr) {
-			::SelectObject(hDCTemp, item.hBitmap);
-			::BitBlt(hMemDC, item.x, item.y, item.bmWidth, item.bmHeight,
-				hDCTemp, 0, 0, SRCCOPY);
-		}
+		DrawImage(hMemDC, item);
 	}
-	::DeleteDC(hDCTemp);
 
 
 	// 画背景指示
@@ -368,7 +348,7 @@
 		}
 
 		// text
-		::DrawText(hMemDC, item.szText, strlen(item.szText),
+		::DrawText(hMemDC, item.szText, static_cast<int>(strlen(item.szText)),
 			&rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
 	}
 
@@ -627,3 +607,71 @@
 		SendMessage(m_hWndTooltip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&tti);
 	}
 }
+
+void CServoGraph::UpdateImageCoordinates(int id, int newX, int newY)
+{
+	IMAGE* pImage = GetImage(id);
+	if (pImage != nullptr) {
+		pImage->x = newX;
+		pImage->y = newY;
+	}
+}
+
+void CServoGraph::UpdateIndicateBoxCoordinates(int id, int newX, int newY)
+{
+	INDICATEBOX* pIndicateBox = GetIndicateBox(id);
+	if (pIndicateBox != nullptr) {
+		pIndicateBox->x = newX;
+		pIndicateBox->y = newY;
+	}
+}
+
+void CServoGraph::UpdateImageAngle(int id, float angle)
+{
+	IMAGE* pImage = GetImage(id);
+	if (pImage != nullptr) {
+		pImage->angle = angle;
+	}
+}
+
+void CServoGraph::DrawImage(HDC hMemDC, IMAGE& item)
+{
+	// 载入BMP
+	if (item.hBitmap == nullptr) {
+		item.hBitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(),
+			item.szPath, IMAGE_BITMAP, 0, 0,
+			LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
+		if (item.hBitmap != nullptr) {
+			BITMAP bitmap;
+			::GetObject(item.hBitmap, sizeof(BITMAP), &bitmap);
+			item.bmWidth = bitmap.bmWidth;
+			item.bmHeight = bitmap.bmHeight;
+		}
+	}
+
+	if (item.hBitmap != nullptr) {
+		// 使用 GDI+ 加载位图,并创建GDI+ Graphics 对象
+		Bitmap bitmap(item.hBitmap, nullptr);
+		Graphics graphics(hMemDC);
+
+		// 如果图像需要旋转
+		if (item.angle != 0.0f) {
+			// 角度转换为弧度
+			float angleInRadians = item.angle;
+
+			// 获取图像中心
+			REAL cx = static_cast<REAL>(item.x + item.bmWidth / 2);  // 将中心X转换为REAL类型
+			REAL cy = static_cast<REAL>(item.y + item.bmHeight / 2); // 将中心Y转换为REAL类型
+
+			// 创建旋转矩阵
+			Matrix rotateMatrix;
+			rotateMatrix.RotateAt(angleInRadians, PointF(cx, cy));
+
+			// 应用旋转矩阵到图形
+			graphics.SetTransform(&rotateMatrix);
+		}
+
+		graphics.DrawImage(&bitmap, item.x, item.y);
+		graphics.ResetTransform();
+	}
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/ServoGraph.h b/SourceCode/Bond/Servo/ServoGraph.h
index a5ca74f..0a58675 100644
--- a/SourceCode/Bond/Servo/ServoGraph.h
+++ b/SourceCode/Bond/Servo/ServoGraph.h
@@ -75,6 +75,7 @@
 		HBITMAP hBitmap;
 		int bmWidth;
 		int bmHeight;
+		float angle;
 	} IMAGE;
 
 
@@ -166,6 +167,12 @@
 	BOOL GetIndicateBoxRect(int id, LPRECT lprcBox);
 	void SetBoxText(int id, const char* pszText, const char* pszTooltip);
 	HWND GetSafeWnd();
+	void UpdateImageCoordinates(int id, int newX, int newY);
+	void UpdateIndicateBoxCoordinates(int id, int newX, int newY);
+	void UpdateImageAngle(int id, float angle);
+
+private:
+	void DrawImage(HDC hMemDC, IMAGE& item);
 
 private:
 	HWND		m_hWnd;
diff --git a/SourceCode/Bond/Servo/stdafx.h b/SourceCode/Bond/Servo/stdafx.h
index ea7a70f..4768d25 100644
--- a/SourceCode/Bond/Servo/stdafx.h
+++ b/SourceCode/Bond/Servo/stdafx.h
@@ -34,6 +34,30 @@
 #include <afxcontrolbars.h>     // 功能区和控件条的 MFC 支持
 
 
+// GDI+
+#include <gdiplus.h>
+using namespace Gdiplus;
+
+
+// 数据库模块
+#include "..\DatabaseSDK\include\Database.h"
+#include "..\DatabaseSDK\include\MySQLDatabase.h"
+#include "..\DatabaseSDK\include\SQLiteDatabase.h"
+
+#if defined(_WIN64)
+#if defined(_DEBUG)
+#pragma comment(lib, "..\\DatabaseSDK\\lib\\x64\\Debug\\DatabaseEx.lib")
+#else
+#pragma comment(lib, "..\\DatabaseSDK\\lib\\x64\\Release\\DatabaseEx.lib")
+#endif
+#else
+#if defined(_DEBUG)
+#pragma comment(lib, "..\\DatabaseSDK\\lib\\Win32\\Debug\\DatabaseEx.lib")
+#else
+#pragma comment(lib, "..\\DatabaseSDK\\lib\\Win32\\Release\\DatabaseEx.lib")
+#endif
+#endif
+
 
 #include "..\RxWindows1.0\include\RxWindowsLib.h"
 #include "..\HSMSSDK\Include\HSMSSDK.h"
diff --git a/SourceCode/Bond/x64/Debug/Res/Robot001.bmp b/SourceCode/Bond/x64/Debug/Res/Robot001.bmp
new file mode 100644
index 0000000..5247864
--- /dev/null
+++ b/SourceCode/Bond/x64/Debug/Res/Robot001.bmp
Binary files differ
diff --git a/SourceCode/Bond/x64/Debug/Res/Servo002.bmp b/SourceCode/Bond/x64/Debug/Res/Servo002.bmp
new file mode 100644
index 0000000..024cf3b
--- /dev/null
+++ b/SourceCode/Bond/x64/Debug/Res/Servo002.bmp
Binary files differ

--
Gitblit v1.9.3