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