From 829fe6c6bc33d53fda9c31fd45a37e1df87befff Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期五, 30 一月 2026 11:16:24 +0800
Subject: [PATCH] Merge branch 'clh' into liuyang
---
SourceCode/Bond/Servo/CHMPropertyPage.h | 23
SourceCode/Bond/Servo/LoginDlg2.cpp | 103
SourceCode/Bond/Servo/CSVData.cpp | 24
SourceCode/Bond/Servo/ProcessJob.cpp | 9
SourceCode/Bond/Servo/HsmsPassive.cpp | 2216 +++++++++
SourceCode/Bond/Servo/stdafx.h | 2
SourceCode/Bond/BondEq/AccordionWnd.cpp | 16
SourceCode/Bond/Servo/LoginDlg2.h | 30
SourceCode/Bond/Servo/CReportEditDlg.h | 30
SourceCode/Bond/Servo/ProductionStats.h | 66
SourceCode/Bond/Servo/Servo.rc | 0
SourceCode/Bond/Servo/HmLabel.cpp | 177
SourceCode/Bond/Servo/ServoDlg.h | 33
SourceCode/Bond/Servo/HsmsPassive.h | 144
SourceCode/Bond/Servo/ProcessJob.h | 1
SourceCode/Bond/Servo/ProductionStats.cpp | 438 +
SourceCode/Bond/Servo/CGlass.cpp | 36
SourceCode/Bond/Servo/CPanelMaster.h | 1
SourceCode/Bond/Servo/CControlJobManagerDlg.cpp | 86
SourceCode/Bond/Servo/CEventEditDlg.cpp | 94
SourceCode/Bond/Servo/CLoadPort.cpp | 108
SourceCode/Bond/Servo/ConfigurationProduction.cpp | 75
SourceCode/Bond/x64/Debug/VariableList.txt | 85
SourceCode/Bond/Servo/CPageGlassList.cpp | 120
SourceCode/Bond/Servo/CPageCollectionEvent.h | 3
SourceCode/Bond/Servo/CPageProdOverview.h | 37
SourceCode/Bond/Servo/CPageVarialbles.cpp | 111
SourceCode/Bond/Servo/Servo.vcxproj.filters | 37
SourceCode/Bond/Servo/AccordionWnd.cpp | 787 +++
SourceCode/Bond/x64/Debug/CollectionEventList.txt | 29
SourceCode/Bond/Servo/TopToolbar.cpp | 28
SourceCode/Bond/Servo/CPageCtrlState.h | 41
SourceCode/Bond/Servo/CPageReport.cpp | 112
SourceCode/Bond/Servo/HmLabel.h | 33
SourceCode/Bond/Servo/HsmsAction.cpp | 22
SourceCode/Bond/Servo/CPageProdOverview.cpp | 165
SourceCode/Bond/Servo/Servo.vcxproj | 35
SourceCode/Bond/Servo/resource.h | 0
SourceCode/Bond/Servo/CHMPropertyDlg.h | 3
SourceCode/Bond/Servo/CServoUtilsTool.h | 9
SourceCode/Bond/EAPSimulator/CHsmsActive.h | 48
SourceCode/Bond/Servo/CMaster.h | 51
SourceCode/Bond/Servo/CReportEditDlg.cpp | 78
SourceCode/Bond/BondEq/BondEq.vcxproj | 6
SourceCode/Bond/Servo/CPageDataVarialbles.cpp | 220
SourceCode/Bond/EAPSimulator/Resource.h | 23
SourceCode/Bond/Servo/CUserManager2Dlg.cpp | 322 +
SourceCode/Bond/Servo/ClientListDlg.cpp | 2
SourceCode/Bond/Servo/AlarmManager.h | 205
SourceCode/Bond/Servo/Model.h | 30
SourceCode/Bond/Servo/CUserManager2.h | 48
SourceCode/Bond/Servo/ToolUnits.h | 6
SourceCode/Bond/Servo/CPageGraph2.cpp | 27
SourceCode/Bond/Servo/CEventEditDlg.h | 37
SourceCode/Bond/Servo/CPageCollectionEvent.cpp | 108
SourceCode/Bond/EAPSimulator/EAPSimulator.rc | 0
SourceCode/Bond/Servo/Model.cpp | 668 ++
SourceCode/Bond/Servo/CHMPropertyPage.cpp | 72
SourceCode/Bond/Servo/CUserEdit2Dlg.cpp | 125
SourceCode/Bond/Servo/CPageVarialbles.h | 5
SourceCode/Bond/Servo/CReport.h | 3
SourceCode/Bond/Servo/AlarmPopupDlg.h | 59
SourceCode/Bond/Servo/CVariableEditDlg2.cpp | 89
SourceCode/Bond/Servo/CVariable.h | 5
SourceCode/Bond/Servo/CPageGraph2.h | 1
SourceCode/Bond/Servo/CPageDataVarialbles.h | 37
SourceCode/Bond/Servo/CReadStep.h | 2
SourceCode/Bond/Servo/AccordionWnd.h | 131
SourceCode/Bond/Servo/CCollectionEvent.cpp | 4
SourceCode/Bond/Servo/CUserXLogDlg.cpp | 178
SourceCode/Bond/x64/Debug/AlarmList.txt | 1
SourceCode/Bond/Servo/Configuration.h | 7
SourceCode/Bond/EAPSimulator/CHsmsActive.cpp | 196
SourceCode/Bond/Servo/EqsGraphWnd.cpp | 315
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp | 349 +
SourceCode/Bond/Servo/ToolUnits.cpp | 65
SourceCode/Bond/USERXLibrary/UserXAPI.h | 136
SourceCode/Bond/Servo/CBonder.h | 2
SourceCode/Bond/Servo/CReport.cpp | 6
SourceCode/Bond/x64/Debug/test.ini | 10
SourceCode/Bond/Servo/CUserManager2.cpp | 250 +
SourceCode/Bond/Servo/CPanelProduction.h | 64
SourceCode/Bond/x64/Debug/DataVariableList.txt | 98
SourceCode/Bond/Servo/CPageReport.h | 3
SourceCode/Bond/Servo/CMyStatusbar.cpp | 35
SourceCode/Bond/Servo/CEquipment.h | 60
SourceCode/Bond/Servo/CDataVariable.h | 13
SourceCode/Bond/Servo/CParam.cpp | 4
SourceCode/Bond/Servo/AlarmManager.cpp | 420 +
SourceCode/Bond/Servo/CMyStatusbar.h | 3
.gitignore | 2
SourceCode/Bond/Servo/CUserManager2Dlg.h | 45
SourceCode/Bond/Servo/Servo.vcxproj.user | 2
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h | 14
SourceCode/Bond/Servo/Servo.cpp | 140
SourceCode/Bond/Servo/CPanelMaster.cpp | 18
SourceCode/Bond/Servo/CPageGlassList.h | 3
SourceCode/Bond/Servo/CHMPropertyDlg.cpp | 104
SourceCode/Bond/Servo/CEquipment.cpp | 184
SourceCode/Bond/Servo/CMaster.cpp | 1055 +++-
SourceCode/Bond/Servo/AlarmPopupDlg.cpp | 321 +
SourceCode/Bond/Servo/CParam.h | 4
SourceCode/Bond/x64/Debug/ReportList.txt | 54
SourceCode/Bond/Servo/CReadStep.cpp | 1
SourceCode/Bond/Servo/EqsGraphWnd.h | 36
SourceCode/Bond/Servo/CUserXLogDlg.h | 42
SourceCode/Bond/Servo/CLoadPort.h | 10
Document/EventSummary_v2.1.xlsx | 0
SourceCode/Bond/Servo/CPanelProduction.cpp | 236 +
SourceCode/Bond/Servo/ServoDlg.cpp | 457 +
SourceCode/Bond/Servo/CPageGraph1.cpp | 60
SourceCode/Bond/Servo/PageRecipe.cpp | 37
SourceCode/Bond/Servo/CCollectionEvent.h | 6
SourceCode/Bond/Servo/CPageCtrlState.cpp | 157
SourceCode/Bond/Servo/PortConfigurationDlg.cpp | 62
Document/VariableList.txt | 14
SourceCode/Bond/EAPSimulator/CPJsDlg.cpp | 2
SourceCode/Bond/BLControlsSDK/include/BLLabel.h | 2
SourceCode/Bond/Servo/CServoUtilsTool.cpp | 69
SourceCode/Bond/Servo/CVariableEditDlg2.h | 34
SourceCode/Bond/x64/Release/AlarmList.txt | 1
Document/Panel Bonder八零联合 SecsTest CheckList_v3.0.xlsx | 0
SourceCode/Bond/Servo/CBonder.cpp | 236
SourceCode/Bond/Servo/CUserEdit2Dlg.h | 23
SourceCode/Bond/Servo/Common.h | 31
125 files changed, 11,790 insertions(+), 1,668 deletions(-)
diff --git a/.gitignore b/.gitignore
index e97c776..7e74b67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,3 +63,5 @@
SourceCode/Bond/x64/Debug/HsmsPassive.cache
SourceCode/Bond/x64/Debug/MasterState.dat
SourceCode/Bond/x64/Debug/Recipe/EQ10_Unit0.recipelist
+SourceCode/Bond/UserX/
+Document/~$Panel Bonder鍏浂鑱斿悎 SecsTest CheckList_v3.0.xlsx
diff --git a/Document/EventSummary_v2.1.xlsx b/Document/EventSummary_v2.1.xlsx
new file mode 100644
index 0000000..9948a9e
--- /dev/null
+++ b/Document/EventSummary_v2.1.xlsx
Binary files differ
diff --git "a/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx" "b/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
index b8f9219..d3d8537 100644
--- "a/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
+++ "b/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
Binary files differ
diff --git a/Document/VariableList.txt b/Document/VariableList.txt
index 1c92863..9db9582 100644
--- a/Document/VariableList.txt
+++ b/Document/VariableList.txt
@@ -1,6 +1,16 @@
SVID,SV Name,SV Format,SV Remark
-100,PortTransferState,U1,"0=OutOfService\r\n1=TransferBlocked\r\n2=ReadyToLoad\r\n3=ReadyToUnload\r\n4=InService\r\n5=TransferReady"
-300,AccessMode,U1,"1=Manual\r\n2=Auto"
+10000,CarrierID_P1,A50,Carrier ID for Port 1
+10001,CarrierID_P2,A50,Carrier ID for Port 2
+10002,CarrierID_P3,A50,Carrier ID for Port 3
+10003,CarrierID_P4,A50,Carrier ID for Port 4
+100,PortTransferState_P1,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+101,PortTransferState_P2,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+102,PortTransferState_P3,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+103,PortTransferState_P4,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+300,AccessMode_P1,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
+301,AccessMode_P2,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
+302,AccessMode_P3,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
+303,AccessMode_P4,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
500,Clock,A50,
600,CurrentControlState,U1,"0:Offline:equipment\r\n1:Offline-Attempt\r\n2:Online\r\n3:Offline:host\r\n4:Online:Local\r\n5:Online:Remote"
601,PreviousControlState,U1,
diff --git a/SourceCode/Bond/BLControlsSDK/include/BLLabel.h b/SourceCode/Bond/BLControlsSDK/include/BLLabel.h
index 88ff5d0..5089fa8 100644
--- a/SourceCode/Bond/BLControlsSDK/include/BLLabel.h
+++ b/SourceCode/Bond/BLControlsSDK/include/BLLabel.h
@@ -1,4 +1,4 @@
-#if !defined(AFX_BLLABEL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)
+锘�#if !defined(AFX_BLLABEL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_)
#define AFX_BLLABEL_H__A4EABEC5_2E8C_11D1_B79F_00805F9ECE10__INCLUDED_
#if _MSC_VER >= 1000
diff --git a/SourceCode/Bond/BondEq/AccordionWnd.cpp b/SourceCode/Bond/BondEq/AccordionWnd.cpp
index 5da6615..026628e 100644
--- a/SourceCode/Bond/BondEq/AccordionWnd.cpp
+++ b/SourceCode/Bond/BondEq/AccordionWnd.cpp
@@ -47,9 +47,16 @@
BOOL CAccordionWnd::RegisterWndClass()
{
- WNDCLASS wc;
+ WNDCLASS wcExisting = {};
+ HINSTANCE hInstance = AfxGetInstanceHandle();
+ if (::GetClassInfo(hInstance, ACCORDIONWND_CLASS, &wcExisting) ||
+ ::GetClassInfo(NULL, ACCORDIONWND_CLASS, &wcExisting)) {
+ return TRUE;
+ }
+
+ WNDCLASS wc = {};
wc.lpszClassName = ACCORDIONWND_CLASS;
- wc.hInstance = AfxGetInstanceHandle();
+ wc.hInstance = hInstance;
wc.lpfnWndProc = WindowProc;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hIcon = 0;
@@ -60,7 +67,10 @@
wc.cbWndExtra = 0;
// 注册自定义类
- return (::RegisterClass(&wc) != 0);
+ if (::RegisterClass(&wc) != 0) {
+ return TRUE;
+ }
+ return (::GetLastError() == ERROR_CLASS_ALREADY_EXISTS);
}
CAccordionWnd * CAccordionWnd::FromHandle(HWND hWnd)
diff --git a/SourceCode/Bond/BondEq/BondEq.vcxproj b/SourceCode/Bond/BondEq/BondEq.vcxproj
index 06553f1..432fb35 100644
--- a/SourceCode/Bond/BondEq/BondEq.vcxproj
+++ b/SourceCode/Bond/BondEq/BondEq.vcxproj
@@ -21,7 +21,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{7864134E-C538-4C0F-AF24-215FFCCBBAB4}</ProjectGuid>
<RootNamespace>BondServo</RootNamespace>
- <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ <WindowsTargetPlatformVersion>10.0.22000.0</WindowsTargetPlatformVersion>
<Keyword>MFCProj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
@@ -43,14 +43,14 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
+ <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<UseOfMfc>Dynamic</UseOfMfc>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
+ <PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<UseOfMfc>Dynamic</UseOfMfc>
diff --git a/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp b/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
index b334708..a2539da 100644
--- a/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
+++ b/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
@@ -1,9 +1,22 @@
-#include "pch.h"
+锘�#include "pch.h"
#include "CHsmsActive.h"
#include "Log.h"
-static unsigned int DATAID = 1;
+static unsigned short DATAID = 1;
+
+// Truncated SECS message logging to avoid overly long strings crashing UI/log
+static void LogSecsMessageBrief(const char* tag, IMessage* pMessage, size_t maxLen = 1024)
+{
+ if (pMessage == nullptr) return;
+ const char* msgStr = pMessage->toString();
+ if (msgStr == nullptr) return;
+ std::string buf(msgStr);
+ if (buf.size() > maxLen) {
+ buf = buf.substr(0, maxLen) + "...<truncated>";
+ }
+ LOGI("%s%s", tag, buf.c_str());
+}
CHsmsActive::CHsmsActive()
{
@@ -64,9 +77,9 @@
HEADER* pHeader = pMessage->getHeader();
int nStream = (pHeader->stream & 0x7F);
- TRACE("收到消息 S%dF%d================\n", pHeader->stream & 0x7F, pHeader->function);
- TRACE("Body:%s\n", pMessage->toString());
- LOGI("onRecvDataMessage(%s).", pMessage->toString());
+ TRACE("鏀跺埌娑堟伅 S%dF%d================\n", pHeader->stream & 0x7F, pHeader->function);
+ LogSecsMessageBrief("Body:", pMessage);
+ LogSecsMessageBrief("onRecvDataMessage:", pMessage);
if (nStream == 5 && pHeader->function == 1) {
// S5F1
@@ -140,6 +153,48 @@
m_pActive->sendMessage(pMessage);
HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsRequestOnline()
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 17, ++m_nSystemByte);
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsRequestOffline()
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 15, ++m_nSystemByte);
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsGoLocal()
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 2 | REPLY, 41, ++m_nSystemByte);
+ ISECS2Item* pBody = pMessage->getBody();
+ pBody->addItem("GoLocal", "RCMD");
+ pBody->addItem(); // L: empty params
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsGoRemote()
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 2 | REPLY, 41, ++m_nSystemByte);
+ ISECS2Item* pBody = pMessage->getBody();
+ pBody->addItem("GoRemote", "RCMD");
+ pBody->addItem(); // L: empty params
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
return 0;
}
@@ -300,10 +355,70 @@
IMessage* pMessage = nullptr;
int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 3, ++m_nSystemByte);
- pMessage->getBody()->addU4Item(SVID, "SVID");
+ pMessage->getBody()->addU2Item(static_cast<unsigned short>(SVID), "SVID");
m_pActive->sendMessage(pMessage);
HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsQueryAllStatusVariables()
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 11, ++m_nSystemByte);
+ // Host sends L:0 (empty list) to request all SVIDs.
+ pMessage->getBody()->addItem(); // empty list
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsQueryAllDataVariables()
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 21, ++m_nSystemByte);
+ // Host sends L:0 (empty list) to request all DVIDs.
+ pMessage->getBody()->addItem(); // empty list
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsQueryAllCollectionEvents()
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 23, ++m_nSystemByte);
+ // Host sends L:0 (empty list) to request all CEIDs.
+ pMessage->getBody()->addItem(); // empty list
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsEquipmentConstantRequest(const std::vector<unsigned short>& ecids)
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 2 | REPLY, 13, ++m_nSystemByte);
+ for (auto id : ecids) {
+ pMessage->getBody()->addU2Item(id, "ECID");
+ }
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsEquipmentConstantSend(const std::vector<std::pair<unsigned short, std::string>>& ecidValues)
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 2 | REPLY, 15, ++m_nSystemByte);
+ ISECS2Item* pBody = pMessage->getBody();
+ for (const auto& kv : ecidValues) {
+ ISECS2Item* pEntry = pBody->addItem();
+ pEntry->addU2Item(kv.first, "ECID");
+ pEntry->addItem(kv.second.c_str(), "ECV");
+ }
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
return 0;
}
@@ -314,6 +429,33 @@
m_pActive->sendMessage(pMessage);
HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsDeletePPID(const std::vector<std::string>& ppids)
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 7 | REPLY, 17, ++m_nSystemByte);
+ if (nRet != 0 || pMessage == nullptr) return -1;
+ ISECS2Item* pBody = pMessage->getBody();
+ for (const auto& ppid : ppids) {
+ pBody->addItem(ppid.c_str(), "PPID");
+ }
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+}
+
+int CHsmsActive::hsmsProcessProgramRequest(const char* pszPPID)
+{
+ if (pszPPID == nullptr || strlen(pszPPID) == 0) return -1;
+ IMessage* pMessage = nullptr;
+ if (HSMS_Create1Message(pMessage, m_nSessionId, 7 | REPLY, 5, ++m_nSystemByte) != 0 || pMessage == nullptr) {
+ return -1;
+ }
+ pMessage->getBody()->setString(pszPPID, "PPID");
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
return 0;
}
@@ -349,12 +491,50 @@
return hsmsCarrierActionRequest(DATAID, "CarrierRelease", pszCarrierId, PTN);
}
+int CHsmsActive::hsmsProceedWithSlotMap(unsigned int DATAID,
+ const char* pszCarrierId,
+ unsigned char PTN,
+ const char* pszLotId,
+ const std::vector<std::string>& panelIds,
+ const std::vector<unsigned char>& slotMap)
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 3 | REPLY, 17, ++m_nSystemByte);
+ if (nRet != 0 || pMessage == nullptr) {
+ return nRet;
+ }
+
+ pMessage->getBody()->addU4Item(DATAID, "DATAID");
+ pMessage->getBody()->addItem("ProceedWithSlotMap", "CARRIERACTION");
+ pMessage->getBody()->addItem(pszCarrierId, "CARRIERID");
+ pMessage->getBody()->addU1Item(PTN, "PTN");
+
+ // Extended params (currently not parsed by Servo side): { LOTID, PANELID_LIST, SLOTMAP_LIST }
+ ISECS2Item* pParams = pMessage->getBody()->addItem(); // L
+ pParams->addItem(pszLotId != nullptr ? pszLotId : "", "LOTID");
+
+ ISECS2Item* pPanelList = pParams->addItem(); // L
+ for (const auto& id : panelIds) {
+ pPanelList->addItem(id.c_str(), "PANELID");
+ }
+
+ ISECS2Item* pSlotMapList = pParams->addItem(); // L
+ for (auto v : slotMap) {
+ pSlotMapList->addU1Item(v, "SLOTSTATE");
+ }
+
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+
+ return 0;
+}
+
int CHsmsActive::hsmsPRJobMultiCreate(std::vector<SERVO::CProcessJob*>& pjs)
{
IMessage* pMessage = nullptr;
int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 16 | REPLY, 15, ++m_nSystemByte);
char szMF[32] = {14};
- pMessage->getBody()->addU4Item(++DATAID, "DATAID");
+ pMessage->getBody()->addU2Item(++DATAID, "DATAID");
auto itemPjs = pMessage->getBody()->addItem();
for (auto pj : pjs) {
auto itemPj = itemPjs->addItem();
@@ -434,7 +614,7 @@
return 0;
}
-// 通用的reply ack函数
+// 閫氱敤鐨剅eply ack鍑芥暟
void CHsmsActive::replyAck(int s, int f, unsigned int systemBytes, BYTE ack, const char* pszAckName)
{
IMessage* pMessage = NULL;
diff --git a/SourceCode/Bond/EAPSimulator/CHsmsActive.h b/SourceCode/Bond/EAPSimulator/CHsmsActive.h
index 29abeb5..9e8eb07 100644
--- a/SourceCode/Bond/EAPSimulator/CHsmsActive.h
+++ b/SourceCode/Bond/EAPSimulator/CHsmsActive.h
@@ -1,4 +1,4 @@
-#pragma once
+锘�#pragma once
#include <string>
#include <vector>
#include <map>
@@ -7,9 +7,18 @@
#include "ProcessJob.h"
-#define SVID_CJobSpace 5001
+#define SVID_ControlState 600
+#define SVID_CurrentProcessState 700
+#define SVID_CJobSpace 5001
#define SVID_PJobSpace 5002
#define SVID_PJobQueued 5003
+#define SVID_EQPPExecName 801
+#define SVID_Bonder1CurrentRecipe 8100
+#define SVID_Bonder2CurrentRecipe 8101
+#define SVID_VacuumBakeCurrentRecipe 8102
+#define SVID_BakeCoolingCurrentRecipe 8103
+#define SVID_MeasurementCurrentRecipe 8104
+#define SVID_EFEMCurrentRecipe 8105
typedef std::function<void(void* pFrom, ACTIVESTATE state)> STATECHANGED;
@@ -36,11 +45,19 @@
// Deselect Request
int hsmsDeselectRequest();
- // 建立通讯(S1F13)
+ // 寤虹珛閫氳(S1F13)
int hsmsEstablishCommunications();
// Are You There
int hsmsAreYouThere();
+
+ // ControlState: Request Online/Offline (S1F17 / S1F15)
+ int hsmsRequestOnline();
+ int hsmsRequestOffline();
+
+ // ControlState: GoLocal/GoRemote (S2F41)
+ int hsmsGoLocal();
+ int hsmsGoRemote();
// Date time sync
int hsmsDatetimeSync();
@@ -63,18 +80,25 @@
// Configure Spooling
int hsmsConfigureSpooling(std::map<unsigned int, std::set<unsigned int>>& spoolingConfig);
- // 发送或清空缓存的消息
+ // 鍙戦�佹垨娓呯┖缂撳瓨鐨勬秷鎭�
int hsmsTransmitSpooledData();
int hsmsPurgeSpooledData();
- // 查询变量
+ // 鏌ヨ鍙橀噺
int hsmsSelectedEquipmentStatusRequest(unsigned int SVID);
+ int hsmsQueryAllStatusVariables(); // S1F11
+ int hsmsQueryAllDataVariables(); // S1F21
+ int hsmsQueryAllCollectionEvents(); // S1F23
+ int hsmsEquipmentConstantRequest(const std::vector<unsigned short>& ecids); // S2F13
+ int hsmsEquipmentConstantSend(const std::vector<std::pair<unsigned short, std::string>>& ecidValues); // S2F15
- // 查询PPID List
+ // 鏌ヨPPID List
int hsmsQueryPPIDList();
+ int hsmsDeletePPID(const std::vector<std::string>& ppids); // S7F17
+ int hsmsProcessProgramRequest(const char* pszPPID); // S7F5
// S3F17
- // 卡匣动作请求
+ // 鍗″專鍔ㄤ綔璇锋眰
int hsmsCarrierActionRequest(unsigned int DATAID,
const char* pszCarrierAction,
const char* pszCarrierId,
@@ -82,7 +106,13 @@
int hsmsProceedWithCarrier(unsigned int DATAID,
const char* pszCarrierId,
unsigned char PTN);
- int CHsmsActive::hsmsCarrierRelease(unsigned int DATAID,
+ int hsmsProceedWithSlotMap(unsigned int DATAID,
+ const char* pszCarrierId,
+ unsigned char PTN,
+ const char* pszLotId,
+ const std::vector<std::string>& panelIds,
+ const std::vector<unsigned char>& slotMap);
+ int hsmsCarrierRelease(unsigned int DATAID,
const char* pszCarrierId,
unsigned char PTN);
@@ -92,7 +122,7 @@
// S14F9
int hsmsCreateControlJob(const char* pszControlJobId, std::vector<std::string>& processJobIds);
- // 通过的reply函数
+ // 閫氳繃鐨剅eply鍑芥暟
void replyAck(int s, int f, unsigned int systemBytes, BYTE ack, const char* pszAckName);
// reply ack0
diff --git a/SourceCode/Bond/EAPSimulator/CPJsDlg.cpp b/SourceCode/Bond/EAPSimulator/CPJsDlg.cpp
index 212291f..9f1c61b 100644
--- a/SourceCode/Bond/EAPSimulator/CPJsDlg.cpp
+++ b/SourceCode/Bond/EAPSimulator/CPJsDlg.cpp
@@ -98,7 +98,7 @@
{
SERVO::CProcessJob* pj = new SERVO::CProcessJob("PJ0001");
std::vector<uint8_t> slots1{ 1, 2, 3 };
- pj->addCarrier("CID1001", slots1);
+ pj->addCarrier("Test-Cassette-001", slots1);
pj->setRecipe(SERVO::RecipeMethod::NoTuning, "P1001");
m_pjs.push_back(pj);
}
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulator.rc b/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
index 53eb764..92e768e 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
Binary files differ
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
index e15ff68..a52341f 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
@@ -9,6 +9,8 @@
#include "afxdialogex.h"
#include "Common.h"
#include <regex>
+#include <string>
+#include <vector>
#include "CTerminalDisplayDlg.h"
#include "CEDEventReportDlg.h"
#include "CDefineReportsDlg.h"
@@ -89,12 +91,26 @@
ON_BN_CLICKED(IDC_BUTTON_TRANSMIT_SPOOLED_DATA, &CEAPSimulatorDlg::OnBnClickedButtonTransmitSpooledData)
ON_BN_CLICKED(IDC_BUTTON_PURGE_SPOOLED_DATA, &CEAPSimulatorDlg::OnBnClickedButtonPurgeSpooledData)
ON_BN_CLICKED(IDC_BUTTON_QUERY_PPID_LIST, &CEAPSimulatorDlg::OnBnClickedButtonQueryPpidList)
+ ON_BN_CLICKED(IDC_BUTTON_DELETE_PPID, &CEAPSimulatorDlg::OnBnClickedButtonDeletePpid)
ON_BN_CLICKED(IDC_BUTTON_PROCEED_WITH_CARRIER, &CEAPSimulatorDlg::OnBnClickedButtonProceedWithCarrier)
+ ON_BN_CLICKED(IDC_BUTTON_PROCEED_WITH_SLOTMAP, &CEAPSimulatorDlg::OnBnClickedButtonProceedWithSlotMap)
ON_BN_CLICKED(IDC_BUTTON_CARRIER_RELEASE, &CEAPSimulatorDlg::OnBnClickedButtonCarrierRelease)
ON_BN_CLICKED(IDC_BUTTON_QUERY_CJ_SPACE, &CEAPSimulatorDlg::OnBnClickedButtonQueryCjSpace)
ON_BN_CLICKED(IDC_BUTTON_QUERY_PJ_SPACE, &CEAPSimulatorDlg::OnBnClickedButtonQueryPjSpace)
ON_BN_CLICKED(IDC_BUTTON_CREATE_PJ, &CEAPSimulatorDlg::OnBnClickedButtonCreatePj)
ON_BN_CLICKED(IDC_BUTTON_CREATE_CJ, &CEAPSimulatorDlg::OnBnClickedButtonCreateCj)
+ ON_BN_CLICKED(IDC_BUTTON_CTRL_OFFLINE, &CEAPSimulatorDlg::OnBnClickedButtonCtrlOffline)
+ ON_BN_CLICKED(IDC_BUTTON_CTRL_ONLINE_LOCAL, &CEAPSimulatorDlg::OnBnClickedButtonCtrlOnlineLocal)
+ ON_BN_CLICKED(IDC_BUTTON_CTRL_ONLINE_REMOTE, &CEAPSimulatorDlg::OnBnClickedButtonCtrlOnlineRemote)
+ ON_BN_CLICKED(IDC_BUTTON_QUERY_CONTROL_STATE, &CEAPSimulatorDlg::OnBnClickedButtonQueryControlState)
+ ON_BN_CLICKED(IDC_BUTTON_QUERY_PROCESS_STATE, &CEAPSimulatorDlg::OnBnClickedButtonQueryProcessState)
+ ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_SVID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllSvid)
+ ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_DVID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllDvid)
+ ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_CEID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllCeid)
+ ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_ECID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllEcid)
+ ON_BN_CLICKED(IDC_BUTTON_SET_ECID, &CEAPSimulatorDlg::OnBnClickedButtonSetEcid)
+ ON_BN_CLICKED(IDC_BUTTON_QUERY_CURRENT_RECIPE, &CEAPSimulatorDlg::OnBnClickedButtonQueryCurrentRecipe)
+ ON_BN_CLICKED(IDC_BUTTON_PP_REQUEST, &CEAPSimulatorDlg::OnBnClickedButtonPpRequest)
END_MESSAGE_MAP()
@@ -177,6 +193,169 @@
// 鎵ц姝ゆ搷浣�
SetIcon(m_hIcon, TRUE); // 璁剧疆澶у浘鏍�
SetIcon(m_hIcon, FALSE); // 璁剧疆灏忓浘鏍�
+
+ // Add missing test button at runtime (resource file is UTF-16, keep it unchanged)
+ {
+ CRect rc(126, 120, 126 + 108, 120 + 14); // dialog units
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S3F17_ProceedWithSlotMap"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_PROCEED_WITH_SLOTMAP, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ // ControlState test buttons at runtime (resource file is UTF-16, keep it unchanged)
+ {
+ const int y = 120;
+ const int w = 70;
+ const int h = 14;
+ const int gap = 3;
+
+ struct BtnDef { int x; int id; const TCHAR* text; };
+ BtnDef defs[] = {
+ { 238, IDC_BUTTON_CTRL_OFFLINE, _T("Ctrl Offline") },
+ { 238 + (w + gap), IDC_BUTTON_CTRL_ONLINE_LOCAL, _T("Ctrl Local") },
+ { 238 + 2 * (w + gap), IDC_BUTTON_CTRL_ONLINE_REMOTE, _T("Ctrl Remote") },
+ };
+
+ for (const auto& d : defs) {
+ CRect rc(d.x, y, d.x + w, y + h); // dialog units
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), d.text,
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)d.id, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ }
+ // S1F3 Query ControlState (SVID=600) button (runtime add to avoid touching UTF-16 RC)
+ {
+ // Place on its own row to avoid overlapping with control-mode buttons.
+ CRect rc(14, 136, 14 + 140, 136 + 14); // dialog units
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S1F3_QueryControlState"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_QUERY_CONTROL_STATE, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ // S1F3 Query ProcessState (SVID=700) button
+ {
+ CRect rc(14 + 140 + 5, 136, 14 + 140 + 5 + 140, 136 + 14); // dialog units, same row offset
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S1F3_QueryProcessState"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_QUERY_PROCESS_STATE, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ // S1F11 QueryAllSVID
+ {
+ CRect rc(14 + 140 + 5 + 140 + 5, 136, 14 + 140 + 5 + 140 + 5 + 140, 136 + 14); // dialog units, next row
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S1F11_QueryAllSVID"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_QUERY_ALL_SVID, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ // S1F23 QueryAllCEID
+ {
+ CRect rc(14, 152, 14 + 140, 152 + 14); // dialog units, next row
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S1F23_QueryAllCEID"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_QUERY_ALL_CEID, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ // S1F21 QueryAllDVID
+ {
+ CRect rc(14 + 140 + 5, 152, 14 + 140 + 5 + 140, 152 + 14);
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S1F21_QueryAllDVID"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_QUERY_ALL_DVID, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ // S2F13 QueryAllECID
+ {
+ CRect rc(14 + 2 * (140 + 5), 192, 14 + 2 * (140 + 5) + 140, 192 + 14);
+ MapDialogRect(&rc);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S2F13_QueryAllECID"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rc.left, rc.top, rc.Width(), rc.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_QUERY_ALL_ECID, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
+ // ECID edit + send (S2F15)
+ {
+ CRect rcEcid(14, 192, 14 + 60, 192 + 14);
+ CRect rcEcv(14 + 60 + 4, 192, 14 + 60 + 4 + 60, 192 + 14);
+ MapDialogRect(&rcEcid);
+ MapDialogRect(&rcEcv);
+ HWND hEditEcid = ::CreateWindow(_T("EDIT"), _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER,
+ rcEcid.left, rcEcid.top, rcEcid.Width(), rcEcid.Height(),
+ m_hWnd, (HMENU)IDC_EDIT_ECID, AfxGetInstanceHandle(), nullptr);
+ HWND hEditEcv = ::CreateWindow(_T("EDIT"), _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER,
+ rcEcv.left, rcEcv.top, rcEcv.Width(), rcEcv.Height(),
+ m_hWnd, (HMENU)IDC_EDIT_ECV, AfxGetInstanceHandle(), nullptr);
+ if (hEditEcid) ::SendMessage(hEditEcid, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ if (hEditEcv) ::SendMessage(hEditEcv, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+
+ CRect rcBtn(14 + 60 + 4 + 60 + 4, 192, 14 + 60 + 4 + 60 + 4 + 90, 192 + 14);
+ MapDialogRect(&rcBtn);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S2F15_SetECID"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rcBtn.left, rcBtn.top, rcBtn.Width(), rcBtn.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_SET_ECID, AfxGetInstanceHandle(), nullptr);
+ if (hBtn) ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ // S1F3 CurrentRecipe (EQ specific) combo + button
+ {
+ CRect rcCombo(14, 168, 14 + 120, 168 + 120); // dropdown height arbitrary
+ MapDialogRect(&rcCombo);
+ HWND hCombo = ::CreateWindow(_T("COMBOBOX"), _T(""),
+ WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL,
+ rcCombo.left, rcCombo.top, rcCombo.Width(), rcCombo.Height(),
+ m_hWnd, (HMENU)IDC_COMBO_EQ_FOR_RECIPE, AfxGetInstanceHandle(), nullptr);
+ if (hCombo != nullptr) {
+ ::SendMessage(hCombo, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ // 绠�鍗曞~鍏呰澶囧垪琛紙鍙寜闇�璋冩暣锛�
+ std::vector<CString> eqs = { _T("Bonder1"), _T("Bonder2"), _T("EFEM"), _T("VacuumBake"), _T("BakeCooling"), _T("Measurement") };
+ for (const auto& eq : eqs) {
+ ::SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)eq);
+ }
+ ::SendMessage(hCombo, CB_SETCURSEL, 0, 0);
+ }
+
+ CRect rcBtn(140, 168, 14 + 140 + 118, 168 + 14);
+ MapDialogRect(&rcBtn);
+ HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S1F3_CurrentRecipe"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ rcBtn.left, rcBtn.top, rcBtn.Width(), rcBtn.Height(),
+ m_hWnd, (HMENU)IDC_BUTTON_QUERY_CURRENT_RECIPE, AfxGetInstanceHandle(), nullptr);
+ if (hBtn != nullptr) {
+ ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
+ }
+ }
SetDlgItemText(IDC_EDIT_IP, _T("127.0.0.1"));
SetDlgItemInt(IDC_EDIT_PORT, 7000);
@@ -282,12 +461,42 @@
GetDlgItem(IDC_BUTTON_TRANSMIT_SPOOLED_DATA)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_PURGE_SPOOLED_DATA)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_QUERY_PPID_LIST)->EnableWindow(enabled);
+ if (GetDlgItem(IDC_BUTTON_DELETE_PPID) != nullptr) {
+ GetDlgItem(IDC_BUTTON_DELETE_PPID)->EnableWindow(enabled);
+ }
+ if (GetDlgItem(IDC_EDIT_DELETE_PPID) != nullptr) {
+ GetDlgItem(IDC_EDIT_DELETE_PPID)->EnableWindow(enabled);
+ }
GetDlgItem(IDC_BUTTON_PROCEED_WITH_CARRIER)->EnableWindow(enabled);
+ if (GetDlgItem(IDC_BUTTON_PROCEED_WITH_SLOTMAP) != nullptr) {
+ GetDlgItem(IDC_BUTTON_PROCEED_WITH_SLOTMAP)->EnableWindow(enabled);
+ }
GetDlgItem(IDC_BUTTON_CARRIER_RELEASE)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_QUERY_CJ_SPACE)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_QUERY_PJ_SPACE)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_CREATE_PJ)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_CREATE_CJ)->EnableWindow(enabled);
+ if (GetDlgItem(IDC_BUTTON_CTRL_OFFLINE) != nullptr) {
+ GetDlgItem(IDC_BUTTON_CTRL_OFFLINE)->EnableWindow(enabled);
+ }
+ if (GetDlgItem(IDC_BUTTON_CTRL_ONLINE_LOCAL) != nullptr) {
+ GetDlgItem(IDC_BUTTON_CTRL_ONLINE_LOCAL)->EnableWindow(enabled);
+ }
+ if (GetDlgItem(IDC_BUTTON_CTRL_ONLINE_REMOTE) != nullptr) {
+ GetDlgItem(IDC_BUTTON_CTRL_ONLINE_REMOTE)->EnableWindow(enabled);
+ }
+ if (GetDlgItem(IDC_BUTTON_QUERY_CONTROL_STATE) != nullptr) {
+ GetDlgItem(IDC_BUTTON_QUERY_CONTROL_STATE)->EnableWindow(enabled);
+ }
+ if (GetDlgItem(IDC_BUTTON_QUERY_PROCESS_STATE) != nullptr) {
+ GetDlgItem(IDC_BUTTON_QUERY_PROCESS_STATE)->EnableWindow(enabled);
+ }
+ if (GetDlgItem(IDC_BUTTON_QUERY_ALL_SVID) != nullptr) {
+ GetDlgItem(IDC_BUTTON_QUERY_ALL_SVID)->EnableWindow(enabled);
+ }
+ if (GetDlgItem(IDC_BUTTON_QUERY_ALL_CEID) != nullptr) {
+ GetDlgItem(IDC_BUTTON_QUERY_ALL_CEID)->EnableWindow(enabled);
+ }
}
void CEAPSimulatorDlg::OnBnClickedButtonConnect()
@@ -400,10 +609,48 @@
theApp.m_model.m_pHsmsActive->hsmsQueryPPIDList();
}
+void CEAPSimulatorDlg::OnBnClickedButtonDeletePpid()
+{
+ CString strPPID;
+ GetDlgItemText(IDC_EDIT_DELETE_PPID, strPPID);
+ strPPID.Trim();
+ std::vector<std::string> ppids;
+ if (!strPPID.IsEmpty()) {
+ CString upper = strPPID;
+ upper.MakeUpper();
+ if (upper != _T("ALL")) {
+ int start = 0;
+ CString token = strPPID.Tokenize(_T(","), start);
+ while (!token.IsEmpty()) {
+ token.Trim();
+ if (!token.IsEmpty()) {
+ ppids.push_back(std::string((LPTSTR)(LPCTSTR)token));
+ }
+ token = strPPID.Tokenize(_T(","), start);
+ }
+ }
+ }
+ // L:0 if ppids empty -> delete all
+ theApp.m_model.m_pHsmsActive->hsmsDeletePPID(ppids);
+}
+
static int DATAID = 1;
void CEAPSimulatorDlg::OnBnClickedButtonProceedWithCarrier()
{
theApp.m_model.m_pHsmsActive->hsmsProceedWithCarrier(DATAID++, "CSX 52078", 1);
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonProceedWithSlotMap()
+{
+ // Example payload (edit as needed)
+ std::vector<std::string> panelIds = {
+ "PANEL_01","PANEL_02","PANEL_03","PANEL_04",
+ "PANEL_05","PANEL_06","PANEL_07","PANEL_08"
+ };
+ std::vector<unsigned char> slotMap = {
+ 3,3,1,1,1,1,1,1 // 1=Empty, 3=Exist
+ };
+ theApp.m_model.m_pHsmsActive->hsmsProceedWithSlotMap(DATAID++, "CSX 52078", 1, "LOT_0001", panelIds, slotMap);
}
void CEAPSimulatorDlg::OnBnClickedButtonCarrierRelease()
@@ -419,6 +666,106 @@
void CEAPSimulatorDlg::OnBnClickedButtonCreateCj()
{
- std::vector<std::string> processJobIds = {"PJ0001", "PJ0003"};
+ std::vector<std::string> processJobIds = {"PJ0001"};
theApp.m_model.m_pHsmsActive->hsmsCreateControlJob("CJ5007", processJobIds);
}
+
+void CEAPSimulatorDlg::OnBnClickedButtonCtrlOffline()
+{
+ theApp.m_model.m_pHsmsActive->hsmsRequestOffline();
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonCtrlOnlineLocal()
+{
+ theApp.m_model.m_pHsmsActive->hsmsRequestOnline();
+ theApp.m_model.m_pHsmsActive->hsmsGoLocal();
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonCtrlOnlineRemote()
+{
+ theApp.m_model.m_pHsmsActive->hsmsRequestOnline();
+ theApp.m_model.m_pHsmsActive->hsmsGoRemote();
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonQueryControlState()
+{
+ theApp.m_model.m_pHsmsActive->hsmsSelectedEquipmentStatusRequest(SVID_ControlState);
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonQueryProcessState()
+{
+ theApp.m_model.m_pHsmsActive->hsmsSelectedEquipmentStatusRequest(SVID_CurrentProcessState);
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonQueryAllSvid()
+{
+ theApp.m_model.m_pHsmsActive->hsmsQueryAllStatusVariables();
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonQueryAllDvid()
+{
+ theApp.m_model.m_pHsmsActive->hsmsQueryAllDataVariables();
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonQueryAllCeid()
+{
+ theApp.m_model.m_pHsmsActive->hsmsQueryAllCollectionEvents();
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonQueryAllEcid()
+{
+ // empty list => all ECID
+ std::vector<unsigned short> ecids;
+ ecids.push_back(2000);
+ theApp.m_model.m_pHsmsActive->hsmsEquipmentConstantRequest(ecids);
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonSetEcid()
+{
+ // simple demo: read ECID and value from edit boxes (reuse PPID edit)
+ CString sEcid, sVal;
+ GetDlgItemText(IDC_EDIT_ECID, sEcid);
+ GetDlgItemText(IDC_EDIT_ECV, sVal);
+ unsigned short id = static_cast<unsigned short>(_ttoi(sEcid));
+ std::string val = CT2A(sVal);
+ std::vector<std::pair<unsigned short, std::string>> kvs;
+ if (id != 0) {
+ kvs.push_back({ id, val });
+ theApp.m_model.m_pHsmsActive->hsmsEquipmentConstantSend(kvs);
+ }
+}
+void CEAPSimulatorDlg::OnBnClickedButtonQueryCurrentRecipe()
+{
+ CString sel;
+ CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO_EQ_FOR_RECIPE);
+ if (pCombo != nullptr) {
+ int idx = pCombo->GetCurSel();
+ if (idx != CB_ERR) {
+ pCombo->GetLBText(idx, sel);
+ }
+ }
+ unsigned int svid = SVID_EQPPExecName; // 榛樿鍏ㄥ眬
+ CString upper = sel;
+ upper.MakeUpper();
+ if (upper.Find(_T("BONDER1")) != -1) svid = SVID_Bonder1CurrentRecipe;
+ else if (upper.Find(_T("BONDER2")) != -1) svid = SVID_Bonder2CurrentRecipe;
+ else if (upper.Find(_T("VACUUMBAKE")) != -1) svid = SVID_VacuumBakeCurrentRecipe;
+ else if (upper.Find(_T("BAKECOOLING")) != -1) svid = SVID_BakeCoolingCurrentRecipe;
+ else if (upper.Find(_T("MEASUREMENT")) != -1) svid = SVID_MeasurementCurrentRecipe;
+ else if (upper.Find(_T("EFEM")) != -1) svid = SVID_EFEMCurrentRecipe;
+
+ theApp.m_model.m_pHsmsActive->hsmsSelectedEquipmentStatusRequest(svid);
+}
+
+void CEAPSimulatorDlg::OnBnClickedButtonPpRequest()
+{
+ CString strPPID;
+ GetDlgItemText(IDC_EDIT_PPID_REQ, strPPID);
+ strPPID.Trim();
+ std::string ppid = CT2A(strPPID);
+ if (ppid.empty()) {
+ AfxMessageBox(_T("璇疯緭鍏� PPID"));
+ return;
+ }
+ theApp.m_model.m_pHsmsActive->hsmsProcessProgramRequest(ppid.c_str());
+}
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
index e10cf1a..d1e5537 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
@@ -57,10 +57,24 @@
afx_msg void OnBnClickedButtonTransmitSpooledData();
afx_msg void OnBnClickedButtonPurgeSpooledData();
afx_msg void OnBnClickedButtonQueryPpidList();
+ afx_msg void OnBnClickedButtonDeletePpid();
afx_msg void OnBnClickedButtonProceedWithCarrier();
+ afx_msg void OnBnClickedButtonProceedWithSlotMap();
afx_msg void OnBnClickedButtonCarrierRelease();
afx_msg void OnBnClickedButtonQueryCjSpace();
afx_msg void OnBnClickedButtonQueryPjSpace();
afx_msg void OnBnClickedButtonCreatePj();
afx_msg void OnBnClickedButtonCreateCj();
+ afx_msg void OnBnClickedButtonCtrlOffline();
+ afx_msg void OnBnClickedButtonCtrlOnlineLocal();
+ afx_msg void OnBnClickedButtonCtrlOnlineRemote();
+ afx_msg void OnBnClickedButtonQueryControlState();
+ afx_msg void OnBnClickedButtonQueryProcessState();
+ afx_msg void OnBnClickedButtonQueryAllSvid();
+ afx_msg void OnBnClickedButtonQueryAllDvid();
+ afx_msg void OnBnClickedButtonQueryAllCeid();
+ afx_msg void OnBnClickedButtonQueryAllEcid();
+ afx_msg void OnBnClickedButtonSetEcid();
+ afx_msg void OnBnClickedButtonQueryCurrentRecipe();
+ afx_msg void OnBnClickedButtonPpRequest();
};
diff --git a/SourceCode/Bond/EAPSimulator/Resource.h b/SourceCode/Bond/EAPSimulator/Resource.h
index 96c191e..5c4789f 100644
--- a/SourceCode/Bond/EAPSimulator/Resource.h
+++ b/SourceCode/Bond/EAPSimulator/Resource.h
@@ -57,14 +57,33 @@
#define IDC_BUTTON_CREATE_PJ2 1040
#define IDC_BUTTON_CREATE_CJ 1040
#define IDC_BUTTON_DELETE 1041
+#define IDC_BUTTON_PROCEED_WITH_SLOTMAP 1042
+#define IDC_BUTTON_CTRL_OFFLINE 1043
+#define IDC_BUTTON_CTRL_ONLINE_LOCAL 1044
+#define IDC_BUTTON_CTRL_ONLINE_REMOTE 1045
+#define IDC_BUTTON_QUERY_CONTROL_STATE 1046
+#define IDC_BUTTON_QUERY_PROCESS_STATE 1047
+#define IDC_BUTTON_QUERY_ALL_SVID 1048
+#define IDC_BUTTON_QUERY_ALL_CEID 1049
+#define IDC_EDIT_DELETE_PPID 1050
+#define IDC_BUTTON_DELETE_PPID 1051
+#define IDC_COMBO_EQ_FOR_RECIPE 1052
+#define IDC_BUTTON_QUERY_CURRENT_RECIPE 1053
+#define IDC_EDIT_PPID_REQ 1054
+#define IDC_BUTTON_PP_REQUEST 1055
+#define IDC_BUTTON_QUERY_ALL_DVID 1056
+#define IDC_BUTTON_QUERY_ALL_ECID 1057
+#define IDC_EDIT_ECID 1058
+#define IDC_EDIT_ECV 1059
+#define IDC_BUTTON_SET_ECID 1060
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 143
+#define _APS_NEXT_RESOURCE_VALUE 146
#define _APS_NEXT_COMMAND_VALUE 32771
-#define _APS_NEXT_CONTROL_VALUE 1042
+#define _APS_NEXT_CONTROL_VALUE 1057
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
diff --git a/SourceCode/Bond/Servo/AccordionWnd.cpp b/SourceCode/Bond/Servo/AccordionWnd.cpp
new file mode 100644
index 0000000..83af762
--- /dev/null
+++ b/SourceCode/Bond/Servo/AccordionWnd.cpp
@@ -0,0 +1,787 @@
+#include "stdafx.h"
+#include "AccordionWnd.h"
+
+
+#define ITEM_HEIGHT 32
+#define ITEM_SPACE 5
+#define TIMER_ID_CHECKHOVER 1
+#define EXPAND_ICON_WIDE 16
+
+#define BORDER 5
+#define SHADOWWIDE 5
+
+CAccordionWnd::CAccordionWnd()
+{
+ m_hWnd = NULL;
+ m_crFrame = GetSysColor(COLOR_WINDOWFRAME);
+ m_crBkgnd = RGB(255, 255, 255);//GetSysColor(COLOR_BTNFACE); ;
+ m_nPadding[PADDING_LEFT] = 5;
+ m_nPadding[PADDING_TOP] = 5;
+ m_nPadding[PADDING_RIGHT] = 5;
+ m_nPadding[PADDING_BOTTOM] = 5;
+ m_crItemBackground[0] = RGB(218, 218, 218);
+ m_crItemBackground[1] = RGB(34, 177, 76);
+ m_crItemFrame[0] = RGB(128, 128, 128);
+ m_crItemFrame[1] = RGB(128, 128, 128);
+ m_crItemText[0] = RGB(68, 84, 111);
+ m_crItemText[1] = RGB(0, 0, 0);
+ m_crSeparateLine = RGB(222, 222, 222);
+ m_crHoverItemBackground = RGB(244, 245, 247);
+ m_crHoverItemFrame = RGB(200, 222, 255);
+ m_hIconExpand = NULL;
+ m_hIconClose = NULL;
+ m_nHoverItem = -1;
+ m_nCheckHoverItem = -1;
+ m_bShadow = FALSE;
+ m_crShadowBkgnd = GetSysColor(COLOR_BTNFACE);
+}
+
+
+CAccordionWnd::~CAccordionWnd()
+{
+ for (size_t i = 0; i < m_vectorItems.size(); i++) {
+ delete m_vectorItems[i];
+ }
+ m_vectorItems.clear();
+}
+
+BOOL CAccordionWnd::RegisterWndClass()
+{
+ WNDCLASS wcExisting = {};
+ HINSTANCE hInstance = AfxGetInstanceHandle();
+ if (::GetClassInfo(hInstance, ACCORDIONWND_CLASS, &wcExisting) ||
+ ::GetClassInfo(NULL, ACCORDIONWND_CLASS, &wcExisting)) {
+ return TRUE;
+ }
+
+ WNDCLASS wc = {};
+ wc.lpszClassName = ACCORDIONWND_CLASS;
+ wc.hInstance = hInstance;
+ wc.lpfnWndProc = WindowProc;
+ wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = 0;
+ wc.lpszMenuName = NULL;
+ wc.hbrBackground = NULL;
+ wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+
+ // 注册自定义类
+ if (::RegisterClass(&wc) != 0) {
+ return TRUE;
+ }
+ return (::GetLastError() == ERROR_CLASS_ALREADY_EXISTS);
+}
+
+CAccordionWnd* CAccordionWnd::FromHandle(HWND hWnd)
+{
+ CAccordionWnd* pAccordionWnd = (CAccordionWnd*)::GetProp(hWnd, ACCORDIONWND_TAG);
+ return pAccordionWnd;
+}
+
+CAccordionWnd* CAccordionWnd::Hook(HWND hWnd)
+{
+ CAccordionWnd* pAccordionWnd = (CAccordionWnd*)GetProp(hWnd, ACCORDIONWND_TAG);
+ if (pAccordionWnd == NULL) {
+ pAccordionWnd = new CAccordionWnd();
+ pAccordionWnd->m_hWnd = hWnd;
+
+ SetProp(hWnd, ACCORDIONWND_TAG, (HANDLE)pAccordionWnd);
+ }
+
+
+ return pAccordionWnd;
+}
+
+void CAccordionWnd::LoadExpandIcon(CString strExpandFile, CString strCloseFile)
+{
+ m_hIconExpand = (HICON)::LoadImage(AfxGetInstanceHandle(), strExpandFile, IMAGE_ICON, EXPAND_ICON_WIDE, EXPAND_ICON_WIDE,
+ LR_LOADFROMFILE | LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
+ m_hIconClose = (HICON)::LoadImage(AfxGetInstanceHandle(), strCloseFile, IMAGE_ICON, EXPAND_ICON_WIDE, EXPAND_ICON_WIDE,
+ LR_LOADFROMFILE | LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
+}
+
+void CAccordionWnd::Setpadding(int type, unsigned int nPadding)
+{
+ if (type >= PADDING_LEFT && PADDING_LEFT <= PADDING_BOTTOM) {
+ m_nPadding[type] = nPadding;
+ }
+}
+
+void CAccordionWnd::SetDefaultItemBackgroundColor(COLORREF crNormal, COLORREF crSel)
+{
+ m_crItemBackground[0] = crNormal;
+ m_crItemBackground[1] = crSel;
+}
+
+void CAccordionWnd::SetDefaultItemFrameColor(COLORREF crNormal, COLORREF crSel)
+{
+ m_crItemFrame[0] = crNormal;
+ m_crItemFrame[1] = crSel;
+}
+
+void CAccordionWnd::SetDefaultItemTextColor(COLORREF crNormal, COLORREF crSel)
+{
+ m_crItemText[0] = crNormal;
+ m_crItemText[1] = crSel;
+}
+
+void CAccordionWnd::Init()
+{
+}
+
+void CAccordionWnd::Release()
+{
+ // delete
+ delete this;
+}
+
+/*
+ * 添加项目
+ * pszName -- 名称
+ * pWnd -- 绑定的窗口
+ * nExpandHeight -- 展开高度,如果为0则自动设置为窗口高
+ */
+void CAccordionWnd::AddItem(char* pszName, CWnd* pWnd, int nExpandHeight, BOOL bExpand/* = TRUE*/, BOOL bEnable/* = TRUE*/)
+{
+ ACCORDIONITEM* pItem = new ACCORDIONITEM;
+ memset(pItem, 0, sizeof(ACCORDIONITEM));
+ pItem->pWnd = pWnd;
+ pItem->bExpand = bExpand;
+ pItem->bEnable = bEnable;
+ strcpy_s(pItem->text, sizeof(pItem->text), pszName);
+ if (nExpandHeight == 0) {
+ RECT rect;
+ pWnd->GetWindowRect(&rect);
+ pItem->nExpandHeight = rect.bottom - rect.top;
+ }
+ else if (nExpandHeight == -1) {
+ pItem->nExpandHeight = -1;
+ }
+ else {
+ pItem->nExpandHeight = nExpandHeight;
+ }
+ m_vectorItems.push_back(pItem);
+
+
+ // 重新调整个子窗口的位置
+ ResizeItemWnd();
+}
+
+void CAccordionWnd::ResizeItemWnd()
+{
+ RECT rcClient, rcItemClient;
+ GetClientRect(m_hWnd, &rcClient);
+
+ for (size_t i = 0; i < m_vectorItems.size(); i++) {
+ ACCORDIONITEM* pItem = m_vectorItems.at(i);
+ if (pItem->pWnd != NULL) {
+ GetItemRect(rcClient, (UINT)i, &rcItemClient);
+ rcItemClient.top += ITEM_HEIGHT;
+ if (pItem->nExpandHeight == -1) {
+ rcItemClient.bottom = rcClient.bottom;
+ }
+ else {
+ rcItemClient.bottom = rcItemClient.top + pItem->nExpandHeight;
+ }
+
+ pItem->pWnd->MoveWindow(&rcItemClient);
+ pItem->pWnd->ShowWindow(pItem->bExpand ? SW_SHOW : SW_HIDE);
+ }
+ }
+}
+
+BOOL CAccordionWnd::GetItemHeaderRect(RECT rcClient, unsigned int nIndex, LPRECT lpRect)
+{
+ RECT rcItem;
+ if (!GetItemRect(rcClient, nIndex, &rcItem)) {
+ return FALSE;
+ }
+
+ rcItem.bottom = rcItem.top + ITEM_HEIGHT;
+ CopyRect(lpRect, &rcItem);
+ return TRUE;
+}
+
+BOOL CAccordionWnd::GetItemRect(RECT rcClient, unsigned int nIndex, LPRECT lpRect)
+{
+ if (nIndex >= m_vectorItems.size()) {
+ return FALSE;
+ }
+
+ RECT rcItemHeader;
+ rcItemHeader.left = rcClient.left + m_nPadding[PADDING_LEFT];
+ rcItemHeader.right = rcClient.right - m_nPadding[PADDING_RIGHT];
+ rcItemHeader.top = rcClient.top + m_nPadding[PADDING_TOP];
+ rcItemHeader.bottom = rcItemHeader.top + ITEM_HEIGHT;
+ for (size_t i = 0; i < m_vectorItems.size(); i++) {
+ ACCORDIONITEM* pItem = m_vectorItems.at(i);
+ if (pItem->bExpand) {
+ rcItemHeader.bottom += pItem->nExpandHeight;
+ }
+
+ if (i == nIndex) {
+ break;;
+ }
+
+ rcItemHeader.top = rcItemHeader.bottom + ITEM_SPACE;
+ rcItemHeader.bottom = rcItemHeader.top + ITEM_HEIGHT;
+ }
+
+
+ CopyRect(lpRect, &rcItemHeader);
+ return TRUE;
+}
+
+int CAccordionWnd::HitTest(POINT pt, int& nHitTest)
+{
+ int nRet = -1;
+ nHitTest = -1;
+ RECT rcClient;
+ GetClientRect(m_hWnd, &rcClient);
+ if (PtInRect(&rcClient, pt)) {
+ nRet = 1;
+ }
+
+ int nItemIndex = -1;
+ RECT rcItemHeader;
+ for (size_t i = 0; i < m_vectorItems.size(); i++) {
+ GetItemHeaderRect(rcClient, (unsigned int)i, &rcItemHeader);
+
+ if (PtInRect(&rcItemHeader, pt)) {
+ nItemIndex = (unsigned int)i;
+
+ break;
+ }
+ }
+
+ if (nItemIndex != -1) {
+ nRet = 2;
+ nHitTest = nItemIndex;
+ }
+
+ return nRet;
+}
+
+BOOL CAccordionWnd::Togle(unsigned int nIndex)
+{
+ if (nIndex >= m_vectorItems.size()) {
+ return FALSE;
+ }
+
+ ACCORDIONITEM* pItem = m_vectorItems[nIndex];
+ pItem->bExpand = !pItem->bExpand;
+
+
+ // 重新调整个子窗口的位置
+ ResizeItemWnd();
+
+ RECT rcClient;
+ GetClientRect(m_hWnd, &rcClient);
+ ::InvalidateRect(m_hWnd, &rcClient, TRUE);
+
+ return TRUE;
+}
+
+void CAccordionWnd::Notify(int nCode, int dwData, int dwData1/* = 0*/, int dwData2/* = 0*/)
+{
+ HWND hParent;
+ hParent = GetParent(m_hWnd);
+ if (hParent != NULL) {
+ ACCORDION_NMHDR accordionWndnmhdr;
+ accordionWndnmhdr.nmhdr.hwndFrom = m_hWnd;
+ accordionWndnmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID);
+ accordionWndnmhdr.nmhdr.code = nCode;
+ accordionWndnmhdr.dwData = dwData;
+ accordionWndnmhdr.dwData1 = dwData1;
+ accordionWndnmhdr.dwData2 = dwData2;
+ SendMessage(hParent, WM_NOTIFY, (WPARAM)accordionWndnmhdr.nmhdr.idFrom, (LPARAM)&accordionWndnmhdr);
+ }
+}
+
+/*
+ * 拦截窗口消息函数
+ */
+LRESULT CALLBACK CAccordionWnd::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ CAccordionWnd* pAccordionWnd = (CAccordionWnd*)GetProp(hWnd, ACCORDIONWND_TAG);
+ if (pAccordionWnd == NULL && uMsg != WM_NCCREATE)
+ {
+ return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+
+
+ // 如果Hook则响应消息
+ ASSERT(hWnd);
+ switch (uMsg)
+ {
+ case WM_NCCREATE:
+ return CAccordionWnd::OnNcCreate(hWnd, wParam, lParam);
+
+ case WM_DESTROY:
+ return pAccordionWnd->OnDestroy(wParam, lParam);
+
+ case WM_NCCALCSIZE:
+ return pAccordionWnd->OnNcCalcsize(wParam, lParam);
+
+ case WM_NCPAINT:
+ return pAccordionWnd->OnNcPaint(wParam, lParam);
+
+ case WM_PAINT:
+ return pAccordionWnd->OnPaint(wParam, lParam);
+
+ case WM_TIMER:
+ return pAccordionWnd->OnTimer(wParam, lParam);
+
+ case WM_MOUSEMOVE:
+ return pAccordionWnd->OnMouseMove(wParam, lParam);
+
+ case WM_LBUTTONDOWN:
+ return pAccordionWnd->OnLButtonDown(wParam, lParam);
+
+ case WM_LBUTTONUP:
+ return pAccordionWnd->OnLButtonUp(wParam, lParam);
+
+ case WM_MOUSEWHEEL:
+ return pAccordionWnd->OnMouseWheel(wParam, lParam);
+
+ case WM_SIZE:
+ return pAccordionWnd->OnSize(wParam, lParam);
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS;
+
+ default:
+ break;
+ }
+
+ return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+/*
+ * WM_NCCREATE
+ * 窗口创建前的初始化工作
+ */
+LRESULT CAccordionWnd::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+ CAccordionWnd* pAccordionWnd = (CAccordionWnd*)GetProp(hWnd, ACCORDIONWND_TAG);
+ ASSERT(pAccordionWnd == NULL);
+
+ Hook(hWnd);
+ return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam);
+}
+
+/*
+ * WM_NCCALCSIZE
+ */
+LRESULT CAccordionWnd::OnNcCalcsize(WPARAM wParam, LPARAM lParam)
+{
+ if (!m_bShadow) {
+ return ::DefWindowProc(m_hWnd, WM_NCCALCSIZE, wParam, lParam);
+ }
+
+
+ LPRECT lprcWnd = (LPRECT)lParam;
+ lprcWnd->left += BORDER;
+ lprcWnd->top += BORDER;
+ lprcWnd->right -= (BORDER + SHADOWWIDE);
+ lprcWnd->bottom -= (BORDER + SHADOWWIDE);
+
+ return 0;
+}
+
+/*
+ * WM_DESTROY
+ * 窗口销毁时
+ */
+LRESULT CAccordionWnd::OnDestroy(WPARAM wParam, LPARAM lParam)
+{
+ Release();
+ return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam);
+}
+
+
+/*
+ * WM_TIMER
+ */
+LRESULT CAccordionWnd::OnTimer(WPARAM wParam, LPARAM lParam)
+{
+ int nTimerId = (int)wParam;
+ if (m_nTimerId == nTimerId) {
+
+ POINT pt;
+ ::GetCursorPos(&pt);
+ ::ScreenToClient(m_hWnd, &pt);
+
+ int nRet, nHitTest;
+ nRet = HitTest(pt, nHitTest);
+ if (m_nCheckHoverItem != nHitTest) {
+ KillTimer(m_hWnd, m_nTimerId);
+ m_nHoverItem = nHitTest;
+ m_nCheckHoverItem = nHitTest;
+
+ RECT rcClient;
+ GetClientRect(m_hWnd, &rcClient);
+ ::InvalidateRect(m_hWnd, &rcClient, TRUE);
+ }
+ }
+
+
+ return ::DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam);
+}
+
+/*
+ * WM_MOUSEMOVE
+ * 鼠标移动时,检测鼠标位置并回调给主窗口
+ */
+LRESULT CAccordionWnd::OnMouseMove(WPARAM wParam, LPARAM lParam)
+{
+ POINT pt;
+ pt.x = (int)LOWORD(lParam);
+ pt.y = (int)HIWORD(lParam);
+
+ int nRet, nHitTest;
+ nRet = HitTest(pt, nHitTest);
+
+ if (nRet == 2) {
+ ACCORDIONITEM* pItem = m_vectorItems[nHitTest];
+ if (pItem != NULL && pItem->bEnable) {
+ ::SetCursor(LoadCursor(NULL, IDC_HAND));
+ }
+ }
+ else {
+ ::SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+
+ int nLastItem = m_nHoverItem;
+ if (m_nHoverItem != nHitTest) {
+ m_nHoverItem = nHitTest;
+
+ RECT rcClient, rcLastItemClient, rcCurItemClient;
+ GetClientRect(m_hWnd, &rcClient);
+ GetItemRect(rcClient, nLastItem, &rcLastItemClient);
+ ::InvalidateRect(m_hWnd, &rcLastItemClient, nHitTest < 0);
+
+ if (nHitTest >= 0) {
+ ACCORDIONITEM* pItem = m_vectorItems.at(nHitTest);
+ if (!pItem->bEnable) {
+ m_nHoverItem = -1;
+ }
+ else {
+ KillTimer(m_hWnd, m_nTimerId);
+ m_nTimerId = SetTimer(m_hWnd, TIMER_ID_CHECKHOVER, 200, NULL);
+ m_nCheckHoverItem = m_nHoverItem;
+
+ GetItemRect(rcClient, m_nHoverItem, &rcCurItemClient);
+ ::InvalidateRect(m_hWnd, &rcCurItemClient, TRUE);
+ }
+ }
+ }
+
+
+ return ::DefWindowProc(m_hWnd, WM_MOUSEMOVE, wParam, lParam);
+}
+
+/*
+ * WM_LBUTTONDOWN
+ * 鼠标左键下压
+ */
+LRESULT CAccordionWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam)
+{
+ POINT pt;
+ pt.x = (int)LOWORD(lParam);
+ pt.y = (int)HIWORD(lParam);
+
+ int nRet, nHitTest;
+ nRet = HitTest(pt, nHitTest);
+
+ if (nRet == 2) {
+ ACCORDIONITEM* pItem = m_vectorItems[nHitTest];
+ if (pItem != NULL && pItem->bEnable) {
+ ::SetCursor(LoadCursor(NULL, IDC_HAND));
+ }
+ }
+ else {
+ ::SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+
+
+ return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
+}
+
+/*
+ * WM_LBUTTONUP
+ * 鼠标左键释放
+ */
+LRESULT CAccordionWnd::OnLButtonUp(WPARAM wParam, LPARAM lParam)
+{
+ POINT pt;
+ pt.x = (int)LOWORD(lParam);
+ pt.y = (int)HIWORD(lParam);
+
+ int nRet, nHitTest;
+ nRet = HitTest(pt, nHitTest);
+
+ if (nRet == 2) {
+ ACCORDIONITEM* pItem = m_vectorItems[nHitTest];
+ if (pItem != NULL && pItem->bEnable) {
+ ::SetCursor(LoadCursor(NULL, IDC_HAND));
+ Togle(nHitTest);
+ }
+ }
+ else {
+ ::SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+
+
+ return ::DefWindowProc(m_hWnd, WM_LBUTTONUP, wParam, lParam);
+}
+
+/*
+ * WM_MOUSEWHEEL
+ * 鼠标滚轮滚动时,缩放图像
+ */
+LRESULT CAccordionWnd::OnMouseWheel(WPARAM wParam, LPARAM lParam)
+{
+ return ::DefWindowProc(m_hWnd, WM_MOUSEWHEEL, wParam, lParam);
+}
+
+/*
+ * WM_NCPAINT
+ */
+LRESULT CAccordionWnd::OnNcPaint(WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lRet = ::DefWindowProc(m_hWnd, WM_NCPAINT, wParam, lParam);
+
+
+ // 然后画边框
+ long styleEx = GetWindowLong(m_hWnd, GWL_EXSTYLE);
+ if ((styleEx & WS_EX_CLIENTEDGE) == WS_EX_CLIENTEDGE) {
+
+ RECT rcWindow, rcClient;
+ GetClientRect(m_hWnd, &rcClient);
+ ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.left);
+ ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.right);
+ GetWindowRect(m_hWnd, &rcWindow);
+ ::OffsetRect(&rcClient, -rcWindow.left, -rcWindow.top);
+ ::OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
+
+ HRGN hRgnWnd = CreateRectRgnIndirect(&rcWindow);
+ HRGN hRgnClient = CreateRectRgnIndirect(&rcClient);
+
+ HDC hDC = GetWindowDC(m_hWnd);
+ ::SelectClipRgn(hDC, hRgnWnd);
+ ::ExtSelectClipRgn(hDC, hRgnClient, RGN_DIFF);
+
+
+ // 没有阴影的边框
+ if (!m_bShadow) {
+ HBRUSH hBrushBK, hBrushFrame;
+ hBrushBK = CreateSolidBrush(m_crBkgnd);
+ ::FillRect(hDC, &rcWindow, hBrushBK);
+ DeleteObject(hBrushBK);
+
+ hBrushFrame = CreateSolidBrush(m_crFrame);
+ ::FrameRect(hDC, &rcWindow, hBrushFrame);
+ DeleteObject(hBrushFrame);
+ }
+
+ // 有阴影的边框
+ else {
+
+ RECT rcFrame0, rcFrame1;
+ rcFrame0.left = rcWindow.left + SHADOWWIDE;
+ rcFrame0.top = rcWindow.top + SHADOWWIDE;
+ rcFrame0.right = rcWindow.right;
+ rcFrame0.bottom = rcWindow.bottom;
+ rcFrame1.left = rcWindow.left;
+ rcFrame1.top = rcWindow.top;
+ rcFrame1.right = rcWindow.right - SHADOWWIDE;
+ rcFrame1.bottom = rcWindow.bottom - SHADOWWIDE;
+
+
+ // 背景框和对话框(父窗体)背景色一致
+ HBRUSH hBrushBK, hBrushFrame;
+ hBrushBK = CreateSolidBrush(m_crShadowBkgnd);
+ ::FillRect(hDC, &rcWindow, hBrushBK);
+ DeleteObject(hBrushBK);
+
+
+ // 阴影框
+ BYTE r = GetRValue(m_crShadowBkgnd);
+ BYTE g = GetGValue(m_crShadowBkgnd);
+ BYTE b = GetBValue(m_crShadowBkgnd);
+ BYTE rstep = (r - GetRValue(m_crFrame)) / SHADOWWIDE;
+ BYTE gstep = (r - GetGValue(m_crFrame)) / SHADOWWIDE;
+ BYTE bstep = (r - GetBValue(m_crFrame)) / SHADOWWIDE;
+
+ for (int i = 0; i < SHADOWWIDE; i++) {
+ hBrushBK = CreateSolidBrush(RGB(r - i * rstep, g - i * gstep, b - i * bstep));
+ ::FillRect(hDC, &rcFrame0, hBrushBK);
+ DeleteObject(hBrushBK);
+ rcFrame0.bottom -= 1;
+ rcFrame0.right -= 1;
+ }
+
+
+ // 前景框
+ hBrushBK = CreateSolidBrush(m_crBkgnd);
+ ::FillRect(hDC, &rcFrame1, hBrushBK);
+ DeleteObject(hBrushBK);
+
+ hBrushFrame = CreateSolidBrush(m_crFrame);
+ ::FrameRect(hDC, &rcFrame1, hBrushFrame);
+ DeleteObject(hBrushFrame);
+ }
+
+
+ ::DeleteObject(hRgnWnd);
+ ::DeleteObject(hRgnClient);
+ ReleaseDC(m_hWnd, hDC);
+ }
+
+ return lRet;
+}
+
+/*
+ * WM_PAINT
+ */
+LRESULT CAccordionWnd::OnPaint(WPARAM wParam, LPARAM lParam)
+{
+ HDC hDC, hMemDC;
+ HBITMAP hBitmap;
+ RECT rcClient;
+ CString strText;
+ HFONT hFont;
+ HBRUSH hBrushBK;
+
+
+ // BeginPaint
+ PAINTSTRUCT ps;
+ hDC = BeginPaint(m_hWnd, &ps);
+ GetClientRect(m_hWnd, &rcClient);
+
+ hMemDC = ::CreateCompatibleDC(hDC);
+ hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left,
+ rcClient.bottom - rcClient.top);
+ ::SelectObject(hMemDC, hBitmap);
+ ::SetBkMode(hMemDC, TRANSPARENT);
+
+
+ // 背景颜色
+ hBrushBK = CreateSolidBrush(m_crBkgnd);
+ ::FillRect(hMemDC, &rcClient, hBrushBK);
+ DeleteObject(hBrushBK);
+
+
+ // 绘子项列表
+ hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ ::SelectObject(hMemDC, hFont);
+
+ HPEN hPenSeparate = ::CreatePen(PS_SOLID, 1, m_crSeparateLine);
+ RECT rcItem, rcItemHeader;
+ for (size_t i = 0; i < m_vectorItems.size(); i++) {
+ ACCORDIONITEM* pItem = m_vectorItems[i];
+ GetItemRect(rcClient, (UINT)i, &rcItem);
+ GetItemHeaderRect(rcClient, (UINT)i, &rcItemHeader);
+
+
+ // 热点项的背景色和边框
+ if (m_nHoverItem == (int)i) {
+ HBRUSH hbrItemHeaderBackground = CreateSolidBrush(m_crHoverItemBackground);
+ HBRUSH hbrItemFrame = CreateSolidBrush(m_crHoverItemFrame);
+
+ HRGN hRgn = CreateRoundRectRgn(rcItemHeader.left, rcItemHeader.top, rcItemHeader.right, rcItemHeader.bottom, 2, 2);
+ ::FillRgn(hMemDC, hRgn, hbrItemHeaderBackground);
+ ::FrameRgn(hMemDC, hRgn, hbrItemFrame, 1, 1);
+ ::DeleteObject(hbrItemHeaderBackground);
+ ::DeleteObject(hbrItemFrame);
+ ::DeleteObject(hRgn);
+ }
+
+
+ // 箭头
+ BOOL bDrawIcon = DrawIconEx(hMemDC, rcItemHeader.left + (ITEM_HEIGHT - EXPAND_ICON_WIDE) / 2, rcItemHeader.top + (ITEM_HEIGHT - EXPAND_ICON_WIDE) / 2,
+ pItem->bExpand ? m_hIconExpand : m_hIconClose, EXPAND_ICON_WIDE, EXPAND_ICON_WIDE, 0, 0, DI_NORMAL);
+
+
+ // 文本
+ ::SetTextColor(hMemDC, m_nHoverItem == (int)i ? m_crItemText[1] : m_crItemText[0]);
+ RECT rcText;
+ rcText.left = rcItemHeader.left + (bDrawIcon ? ITEM_HEIGHT : (ITEM_HEIGHT - EXPAND_ICON_WIDE) / 2);
+ rcText.top = rcItemHeader.top;
+ rcText.right = rcItemHeader.right;
+ rcText.bottom = rcItemHeader.bottom;
+ ::DrawText(hMemDC, pItem->text, (int)strlen(pItem->text), &rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
+
+
+ // 文本右边分隔线
+ SIZE sizeText;
+ GetTextExtentPoint32(hMemDC, pItem->text, (int)strlen(pItem->text), &sizeText);
+
+
+ HPEN hOldPen = (HPEN)::SelectObject(hMemDC, hPenSeparate);
+ MoveToEx(hMemDC, rcText.left + sizeText.cx + 10, rcItemHeader.top + (rcItemHeader.bottom - rcItemHeader.top - 1) / 2, NULL);
+ LineTo(hMemDC, rcItemHeader.right - 10, rcItemHeader.top + (rcItemHeader.bottom - rcItemHeader.top - 1) / 2);
+ ::SelectObject(hMemDC, hOldPen);
+
+
+ rcItemHeader.top = rcItemHeader.bottom + ITEM_SPACE;
+ rcItemHeader.bottom = rcItemHeader.top + ITEM_HEIGHT;
+ }
+ ::DeleteObject(hPenSeparate);
+
+
+ // EndPaint
+ ::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
+ hMemDC, 0, 0, SRCCOPY);
+ EndPaint(m_hWnd, &ps);
+ ::DeleteObject(hBitmap);
+ ::DeleteDC(hMemDC);
+
+
+ return 1;
+}
+
+/*
+ * WM_SIZE
+ */
+LRESULT CAccordionWnd::OnSize(WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lRet = ::DefWindowProc(m_hWnd, WM_SIZE, wParam, lParam);
+
+ ResizeItemWnd();
+
+ return lRet;
+}
+
+/*
+ * 设置背景色
+ * color -- 背景色
+ */
+void CAccordionWnd::SetBkgndColor(COLORREF color)
+{
+ m_crBkgnd = color;
+}
+
+
+/*
+ * 设置阴影的背景色(即父窗口的背景)
+ * color -- 背景色
+ */
+void CAccordionWnd::SetShadowBkgnd(COLORREF color)
+{
+ m_crShadowBkgnd = color;
+}
+
+/*
+ * 设置边框颜色
+ * color -- 边框颜色
+ */
+void CAccordionWnd::SetFrameColor(COLORREF color, BOOL bShadow/* = FALSE*/)
+{
+ m_crFrame = color;
+ m_bShadow = bShadow;
+ SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+}
+
diff --git a/SourceCode/Bond/Servo/AccordionWnd.h b/SourceCode/Bond/Servo/AccordionWnd.h
new file mode 100644
index 0000000..46fa7a1
--- /dev/null
+++ b/SourceCode/Bond/Servo/AccordionWnd.h
@@ -0,0 +1,131 @@
+#pragma once
+#include <functional>
+#include <vector>
+
+#ifndef ACCORDIONWND_TAG
+
+#ifdef _WIN32
+
+#define ACCORDIONWND_CLASSA "AccordionWnd"
+#define ACCORDIONWND_CLASSW L"AccordionWnd"
+
+#ifdef UNICODE
+#define ACCORDIONWND_CLASS ACCORDIONWND_CLASSW
+#else
+#define ACCORDIONWND_CLASS ACCORDIONWND_CLASSA
+#endif
+
+#else
+#define ACCORDIONWND_CLASS "AccordionWnd"
+#endif
+
+
+#define ACCORDIONWND_TAG _T("ACCORDIONWND_TAG")
+
+#define ACCORDIONWND_FIRST (0U-3590U)
+#define ACCORDIONWND_LAST (0U-5350U)
+#define ACCORDIONWND_ONTOGLE (ACCORDIONWND_FIRST - 1)
+
+typedef struct tagACCORDION_NMHDR
+{
+ NMHDR nmhdr;
+ DWORD dwData;
+ DWORD dwData1;
+ DWORD dwData2;
+} ACCORDION_NMHDR;
+
+typedef struct tagACCORDIONITEM
+{
+ unsigned int id;
+ int nExpandHeight;
+ COLORREF crBackground[2];
+ COLORREF crFrame[2];
+ COLORREF crText[2];
+ char text[256];
+ CWnd *pWnd;
+ BOOL bExpand;
+ BOOL bEnable; // 是否可以点击展开和收起
+} ACCORDIONITEM;
+
+#endif
+
+#define PADDING_LEFT 0
+#define PADDING_TOP 1
+#define PADDING_RIGHT 2
+#define PADDING_BOTTOM 3
+
+class CAccordionWnd
+{
+public:
+ CAccordionWnd();
+ ~CAccordionWnd();
+
+
+public:
+ static BOOL RegisterWndClass();
+ static CAccordionWnd * FromHandle(HWND hWnd);
+ void SetFrameColor(COLORREF color, BOOL bShadow = FALSE);
+ void SetBkgndColor(COLORREF color);
+ void SetShadowBkgnd(COLORREF color);
+
+public:
+ void LoadExpandIcon(CString strExpandFile, CString strCloseFile);
+ void Setpadding(int type, unsigned int nPadding);
+ void SetDefaultItemBackgroundColor(COLORREF crNormal, COLORREF crSel);
+ void SetDefaultItemFrameColor(COLORREF crNormal, COLORREF crSel);
+ void SetDefaultItemTextColor(COLORREF crNormal, COLORREF crSel);
+ void AddItem(char *pszName, CWnd *pWnd, int nExpandHeight, BOOL bExpand = TRUE, BOOL bEnable = TRUE);
+ BOOL Togle(unsigned int nIndex);
+ int GetItemHeaderHeight();
+ BOOL IsExpand(unsigned int nIndex);
+
+private:
+ void Init();
+ void Notify(int nCode, int dwData, int dwData1 = 0, int dwData2 = 0);
+ void Release();
+ void ResizeItemWnd();
+ static CAccordionWnd* Hook(HWND hWnd);
+ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ static LRESULT OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
+ LRESULT OnDestroy(WPARAM wParam, LPARAM lParam);
+ LRESULT OnTimer(WPARAM wParam, LPARAM lParam);
+ LRESULT OnNcPaint(WPARAM wParam, LPARAM lParam);
+ LRESULT OnNcCalcsize(WPARAM wParam, LPARAM lParam);
+ LRESULT OnPaint(WPARAM wParam, LPARAM lParam);
+ LRESULT OnMouseMove(WPARAM wParam, LPARAM lParam);
+ LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam);
+ LRESULT OnLButtonUp(WPARAM wParam, LPARAM lParam);
+ LRESULT OnMouseWheel(WPARAM wParam, LPARAM lParam);
+ LRESULT OnSize(WPARAM wParam, LPARAM lParam);
+
+private:
+ HWND m_hWnd;
+ COLORREF m_crBkgnd;
+ COLORREF m_crFrame;
+ HICON m_hIconClose;
+ HICON m_hIconExpand;
+ int m_nHoverItem;
+ int m_nCheckHoverItem;
+ BOOL m_bShadow; // 阴影
+ COLORREF m_crShadowBkgnd; // 阴影背景色(即父窗口的颜色)
+
+private:
+ unsigned int m_nPadding[4];
+ COLORREF m_crItemBackground[2];
+ COLORREF m_crItemFrame[2];
+ COLORREF m_crItemText[2];
+ COLORREF m_crSeparateLine;
+ COLORREF m_crHoverItemBackground;
+ COLORREF m_crHoverItemFrame;
+ CString m_strExpandIconFilepath[2];
+
+private:
+ std::vector<ACCORDIONITEM *> m_vectorItems;
+ int m_nTimerId;
+
+private:
+ int HitTest(POINT pt, int &nHitTest);
+ BOOL GetItemHeaderRect(RECT rcClient, unsigned int nIndex, LPRECT lpRect);
+ BOOL GetItemRect(RECT rcClient, unsigned int nIndex, LPRECT lpRect);
+};
+
diff --git a/SourceCode/Bond/Servo/AlarmManager.cpp b/SourceCode/Bond/Servo/AlarmManager.cpp
index 56d6cbb..8cd9acb 100644
--- a/SourceCode/Bond/Servo/AlarmManager.cpp
+++ b/SourceCode/Bond/Servo/AlarmManager.cpp
@@ -1,6 +1,7 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "Common.h"
#include "AlarmManager.h"
+#include "Log.h"
#include <sstream>
#include <fstream>
#include <iostream>
@@ -8,25 +9,26 @@
#include <ctime>
#include <iomanip>
#include <random>
+#include <chrono>
-// 常量
+// 甯搁噺
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;
@@ -34,7 +36,7 @@
}
}
-// 初始化报警表
+// 鍒濆鍖栨姤璀﹁〃
bool AlarmManager::initAlarmTable() {
char path[MAX_PATH];
GetModuleFileName(NULL, path, MAX_PATH);
@@ -49,7 +51,7 @@
throw std::runtime_error("Failed to connect to database.");
}
- // 创建设备表
+ // 鍒涘缓璁惧琛�
const std::string createDevicesTableQuery = R"(
CREATE TABLE IF NOT EXISTS devices (
device_id TEXT PRIMARY KEY NOT NULL,
@@ -60,7 +62,7 @@
return false;
}
- // 创建单元表,设备ID和单元ID组合作为主键
+ // 鍒涘缓鍗曞厓琛紝璁惧ID鍜屽崟鍏僆D缁勫悎浣滀负涓婚敭
const std::string createUnitsTableQuery = R"(
CREATE TABLE IF NOT EXISTS units (
device_id TEXT NOT NULL,
@@ -74,7 +76,7 @@
return false;
}
- // 创建报警表,报警记录的alarm_event_id是主键
+ // 鍒涘缓鎶ヨ琛紝鎶ヨ璁板綍鐨刟larm_event_id鏄富閿�
const std::string createAlarmsTableQuery = R"(
CREATE TABLE IF NOT EXISTS alarms (
alarm_event_id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -93,8 +95,9 @@
return false;
}
- // 设备列表 (ID -> 名称)
+ // 璁惧鍒楄〃 (ID -> 鍚嶇О)
std::vector<std::pair<int, std::string>> devices = {
+ {0, "Software"},
{EQ_ID_LOADPORT1, EQ_NAME_LOADPORT1},
{EQ_ID_LOADPORT2, EQ_NAME_LOADPORT2},
{EQ_ID_LOADPORT3, EQ_NAME_LOADPORT3},
@@ -113,12 +116,12 @@
{EQ_ID_OPERATOR_REMOVE, EQ_NAME_OPERATOR_REMOVE}
};
- // 插入 devices 和对应的默认 unit
+ // 鎻掑叆 devices 鍜屽搴旂殑榛樿 unit
for (const auto& dev : devices) {
int nDeviceId = dev.first;
const std::string& strDeviceName = dev.second;
- // 插入设备
+ // 鎻掑叆璁惧
std::ostringstream ossDev;
ossDev << "INSERT OR IGNORE INTO devices (device_id, device_name) VALUES("
<< nDeviceId << ", '" << strDeviceName << "')";
@@ -126,7 +129,7 @@
return false;
}
- // 插入默认单元 (unit_id = 0, unit_name = device_name)
+ // 鎻掑叆榛樿鍗曞厓 (unit_id = 0, unit_name = device_name)
std::ostringstream ossUnit;
ossUnit << "INSERT OR IGNORE INTO units (device_id, unit_id, unit_name) VALUES("
<< nDeviceId << ", 0, '" << strDeviceName << "')";
@@ -138,14 +141,14 @@
return true;
}
-// 销毁报警表
+// 閿�姣佹姤璀﹁〃
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.");
@@ -154,9 +157,9 @@
return m_pDB->executeQuery(dropTableQuery);
}
-// 插入模拟数据
+// 鎻掑叆妯℃嫙鏁版嵁
void AlarmManager::insertMockData() {
- // 插入设备数据
+ // 鎻掑叆璁惧鏁版嵁
for (int i = 1; i <= 3; ++i) {
std::string deviceName = "Device" + std::to_string(i);
std::stringstream query;
@@ -166,7 +169,7 @@
}
}
- // 插入单元数据
+ // 鎻掑叆鍗曞厓鏁版嵁
for (int i = 1; i <= 3; ++i) {
for (int j = 0; j <= 3; ++j) {
int unitId = j;
@@ -175,8 +178,8 @@
std::stringstream query;
query << "INSERT INTO units (device_id, unit_id, unit_name) VALUES ('"
- << deviceId << "', '" // 插入设备ID,确保是字符串
- << unitId << "', '" // 插入单元ID,确保是字符串
+ << deviceId << "', '" // 鎻掑叆璁惧ID锛岀‘淇濇槸瀛楃涓�
+ << unitId << "', '" // 鎻掑叆鍗曞厓ID锛岀‘淇濇槸瀛楃涓�
<< unitName << "');";
if (!m_pDB->executeQuery(query.str())) {
@@ -186,7 +189,7 @@
}
/*
- // 初始化随机数生成器
+ // 鍒濆鍖栭殢鏈烘暟鐢熸垚鍣�
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> deviceDis(1, 3);
@@ -195,7 +198,7 @@
std::uniform_int_distribution<> severityDis(0, 3);
std::vector<std::string> descriptions = { "Overheat", "Sensor failure", "Power outage" };
- // 时间相关
+ // 鏃堕棿鐩稿叧
auto now = std::chrono::system_clock::now();
auto start_time = std::chrono::system_clock::to_time_t(now);
auto end_time = std::chrono::system_clock::to_time_t(now + std::chrono::minutes(10));
@@ -205,13 +208,13 @@
localtime_s(&start_tm, &start_time);
localtime_s(&end_tm, &end_time);
- // 插入模拟数据
+ // 鎻掑叆妯℃嫙鏁版嵁
for (int i = 0; i < 10; ++i) {
- int deviceId = deviceDis(gen); // 随机设备ID
- int unitId = unitDis(gen); // 随机单元ID
- int errorCode = errorCodeDis(gen); // 随机错误码
- int severityLevel = severityDis(gen); // 随机生成报警等级
- std::string description = descriptions[errorCodeDis(gen) % descriptions.size()]; // 随机报警描述
+ int deviceId = deviceDis(gen); // 闅忔満璁惧ID
+ int unitId = unitDis(gen); // 闅忔満鍗曞厓ID
+ int errorCode = errorCodeDis(gen); // 闅忔満閿欒鐮�
+ int severityLevel = severityDis(gen); // 闅忔満鐢熸垚鎶ヨ绛夌骇
+ std::string description = descriptions[errorCodeDis(gen) % descriptions.size()]; // 闅忔満鎶ヨ鎻忚堪
std::stringstream query;
query << "INSERT INTO alarms (id, severity_level, device_id, unit_id, description, start_time, end_time) "
@@ -226,38 +229,38 @@
*/
}
-// 添加报警信息
+// 娣诲姞鎶ヨ淇℃伅
bool AlarmManager::addAlarm(const AlarmData& alarmData, int& alarmEventId) {
if (!m_pDB) {
return false;
}
#if 0
- // 开始事务
+ // 寮�濮嬩簨鍔�
m_pDB->executeQuery("BEGIN TRANSACTION;");
- // 构建插入查询
+ // 鏋勫缓鎻掑叆鏌ヨ
std::ostringstream query;
query << "INSERT INTO alarms (id, severity_level, device_id, unit_id, description, start_time, end_time) VALUES ("
- << alarmData.nId << ", " // 错误码
- << alarmData.nSeverityLevel << ", " // 报警等级
- << alarmData.nDeviceId << ", " // 设备ID
- << alarmData.nUnitId << ", '" // 单元ID
- << alarmData.strDescription << "', '" // 描述
- << alarmData.strStartTime << "', '" // 开始时间
- << alarmData.strEndTime << "')"; // 结束时间
+ << alarmData.nId << ", " // 閿欒鐮�
+ << alarmData.nSeverityLevel << ", " // 鎶ヨ绛夌骇
+ << alarmData.nDeviceId << ", " // 璁惧ID
+ << alarmData.nUnitId << ", '" // 鍗曞厓ID
+ << alarmData.strDescription << "', '" // 鎻忚堪
+ << alarmData.strStartTime << "', '" // 寮�濮嬫椂闂�
+ << alarmData.strEndTime << "')"; // 缁撴潫鏃堕棿
- // 使用锁保护多线程安全
+ // 浣跨敤閿佷繚鎶ゅ绾跨▼瀹夊叏
std::lock_guard<std::mutex> lock(m_mutex);
- // 执行插入查询
+ // 鎵ц鎻掑叆鏌ヨ
bool result = m_pDB->executeQuery(query.str());
if (result) {
alarmEventId = getLastInsertId();
m_alarmCache[alarmEventId] = alarmData;
}
- // 提交事务
+ // 鎻愪氦浜嬪姟
m_pDB->executeQuery("COMMIT;");
return result;
@@ -273,23 +276,23 @@
}
}
- // 构建插入查询并使用 RETURNING 获取插入后的 alarm_event_id
+ // 鏋勫缓鎻掑叆鏌ヨ骞朵娇鐢� RETURNING 鑾峰彇鎻掑叆鍚庣殑 alarm_event_id
std::ostringstream query;
query << "INSERT INTO alarms (id, severity_level, device_id, unit_id, description, start_time, end_time) "
<< "VALUES (" << alarmData.nId << ", " << alarmData.nSeverityLevel << ", " << alarmData.nDeviceId << ", "
<< alarmData.nUnitId << ", '" << alarmData.strDescription << "', '" << alarmData.strStartTime << "', '"
<< alarmData.strEndTime << "') RETURNING alarm_event_id;";
- // 使用锁保护多线程安全
+ // 浣跨敤閿佷繚鎶ゅ绾跨▼瀹夊叏
std::lock_guard<std::mutex> lock(m_mutex);
- // 执行查询并获取结果
+ // 鎵ц鏌ヨ骞惰幏鍙栫粨鏋�
auto results = m_pDB->fetchResults(query.str());
if (!results.empty() && !results[0].empty()) {
try {
- // 提取并转换 alarm_event_id
+ // 鎻愬彇骞惰浆鎹� alarm_event_id
alarmEventId = std::stoi(results[0][0]);
- // 将插入的报警数据添加到缓存
+ // 灏嗘彃鍏ョ殑鎶ヨ鏁版嵁娣诲姞鍒扮紦瀛�
m_mapCache[alarmEventId] = alarmData;
return true;
}
@@ -303,13 +306,13 @@
#endif
}
-// 查询所有报警数据
+// 鏌ヨ鎵�鏈夋姤璀︽暟鎹�
std::vector<AlarmData> AlarmManager::getAllAlarms() {
if (!m_pDB) {
return {};
}
- // 查询所有报警数据(包括设备名称和单元名称)
+ // 鏌ヨ鎵�鏈夋姤璀︽暟鎹紙鍖呮嫭璁惧鍚嶇О鍜屽崟鍏冨悕绉帮級
const std::string query = R"(
SELECT a.id, a.severity_level, a.device_id, a.unit_id, d.device_name, u.unit_name, a.description, a.start_time, a.end_time
FROM alarms a
@@ -319,19 +322,19 @@
auto results = m_pDB->fetchResults(query);
- // 遍历查询结果,填充 AlarmData 结构体
+ // 閬嶅巻鏌ヨ缁撴灉锛屽~鍏� AlarmData 缁撴瀯浣�
std::vector<AlarmData> alarms;
for (const auto& row : results) {
AlarmData alarmData;
- alarmData.nId = std::stoi(row[0]); // 错误码
- alarmData.nSeverityLevel = std::stoi(row[1]); // 报警等级
- alarmData.nDeviceId = std::stoi(row[2]); // 设备ID
- alarmData.nUnitId = std::stoi(row[3]); // 单元ID
- alarmData.strDeviceName = row[4]; // 设备名称
- alarmData.strUnitName = row[5]; // 单元名称
- alarmData.strDescription = row[6]; // 描述
- alarmData.strStartTime = row[7]; // 开始时间
- alarmData.strEndTime = row[8]; // 结束时间
+ alarmData.nId = std::stoi(row[0]); // 閿欒鐮�
+ alarmData.nSeverityLevel = std::stoi(row[1]); // 鎶ヨ绛夌骇
+ alarmData.nDeviceId = std::stoi(row[2]); // 璁惧ID
+ alarmData.nUnitId = std::stoi(row[3]); // 鍗曞厓ID
+ alarmData.strDeviceName = row[4]; // 璁惧鍚嶇О
+ alarmData.strUnitName = row[5]; // 鍗曞厓鍚嶇О
+ alarmData.strDescription = row[6]; // 鎻忚堪
+ alarmData.strStartTime = row[7]; // 寮�濮嬫椂闂�
+ alarmData.strEndTime = row[8]; // 缁撴潫鏃堕棿
alarms.push_back(alarmData);
}
@@ -339,7 +342,7 @@
return alarms;
}
-// 根据报警ID查询报警
+// 鏍规嵁鎶ヨID鏌ヨ鎶ヨ
std::vector<AlarmData> AlarmManager::getAlarmsById(const std::string& id) {
if (!m_pDB) {
return {};
@@ -355,19 +358,19 @@
auto results = m_pDB->fetchResults(query.str());
- // 遍历查询结果,填充 AlarmData 结构体
+ // 閬嶅巻鏌ヨ缁撴灉锛屽~鍏� AlarmData 缁撴瀯浣�
std::vector<AlarmData> alarms;
for (const auto& row : results) {
AlarmData alarmData;
- alarmData.nId = std::stoi(row[0]); // 错误码
- alarmData.nSeverityLevel = std::stoi(row[1]); // 报警等级
- alarmData.nDeviceId = std::stoi(row[2]); // 设备ID
- alarmData.nUnitId = std::stoi(row[3]); // 单元ID
- alarmData.strDeviceName = row[4]; // 设备名称
- alarmData.strUnitName = row[5]; // 单元名称
- alarmData.strDescription = row[6]; // 描述
- alarmData.strStartTime = row[7]; // 开始时间
- alarmData.strEndTime = row[8]; // 结束时间
+ alarmData.nId = std::stoi(row[0]); // 閿欒鐮�
+ alarmData.nSeverityLevel = std::stoi(row[1]); // 鎶ヨ绛夌骇
+ alarmData.nDeviceId = std::stoi(row[2]); // 璁惧ID
+ alarmData.nUnitId = std::stoi(row[3]); // 鍗曞厓ID
+ alarmData.strDeviceName = row[4]; // 璁惧鍚嶇О
+ alarmData.strUnitName = row[5]; // 鍗曞厓鍚嶇О
+ alarmData.strDescription = row[6]; // 鎻忚堪
+ alarmData.strStartTime = row[7]; // 寮�濮嬫椂闂�
+ alarmData.strEndTime = row[8]; // 缁撴潫鏃堕棿
alarms.push_back(alarmData);
}
@@ -375,7 +378,7 @@
return alarms;
}
-// 根据描述查询报警
+// 鏍规嵁鎻忚堪鏌ヨ鎶ヨ
std::vector<AlarmData> AlarmManager::getAlarmsByDescription(const std::string& description) {
if (!m_pDB) {
return {};
@@ -391,19 +394,19 @@
auto results = m_pDB->fetchResults(query.str());
- // 遍历查询结果,填充 AlarmData 结构体
+ // 閬嶅巻鏌ヨ缁撴灉锛屽~鍏� AlarmData 缁撴瀯浣�
std::vector<AlarmData> alarms;
for (const auto& row : results) {
AlarmData alarmData;
- alarmData.nId = std::stoi(row[0]); // 错误码
- alarmData.nSeverityLevel = std::stoi(row[1]); // 报警等级
- alarmData.nDeviceId = std::stoi(row[2]); // 设备ID
- alarmData.nUnitId = std::stoi(row[3]); // 单元ID
- alarmData.strDeviceName = row[4]; // 设备名称
- alarmData.strUnitName = row[5]; // 单元名称
- alarmData.strDescription = row[6]; // 描述
- alarmData.strStartTime = row[7]; // 开始时间
- alarmData.strEndTime = row[8]; // 结束时间
+ alarmData.nId = std::stoi(row[0]); // 閿欒鐮�
+ alarmData.nSeverityLevel = std::stoi(row[1]); // 鎶ヨ绛夌骇
+ alarmData.nDeviceId = std::stoi(row[2]); // 璁惧ID
+ alarmData.nUnitId = std::stoi(row[3]); // 鍗曞厓ID
+ alarmData.strDeviceName = row[4]; // 璁惧鍚嶇О
+ alarmData.strUnitName = row[5]; // 鍗曞厓鍚嶇О
+ alarmData.strDescription = row[6]; // 鎻忚堪
+ alarmData.strStartTime = row[7]; // 寮�濮嬫椂闂�
+ alarmData.strEndTime = row[8]; // 缁撴潫鏃堕棿
alarms.push_back(alarmData);
}
@@ -411,7 +414,7 @@
return alarms;
}
-// 根据时间范围查询报警
+// 鏍规嵁鏃堕棿鑼冨洿鏌ヨ鎶ヨ
std::vector<AlarmData> AlarmManager::getAlarmsByTimeRange(const std::string& startTime, const std::string& endTime) {
if (!m_pDB) {
return {};
@@ -434,19 +437,19 @@
auto results = m_pDB->fetchResults(query.str());
- // 遍历查询结果,填充 AlarmData 结构体
+ // 閬嶅巻鏌ヨ缁撴灉锛屽~鍏� AlarmData 缁撴瀯浣�
std::vector<AlarmData> alarms;
for (const auto& row : results) {
AlarmData alarmData;
- alarmData.nId = std::stoi(row[0]); // 错误码
- alarmData.nSeverityLevel = std::stoi(row[1]); // 报警等级
- alarmData.nDeviceId = std::stoi(row[2]); // 设备ID
- alarmData.nUnitId = std::stoi(row[3]); // 单元ID
- alarmData.strDeviceName = row[4]; // 设备名称
- alarmData.strUnitName = row[5]; // 单元名称
- alarmData.strDescription = row[6]; // 描述
- alarmData.strStartTime = row[7]; // 开始时间
- alarmData.strEndTime = row[8]; // 结束时间
+ alarmData.nId = std::stoi(row[0]); // 閿欒鐮�
+ alarmData.nSeverityLevel = std::stoi(row[1]); // 鎶ヨ绛夌骇
+ alarmData.nDeviceId = std::stoi(row[2]); // 璁惧ID
+ alarmData.nUnitId = std::stoi(row[3]); // 鍗曞厓ID
+ alarmData.strDeviceName = row[4]; // 璁惧鍚嶇О
+ alarmData.strUnitName = row[5]; // 鍗曞厓鍚嶇О
+ alarmData.strDescription = row[6]; // 鎻忚堪
+ alarmData.strStartTime = row[7]; // 寮�濮嬫椂闂�
+ alarmData.strEndTime = row[8]; // 缁撴潫鏃堕棿
alarms.push_back(alarmData);
}
@@ -454,7 +457,7 @@
return alarms;
}
-// 根据ID、开始时间和结束时间查询报警
+// 鏍规嵁ID銆佸紑濮嬫椂闂村拰缁撴潫鏃堕棿鏌ヨ鎶ヨ
std::vector<AlarmData> AlarmManager::getAlarmsByIdAndTimeRange(const std::string& id, const std::string& startTime, const std::string& endTime) {
if (!m_pDB) {
return {};
@@ -477,19 +480,19 @@
auto results = m_pDB->fetchResults(query.str());
- // 遍历查询结果,填充 AlarmData 结构体
+ // 閬嶅巻鏌ヨ缁撴灉锛屽~鍏� AlarmData 缁撴瀯浣�
std::vector<AlarmData> alarms;
for (const auto& row : results) {
AlarmData alarmData;
- alarmData.nId = std::stoi(row[0]); // 错误码
- alarmData.nSeverityLevel = std::stoi(row[1]); // 报警等级
- alarmData.nDeviceId = std::stoi(row[2]); // 设备ID
- alarmData.nUnitId = std::stoi(row[3]); // 单元ID
- alarmData.strDeviceName = row[4]; // 设备名称
- alarmData.strUnitName = row[5]; // 单元名称
- alarmData.strDescription = row[6]; // 描述
- alarmData.strStartTime = row[7]; // 开始时间
- alarmData.strEndTime = row[8]; // 结束时间
+ alarmData.nId = std::stoi(row[0]); // 閿欒鐮�
+ alarmData.nSeverityLevel = std::stoi(row[1]); // 鎶ヨ绛夌骇
+ alarmData.nDeviceId = std::stoi(row[2]); // 璁惧ID
+ alarmData.nUnitId = std::stoi(row[3]); // 鍗曞厓ID
+ alarmData.strDeviceName = row[4]; // 璁惧鍚嶇О
+ alarmData.strUnitName = row[5]; // 鍗曞厓鍚嶇О
+ alarmData.strDescription = row[6]; // 鎻忚堪
+ alarmData.strStartTime = row[7]; // 寮�濮嬫椂闂�
+ alarmData.strEndTime = row[8]; // 缁撴潫鏃堕棿
alarms.push_back(alarmData);
}
@@ -497,7 +500,7 @@
return alarms;
}
-// 分页查询报警数据
+// 鍒嗛〉鏌ヨ鎶ヨ鏁版嵁
std::vector<AlarmData> AlarmManager::getAlarms(int startPosition, int count) {
if (!m_pDB) {
return {};
@@ -513,19 +516,19 @@
auto results = m_pDB->fetchResults(query.str());
- // 遍历查询结果,填充 AlarmData 结构体
+ // 閬嶅巻鏌ヨ缁撴灉锛屽~鍏� AlarmData 缁撴瀯浣�
std::vector<AlarmData> alarms;
for (const auto& row : results) {
AlarmData alarmData;
- alarmData.nId = std::stoi(row[0]); // 错误码
- alarmData.nSeverityLevel = std::stoi(row[1]); // 报警等级
- alarmData.nDeviceId = std::stoi(row[2]); // 设备ID
- alarmData.nUnitId = std::stoi(row[3]); // 单元ID
- alarmData.strDeviceName = row[4]; // 设备名称
- alarmData.strUnitName = row[5]; // 单元名称
- alarmData.strDescription = row[6]; // 描述
- alarmData.strStartTime = row[7]; // 开始时间
- alarmData.strEndTime = row[8]; // 结束时间
+ alarmData.nId = std::stoi(row[0]); // 閿欒鐮�
+ alarmData.nSeverityLevel = std::stoi(row[1]); // 鎶ヨ绛夌骇
+ alarmData.nDeviceId = std::stoi(row[2]); // 璁惧ID
+ alarmData.nUnitId = std::stoi(row[3]); // 鍗曞厓ID
+ alarmData.strDeviceName = row[4]; // 璁惧鍚嶇О
+ alarmData.strUnitName = row[5]; // 鍗曞厓鍚嶇О
+ alarmData.strDescription = row[6]; // 鎻忚堪
+ alarmData.strStartTime = row[7]; // 寮�濮嬫椂闂�
+ alarmData.strEndTime = row[8]; // 缁撴潫鏃堕棿
alarms.push_back(alarmData);
}
@@ -533,7 +536,86 @@
return alarms;
}
-// 筛选报警数据
+// 鑾峰彇褰撳墠鏈粨鏉熺殑鎶ヨ锛坋nd_time 涓虹┖锛夛紝骞跺彲鎸� start_time 鏈�杩� N 灏忔椂杩囨护
+std::vector<AlarmData> AlarmManager::getActiveAlarms(int recentHours /*=12*/) {
+ if (!m_pDB) {
+ return {};
+ }
+
+ // 璁$畻鏃堕棿闃堝�硷細褰撳墠鏃堕棿鍑� recentHours
+ std::string cutoffTime;
+ if (recentHours > 0) {
+ using namespace std::chrono;
+ auto cutoff = system_clock::now() - hours(recentHours);
+ std::time_t t = system_clock::to_time_t(cutoff);
+ std::tm tm {};
+ localtime_s(&tm, &t);
+ char buf[32] = { 0 };
+ std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
+ cutoffTime = buf;
+ }
+
+ std::ostringstream query;
+ query << R"(
+ SELECT a.id, a.severity_level, a.device_id, a.unit_id, d.device_name, u.unit_name, a.description, a.start_time, a.end_time
+ FROM alarms a
+ JOIN devices d ON a.device_id = d.device_id
+ JOIN units u ON a.device_id = u.device_id AND a.unit_id = u.unit_id
+ WHERE a.end_time IS NULL OR a.end_time = ''
+ )";
+ if (!cutoffTime.empty()) {
+ query << " AND a.start_time >= '" << cutoffTime << "'";
+ }
+ query << " ORDER BY a.start_time DESC";
+
+ auto lastErrStr = []() -> std::string {
+ DWORD gle = GetLastError();
+ if (gle == 0) return {};
+ LPSTR buf = nullptr;
+ size_t len = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, gle, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, nullptr);
+ std::string s = (buf && len > 0) ? std::string(buf, len) : "";
+ if (buf) LocalFree(buf);
+ return s;
+ };
+ std::vector<std::vector<std::string>> results;
+ try {
+ results = m_pDB->fetchResults(query.str());
+ }
+ catch (const std::exception& ex) {
+ DWORD gle = GetLastError();
+ auto errStr = lastErrStr();
+ LOGE("<AlarmManager>getActiveAlarms failed: %s, GLE=%lu (%s)", ex.what(), gle, errStr.c_str());
+ return {};
+ }
+ std::vector<AlarmData> alarms;
+ auto toInt = [](const std::string& s) -> int {
+ try {
+ return std::stoi(s);
+ }
+ catch (...) {
+ return 0;
+ }
+ };
+ for (const auto& row : results) {
+ if (row.size() < 9) continue;
+ AlarmData alarmData;
+ alarmData.nId = toInt(row[0]);
+ alarmData.nSeverityLevel = toInt(row[1]);
+ alarmData.nDeviceId = toInt(row[2]);
+ alarmData.nUnitId = toInt(row[3]);
+ alarmData.strDeviceName = row[4];
+ alarmData.strUnitName = row[5];
+ alarmData.strDescription = row[6];
+ alarmData.strStartTime = row[7];
+ alarmData.strEndTime = row[8];
+ alarms.push_back(alarmData);
+ }
+
+ return alarms;
+}
+
+// 绛涢�夋姤璀︽暟鎹�
std::vector<AlarmData> AlarmManager::getFilteredAlarms(const std::string& keyword, const std::string& startTime, const std::string& endTime, int pageNumber, int pageSize) {
if (!m_pDB) {
return {};
@@ -547,7 +629,7 @@
JOIN units u ON a.device_id = u.device_id AND a.unit_id = u.unit_id
WHERE 1=1)";
- // 统一关键字模糊查询
+ // 缁熶竴鍏抽敭瀛楁ā绯婃煡璇�
if (!keyword.empty()) {
query << " AND ("
<< "a.id LIKE '%" << keyword << "%' OR "
@@ -557,7 +639,7 @@
<< "a.description LIKE '%" << keyword << "%')";
}
- // 时间条件
+ // 鏃堕棿鏉′欢
if (!startTime.empty()) {
query << " AND a.start_time >= '" << startTime << "'";
}
@@ -565,26 +647,26 @@
query << " AND a.end_time <= '" << endTime << "'";
}
- // 分页设置
+ // 鍒嗛〉璁剧疆
int nOffset = (pageNumber - 1) * pageSize;
query << " ORDER BY a.start_time DESC LIMIT " << pageSize << " OFFSET " << nOffset;
- // 查询
+ // 鏌ヨ
auto results = m_pDB->fetchResults(query.str());
- // 遍历查询结果,填充 AlarmData 结构体
+ // 閬嶅巻鏌ヨ缁撴灉锛屽~鍏� AlarmData 缁撴瀯浣�
std::vector<AlarmData> alarms;
for (const auto& row : results) {
AlarmData alarmData;
- alarmData.nId = std::stoi(row[0]); // 错误码
- alarmData.nSeverityLevel = std::stoi(row[1]); // 报警等级 (字符串)
- alarmData.nDeviceId = std::stoi(row[2]); // 设备ID
- alarmData.nUnitId = std::stoi(row[3]); // 单元ID
- alarmData.strDeviceName = row[4]; // 设备名称
- alarmData.strUnitName = row[5]; // 单元名称
- alarmData.strDescription = row[6]; // 描述
- alarmData.strStartTime = row[7]; // 开始时间
- alarmData.strEndTime = row[8]; // 结束时间
+ alarmData.nId = std::stoi(row[0]); // 閿欒鐮�
+ alarmData.nSeverityLevel = std::stoi(row[1]); // 鎶ヨ绛夌骇 (瀛楃涓�)
+ alarmData.nDeviceId = std::stoi(row[2]); // 璁惧ID
+ alarmData.nUnitId = std::stoi(row[3]); // 鍗曞厓ID
+ alarmData.strDeviceName = row[4]; // 璁惧鍚嶇О
+ alarmData.strUnitName = row[5]; // 鍗曞厓鍚嶇О
+ alarmData.strDescription = row[6]; // 鎻忚堪
+ alarmData.strStartTime = row[7]; // 寮�濮嬫椂闂�
+ alarmData.strEndTime = row[8]; // 缁撴潫鏃堕棿
alarms.push_back(alarmData);
}
@@ -592,7 +674,7 @@
return alarms;
}
-// 获取符合条件的报警总数
+// 鑾峰彇绗﹀悎鏉′欢鐨勬姤璀︽�绘暟
int AlarmManager::getTotalAlarmCount(const std::string& keyword, const std::string& startTime, const std::string& endTime) {
if (!m_pDB) {
return 0;
@@ -606,7 +688,7 @@
JOIN units u ON a.device_id = u.device_id AND a.unit_id = u.unit_id
WHERE 1=1)";
- // 统一关键字模糊查询
+ // 缁熶竴鍏抽敭瀛楁ā绯婃煡璇�
if (!keyword.empty()) {
query << " AND ("
<< "a.id LIKE '%" << keyword << "%' OR "
@@ -616,7 +698,7 @@
<< "a.description LIKE '%" << keyword << "%')";
}
- // 时间条件
+ // 鏃堕棿鏉′欢
if (!startTime.empty()) {
query << " AND a.start_time >= '" << startTime << "'";
}
@@ -628,7 +710,7 @@
return (!results.empty() && !results[0].empty()) ? std::stoi(results[0][0]) : 0;
}
-// 更新报警的结束时间
+// 鏇存柊鎶ヨ鐨勭粨鏉熸椂闂�
bool AlarmManager::updateAlarmEndTime(
const std::string& id,
const std::string& severityLevel,
@@ -642,7 +724,7 @@
return false;
}
- // 更新报警结束时间
+ // 鏇存柊鎶ヨ缁撴潫鏃堕棿
std::ostringstream updateQuery;
updateQuery << "UPDATE alarms SET end_time = '" << newEndTime << "'"
<< " WHERE id = '" << id << "'"
@@ -655,7 +737,7 @@
return m_pDB->executeQuery(updateQuery.str());
}
-// 清理旧报警数据
+// 娓呯悊鏃ф姤璀︽暟鎹�
void AlarmManager::cleanOldAlarms(int daysToKeep, const std::string& deviceId, const std::string& unitId) {
if (!m_pDB) {
return;
@@ -674,7 +756,7 @@
m_pDB->executeQuery(query.str());
}
-// 通过设备ID获取设备名称
+// 閫氳繃璁惧ID鑾峰彇璁惧鍚嶇О
std::string AlarmManager::getDeviceNameById(int deviceId) {
if (!m_pDB) {
return "";
@@ -688,10 +770,10 @@
return "";
}
- return result[0][0]; // 返回查询到的设备名称
+ return result[0][0]; // 杩斿洖鏌ヨ鍒扮殑璁惧鍚嶇О
}
-// 通过设备ID和单元ID获取单元名称
+// 閫氳繃璁惧ID鍜屽崟鍏僆D鑾峰彇鍗曞厓鍚嶇О
std::string AlarmManager::getUnitNameById(int deviceId, int unitId) {
if (!m_pDB) {
return "";
@@ -706,10 +788,10 @@
return "";
}
- return result[0][0]; // 返回查询到的单元名称
+ return result[0][0]; // 杩斿洖鏌ヨ鍒扮殑鍗曞厓鍚嶇О
}
-// 获取最近插入的 alarm_event_id
+// 鑾峰彇鏈�杩戞彃鍏ョ殑 alarm_event_id
int AlarmManager::getLastInsertId() {
std::string query = "SELECT last_insert_rowid();";
auto results = m_pDB->fetchResults(query);
@@ -718,12 +800,12 @@
return -1;
}
- // 从查询结果中获取最后插入的 ID
+ // 浠庢煡璇㈢粨鏋滀腑鑾峰彇鏈�鍚庢彃鍏ョ殑 ID
int lastInsertId = std::stoi(results[0][0]);
return lastInsertId;
}
-// 通过 alarm_event_id 解除报警(更新结束时间)
+// 閫氳繃 alarm_event_id 瑙i櫎鎶ヨ锛堟洿鏂扮粨鏉熸椂闂达級
bool AlarmManager::clearAlarmByEventId(int alarmEventId, const std::string& endTime) {
if (!m_pDB) {
return false;
@@ -745,7 +827,7 @@
return result;
}
-// 通过多个属性查找并解除报警(更新结束时间)
+// 閫氳繃澶氫釜灞炴�ф煡鎵惧苟瑙i櫎鎶ヨ锛堟洿鏂扮粨鏉熸椂闂达級
bool AlarmManager::clearAlarmByAttributes(int nId, int nDeviceId, int nUnitId, const std::string& endTime) {
if (!m_pDB) {
return false;
@@ -753,7 +835,7 @@
std::lock_guard<std::mutex> lock(m_mutex);
- // 先在缓存中查找匹配的 alarm_event_id
+ // 鍏堝湪缂撳瓨涓煡鎵惧尮閰嶇殑 alarm_event_id
int alarmEventId = -1;
for (AlarmDataMap::const_iterator it = m_mapCache.begin(); it != m_mapCache.end(); ++it) {
const AlarmData& alarm = it->second;
@@ -766,12 +848,32 @@
}
}
- // 如果没找到匹配的记录,则直接返回 false
- if (alarmEventId == -1) {
- return false;
- }
+ // 缂撳瓨鏈懡涓椂锛屼粠鏁版嵁搴撴煡鎵句粛鏈粨鏉熺殑璁板綍锛堝彇鏈�鏂颁竴鏉★級
+ if (alarmEventId == -1) {
+ std::ostringstream querySel;
+ querySel << "SELECT alarm_event_id FROM alarms WHERE "
+ << "id = " << nId
+ << " AND device_id = " << nDeviceId
+ << " AND unit_id = " << nUnitId
+ << " AND (end_time IS NULL OR end_time = '') "
+ << "ORDER BY start_time DESC LIMIT 1;";
+ auto results = m_pDB->fetchResults(querySel.str());
+ if (!results.empty() && !results[0].empty()) {
+ try {
+ alarmEventId = std::stoi(results[0][0]);
+ }
+ catch (...) {
+ alarmEventId = -1;
+ }
+ }
+ }
- // 构建 SQL 语句,使用找到的 alarm_event_id 来更新结束时间
+ // 濡傛灉娌℃壘鍒板尮閰嶇殑璁板綍锛屽垯鐩存帴杩斿洖 false
+ if (alarmEventId == -1) {
+ return false;
+ }
+
+ // 鏋勫缓 SQL 璇彞锛屼娇鐢ㄦ壘鍒扮殑 alarm_event_id 鏉ユ洿鏂扮粨鏉熸椂闂�
std::ostringstream query;
query << "UPDATE alarms SET end_time = '" << endTime << "' WHERE alarm_event_id = " << alarmEventId << ";";
bool result = m_pDB->executeQuery(query.str());
@@ -782,7 +884,7 @@
return result;
}
-// 读取报警文件
+// 璇诲彇鎶ヨ鏂囦欢
bool AlarmManager::readAlarmFile(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file.is_open()) {
@@ -795,7 +897,7 @@
char ch;
while (f.get(ch)) {
if (ch == '\r') {
- // 处理 \r\n 或 单独 \r
+ // 澶勭悊 \r\n 鎴� 鍗曠嫭 \r
if (f.peek() == '\n') f.get();
break;
}
@@ -850,19 +952,19 @@
return true;
}
-// 将报警数据保存到文件
+// 灏嗘姤璀︽暟鎹繚瀛樺埌鏂囦欢
bool AlarmManager::saveAlarmFile(const std::string& filename) {
std::ofstream file(filename);
if (!file.is_open()) {
- std::cerr << "打开文件写入失败!" << std::endl;
+ std::cerr << "鎵撳紑鏂囦欢鍐欏叆澶辫触!" << std::endl;
return false;
}
- // 写入标题行
+ // 鍐欏叆鏍囬琛�
file << "No,UNIT ID,UNIT NO,Alarm Level,Alarm Code,AlarmID,Alarm Text,Description\n";
- // 写入报警数据
+ // 鍐欏叆鎶ヨ鏁版嵁
int nIndex = 1;
for (const auto& pair : m_mapAlarm) {
const AlarmInfo& alarm = pair.second;
@@ -880,19 +982,19 @@
return true;
}
-// 通过 AlarmID 查询对应的报警信息
+// 閫氳繃 AlarmID 鏌ヨ瀵瑰簲鐨勬姤璀︿俊鎭�
const AlarmInfo* AlarmManager::getAlarmInfoByID(int nAlarmID) const {
auto it = m_mapAlarm.find(nAlarmID);
if (it != m_mapAlarm.end()) {
return &(it->second);
}
else {
- std::cerr << "未找到 AlarmID: " << nAlarmID << std::endl;
+ std::cerr << "鏈壘鍒� AlarmID: " << nAlarmID << std::endl;
return nullptr;
}
}
-// 通过多个 AlarmID 查询对应的报警信息
+// 閫氳繃澶氫釜 AlarmID 鏌ヨ瀵瑰簲鐨勬姤璀︿俊鎭�
std::vector<AlarmInfo> AlarmManager::getAlarmsInfoByIDs(const std::vector<int>& alarmIDs) const {
std::vector<AlarmInfo> alarms;
@@ -902,7 +1004,7 @@
alarms.push_back(it->second);
}
else {
- std::cerr << "未找到 AlarmID: " << alarmID << std::endl;
+ std::cerr << "鏈壘鍒� AlarmID: " << alarmID << std::endl;
}
}
diff --git a/SourceCode/Bond/Servo/AlarmManager.h b/SourceCode/Bond/Servo/AlarmManager.h
index d98fce9..1cd55a9 100644
--- a/SourceCode/Bond/Servo/AlarmManager.h
+++ b/SourceCode/Bond/Servo/AlarmManager.h
@@ -1,4 +1,4 @@
-#ifndef ALARM_MANAGER_H
+锘�#ifndef ALARM_MANAGER_H
#define ALARM_MANAGER_H
#include <string>
@@ -18,15 +18,15 @@
};
struct AlarmData {
- int nId; // 错误码
- int nSeverityLevel; // 报警等级
- int nDeviceId; // 设备ID
- int nUnitId; // 单元ID
- std::string strDeviceName; // 设备名称
- std::string strUnitName; // 单元名称
- std::string strDescription; // 描述
- std::string strStartTime; // 开始时间
- std::string strEndTime; // 结束时间
+ int nId; // 閿欒鐮�
+ int nSeverityLevel; // 鎶ヨ绛夌骇
+ int nDeviceId; // 璁惧ID
+ int nUnitId; // 鍗曞厓ID
+ std::string strDeviceName; // 璁惧鍚嶇О
+ std::string strUnitName; // 鍗曞厓鍚嶇О
+ std::string strDescription; // 鎻忚堪
+ std::string strStartTime; // 寮�濮嬫椂闂�
+ std::string strEndTime; // 缁撴潫鏃堕棿
};
using AlarmInfoMap = std::unordered_map<int, AlarmInfo>;
@@ -35,116 +35,117 @@
class AlarmManager {
public:
/**
- * 获取单例实例
- * @return AlarmManager实例的引用
+ * 鑾峰彇鍗曚緥瀹炰緥
+ * @return AlarmManager瀹炰緥鐨勫紩鐢�
*/
static AlarmManager& getInstance();
/**
- * 初始化报警表
- * @return 成功返回true,失败返回false
+ * 鍒濆鍖栨姤璀﹁〃
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool initAlarmTable();
/**
- * 销毁报警表
+ * 閿�姣佹姤璀﹁〃
*/
void termAlarmTable();
/**
- * 销毁报警表
- * @return 成功返回true,失败返回false
+ * 閿�姣佹姤璀﹁〃
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool destroyAlarmTable();
/**
- * 插入模拟数据
+ * 鎻掑叆妯℃嫙鏁版嵁
*/
void insertMockData();
/**
- * 添加报警
- * @param alarmData 报警数据的结构体
- * @param alarmEventId 最近插入的 alarm_event_id
- * @return 成功返回true,失败返回false
+ * 娣诲姞鎶ヨ
+ * @param alarmData 鎶ヨ鏁版嵁鐨勭粨鏋勪綋
+ * @param alarmEventId 鏈�杩戞彃鍏ョ殑 alarm_event_id
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool addAlarm(const AlarmData& alarmData, int& alarmEventId);
/**
- * 查询所有报警数据
- * @return 包含所有报警数据的结构体
+ * 鏌ヨ鎵�鏈夋姤璀︽暟鎹�
+ * @return 鍖呭惈鎵�鏈夋姤璀︽暟鎹殑缁撴瀯浣�
*/
std::vector<AlarmData> getAllAlarms();
/**
- * 根据报警ID查询报警
- * @param id 报警ID
- * @return 包含筛选后报警数据的结构体
+ * 鏍规嵁鎶ヨID鏌ヨ鎶ヨ
+ * @param id 鎶ヨID
+ * @return 鍖呭惈绛涢�夊悗鎶ヨ鏁版嵁鐨勭粨鏋勪綋
*/
std::vector<AlarmData> getAlarmsById(const std::string& id);
/**
- * 根据描述查询报警
- * @param description 报警描述的筛选条件
- * @return 包含筛选后报警数据的结构体
+ * 鏍规嵁鎻忚堪鏌ヨ鎶ヨ
+ * @param description 鎶ヨ鎻忚堪鐨勭瓫閫夋潯浠�
+ * @return 鍖呭惈绛涢�夊悗鎶ヨ鏁版嵁鐨勭粨鏋勪綋
*/
std::vector<AlarmData> getAlarmsByDescription(const std::string& description);
/**
- * 根据时间范围查询报警
- * @param startTime 起始时间
- * @param endTime 结束时间
- * @return 包含查询结果的报警数据
+ * 鏍规嵁鏃堕棿鑼冨洿鏌ヨ鎶ヨ
+ * @param startTime 璧峰鏃堕棿
+ * @param endTime 缁撴潫鏃堕棿
+ * @return 鍖呭惈鏌ヨ缁撴灉鐨勬姤璀︽暟鎹�
*/
std::vector<AlarmData> getAlarmsByTimeRange(const std::string& startTime, const std::string& endTime);
/**
- * 根据ID和时间范围查询报警
- * @param id 报警ID
- * @param startTime 起始时间
- * @param endTime 结束时间
- * @return 包含查询结果的报警数据
+ * 鏍规嵁ID鍜屾椂闂磋寖鍥存煡璇㈡姤璀�
+ * @param id 鎶ヨID
+ * @param startTime 璧峰鏃堕棿
+ * @param endTime 缁撴潫鏃堕棿
+ * @return 鍖呭惈鏌ヨ缁撴灉鐨勬姤璀︽暟鎹�
*/
std::vector<AlarmData> getAlarmsByIdAndTimeRange(const std::string& id, const std::string& startTime, const std::string& endTime);
/**
- * 获取报警数据
- * @param startPosition 起始位置
- * @param count 获取的记录数量
- * @return 包含查询结果的报警数据
+ * 鑾峰彇鎶ヨ鏁版嵁
+ * @param startPosition 璧峰浣嶇疆
+ * @param count 鑾峰彇鐨勮褰曟暟閲�
+ * @return 鍖呭惈鏌ヨ缁撴灉鐨勬姤璀︽暟鎹�
*/
std::vector<AlarmData> getAlarms(int startPosition, int count);
+ std::vector<AlarmData> getActiveAlarms(int recentHours = 12);
/**
- * 筛选报警数据
- * @param keyword 关键字筛选条件
- * @param startTime 起始时间筛选条件
- * @param endTime 结束时间筛选条件
- * @param pageNumber 页码
- * @param pageSize 每页记录数
- * @return 包含筛选后报警数据的结构体
+ * 绛涢�夋姤璀︽暟鎹�
+ * @param keyword 鍏抽敭瀛楃瓫閫夋潯浠�
+ * @param startTime 璧峰鏃堕棿绛涢�夋潯浠�
+ * @param endTime 缁撴潫鏃堕棿绛涢�夋潯浠�
+ * @param pageNumber 椤电爜
+ * @param pageSize 姣忛〉璁板綍鏁�
+ * @return 鍖呭惈绛涢�夊悗鎶ヨ鏁版嵁鐨勭粨鏋勪綋
*/
std::vector<AlarmData> getFilteredAlarms(const std::string& keyword, const std::string& startTime, const std::string& endTime, int pageNumber, int pageSize);
/**
- * 获取符合条件的报警总数
- * @param keyword 关键字筛选条件
- * @param startTime 起始时间筛选条件
- * @param endTime 结束时间筛选条件
- * @return 符合条件的报警总数
+ * 鑾峰彇绗﹀悎鏉′欢鐨勬姤璀︽�绘暟
+ * @param keyword 鍏抽敭瀛楃瓫閫夋潯浠�
+ * @param startTime 璧峰鏃堕棿绛涢�夋潯浠�
+ * @param endTime 缁撴潫鏃堕棿绛涢�夋潯浠�
+ * @return 绗﹀悎鏉′欢鐨勬姤璀︽�绘暟
*/
int getTotalAlarmCount(const std::string& keyword, const std::string& startTime, const std::string& endTime);
/**
- * 更新报警结束时间
- * @param id 报警ID
- * @param severityLevel 报警等级筛选条件
- * @param deviceId 设备ID
- * @param unitId 单元ID
- * @param description 报警描述
- * @param startTime 报警开始时间
- * @param newEndTime 新的报警结束时间
- * @return 成功返回true,失败返回false
+ * 鏇存柊鎶ヨ缁撴潫鏃堕棿
+ * @param id 鎶ヨID
+ * @param severityLevel 鎶ヨ绛夌骇绛涢�夋潯浠�
+ * @param deviceId 璁惧ID
+ * @param unitId 鍗曞厓ID
+ * @param description 鎶ヨ鎻忚堪
+ * @param startTime 鎶ヨ寮�濮嬫椂闂�
+ * @param newEndTime 鏂扮殑鎶ヨ缁撴潫鏃堕棿
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool updateAlarmEndTime(
const std::string& id,
@@ -156,79 +157,79 @@
const std::string& newEndTime);
/**
- * 清理旧报警
- * @param daysToKeep 保留的天数
- * @param deviceId 设备ID
- * @param unitId 单元ID
+ * 娓呯悊鏃ф姤璀�
+ * @param daysToKeep 淇濈暀鐨勫ぉ鏁�
+ * @param deviceId 璁惧ID
+ * @param unitId 鍗曞厓ID
*/
void cleanOldAlarms(int daysToKeep = 30, const std::string& deviceId = "", const std::string& unitId = "");
/**
- * 通过设备ID获取设备名称
- * @param deviceId 设备ID
- * @return 成功返回设备名称,失败返回空
+ * 閫氳繃璁惧ID鑾峰彇璁惧鍚嶇О
+ * @param deviceId 璁惧ID
+ * @return 鎴愬姛杩斿洖璁惧鍚嶇О锛屽け璐ヨ繑鍥炵┖
*/
std::string getDeviceNameById(int deviceId);
/**
- * 通过设备ID和单元ID获取单元名称
- * @param deviceId 设备ID
- * @param unitId 单元ID
- * @return 成功返回单元名称,失败返回空
+ * 閫氳繃璁惧ID鍜屽崟鍏僆D鑾峰彇鍗曞厓鍚嶇О
+ * @param deviceId 璁惧ID
+ * @param unitId 鍗曞厓ID
+ * @return 鎴愬姛杩斿洖鍗曞厓鍚嶇О锛屽け璐ヨ繑鍥炵┖
*/
std::string getUnitNameById(int deviceId, int unitId);
/**
- * 获取最近插入的 alarm_event_id
- * @return 失败返回-1,成功返回最近插入的 alarm_event_id
+ * 鑾峰彇鏈�杩戞彃鍏ョ殑 alarm_event_id
+ * @return 澶辫触杩斿洖-1锛屾垚鍔熻繑鍥炴渶杩戞彃鍏ョ殑 alarm_event_id
*/
int getLastInsertId();
/**
- * 通过事件id解除报警(更新结束时间)
- * @param alarmEventId 事件ID
- * @param endTime 结束时间
- * @return 成功返回true,失败返回false
+ * 閫氳繃浜嬩欢id瑙i櫎鎶ヨ锛堟洿鏂扮粨鏉熸椂闂达級
+ * @param alarmEventId 浜嬩欢ID
+ * @param endTime 缁撴潫鏃堕棿
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool clearAlarmByEventId(int alarmEventId, const std::string& endTime);
/**
- * 通过多个属性查找并解除报警(更新结束时间)
- * @param nId 报警ID
- * @param nSeverityLevel 报警等级
- * @param nDeviceId 设备ID
- * @param nUnitId 单元ID
- * @param strDescription 描述
- * @param endTime 结束时间
- * @return 成功返回true,失败返回false
+ * 閫氳繃澶氫釜灞炴�ф煡鎵惧苟瑙i櫎鎶ヨ锛堟洿鏂扮粨鏉熸椂闂达級
+ * @param nId 鎶ヨID
+ * @param nSeverityLevel 鎶ヨ绛夌骇
+ * @param nDeviceId 璁惧ID
+ * @param nUnitId 鍗曞厓ID
+ * @param strDescription 鎻忚堪
+ * @param endTime 缁撴潫鏃堕棿
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool clearAlarmByAttributes(int nId, int nDeviceId, int nUnitId, const std::string& endTime);
/**
- * 读取报警文件
- * @param filename 文件名
- * @return 成功返回true,失败返回false
+ * 璇诲彇鎶ヨ鏂囦欢
+ * @param filename 鏂囦欢鍚�
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool readAlarmFile(const std::string& filename);
/**
- * 保存报警文件
- * @param filename 文件名
- * @return 成功返回true,失败返回false
+ * 淇濆瓨鎶ヨ鏂囦欢
+ * @param filename 鏂囦欢鍚�
+ * @return 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse
*/
bool saveAlarmFile(const std::string& filename);
/**
- * 通过报警ID查询报警信息
- * @param nAlarmID 报警ID
- * @return 报警信息的指针
+ * 閫氳繃鎶ヨID鏌ヨ鎶ヨ淇℃伅
+ * @param nAlarmID 鎶ヨID
+ * @return 鎶ヨ淇℃伅鐨勬寚閽�
*/
const AlarmInfo* getAlarmInfoByID(int nAlarmID) const;
/**
- * 通过多个报警ID查询对应的报警信息
- * @param alarmIDs 多个报警ID
- * @return 返回多个报警信息
+ * 閫氳繃澶氫釜鎶ヨID鏌ヨ瀵瑰簲鐨勬姤璀︿俊鎭�
+ * @param alarmIDs 澶氫釜鎶ヨID
+ * @return 杩斿洖澶氫釜鎶ヨ淇℃伅
*/
std::vector<AlarmInfo> getAlarmsInfoByIDs(const std::vector<int>& alarmIDs) const;
@@ -236,7 +237,7 @@
AlarmManager();
~AlarmManager();
- // 禁止拷贝和赋值
+ // 绂佹鎷疯礉鍜岃祴鍊�
AlarmManager(const AlarmManager&) = delete;
AlarmManager& operator=(const AlarmManager&) = delete;
diff --git a/SourceCode/Bond/Servo/AlarmPopupDlg.cpp b/SourceCode/Bond/Servo/AlarmPopupDlg.cpp
new file mode 100644
index 0000000..8f7b50a
--- /dev/null
+++ b/SourceCode/Bond/Servo/AlarmPopupDlg.cpp
@@ -0,0 +1,321 @@
+锘�#include "stdafx.h"
+#include "Servo.h"
+#include "AlarmPopupDlg.h"
+#include "afxdialogex.h"
+#include "Log.h"
+#include "Common.h"
+#include "HorizontalLine.h"
+#include "ServoDlg.h"
+
+
+IMPLEMENT_DYNAMIC(CAlarmPopupDlg, CDialogEx)
+
+CAlarmPopupDlg::CAlarmPopupDlg(CWnd* pParent /*=NULL*/)
+ : CDialogEx(IDD_DIALOG_POPUP_ALARM, pParent)
+ , m_hasActive(false)
+{
+ memset(&m_activeAlarm, 0, sizeof(m_activeAlarm));
+ m_crBkgnd = RGB(112, 146, 190);
+ m_hbrBkgnd = nullptr;
+}
+
+CAlarmPopupDlg::~CAlarmPopupDlg()
+{
+}
+
+void CAlarmPopupDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+}
+
+BEGIN_MESSAGE_MAP(CAlarmPopupDlg, CDialogEx)
+ ON_BN_CLICKED(IDC_POPUP_BTN_CLOSE, &CAlarmPopupDlg::OnBnClickedClose)
+ ON_BN_CLICKED(IDC_BUTTON_ALARM_OFF, &CAlarmPopupDlg::OnBnClickedAlarmOff)
+ ON_BN_CLICKED(IDC_BUTTON_PREV, &CAlarmPopupDlg::OnBnClickedPrev)
+ ON_BN_CLICKED(IDC_BUTTON_NEXT, &CAlarmPopupDlg::OnBnClickedNext)
+ ON_WM_CTLCOLOR()
+ ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+BOOL CAlarmPopupDlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ // 鑳屾櫙鍒�
+ if (m_hbrBkgnd != nullptr) {
+ ::DeleteObject(m_hbrBkgnd);
+ }
+ m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
+
+ // 瀛椾綋
+ HFONT hFontDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ LOGFONT lf;
+
+ ::GetObject(hFontDefault, sizeof(LOGFONT), &lf);
+ lf.lfHeight -= 8;
+ lf.lfWeight = 700;
+ m_fontTitle.CreateFontIndirect(&lf);
+
+ ::GetObject(hFontDefault, sizeof(LOGFONT), &lf);
+ lf.lfHeight -= 8;
+ m_fontLevel.CreateFontIndirect(&lf);
+
+ ::GetObject(hFontDefault, sizeof(LOGFONT), &lf);
+ lf.lfHeight -= 16;
+ m_fontName.CreateFontIndirect(&lf);
+
+ ::GetObject(hFontDefault, sizeof(LOGFONT), &lf);
+ lf.lfHeight -= 8;
+ m_fontDescription.CreateFontIndirect(&lf);
+
+ GetDlgItem(IDC_LABEL_TITLE)->SetFont(&m_fontTitle);
+ GetDlgItem(IDC_LABEL_LEVEL)->SetFont(&m_fontLevel);
+ GetDlgItem(IDC_LABEL_NAME)->SetFont(&m_fontName);
+ GetDlgItem(IDC_LABEL_DESCRIPTION)->SetFont(&m_fontDescription);
+ GetDlgItem(IDC_LABEL_NO_ALARM)->SetFont(&m_fontDescription);
+
+
+ // 鍥炬爣
+ CString strIcon1;
+ HICON hIcon;
+ CStatic* pStatic;
+
+ strIcon1.Format(_T("%s\\Res\\Alarm_o_24.ico"), theApp.m_strAppDir);
+ pStatic = (CStatic*)GetDlgItem(IDC_ICON_TITLE);
+ hIcon = (HICON)::LoadImage(AfxGetInstanceHandle(),
+ strIcon1, IMAGE_ICON, 24, 24,
+ LR_LOADFROMFILE | LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
+ pStatic->SetIcon(hIcon);
+
+ strIcon1.Format(_T("%s\\Res\\Alarm_o_64.ico"), theApp.m_strAppDir);
+ pStatic = (CStatic*)GetDlgItem(IDC_ICON_ALARM);
+ hIcon = (HICON)::LoadImage(AfxGetInstanceHandle(),
+ strIcon1, IMAGE_ICON, 64, 64,
+ LR_LOADFROMFILE | LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
+ pStatic->SetIcon(hIcon);
+
+
+ // 鍏抽棴鎸夐挳
+ strIcon1.Format(_T("%s\\Res\\close_blcak_24.ico"), theApp.m_strAppDir);
+ pStatic = (CStatic*)GetDlgItem(IDC_ICON_ALARM);
+ hIcon = (HICON)::LoadImage(AfxGetInstanceHandle(),
+ strIcon1, IMAGE_ICON, 128, 128,
+ LR_LOADFROMFILE | LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
+ m_btnClose.SubclassDlgItem(IDC_POPUP_BTN_CLOSE, this);
+ m_btnClose.SetIcon(hIcon, hIcon, 24);
+ m_btnClose.SetFaceColor(m_crBkgnd);
+ m_btnClose.SetBkgndColor(BS_NORMAL, BTN_ALARM_OFF_BKGND_NORMAL);
+ m_btnClose.SetBkgndColor(BS_HOVER, BTN_ALARM_OFF_BKGND_NORMAL);
+ m_btnClose.SetBkgndColor(BS_PRESS, BTN_ALARM_OFF_BKGND_PRESS);
+ m_btnClose.SetFrameColor(m_crBkgnd);
+
+
+ // 瑙i櫎璀﹀憡鎸夐挳
+ m_btnAlarmOff.SubclassDlgItem(IDC_BUTTON_ALARM_OFF, this);
+ m_btnAlarmOff.SetFrameColor(BS_NORMAL, BTN_ALARM_OFF_FRAME_NORMAL);
+ m_btnAlarmOff.SetFrameColor(BS_HOVER, BTN_ALARM_OFF_FRAME_HOVER);
+ m_btnAlarmOff.SetFrameColor(BS_PRESS, BTN_ALARM_OFF_FRAME_PRESS);
+ m_btnAlarmOff.SetBkgndColor(BS_NORMAL, BTN_ALARM_OFF_BKGND_NORMAL);
+ m_btnAlarmOff.SetBkgndColor(BS_HOVER, BTN_ALARM_OFF_BKGND_HOVER);
+ m_btnAlarmOff.SetBkgndColor(BS_PRESS, BTN_ALARM_OFF_BKGND_PRESS);
+
+
+ // 闈欓煶鎸夐挳
+ bool bMute = false;
+ m_btnSoundOff.SubclassDlgItem(IDC_BUTTON_SOUND_OFF, this);
+ m_btnSoundOff.SetFrameColor(BS_NORMAL, BTN_SOUND_OFF_FRAME_NORMAL);
+ m_btnSoundOff.SetFrameColor(BS_HOVER, BTN_SOUND_OFF_FRAME_HOVER);
+ m_btnSoundOff.SetFrameColor(BS_PRESS, BTN_SOUND_OFF_FRAME_PRESS);
+ SetButtonBackgroundColors(bMute);
+
+ // 妯嚎1
+ CHorizontalLine* pLine = CHorizontalLine::Hook(GetDlgItem(IDC_LINE1)->m_hWnd);
+ pLine->SetBkgndColor(m_crBkgnd);
+ pLine->SetLineColor(RGB(168, 168, 168));
+
+ pLine = CHorizontalLine::Hook(GetDlgItem(IDC_LINE2)->m_hWnd);
+ pLine->SetBkgndColor(m_crBkgnd);
+ pLine->SetLineColor(RGB(168, 168, 168));
+
+ RefreshContent();
+ return TRUE;
+}
+
+void CAlarmPopupDlg::ShowNoAlarmControls(bool bShow)
+{
+ const int ids[] = { IDC_LABEL_NO_ALARM };
+ for (int id : ids) {
+ if (auto* p = GetDlgItem(id)) {
+ p->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
+ }
+ }
+}
+
+void CAlarmPopupDlg::ShowAlarmControls(bool bShow)
+{
+ const int ids[] = {
+ IDC_BUTTON_PREV, IDC_BUTTON_NEXT,
+ IDC_LABEL_TITLE, IDC_ICON_ALARM, IDC_ICON_TITLE,
+ IDC_LABEL_LEVEL, IDC_LABEL_NAME,
+ IDC_LINE1, IDC_BUTTON_SOUND_OFF, IDC_BUTTON_ALARM_OFF,
+ IDC_LINE2, IDC_LABEL_DESCRIPTION
+ };
+ for (int id : ids) {
+ if (auto* p = GetDlgItem(id)) {
+ p->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
+ }
+ }
+}
+
+void CAlarmPopupDlg::RefreshContent()
+{
+ m_activeAlarms = AlarmManager::getInstance().getActiveAlarms();
+ m_activeIndex = 0;
+
+ if (!m_activeAlarms.empty()) {
+ m_hasActive = true;
+ DisplayActiveAt(m_activeIndex);
+ ShowAlarmControls(true);
+ ShowNoAlarmControls(false);
+ }
+ else {
+ m_hasActive = false;
+ SetDlgItemText(IDC_LABEL_NO_ALARM, _T("褰撳墠鏃犳姤璀�"));
+ SetDlgItemText(IDC_LABEL_NAME, _T(""));
+ SetDlgItemText(IDC_LABEL_LEVEL, _T(""));
+ SetDlgItemText(IDC_LABEL_DESCRIPTION, _T(""));
+ ShowAlarmControls(false);
+ ShowNoAlarmControls(true);
+ }
+}
+
+void CAlarmPopupDlg::OnBnClickedClose()
+{
+ ShowWindow(SW_HIDE);
+}
+
+void CAlarmPopupDlg::OnBnClickedAlarmOff()
+{
+ if (!m_hasActive) return;
+
+ AlarmManager& alarmManager = AlarmManager::getInstance();
+ alarmManager.clearAlarmByAttributes(
+ m_activeAlarm.nId,
+ m_activeAlarm.nDeviceId,
+ m_activeAlarm.nUnitId,
+ CToolUnits::getCurrentTimeString());
+ RefreshContent();
+}
+
+void CAlarmPopupDlg::OnBnClickedPrev()
+{
+ if (m_activeIndex > 0) {
+ --m_activeIndex;
+ DisplayActiveAt(m_activeIndex);
+ }
+}
+
+void CAlarmPopupDlg::OnBnClickedNext()
+{
+ if (m_activeIndex + 1 < static_cast<int>(m_activeAlarms.size())) {
+ ++m_activeIndex;
+ DisplayActiveAt(m_activeIndex);
+ }
+}
+
+void CAlarmPopupDlg::DisplayActiveAt(int idx)
+{
+ if (idx < 0 || idx >= static_cast<int>(m_activeAlarms.size())) return;
+
+ m_activeAlarm = m_activeAlarms[idx];
+
+ // 鏍囪宸茶
+ if (auto* pParent = dynamic_cast<CServoDlg*>(GetParent())) {
+ pParent->AckAlarm(m_activeAlarm.nId);
+ }
+
+ AlarmManager& alarmManager = AlarmManager::getInstance();
+ const AlarmInfo* info = alarmManager.getAlarmInfoByID(m_activeAlarm.nId);
+
+ CString title, level, name, desc;
+ level.Format(_T("绛夌骇: %d"), m_activeAlarm.nSeverityLevel);
+
+ if (info != nullptr && !info->strAlarmText.empty()) {
+ name = CString(info->strAlarmText.c_str());
+ }
+ else {
+ name.Format(_T("ID:%d (%s)"), m_activeAlarm.nId, CString(m_activeAlarm.strDeviceName.c_str()));
+ }
+
+ if (!m_activeAlarm.strDescription.empty()) {
+ desc = CString(m_activeAlarm.strDescription.c_str());
+ }
+ else if (info != nullptr && !info->strDescription.empty()) {
+ desc = CString(info->strDescription.c_str());
+ }
+ else {
+ desc = _T("鏆傛棤鎻忚堪");
+ }
+
+ title.Format(_T("璁惧:%s 鍗曞厓:%s"),
+ CString(m_activeAlarm.strDeviceName.c_str()),
+ CString(m_activeAlarm.strUnitName.c_str()));
+
+ SetDlgItemText(IDC_LABEL_TITLE, title);
+ SetDlgItemText(IDC_LABEL_NAME, name);
+ SetDlgItemText(IDC_LABEL_LEVEL, level);
+ SetDlgItemText(IDC_LABEL_DESCRIPTION, desc);
+
+ UpdateNavButtons();
+ ShowWindow(SW_SHOW);
+}
+
+void CAlarmPopupDlg::UpdateNavButtons()
+{
+ auto* pPrev = GetDlgItem(IDC_BUTTON_PREV);
+ auto* pNext = GetDlgItem(IDC_BUTTON_NEXT);
+ if (pPrev) pPrev->EnableWindow(m_activeIndex > 0);
+ if (pNext) pNext->EnableWindow(m_activeIndex + 1 < static_cast<int>(m_activeAlarms.size()));
+}
+
+HBRUSH CAlarmPopupDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
+
+ if (nCtlColor == CTLCOLOR_STATIC) {
+ pDC->SetBkColor(m_crBkgnd);
+ pDC->SetTextColor(RGB(30, 30, 30));
+ hbr = m_hbrBkgnd;
+ }
+ else if (nCtlColor == CTLCOLOR_DLG) {
+ hbr = m_hbrBkgnd;
+ }
+
+ return hbr;
+}
+
+void CAlarmPopupDlg::OnDestroy()
+{
+ CDialogEx::OnDestroy();
+
+ if (m_hbrBkgnd != nullptr) {
+ ::DeleteObject(m_hbrBkgnd);
+ m_hbrBkgnd = nullptr;
+ }
+}
+
+void CAlarmPopupDlg::SetButtonBackgroundColors(bool bMute)
+{
+ if (!bMute) {
+ m_btnSoundOff.SetBkgndColor(BS_NORMAL, BTN_SOUND_OFF_BKGND_NORMAL);
+ m_btnSoundOff.SetBkgndColor(BS_HOVER, BTN_SOUND_OFF_BKGND_HOVER);
+ m_btnSoundOff.SetBkgndColor(BS_PRESS, BTN_SOUND_OFF_BKGND_PRESS);
+ m_btnSoundOff.Invalidate();
+ }
+ else {
+ m_btnSoundOff.SetBkgndColor(BS_NORMAL, BTN_SOUND_ON_BKGND_NORMAL);
+ m_btnSoundOff.SetBkgndColor(BS_HOVER, BTN_SOUND_ON_BKGND_HOVER);
+ m_btnSoundOff.SetBkgndColor(BS_PRESS, BTN_SOUND_ON_BKGND_PRESS);
+ m_btnSoundOff.Invalidate();
+ }
+}
diff --git a/SourceCode/Bond/Servo/AlarmPopupDlg.h b/SourceCode/Bond/Servo/AlarmPopupDlg.h
new file mode 100644
index 0000000..9822f18
--- /dev/null
+++ b/SourceCode/Bond/Servo/AlarmPopupDlg.h
@@ -0,0 +1,59 @@
+锘�#pragma once
+#include "afxwin.h"
+#include "AlarmManager.h"
+#include "Common.h"
+#include "ToolUnits.h"
+#include "BlButton.h"
+
+// 绠�鍖栫増鎶ヨ寮圭獥锛屽鎺� AlarmManager 鐨勬椿璺冨憡璀�
+class CAlarmPopupDlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CAlarmPopupDlg)
+
+public:
+ CAlarmPopupDlg(CWnd* pParent = NULL);
+ virtual ~CAlarmPopupDlg();
+
+public:
+ void RefreshContent(); // 鍒锋柊褰撳墠鍛婅鏄剧ず
+
+private:
+ COLORREF m_crBkgnd;
+ HBRUSH m_hbrBkgnd;
+ CFont m_fontTitle;
+ CFont m_fontLevel;
+ CFont m_fontName;
+ CFont m_fontDescription;
+ CBlButton m_btnClose;
+ CBlButton m_btnSoundOff;
+ CBlButton m_btnAlarmOff;
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_DIALOG_POPUP_ALARM };
+#endif
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 鏀寔
+
+ DECLARE_MESSAGE_MAP()
+public:
+ virtual BOOL OnInitDialog();
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg void OnDestroy();
+ afx_msg void OnBnClickedClose();
+ afx_msg void OnBnClickedAlarmOff();
+ afx_msg void OnBnClickedPrev();
+ afx_msg void OnBnClickedNext();
+
+private:
+ bool m_hasActive;
+ AlarmData m_activeAlarm;
+ std::vector<AlarmData> m_activeAlarms;
+ int m_activeIndex = 0;
+ void SetButtonBackgroundColors(bool bMute);
+ void ShowNoAlarmControls(bool bShow);
+ void ShowAlarmControls(bool bShow);
+ void UpdateNavButtons();
+ void DisplayActiveAt(int idx);
+};
diff --git a/SourceCode/Bond/Servo/CBonder.cpp b/SourceCode/Bond/Servo/CBonder.cpp
index b9f8343..4bccfbc 100644
--- a/SourceCode/Bond/Servo/CBonder.cpp
+++ b/SourceCode/Bond/Servo/CBonder.cpp
@@ -1,4 +1,4 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "CBonder.h"
@@ -29,10 +29,10 @@
CEquipment::term();
}
- // 必须要实现的虚函数,在此初始化Pin列表
+ // 蹇呴』瑕佸疄鐜扮殑铏氬嚱鏁帮紝鍦ㄦ鍒濆鍖朠in鍒楄〃
void CBonder::initPins()
{
- // 加入Pin初始化代码
+ // 鍔犲叆Pin鍒濆鍖栦唬鐮�
LOGI("<CBonder>initPins");
addPin(SERVO::PinType::INPUT, _T("In1"));
addPin(SERVO::PinType::INPUT, _T("In2"));
@@ -137,8 +137,8 @@
{
// CIM Message Confirm
- // 要将int32的值拆分为两个short, 分别为msg id和panel id
- // 65538, 2为msg id, 1为panel id
+ // 瑕佸皢int32鐨勫�兼媶鍒嗕负涓や釜short, 鍒嗗埆涓簃sg id鍜宲anel id
+ // 65538, 2涓簃sg id, 1涓簆anel id
CEqReadIntStep* pStep = new CEqReadIntStep(__INT32, m_nIndex == 0 ? 0x9d80 : 0xdd80);
pStep->setName(STEP_EQ_CIM_MESSAGE_CONFIRM);
pStep->setWriteSignalDev(m_nIndex == 0 ? 0x349 : 0x649);
@@ -178,7 +178,7 @@
}
{
- // 请求主配方列表的step
+ // 璇锋眰涓婚厤鏂瑰垪琛ㄧ殑step
CEqWriteStep* pStep = new CEqWriteStep();
pStep->setName(STEP_EQ_MASTER_RECIPE_LIST_REQ);
pStep->setWriteSignalDev(m_nIndex == 0 ? 0x366 : 0x666);
@@ -195,7 +195,7 @@
CEqReadStep* pTmpStep = (CEqReadStep*)pFrom;
short ret = MRLRC_OK;
if (code == ROK && pszData != nullptr && size > 0) {
- // 此处解释配方数据
+ // 姝ゅ瑙i噴閰嶆柟鏁版嵁
ret = decodeRecipeListReport(pszData, size);
}
pTmpStep->setReturnCode(ret);
@@ -210,7 +210,7 @@
}
{
- // 请求配方参数
+ // 璇锋眰閰嶆柟鍙傛暟
CEqWriteStep* pStep = new CEqWriteStep();
pStep->setName(STEP_EQ_RECIPE_PARAMETER_REQ);
pStep->setWriteSignalDev(m_nIndex == 0 ? 0x367 : 0x667);
@@ -227,7 +227,7 @@
CEqReadStep* pTmpStep = (CEqReadStep*)pFrom;
short ret = MRLRC_OK;
if (code == ROK && pszData != nullptr && size > 0) {
- // 此处解释配方数据
+ // 姝ゅ瑙i噴閰嶆柟鏁版嵁
ret = decodeRecipeParameterReport(pszData, size);
}
pTmpStep->setReturnCode(ret);
@@ -241,7 +241,7 @@
}
}
- // 使用CEqReadStep替换CEqJobEventStep
+ // 浣跨敤CEqReadStep鏇挎崲CEqJobEventStep
{
// Received Job Report Upstream #1~9
char szBuffer[256];
@@ -404,7 +404,7 @@
}
}
- // 必须要实现的虚函数,在此初始化Slot信息
+ // 蹇呴』瑕佸疄鐜扮殑铏氬嚱鏁帮紝鍦ㄦ鍒濆鍖朣lot淇℃伅
void CBonder::initSlots()
{
m_slot[0].enable();
@@ -487,35 +487,35 @@
return 0;
}
- int CBonder::onProcessStateChanged(int slotNo, PROCESS_STATE state)
+ int CBonder::onProcessStateChanged(int slotNo, PROCESS_STATE prevState, PROCESS_STATE state)
{
- CEquipment::onProcessStateChanged(slotNo, state);
+ CEquipment::onProcessStateChanged(slotNo, prevState, state);
if (state == PROCESS_STATE::Complete) {
- // 检查数据,当前两片玻璃,一片为G1, 一片为G2, 且pProcessData中的id能匹配G1或G2
+ // 妫�鏌ユ暟鎹紝褰撳墠涓ょ墖鐜荤拑锛屼竴鐗囦负G1, 涓�鐗囦负G2, 涓攑ProcessData涓殑id鑳藉尮閰岹1鎴朑2
Lock();
CGlass* pGlass2 = getGlassFromSlot(1);
CGlass* pGlass1 = getGlassFromSlot(2);
if (pGlass1 == nullptr || pGlass2 == nullptr) {
- LOGE("<CBonder-%s>onProcessData,错误!不满足两片玻璃且分别为G1与G2的条件,请检查数据是否正确!", m_strName.c_str());
+ LOGE("<CBonder-%s>onProcessData,閿欒!涓嶆弧瓒充袱鐗囩幓鐠冧笖鍒嗗埆涓篏1涓嶨2鐨勬潯浠讹紝璇锋鏌ユ暟鎹槸鍚︽纭�!", m_strName.c_str());
Unlock();
return -1;
}
if (pGlass1->getBuddy() != nullptr) {
- LOGE("<CBonder-%s>onProcessData,错误!玻璃较早前已被绑定,请检查数据是否正确!", m_strName.c_str());
+ LOGE("<CBonder-%s>onProcessData,閿欒!鐜荤拑杈冩棭鍓嶅凡琚粦瀹氾紝璇锋鏌ユ暟鎹槸鍚︽纭�!", m_strName.c_str());
Unlock();
return -1;
}
if (pGlass1->getType() != MaterialsType::G1 || pGlass2->getType() != MaterialsType::G2) {
- LOGE("<CBonder-%s>onProcessData,错误!两片玻璃未匹配,必须分别为G1和G2类型,请检查数据是否正确!", m_strName.c_str());
+ LOGE("<CBonder-%s>onProcessData,閿欒!涓ょ墖鐜荤拑鏈尮閰嶏紝蹇呴』鍒嗗埆涓篏1鍜孏2绫诲瀷锛岃妫�鏌ユ暟鎹槸鍚︽纭�!", m_strName.c_str());
Unlock();
return -1;
}
pGlass1->setBuddy(pGlass2);
getSlot(0)->setContext(nullptr);
- LOGE("<CBonder-%s>onProcessStateChanged,%s和%s已贴合!", m_strName.c_str(),
+ LOGE("<CBonder-%s>onProcessStateChanged,%s鍜�%s宸茶创鍚�!", m_strName.c_str(),
pGlass1->getID().c_str(), pGlass2->getID().c_str());
Unlock();
}
@@ -535,112 +535,112 @@
int i = 0, v;
- // 1.校正对位延时
+ // 1.鏍℃瀵逛綅寤舵椂
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("校正对位延时", "", this->getName().c_str(), v * 0.01f));
+ params.push_back(CParam("鏍℃瀵逛綅寤舵椂", "", this->getName().c_str(), v * 0.01f));
i += 2;
- // 2.保压时间
+ // 2.淇濆帇鏃堕棿
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("保压时间", "", this->getName().c_str(), v * 0.01f));
+ params.push_back(CParam("淇濆帇鏃堕棿", "", this->getName().c_str(), v * 0.01f));
i += 2;
- // 3.腔体破真空延时
+ // 3.鑵斾綋鐮寸湡绌哄欢鏃�
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("腔体破真空延时", "", this->getName().c_str(), v * 0.01f));
+ params.push_back(CParam("鑵斾綋鐮寸湡绌哄欢鏃�", "", this->getName().c_str(), v * 0.01f));
i += 2;
- // 4.腔体分子泵启动延时
+ // 4.鑵斾綋鍒嗗瓙娉靛惎鍔ㄥ欢鏃�
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("腔体分子泵启动延时", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("鑵斾綋鍒嗗瓙娉靛惎鍔ㄥ欢鏃�", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 5.腔体贴附抽真空延时
+ // 5.鑵斾綋璐撮檮鎶界湡绌哄欢鏃�
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("腔体贴附抽真空延时", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("鑵斾綋璐撮檮鎶界湡绌哄欢鏃�", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 6.加热等待延时
+ // 6.鍔犵儹绛夊緟寤舵椂
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("加热等待延时", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("鍔犵儹绛夊緟寤舵椂", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 7.气囊压力设定
+ // 7.姘斿泭鍘嬪姏璁惧畾
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("气囊压力设定", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("姘斿泭鍘嬪姏璁惧畾", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 8.气囊加压速率
+ // 8.姘斿泭鍔犲帇閫熺巼
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("气囊加压速率", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("姘斿泭鍔犲帇閫熺巼", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 9.气囊泄压速率
+ // 9.姘斿泭娉勫帇閫熺巼
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("气囊泄压速率", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("姘斿泭娉勫帇閫熺巼", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 10.贴附压力上限
+ // 10.璐撮檮鍘嬪姏涓婇檺
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("贴附压力上限", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("璐撮檮鍘嬪姏涓婇檺", "", this->getName().c_str(), v * 0.1f));
i += 4;
- // 11.Z轴转矩速度设定
+ // 11.Z杞磋浆鐭╅�熷害璁惧畾
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("上腔Z轴转矩速度设定", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓婅厰Z杞磋浆鐭╅�熷害璁惧畾", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 12.上腔温度设定
+ // 12.涓婅厰娓╁害璁惧畾
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔温度设定", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓婅厰娓╁害璁惧畾", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 13.下腔温度设定
+ // 13.涓嬭厰娓╁害璁惧畾
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("下腔温度设定", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓嬭厰娓╁害璁惧畾", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 14.上腔Z轴预贴合位速度
+ // 14.涓婅厰Z杞撮璐村悎浣嶉�熷害
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("上腔Z轴预贴合位速度", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓婅厰Z杞撮璐村悎浣嶉�熷害", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 15.上腔Z轴贴附位速度
+ // 15.涓婅厰Z杞磋创闄勪綅閫熷害
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("上腔Z轴贴附位速度", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓婅厰Z杞磋创闄勪綅閫熷害", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 16.上腔Z上腔加热位间距
+ // 16.涓婅厰Z涓婅厰鍔犵儹浣嶉棿璺�
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("上腔Z上腔加热位间距", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓婅厰Z涓婅厰鍔犵儹浣嶉棿璺�", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 17.上腔贴附位压入量
+ // 17.涓婅厰璐撮檮浣嶅帇鍏ラ噺
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("上腔贴附位压入量", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓婅厰璐撮檮浣嶅帇鍏ラ噺", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 18.上腔Z轴破真空距离
+ // 18.涓婅厰Z杞寸牬鐪熺┖璺濈
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("上腔Z轴破真空距离", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓婅厰Z杞寸牬鐪熺┖璺濈", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 19.下顶Pin破真空距离
+ // 19.涓嬮《Pin鐮寸湡绌鸿窛绂�
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("下顶Pin破真空距离", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓嬮《Pin鐮寸湡绌鸿窛绂�", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 20.下顶Pin加热位间距
+ // 20.涓嬮《Pin鍔犵儹浣嶉棿璺�
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("下顶Pin加热位间距", "", this->getName().c_str(), v * 0.001f));
+ params.push_back(CParam("涓嬮《Pin鍔犵儹浣嶉棿璺�", "", this->getName().c_str(), v * 0.001f));
i += 4;
- // 21.腔体真空泵真空规设定值
- params.push_back(CParam("腔体真空泵真空规设定值", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
+ // 21.鑵斾綋鐪熺┖娉电湡绌鸿璁惧畾鍊�
+ params.push_back(CParam("鑵斾綋鐪熺┖娉电湡绌鸿璁惧畾鍊�", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
i += 4;
- // 22.腔体分子泵到达设定值
- params.push_back(CParam("腔体分子泵到达设定值", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
+ // 22.鑵斾綋鍒嗗瓙娉靛埌杈捐瀹氬��
+ params.push_back(CParam("鑵斾綋鍒嗗瓙娉靛埌杈捐瀹氬��", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
i += 4;
@@ -655,24 +655,24 @@
int CBonder::parsingSVData(const char* pszData, size_t size, std::vector<CParam>& params)
{
/*
- 1 工艺运行步骤 1Word 123456
- 2 气囊压力当前 2Word 12345.6
- 3 上腔压力合计 1Word 1234.56
- 4 管道真空规值 FLOAT 123.456
- 5 腔体真空规值 FLOAT 123.456
- 6 上腔温度1 1Word 12345.6
- 7 上腔温度2 1Word 12345.6
- 8 上腔温度3 1Word 12345.6
- 9 上腔温度4 1Word 12345.6
- 10 上腔温度5 1Word 12345.6
- 11 上腔温度6 1Word 12345.6
- 12 下腔温度1 1Word 12345.6
- 13 下腔温度2 1Word 12345.6
- 14 下腔温度3 1Word 12345.6
- 15 下腔温度4 1Word 12345.6
- 16 下腔温度5 1Word 12345.6
- 17 下腔温度6 1Word 12345.6
- 18 压合剩余时间 1Word 1234.56
+ 1 宸ヨ壓杩愯姝ラ 1Word 123456
+ 2 姘斿泭鍘嬪姏褰撳墠 2Word 12345.6
+ 3 涓婅厰鍘嬪姏鍚堣 1Word 1234.56
+ 4 绠¢亾鐪熺┖瑙勫�� FLOAT 123.456
+ 5 鑵斾綋鐪熺┖瑙勫�� FLOAT 123.456
+ 6 涓婅厰娓╁害1 1Word 12345.6
+ 7 涓婅厰娓╁害2 1Word 12345.6
+ 8 涓婅厰娓╁害3 1Word 12345.6
+ 9 涓婅厰娓╁害4 1Word 12345.6
+ 10 涓婅厰娓╁害5 1Word 12345.6
+ 11 涓婅厰娓╁害6 1Word 12345.6
+ 12 涓嬭厰娓╁害1 1Word 12345.6
+ 13 涓嬭厰娓╁害2 1Word 12345.6
+ 14 涓嬭厰娓╁害3 1Word 12345.6
+ 15 涓嬭厰娓╁害4 1Word 12345.6
+ 16 涓嬭厰娓╁害5 1Word 12345.6
+ 17 涓嬭厰娓╁害6 1Word 12345.6
+ 18 鍘嬪悎鍓╀綑鏃堕棿 1Word 1234.56
*/
ASSERT(pszData);
@@ -680,97 +680,97 @@
int i = 0, v;
- // 1.工艺运行步骤
+ // 1.宸ヨ壓杩愯姝ラ
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("工艺运行步骤", "", this->getName().c_str(), v));
+ params.push_back(CParam("宸ヨ壓杩愯姝ラ", "", this->getName().c_str(), v));
i += 2;
- // 2.气囊压力当前
+ // 2.姘斿泭鍘嬪姏褰撳墠
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8 | (pszData[i + 2] & 0xff) << 16 | (pszData[i + 3] & 0xff) << 24;
- params.push_back(CParam("气囊压力当前", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("姘斿泭鍘嬪姏褰撳墠", "", this->getName().c_str(), v * 0.1f));
i += 4;
- // 3.上腔压力合计
+ // 3.涓婅厰鍘嬪姏鍚堣
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔压力合计", "", this->getName().c_str(), ((short)v) * 0.01f));
+ params.push_back(CParam("涓婅厰鍘嬪姏鍚堣", "", this->getName().c_str(), ((short)v) * 0.01f));
i += 2;
- // 4.管道真空规值
- params.push_back(CParam("管道真空规值", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
+ // 4.绠¢亾鐪熺┖瑙勫��
+ params.push_back(CParam("绠¢亾鐪熺┖瑙勫��", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
i += 4;
- // 5.腔体真空规值
- params.push_back(CParam("腔体真空规值", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
+ // 5.鑵斾綋鐪熺┖瑙勫��
+ params.push_back(CParam("鑵斾綋鐪熺┖瑙勫��", "", this->getName().c_str(), (double)toFloat(&pszData[i])));
i += 4;
- // 6.上腔温度1
+ // 6.涓婅厰娓╁害1
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔温度1", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓婅厰娓╁害1", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 7.上腔温度2
+ // 7.涓婅厰娓╁害2
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔温度2", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓婅厰娓╁害2", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 8.上腔温度3
+ // 8.涓婅厰娓╁害3
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔温度3", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓婅厰娓╁害3", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 9.上腔温度4
+ // 9.涓婅厰娓╁害4
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔温度4", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓婅厰娓╁害4", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 10.上腔温度5
+ // 10.涓婅厰娓╁害5
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔温度5", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓婅厰娓╁害5", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 11.上腔温度6
+ // 11.涓婅厰娓╁害6
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("上腔温度6", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓婅厰娓╁害6", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 12.下腔温度1
+ // 12.涓嬭厰娓╁害1
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("下腔温度1", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓嬭厰娓╁害1", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 13.下腔温度2
+ // 13.涓嬭厰娓╁害2
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("下腔温度2", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓嬭厰娓╁害2", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 14.下腔温度3
+ // 14.涓嬭厰娓╁害3
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("下腔温度3", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓嬭厰娓╁害3", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 15.下腔温度4
+ // 15.涓嬭厰娓╁害4
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("下腔温度4", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓嬭厰娓╁害4", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 16.下腔温度5
+ // 16.涓嬭厰娓╁害5
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("下腔温度5", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓嬭厰娓╁害5", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 17.下腔温度6
+ // 17.涓嬭厰娓╁害6
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("下腔温度6", "", this->getName().c_str(), v * 0.1f));
+ params.push_back(CParam("涓嬭厰娓╁害6", "", this->getName().c_str(), v * 0.1f));
i += 2;
- // 18.加热剩余时间
+ // 18.鍔犵儹鍓╀綑鏃堕棿
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("加热剩余时间", "", this->getName().c_str(), v * 0.01f));
+ params.push_back(CParam("鍔犵儹鍓╀綑鏃堕棿", "", this->getName().c_str(), v * 0.01f));
i += 2;
- // 19.压合剩余时间
+ // 19.鍘嬪悎鍓╀綑鏃堕棿
v = (pszData[i] & 0xff) | (pszData[i + 1] & 0xff) << 8;
- params.push_back(CParam("压合剩余时间", "", this->getName().c_str(), v * 0.01f));
+ params.push_back(CParam("鍘嬪悎鍓╀綑鏃堕棿", "", this->getName().c_str(), v * 0.01f));
i += 2;
return (int)params.size();
diff --git a/SourceCode/Bond/Servo/CBonder.h b/SourceCode/Bond/Servo/CBonder.h
index 89631dc..e9a68e0 100644
--- a/SourceCode/Bond/Servo/CBonder.h
+++ b/SourceCode/Bond/Servo/CBonder.h
@@ -22,7 +22,7 @@
virtual void getAttributeVector(CAttributeVector& attrubutes);
virtual int recvIntent(CPin* pPin, CIntent* pIntent);
virtual int onProcessData(CProcessData* pProcessData);
- virtual int onProcessStateChanged(int slotNo, PROCESS_STATE state);
+ virtual int onProcessStateChanged(int slotNo, PROCESS_STATE prevState, PROCESS_STATE state);
virtual int getIndexerOperationModeBaseValue();
virtual int parsingParams(const char* pszData, size_t size, std::vector<CParam>& parsms);
virtual int parsingProcessData(const char* pszData, size_t size, std::vector<CParam>& parsms);
diff --git a/SourceCode/Bond/Servo/CCollectionEvent.cpp b/SourceCode/Bond/Servo/CCollectionEvent.cpp
index ce5d1a2..13e23b2 100644
--- a/SourceCode/Bond/Servo/CCollectionEvent.cpp
+++ b/SourceCode/Bond/Servo/CCollectionEvent.cpp
@@ -59,9 +59,9 @@
}
}
- unsigned int CCollectionEvent::getFirstPortID()
+ unsigned int CCollectionEvent::getFirstReportID()
{
- if (m_reports.empty()) return -1;
+ if (m_reports.empty()) return 0;
return m_reports.front()->getReportId();
}
diff --git a/SourceCode/Bond/Servo/CCollectionEvent.h b/SourceCode/Bond/Servo/CCollectionEvent.h
index a0b023d..7dfe985 100644
--- a/SourceCode/Bond/Servo/CCollectionEvent.h
+++ b/SourceCode/Bond/Servo/CCollectionEvent.h
@@ -2,7 +2,6 @@
#include "CReport.h"
#include <vector>
-
namespace SERVO {
class CCollectionEvent
{
@@ -17,13 +16,14 @@
std::string& getDescription();
std::vector<CReport*>& getReports();
std::string getReportIdsText();
+ const std::vector<unsigned int>& getReportIds() const { return m_rptids; }
BOOL addReport(CReport* pReport);
BOOL deleteReport(unsigned int nReportId);
CReport* getReport(unsigned int nReportId);
/* 如果一个CEID只有一个Report的场景,调用此函数设置或取消 */
void setReport(CReport* pReport);
- unsigned int getFirstPortID();
+ unsigned int getFirstReportID();
CReport* getFirstReport();
private:
@@ -33,4 +33,4 @@
std::vector<unsigned int> m_rptids;
std::vector<CReport*> m_reports;
};
-}
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/CControlJobManagerDlg.cpp b/SourceCode/Bond/Servo/CControlJobManagerDlg.cpp
index 2e2d92c..78036f4 100644
--- a/SourceCode/Bond/Servo/CControlJobManagerDlg.cpp
+++ b/SourceCode/Bond/Servo/CControlJobManagerDlg.cpp
@@ -534,15 +534,6 @@
m_pControlJob->setPJs(pjs);
m_pControlJob->clearIssues();
int nRet = master.setProcessJobs(pjs);
-
- // 娌℃湁闂鐨刾j瑕侀噴鏀�
- for (auto pj : pjs) {
- if (!pj->issues().empty()) {
- delete pj;
- }
- }
- pjs.clear();
-
if (nRet <= 0) {
std::string msg("鍚屾Process Job澶辫触!");
for (auto pj : pjs) {
@@ -559,11 +550,20 @@
msg.append("\n");
}
}
+ delete pj;
}
+ pjs.clear();
AfxMessageBox(msg.c_str());
-
return;
}
+
+ // 缁х画閲婃斁鏈夐棶棰樼殑 ProcessJob
+ for (auto pj : pjs) {
+ if (!pj->issues().empty()) {
+ delete pj;
+ }
+ }
+ pjs.clear();
nRet = master.setControlJob(*m_pControlJob);
if (nRet != 0) {
@@ -592,55 +592,55 @@
SERVO::CLoadPort* pLoadPort = pPorts[m_pjWarps[p].port];
for (int i = 0; i < SLOT_MAX; ++i) {
SERVO::CSlot* pSlot = pLoadPort->getSlot(i);
- if (!pSlot) {
- continue;
- }
-
+ if (!pSlot) continue;
+
+ SERVO::CGlass* pGlass = dynamic_cast<SERVO::CGlass*>(pSlot->getContext());
+ if (pGlass == nullptr) continue;
+
+ SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS();
+ if (pJobDataS == nullptr) continue;
+
+
// 璁剧疆 Panel ID 鍜屽嬀閫夋
SERVO::CProcessJob* pj = (SERVO::CProcessJob*)m_pjWarps[p].pj;
int nRecipeID = RecipeManager::getInstance().getIdByPPID(pj->recipeSpec());
RecipeInfo stRecipeInfo = RecipeManager::getInstance().getRecipeByPPID(pj->recipeSpec());
std::vector<DeviceRecipe> vecRecipeInfo = stRecipeInfo.vecDeviceList;
- SERVO::CGlass* pGlass = dynamic_cast<SERVO::CGlass*>(pSlot->getContext());
- SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS();
- if (pGlass != nullptr && pJobDataS != nullptr) {
- pGlass->setScheduledForProcessing(m_pjWarps[p].checkSlot[i]);
- pGlass->setType(static_cast<SERVO::MaterialsType>(m_pjWarps[p].material[i]));
- SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS();
- pJobDataS->setLotId(pj->getLotId().c_str());
+ pGlass->setScheduledForProcessing(m_pjWarps[p].checkSlot[i]);
+ pGlass->setType(static_cast<SERVO::MaterialsType>(m_pjWarps[p].material[i]));
+
+ pJobDataS->setLotId(pj->getLotId().c_str());
pJobDataS->setProductId(pj->getProductId().c_str());
pJobDataS->setOperationId(pj->getOperationId().c_str());
pJobDataS->setMaterialsType(m_pjWarps[p].material[i]);
pJobDataS->setMasterRecipe(nRecipeID);
- for (const auto& info : vecRecipeInfo) {
- const std::string& name = info.strDeviceName;
- short nRecipeID = (short)info.nRecipeID;
+ for (const auto& info : vecRecipeInfo) {
+ const std::string& name = info.strDeviceName;
+ short nRecipeID = (short)info.nRecipeID;
- if (name == EQ_NAME_EFEM) {
- pJobDataS->setDeviceRecipeId(0, nRecipeID);
- }
- else if (name == EQ_NAME_BONDER1) {
- pJobDataS->setDeviceRecipeId(1, nRecipeID);
- }
- else if (name == EQ_NAME_BONDER2) {
- pJobDataS->setDeviceRecipeId(2, nRecipeID);
- }
- else if (name == EQ_NAME_BAKE_COOLING) {
- pJobDataS->setDeviceRecipeId(3, nRecipeID);
- }
- else if (name == EQ_NAME_VACUUMBAKE) {
- pJobDataS->setDeviceRecipeId(4, nRecipeID);
- }
- else if (name == EQ_NAME_MEASUREMENT) {
- pJobDataS->setDeviceRecipeId(5, nRecipeID);
- }
+ if (name == EQ_NAME_EFEM) {
+ pJobDataS->setDeviceRecipeId(0, nRecipeID);
+ }
+ else if (name == EQ_NAME_BONDER1) {
+ pJobDataS->setDeviceRecipeId(1, nRecipeID);
+ }
+ else if (name == EQ_NAME_BONDER2) {
+ pJobDataS->setDeviceRecipeId(2, nRecipeID);
+ }
+ else if (name == EQ_NAME_BAKE_COOLING) {
+ pJobDataS->setDeviceRecipeId(3, nRecipeID);
+ }
+ else if (name == EQ_NAME_VACUUMBAKE) {
+ pJobDataS->setDeviceRecipeId(4, nRecipeID);
+ }
+ else if (name == EQ_NAME_MEASUREMENT) {
+ pJobDataS->setDeviceRecipeId(5, nRecipeID);
}
}
}
}
-
// process start
for (int p = 0; p < 4; p++) {
diff --git a/SourceCode/Bond/Servo/CDataVariable.h b/SourceCode/Bond/Servo/CDataVariable.h
new file mode 100644
index 0000000..0452da1
--- /dev/null
+++ b/SourceCode/Bond/Servo/CDataVariable.h
@@ -0,0 +1,13 @@
+锘�#pragma once
+
+#include "CVariable.h"
+
+namespace SERVO {
+ // Data Variable:璇箟鍚� CVariable锛屼絾鐢ㄤ簬 DVID锛堟暟鎹彉閲忥級闆嗗悎銆�
+ class CDataVariable : public CVariable
+ {
+ public:
+ using CVariable::CVariable; // 澶嶇敤鍩虹被鏋勯��
+ ~CDataVariable() = default;
+ };
+}
diff --git a/SourceCode/Bond/Servo/CEquipment.cpp b/SourceCode/Bond/Servo/CEquipment.cpp
index f9d930a..36217c3 100644
--- a/SourceCode/Bond/Servo/CEquipment.cpp
+++ b/SourceCode/Bond/Servo/CEquipment.cpp
@@ -1,4 +1,4 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "CEquipment.h"
#include "ToolUnits.h"
#include <regex>
@@ -103,7 +103,7 @@
void CEquipment::getProperties(std::vector<std::pair<std::string, std::string>>& container)
{
container.clear();
- // 示例:将一些属性添加到容器
+ // 绀轰緥锛氬皢涓�浜涘睘鎬ф坊鍔犲埌瀹瑰櫒
container.push_back(std::make_pair("DeviceName", "ServoMotor"));
container.push_back(std::make_pair("SerialNumber", "123456789"));
container.push_back(std::make_pair("Version", "1.0"));
@@ -147,11 +147,12 @@
{
if (nSlotNo <= 0 || nSlotNo > 8) return;
+ const auto prevState = m_processState[nSlotNo - 1];
m_processState[nSlotNo - 1] = state;
- onProcessStateChanged(nSlotNo, m_processState[nSlotNo - 1]);
+ onProcessStateChanged(nSlotNo, prevState, m_processState[nSlotNo - 1]);
if (m_listener.onProcessStateChanged != nullptr) {
- m_listener.onProcessStateChanged(this, nSlotNo, m_processState[nSlotNo - 1]);
+ m_listener.onProcessStateChanged(this, nSlotNo, prevState, m_processState[nSlotNo - 1]);
}
}
@@ -205,6 +206,21 @@
std::string& CEquipment::getDescription()
{
return m_strDescription;
+ }
+
+ void CEquipment::setCurrentRecipe(const std::string& recipe)
+ {
+ Lock();
+ m_currentRecipe = recipe;
+ Unlock();
+ }
+
+ std::string CEquipment::getCurrentRecipe()
+ {
+ Lock();
+ std::string out = m_currentRecipe;
+ Unlock();
+ return out;
}
void CEquipment::setStation(int network, int station)
@@ -315,7 +331,7 @@
void CEquipment::onTimer(UINT nTimerid)
{
- // 每隔一秒,检查一下ALIVE状态
+ // 姣忛殧涓�绉掞紝妫�鏌ヤ竴涓婣LIVE鐘舵��
static int tick = 0;
tick++;
@@ -379,7 +395,7 @@
}
}
- // 梳理各玻璃之间的绑定关系
+ // 姊崇悊鍚勭幓鐠冧箣闂寸殑缁戝畾鍏崇郴
/*
Lock();
for (int i = 0; i < SLOT_MAX; i++) {
@@ -391,7 +407,7 @@
CGlass* pBudy = (CGlass*)m_slot[j].getContext();
if (pBudy != nullptr && strBuddyId.compare(pBudy->getID()) == 0) {
pGlass->setBuddy(pBudy);
- TRACE("绑定关系: %s <- %s\n", pGlass->getID().c_str(), pBudy->getID().c_str());
+ TRACE("缁戝畾鍏崇郴: %s <- %s\n", pGlass->getID().c_str(), pBudy->getID().c_str());
}
}
}
@@ -412,7 +428,7 @@
}
*/
- // 连接信号解释和保存
+ // 杩炴帴淇″彿瑙i噴鍜屼繚瀛�
BOOL bFlag;
int index = 0;
for (int i = 0; i < 8; i++) {
@@ -442,7 +458,7 @@
}
- // 其它信号及响应
+ // 鍏跺畠淇″彿鍙婂搷搴�
index = 0x540;
@@ -452,7 +468,7 @@
m_alive.flag = bFlag;
m_alive.count = 0;
- // 状态
+ // 鐘舵��
if (!m_alive.alive) {
m_alive.alive = TRUE;
if (m_listener.onAlive != nullptr) {
@@ -501,7 +517,7 @@
}
- // 以下根据信号做流程处理
+ // 浠ヤ笅鏍规嵁淇″彿鍋氭祦绋嬪鐞�
for (int i = 0; i < 7; i++) {
CHECK_READ_STEP_SIGNAL(STEP_ID_EQMODE_CHANGED + i, pszData, size);
}
@@ -510,15 +526,22 @@
CHECK_READ_STEP_SIGNAL(STEP_ID_PROCESS_DATA_REPORT, pszData, size);
// FAC Data report
- CHECK_READ_STEP_SIGNAL(STEP_ID_FAC_DATA_REPORT, pszData, size);
+ // CHECK_READ_STEP_SIGNAL(STEP_ID_FAC_DATA_REPORT, pszData, size);
+ {
+ SERVO::CStep* pStep = getStep(STEP_ID_FAC_DATA_REPORT);
+ if (pStep != nullptr) {
+ ((CReadStep*)pStep)->onReadSignal(TRUE);
+ }
+ }
- // 配方改变
+
+ // 閰嶆柟鏀瑰彉
CHECK_READ_STEP_SIGNAL(STEP_ID_CURRENT_RECIPE_CHANGE_REPORT, pszData, size);
- // 主配方上报
+ // 涓婚厤鏂逛笂鎶�
CHECK_READ_STEP_SIGNAL(STEP_ID_MASTER_RECIPE_LIST_REPORT, pszData, size);
- // 配方参数
+ // 閰嶆柟鍙傛暟
CHECK_WRITE_STEP_SIGNAL(STEP_ID_RECIPE_PARAMETER_CMD_REPLY, pszData, size);
CHECK_READ_STEP_SIGNAL(STEP_ID_RECIPE_PARAMETER_REPORT, pszData, size);
@@ -759,8 +782,8 @@
else if (isCimMessageConfirmStep(pStep)) {
SERVO::CEqReadIntStep* pEqReadIntStep = (SERVO::CEqReadIntStep*)pStep;
int value = pEqReadIntStep->getValue();
- // 此处将value按高低位拆分为message id和panel no.
- // 可能还需要上报到cim
+ // 姝ゅ灏唙alue鎸夐珮浣庝綅鎷嗗垎涓簃essage id鍜宲anel no.
+ // 鍙兘杩橀渶瑕佷笂鎶ュ埌cim
short msgId, panelNo;
msgId = (value & 0xffff0000 >> 16);
panelNo = (value & 0xffff);
@@ -775,7 +798,7 @@
m_listener.onVcrEventReport(this, pVcrEventReport);
}
- // 0426, 先固定返回1(OK)
+ // 0426, 鍏堝浐瀹氳繑鍥�1(OK)
pEqVcrEventStep->setReturnCode(1);
return 1;
}
@@ -788,12 +811,12 @@
CPin* CEquipment::addPin(PinType type, char* pszName)
{
- // 不允许名字添加重复的pin
+ // 涓嶅厑璁稿悕瀛楁坊鍔犻噸澶嶇殑pin
CPin* pPin = getPin(pszName);
if (pPin != nullptr) return nullptr;
- // 添加到Pin列表,看是输入pin或输出pin
+ // 娣诲姞鍒癙in鍒楄〃锛岀湅鏄緭鍏in鎴栬緭鍑簆in
if (type == PinType::INPUT) {
pPin = new CPin(this, type, pszName);
m_inputPins.push_back(pPin);
@@ -858,7 +881,7 @@
CEquipment* pFromEq = pFromPin->getEquipment();
ASSERT(pFromEq);
- LOGD("<CEquipment><%s-%s>收到来自<%s.%s>的Intent<%d,%s,0x%x>",
+ LOGD("<CEquipment><%s-%s>鏀跺埌鏉ヨ嚜<%s.%s>鐨処ntent<%d,%s,0x%x>",
this->getName().c_str(),
pPin->getName().c_str(),
pFromEq->getName().c_str(),
@@ -869,11 +892,11 @@
- // 以下解释处理数据
+ // 浠ヤ笅瑙i噴澶勭悊鏁版嵁
int code = pIntent->getCode();
- // 测试
+ // 娴嬭瘯
if (code == FLOW_TEST) {
AfxMessageBox(pIntent->getMsg());
}
@@ -888,7 +911,7 @@
return -1;
}
- // 找到指定的glass id,
+ // 鎵惧埌鎸囧畾鐨刧lass id,
Lock();
CGlass* pContext = nullptr;
for (int i = 0; i < SLOT_MAX; i++) {
@@ -939,7 +962,7 @@
CGlass* pBuddy = pGlass->getBuddy();
if (pBuddy != nullptr) pBuddy->addPath(m_nID, getSlotUnit(putSlot), putSlot);
m_slot[putSlot - 1].setContext(pGlass);
- pGlass->release(); // tempFetchOut需要调用一次release
+ pGlass->release(); // tempFetchOut闇�瑕佽皟鐢ㄤ竴娆elease
Unlock();
/*
@@ -1167,17 +1190,17 @@
return -1;
}
- LOGI("<CEquipment-%s>准备设置DispatchingMode<%d>", m_strName.c_str(), (int)mode);
+ LOGI("<CEquipment-%s>鍑嗗璁剧疆DispatchingMode<%d>", m_strName.c_str(), (int)mode);
if (onWritedBlock != nullptr) {
pStep->writeShort((short)mode, onWritedBlock);
}
else {
pStep->writeShort((short)mode, [&, mode](int code) -> int {
if (code == WOK) {
- LOGI("<CEquipment-%s>设置DispatchingMode成功.", m_strName.c_str());
+ LOGI("<CEquipment-%s>璁剧疆DispatchingMode鎴愬姛.", m_strName.c_str());
}
else {
- LOGE("<CEquipment-%s>设置DispatchingMode失败,code:%d", m_strName.c_str(), code);
+ LOGE("<CEquipment-%s>璁剧疆DispatchingMode澶辫触锛宑ode:%d", m_strName.c_str(), code);
}
return 0;
@@ -1195,19 +1218,19 @@
}
unsigned short operationMode = (unsigned short)((unsigned short)mode + getIndexerOperationModeBaseValue());
- LOGI("<CEquipment-%s>准备设置indexerOperationMode<%d>", m_strName.c_str(), (int)mode);
+ LOGI("<CEquipment-%s>鍑嗗璁剧疆indexerOperationMode<%d>", m_strName.c_str(), (int)mode);
pStep->writeShort(operationMode, [&, pStep, mode, onWritedRetBlock](int code) -> int {
int retCode = 0;
if (code == WOK) {
- LOGI("<CEquipment-%s>设置indexerOperationMode成功.", m_strName.c_str());
+ LOGI("<CEquipment-%s>璁剧疆indexerOperationMode鎴愬姛.", m_strName.c_str());
const char* pszRetData = nullptr;
pStep->getReturnData(pszRetData);
ASSERT(pszRetData);
retCode = (unsigned int)CToolUnits::toInt16(pszRetData);
- LOGI("<CEquipment-%s>返回值: %d", m_strName.c_str(), retCode);
+ LOGI("<CEquipment-%s>杩斿洖鍊�: %d", m_strName.c_str(), retCode);
}
else {
- LOGE("<CEquipment-%s>设置indexerOperationMode失败,code:%d", m_strName.c_str(), code);
+ LOGE("<CEquipment-%s>璁剧疆indexerOperationMode澶辫触锛宑ode:%d", m_strName.c_str(), code);
}
if (onWritedRetBlock != nullptr) {
@@ -1227,18 +1250,18 @@
return -1;
}
- LOGI("<CEquipment-%s>正在请求单元<%d>主配方列表", m_strName.c_str(), unitNo);
+ LOGI("<CEquipment-%s>姝e湪璇锋眰鍗曞厓<%d>涓婚厤鏂瑰垪琛�", m_strName.c_str(), unitNo);
m_recipesManager.setOnSyncingStateChanged(block);
if (m_recipesManager.syncing() != 0) {
return -2;
}
pStep->writeShort(unitNo, [&, unitNo](int code) -> int {
if (code == WOK) {
- LOGI("<CEquipment-%s>请求单元<%d>主配方列表成功,正在等待数据.", m_strName.c_str(), unitNo);
+ LOGI("<CEquipment-%s>璇锋眰鍗曞厓<%d>涓婚厤鏂瑰垪琛ㄦ垚鍔燂紝姝e湪绛夊緟鏁版嵁.", m_strName.c_str(), unitNo);
}
else {
m_recipesManager.syncFailed();
- LOGE("<CEquipment-%s>请求单元<%d>主配方列表失败,code:%d", m_strName.c_str(), unitNo, code);
+ LOGE("<CEquipment-%s>璇锋眰鍗曞厓<%d>涓婚厤鏂瑰垪琛ㄥけ璐ワ紝code:%d", m_strName.c_str(), unitNo, code);
}
return 0;
@@ -1248,7 +1271,7 @@
int CEquipment::recipeParameterRequest(short masterRecipeId, short localRecipeId, short unitNo, ONSYNCINGSTATECHANGED block)
{
- LOGI("<CEquipment-%s>正在请求单元<%d>主配参数列表", m_strName.c_str(), unitNo);
+ LOGI("<CEquipment-%s>姝e湪璇锋眰鍗曞厓<%d>涓婚厤鍙傛暟鍒楄〃", m_strName.c_str(), unitNo);
m_recipesManager.setOnSyncingStateChanged(block);
if (m_recipesManager.syncing() != 0) {
return -2;
@@ -1269,11 +1292,11 @@
pStep->writeDataEx(szBuffer, 14 * 2, [&, unitNo](int code) -> int {
if (code == WOK) {
- LOGI("<CEquipment-%s>请求单元<%d>主配方参数列表成功,正在等待数据.", m_strName.c_str(), unitNo);
+ LOGI("<CEquipment-%s>璇锋眰鍗曞厓<%d>涓婚厤鏂瑰弬鏁板垪琛ㄦ垚鍔燂紝姝e湪绛夊緟鏁版嵁.", m_strName.c_str(), unitNo);
}
else {
m_recipesManager.syncFailed();
- LOGE("<CEquipment-%s>请求单元<%d>主配方参数列表失败,code:%d", m_strName.c_str(), unitNo, code);
+ LOGE("<CEquipment-%s>璇锋眰鍗曞厓<%d>涓婚厤鏂瑰弬鏁板垪琛ㄥけ璐ワ紝code:%d", m_strName.c_str(), unitNo, code);
}
return 0;
@@ -1577,7 +1600,7 @@
int nRet = processData.unserialize(&pszData[0], (int)size);
if (nRet < 0) return nRet;
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
CAttributeVector& attrubutes = pStep->attributeVector();
processData.getAttributeVector(attrubutes, weight);
@@ -1585,11 +1608,11 @@
- // 找到玻璃,关联数据
+ // 鎵惧埌鐜荤拑锛屽叧鑱旀暟鎹�
CGlass* pGlass = this->getGlassWithCassette(processData.getCassetteSequenceNo(),
processData.getJobSequenceNo());
if (pGlass == nullptr) {
- LOGE("<CEquipment-%s>找不到对应Glass, 关联工艺参数失败。CassetteSequenceNo:%d/%d",
+ LOGE("<CEquipment-%s>鎵句笉鍒板搴擥lass, 鍏宠仈宸ヨ壓鍙傛暟澶辫触銆侰assetteSequenceNo:%d/%d",
this->getName().c_str(),
processData.getCassetteSequenceNo(),
processData.getJobSequenceNo());
@@ -1600,15 +1623,21 @@
std::vector<CParam> tempParams;
this->parsingProcessData((const char*)rawData.data(), rawData.size(), tempParams);
int n = processData.getTotalParameter();
- std::vector<CParam> params(tempParams.begin(), tempParams.begin() + min(n, (int)tempParams.size()));
+ std::vector<CParam> params(tempParams.begin(), tempParams.begin() + (std::min)(n, (int)tempParams.size()));
pGlass->addParams(params);
+ if (m_listener.onProcessDataReport != nullptr) {
+ m_listener.onProcessDataReport(this, params);
+ }
- // 关联的Glass也要更新
+ // 鍏宠仈鐨凣lass涔熻鏇存柊
CGlass* pBuddy = pGlass->getBuddy();
LOGI("<Equipment-%s>decodeProcessDataReport pBuddy=%x %s", getName().c_str(), pBuddy, pGlass->getID().c_str());
if (pBuddy != nullptr) {
LOGI("<Equipment-%s>decodeProcessDataReport addParams pBuddy=%x %s", getName().c_str(), pBuddy, pGlass->getID().c_str());
pBuddy->addParams(params);
+ if (m_listener.onProcessDataReport != nullptr) {
+ m_listener.onProcessDataReport(this, params);
+ }
}
return nRet;
@@ -1620,7 +1649,7 @@
int nRet = jobDataS.unserialize(&pszData[0], (int)size);
if (nRet < 0) return nRet;
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
CAttributeVector& attrubutes = pStep->attributeVector();
jobDataS.getAttributeVector(attrubutes, weight);
@@ -1634,11 +1663,15 @@
LOGI("<CEquipment-%s>onReceivedJob.", m_strName.c_str());
- // 可以在此更新JobDataS数据了
+ // 鍙互鍦ㄦ鏇存柊JobDataS鏁版嵁浜�
int nRet = ((CArm*)m_pArm)->glassUpdateJobDataS(pJobDataS);
if (nRet < 0) {
- LOGE("<CEquipment-%s>onReceivedJob,更新JobDataS失败,glassUpdateJobDataS返回%d",
+ LOGE("<CEquipment-%s>onReceivedJob,鏇存柊JobDataS澶辫触锛実lassUpdateJobDataS杩斿洖%d",
m_strName.c_str(), nRet);
+ }
+
+ if (m_listener.onReceivedJob != nullptr) {
+ m_listener.onReceivedJob(this, port, pJobDataS);
}
return nRet;
@@ -1650,7 +1683,7 @@
int nRet = jobDataS.unserialize(&pszData[0], (int)size);
if (nRet < 0) return nRet;
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
CAttributeVector& attrubutes = pStep->attributeVector();
jobDataS.getAttributeVector(attrubutes, weight);
@@ -1662,6 +1695,10 @@
int CEquipment::onSentOutJob(int port, CJobDataS* pJobDataS)
{
LOGI("<CEquipment-%s>onSentOutJob.", m_strName.c_str());
+
+ if (m_listener.onSentOutJob != nullptr) {
+ m_listener.onSentOutJob(this, port, pJobDataS);
+ }
return 0;
}
@@ -1685,7 +1722,7 @@
index += sizeof(short);
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
pStep->addAttribute(new CAttribute("UnitOrPort",
std::to_string(unitOrPort).c_str(), "", weight++));
@@ -1729,7 +1766,7 @@
return fetchedOutJob(port, pJobDataB);
}
- // 数据异常,处理或显示
+ // 鏁版嵁寮傚父锛屽鐞嗘垨鏄剧ず
LOGI("<CEquipment-%s>onFetchedOutJob Error.ort:%d|GlassId:%s",
m_strName.c_str(), port, pJobDataB->getGlassId().c_str());
return -1;
@@ -1754,7 +1791,7 @@
index += sizeof(short);
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
pStep->addAttribute(new CAttribute("UnitOrPort",
std::to_string(unitOrPort).c_str(), "", weight++));
@@ -1786,7 +1823,7 @@
vcrEventReport.getGlassId().c_str());
- // 更新Glass的ID
+ // 鏇存柊Glass鐨処D
CGlass* pGlass = getGlassWithCassette(vcrEventReport.getCassetteSequenceNo(),
vcrEventReport.getJobSequenceNo());
if (pGlass != nullptr) {
@@ -1794,13 +1831,13 @@
}
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
CAttributeVector& attrubutes = pStep->attributeVector();
vcrEventReport.getAttributeVector(attrubutes, weight);
- // 0426, 先固定返回1(OK)
+ // 0426, 鍏堝浐瀹氳繑鍥�1(OK)
((CReadStep*)pStep)->setReturnCode((short)VCR_Reply_Code::OK);
@@ -1822,7 +1859,7 @@
index += 256 * 2;
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
pStep->addAttribute(new CAttribute("CassetteNo",
std::to_string(cassetteNo).c_str(), "", weight++));
@@ -1834,10 +1871,10 @@
strPanelGradeData.c_str(), "", weight++));
- // 更新检测结果
+ // 鏇存柊妫�娴嬬粨鏋�
CGlass* pGlass = getGlassWithCassette(cassetteNo, jobSequenceNo);
if (pGlass == nullptr) {
- LOGE("<CEquipment-%s>更新Panel Data失败,找不到对应的Glass.cassetteNo=%d, jobSequenceNo=%d",
+ LOGE("<CEquipment-%s>鏇存柊Panel Data澶辫触锛屾壘涓嶅埌瀵瑰簲鐨凣lass.cassetteNo=%d, jobSequenceNo=%d",
getName().c_str(), cassetteNo, jobSequenceNo);
return -1;
}
@@ -1856,7 +1893,9 @@
CSVData svData;
int nRet = svData.unserialize(&pszData[0], (int)size);
if (nRet < 0) return nRet;
+ Lock();
m_svDatas.push_back(svData);
+ Unlock();
if (m_listener.onSVDataReport != nullptr) {
m_listener.onSVDataReport(this, &svData);
@@ -1878,7 +1917,7 @@
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
pStep->addAttribute(new CAttribute("CassetteSequenceNo",
(std::to_string(cassetteSequenceNo)).c_str(), "", weight++));
@@ -1934,7 +1973,7 @@
CGlass* pGlass = getGlassFromSlot(slotNo);
if (pGlass == nullptr) {
- LOGE("<CEquipment-%s>decodeJobProcessStartReport, 找不到对应glass", getName().c_str());
+ LOGE("<CEquipment-%s>decodeJobProcessStartReport, 鎵句笉鍒板搴攇lass", getName().c_str());
}
if (slotNo <= 0 || slotNo > 8) return -1;
@@ -1946,7 +1985,7 @@
}
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
pStep->addAttribute(new CAttribute("CassetteNo",
std::to_string(cassetteNo).c_str(), "", weight++));
@@ -2016,7 +2055,7 @@
}
if (pGlass == nullptr) {
- LOGE("<CEquipment-%s>decodeJobProcessEndReport, 找不到对应glass", getName().c_str());
+ LOGE("<CEquipment-%s>decodeJobProcessEndReport, 鎵句笉鍒板搴攇lass", getName().c_str());
}
else {
CJobDataS* pJs = pGlass->getJobDataS();
@@ -2025,7 +2064,7 @@
pGlass->processEnd(m_nID, getSlotUnit(slotNo));
}
else {
- LOGE("<CEquipment-%s>decodeJobProcessEndReport, jobSequenceNo或jobSequenceNo不匹配",
+ LOGE("<CEquipment-%s>decodeJobProcessEndReport, jobSequenceNo鎴杍obSequenceNo涓嶅尮閰�",
getName().c_str());
}
}
@@ -2033,7 +2072,7 @@
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
pStep->addAttribute(new CAttribute("CassetteNo",
std::to_string(cassetteNo).c_str(), "", weight++));
@@ -2057,10 +2096,10 @@
LOGI("<CEquipment-%s>onPreStoredJob:port:%d|GlassId:%s",
m_strName.c_str(), port, pJobDataB->getGlassId().c_str());
- // 当前要存片,之前肯定有拔片,因此片子在Arm那里
+ // 褰撳墠瑕佸瓨鐗囷紝涔嬪墠鑲畾鏈夋嫈鐗囷紝鍥犳鐗囧瓙鍦ˋrm閭i噷
CGlass* pGlass = ((CArm*)m_pArm)->getGlassFromSlot(1);
if (pGlass == nullptr) {
- LOGE("<CFliper-%s>onPreStoredJob,缓存中没有找到对应的Glass(CassetteSequenceNo:%d, JobSequenceNo:%d),请检查数据,注意风险。", m_strName.c_str(),
+ LOGE("<CFliper-%s>onPreStoredJob,缂撳瓨涓病鏈夋壘鍒板搴旂殑Glass(CassetteSequenceNo:%d, JobSequenceNo:%d)锛岃妫�鏌ユ暟鎹紝娉ㄦ剰椋庨櫓銆�", m_strName.c_str(),
pJobDataB->getCassetteSequenceNo(), pJobDataB->getJobSequenceNo());
return FALSE;
}
@@ -2068,19 +2107,19 @@
CJobDataS* pJobDataS = pGlass->getJobDataS();
ASSERT(pJobDataS);
if (!compareJobData(pJobDataB, pJobDataS)) {
- LOGE("<CEquipemnt-%s>onPreStoredJob,JobData数据不匹配(JobDataB(%d, %d),JobDataS(%d, %d)), 注意排查风险!", m_strName.c_str(),
+ LOGE("<CEquipemnt-%s>onPreStoredJob,JobData鏁版嵁涓嶅尮閰�(JobDataB(%d, %d),JobDataS(%d, %d)), 娉ㄦ剰鎺掓煡椋庨櫓!", m_strName.c_str(),
pJobDataB->getCassetteSequenceNo(), pJobDataB->getJobSequenceNo(),
pJobDataS->getCassetteSequenceNo(), pJobDataS->getJobSequenceNo());
return FALSE;
}
- // 如果没有可用位置,报错
+ // 濡傛灉娌℃湁鍙敤浣嶇疆锛屾姤閿�
Lock();
CSlot* pSlot = getSlot(putSlot - 1);
ASSERT(pSlot);
if (pSlot->getContext() != nullptr) {
Unlock();
- LOGE("<CEquipemnt-%s>onPreStoredJob,指定slot(port:%d)有料,请注意风险!", m_strName.c_str(), port);
+ LOGE("<CEquipemnt-%s>onPreStoredJob,鎸囧畾slot(port:%d)鏈夋枡锛岃娉ㄦ剰椋庨櫓锛�", m_strName.c_str(), port);
return FALSE;
}
Unlock();
@@ -2110,7 +2149,7 @@
return storedJob(port, pJobDataB, putSlot);
}
- // 数据异常,处理或显示
+ // 鏁版嵁寮傚父锛屽鐞嗘垨鏄剧ず
LOGI("<CEquipment-%s>onStoredJob Error.port:%d|GlassId:%s",
m_strName.c_str(), port, pJobDataB->getGlassId().c_str());
return -1;
@@ -2124,8 +2163,8 @@
}
/*
- * 当从CC-Link检测到设备Send Able为On时调用此函数
- * 可能会多次重复调用(根据扫描频率), 注意防呆
+ * 褰撲粠CC-Link妫�娴嬪埌璁惧Send Able涓篛n鏃惰皟鐢ㄦ鍑芥暟
+ * 鍙兘浼氬娆¢噸澶嶈皟鐢�(鏍规嵁鎵弿棰戠巼), 娉ㄦ剰闃插憜
*/
int CEquipment::onSendAble(int port)
{
@@ -2141,7 +2180,7 @@
return 0;
}
- int CEquipment::onProcessStateChanged(int nSlotNo, PROCESS_STATE state)
+ int CEquipment::onProcessStateChanged(int nSlotNo, PROCESS_STATE prevState, PROCESS_STATE state)
{
return 0;
}
@@ -2243,6 +2282,7 @@
});
pStep->setName(STEP_EQ_FAC_DATA_REPORT);
pStep->setProp("Port", (void*)(__int64)port);
+ pStep->setReadContinue(TRUE);
pStep->setWriteSignalDev(writeSignalDev);
if (addStep(STEP_ID_FAC_DATA_REPORT, pStep) != 0) {
delete pStep;
@@ -2253,4 +2293,4 @@
{
return m_svDatas;
}
-}
\ No newline at end of file
+}
diff --git a/SourceCode/Bond/Servo/CEquipment.h b/SourceCode/Bond/Servo/CEquipment.h
index 36a05c9..0ed9452 100644
--- a/SourceCode/Bond/Servo/CEquipment.h
+++ b/SourceCode/Bond/Servo/CEquipment.h
@@ -1,4 +1,4 @@
-#pragma once
+锘�#pragma once
#include "Log.h"
#include "ServoCommo.h"
#include "CCLinkIEControl.h"
@@ -55,9 +55,12 @@
typedef std::function<void(void* pEiuipment, void* pReport)> ONVCREVENTREPORT;
typedef std::function<BOOL(void* pEiuipment, int port, CJobDataB* pJobDataB)> ONPREFETCHEDOUTJOB;
typedef std::function<BOOL(void* pEiuipment, int port, CJobDataB* pJobDataB, short& putSlot)> ONPRESTOREDJOB;
- typedef std::function<void(void* pEiuipment, int nSlotNo, PROCESS_STATE state)> ONPROCESSSTATE;
+ typedef std::function<void(void* pEiuipment, int nSlotNo, PROCESS_STATE prevState, PROCESS_STATE state)> ONPROCESSSTATE;
typedef std::function<void(void* pEiuipment, short scanMap, short downMap)> ONMAPMISMATCH;
typedef std::function<void(void* pEiuipment, short status, __int64 data)> ONPORTSTATUSCHANGED;
+ typedef std::function<void(void* pEiuipment, const std::vector<CParam>& params)> ONPROCESSDATAREPORT;
+ typedef std::function<void(void* pEiuipment, int port, CJobDataS* pJobDataS)> ONRECEIVEDJOB;
+ typedef std::function<void(void* pEiuipment, int port, CJobDataS* pJobDataS)> ONSENTOUTJOB;
typedef struct _EquipmentListener
{
@@ -73,6 +76,9 @@
ONPORTSTATUSCHANGED onPortStatusChanged;
ONVCREVENTREPORT onSVDataReport;
ONVCREVENTREPORT onPanelDataReport;
+ ONPROCESSDATAREPORT onProcessDataReport;
+ ONRECEIVEDJOB onReceivedJob;
+ ONSENTOUTJOB onSentOutJob;
} EquipmentListener;
@@ -99,6 +105,8 @@
std::string& getName();
void setDescription(const char* pszDescription);
std::string& getDescription();
+ void setCurrentRecipe(const std::string& recipe);
+ std::string getCurrentRecipe();
void setStation(int network, int station);
const StationIdentifier& getStation();
virtual void getAttributeVector(CAttributeVector& attrubutes);
@@ -140,7 +148,7 @@
virtual int onProcessData(CProcessData* pProcessData);
virtual int onSendAble(int port);
virtual int onReceiveAble(int port);
- virtual int onProcessStateChanged(int nSlotNo, PROCESS_STATE state);
+ virtual int onProcessStateChanged(int nSlotNo, PROCESS_STATE prevState, PROCESS_STATE state);
virtual int getIndexerOperationModeBaseValue();
virtual bool isSlotProcessed(int slot) { return true; };
bool isAlarmStep(SERVO::CStep* pStep);
@@ -163,47 +171,47 @@
void printDebugString001();
std::vector<SERVO::CSVData>& getSVDatas();
- // 请求主配方列表
+ // 璇锋眰涓婚厤鏂瑰垪琛�
// unitNo: 0:local; Others:unit No
int masterRecipeListRequest(short unitNo, ONSYNCINGSTATECHANGED block);
- // 请求配方参数
- // masterRecipeId: 主配方id
- // localRecipeId: 本地配方id
+ // 璇锋眰閰嶆柟鍙傛暟
+ // masterRecipeId: 涓婚厤鏂筰d
+ // localRecipeId: 鏈湴閰嶆柟id
// unitNo: 0:local; Others:unit No
int recipeParameterRequest(short masterRecipeId, short localRecipeId, short unitNo, ONSYNCINGSTATECHANGED block);
- // 解析配方参数列表
+ // 瑙f瀽閰嶆柟鍙傛暟鍒楄〃
virtual int parsingParams(const char* pszData, size_t size, std::vector<CParam>& params) { return 0; };
virtual int parsingParams(const char* pszData, size_t size, std::string& strOut);
virtual int parsingProcessData(const char* pszData, size_t size, std::vector<CParam>& params) { return 0; };
virtual int parsingSVData(const char* pszData, size_t size, std::vector<CParam>& params) { return 0; };
- // 获取指定的Slot
+ // 鑾峰彇鎸囧畾鐨凷lot
CSlot* getSlot(int index);
CSlot* getSlotWithNo(int slotNo);
- // 获取一个可用的槽位
+ // 鑾峰彇涓�涓彲鐢ㄧ殑妲戒綅
CSlot* getAvailableSlot();
- // 获取一个指定物料类型(G1,G2,G1&G2)的空槽位
+ // 鑾峰彇涓�涓寚瀹氱墿鏂欑被鍨�(G1,G2,G1&G2)鐨勭┖妲戒綅
CSlot* getAvailableSlotForGlass(MaterialsType type);
CSlot* getAvailableSlotForGlassExcludeSignal(MaterialsType type);
CSlot* isSlotAvailable(unsigned int slot);
- // 在指定的槽列表中,获取一个指定物料类型(G1,G2,G1&G2)的空槽位
+ // 鍦ㄦ寚瀹氱殑妲藉垪琛ㄤ腑锛岃幏鍙栦竴涓寚瀹氱墿鏂欑被鍨�(G1,G2,G1&G2)鐨勭┖妲戒綅
CSlot* getAvailableSlotForGlass2(MaterialsType type, const std::vector<int>& candidates);
- // 获取一个指定物料类型(G1,G2,G1&G2)的非空槽位
+ // 鑾峰彇涓�涓寚瀹氱墿鏂欑被鍨�(G1,G2,G1&G2)鐨勯潪绌烘Ы浣�
CSlot* getNonEmptySlot(MaterialsType type);
- // 获取一个指定物料类型(G1,G2,G1&G2)的且已经加工处理的槽位
+ // 鑾峰彇涓�涓寚瀹氱墿鏂欑被鍨�(G1,G2,G1&G2)鐨勪笖宸茬粡鍔犲伐澶勭悊鐨勬Ы浣�
CSlot* getProcessedSlot(MaterialsType putSlotType, BOOL bJobMode = FALSE);
CSlot* getProcessedSlot2(MaterialsType putSlotType, const std::vector<int>& candidates);
CSlot* getInspFailSlot();
CSlot* getProcessedSlotCt(unsigned int slot);
- // 获取玻璃物料
+ // 鑾峰彇鐜荤拑鐗╂枡
CGlass* getGlassFromSlot(int slotNo);
CGlass* getGlassWithCassette(int cassetteSequenceNo, int jobSequenceNo);
CGlass* getAnyGlass();
@@ -211,26 +219,27 @@
int getAllGlass(std::vector<CGlass*>& glasses);
CJobDataS* getJobDataSWithCassette(int cassetteSequenceNo, int jobSequenceNo);
- // 验证玻璃和槽是否匹配
+ // 楠岃瘉鐜荤拑鍜屾Ы鏄惁鍖归厤
BOOL ValidateGlassSlotMatch();
- // 是否有玻璃
+ // 鏄惁鏈夌幓鐠�
BOOL hasGlass();
BOOL slotHasGlass(int slotIndex = 0);
- // 指定槽位是否可以放置玻璃
+ // 鎸囧畾妲戒綅鏄惁鍙互鏀剧疆鐜荤拑
BOOL canPlaceGlassInSlot(const short slotIndex);
- // 手动移除物料
+ // 鎵嬪姩绉婚櫎鐗╂枡
int removeGlass(int slotNo);
- // 字符串检测结果转换
+ // 瀛楃涓叉娴嬬粨鏋滆浆鎹�
InspResult judgeStringToInspResult(std::string& strJudge);
+ // for test
+ void fireSetProcessState(int nSlotNo, PROCESS_STATE state) { return setProcessState(nSlotNo, state); }
-
- // 以下为从CC-Link读取到的Bit标志位检测函数
+ // 浠ヤ笅涓轰粠CC-Link璇诲彇鍒扮殑Bit鏍囧織浣嶆娴嬪嚱鏁�
public:
BOOL isAlive();
BOOL isCimOn();
@@ -242,7 +251,7 @@
BOOL isLinkSignalUpstreamOn(unsigned int path, unsigned int signal);
BOOL isLinkSignalDownstreamOn(unsigned int path, unsigned int signal);
- // 只在模拟测试时使用的函数,用于模拟信号
+ // 鍙湪妯℃嫙娴嬭瘯鏃朵娇鐢ㄧ殑鍑芥暟锛岀敤浜庢ā鎷熶俊鍙�
void setLinkSignalUpstream(unsigned int path, unsigned int signal, BOOL bOn);
void setLinkSignalUpstreamBlock(unsigned int path, BOOL* pSignal);
void setLinkSignalDownstream(unsigned int path, unsigned int signal, BOOL bOn);
@@ -271,7 +280,7 @@
float toFloat(const char* pszAddr);
protected:
- // 部分优化/简化代码、暂实现部分,到时平铺开
+ // 閮ㄥ垎浼樺寲/绠�鍖栦唬鐮併�佹殏瀹炵幇閮ㄥ垎锛屽埌鏃跺钩閾哄紑
void addFacDataReportStep(int dataDev, int writeSignalDev, int port);
@@ -281,6 +290,7 @@
int m_nID;
std::string m_strName;
std::string m_strDescription;
+ std::string m_currentRecipe;
CRITICAL_SECTION m_criticalSection;
StationIdentifier m_station;
MemoryBlock m_blockReadBit;
@@ -289,7 +299,7 @@
std::vector<CPin*> m_outputPins;
- // 以下为从CC-Link读取到的Bit标志位
+ // 浠ヤ笅涓轰粠CC-Link璇诲彇鍒扮殑Bit鏍囧織浣�
protected:
ALIVE m_alive;
BOOL m_bCimState; // ON/OFF
diff --git a/SourceCode/Bond/Servo/CEventEditDlg.cpp b/SourceCode/Bond/Servo/CEventEditDlg.cpp
new file mode 100644
index 0000000..1148a59
--- /dev/null
+++ b/SourceCode/Bond/Servo/CEventEditDlg.cpp
@@ -0,0 +1,94 @@
+#include "stdafx.h"
+#include "CEventEditDlg.h"
+#include "Servo.h"
+#include "resource.h"
+#include <algorithm>
+
+IMPLEMENT_DYNAMIC(CEventEditDlg, CDialogEx)
+
+CEventEditDlg::CEventEditDlg(const CString& title, int eventId, const CString& name, const CString& desc, const std::vector<unsigned int>& rptIds, CWnd* pParent)
+ : CDialogEx(IDD_DIALOG_EVENT_EDIT, pParent)
+ , m_strTitle(title)
+ , m_eventId(eventId)
+ , m_strName(name)
+ , m_strDesc(desc)
+ , m_rptIds(rptIds)
+{
+}
+
+CEventEditDlg::~CEventEditDlg()
+{
+}
+
+void CEventEditDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_EDIT_EVT_ID, m_editId);
+ DDX_Control(pDX, IDC_EDIT_EVT_NAME, m_editName);
+ DDX_Control(pDX, IDC_EDIT_EVT_DESC, m_editDesc);
+ DDX_Control(pDX, IDC_LIST_EVT_RPTS, m_listRpt);
+}
+
+BEGIN_MESSAGE_MAP(CEventEditDlg, CDialogEx)
+END_MESSAGE_MAP()
+
+BOOL CEventEditDlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+ SetWindowText(m_strTitle);
+
+ CString strId;
+ strId.Format(_T("%d"), m_eventId);
+ m_editId.SetWindowText(strId);
+ m_editId.SetReadOnly(TRUE);
+ m_editName.SetWindowText(m_strName);
+ m_editDesc.SetWindowText(m_strDesc);
+
+ m_listRpt.SetExtendedStyle(m_listRpt.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_CHECKBOXES);
+ m_listRpt.InsertColumn(0, _T("RPT ID"), LVCFMT_LEFT, 120);
+ m_listRpt.InsertColumn(1, _T("VIDs"), LVCFMT_LEFT, 240);
+
+ auto& reports = theApp.m_model.m_hsmsPassive.getReports();
+ for (int i = 0; i < (int)reports.size(); ++i) {
+ auto rpt = reports[i];
+ if (rpt == nullptr) continue;
+ int idx = m_listRpt.InsertItem(m_listRpt.GetItemCount(), std::to_string(rpt->getReportId()).c_str());
+ m_listRpt.SetItemText(idx, 1, rpt->getVariablesIdsText().c_str());
+ m_listRpt.SetItemData(idx, (DWORD_PTR)rpt->getReportId());
+ if (std::find(m_rptIds.begin(), m_rptIds.end(), rpt->getReportId()) != m_rptIds.end()) {
+ m_listRpt.SetCheck(idx, TRUE);
+ }
+ }
+
+ return TRUE;
+}
+
+void CEventEditDlg::OnOK()
+{
+ CString name, desc;
+ m_editName.GetWindowText(name);
+ m_editDesc.GetWindowText(desc);
+ name.Trim();
+ desc.Trim();
+ if (name.IsEmpty()) {
+ AfxMessageBox(_T("鍚嶇О涓嶈兘涓虹┖"));
+ return;
+ }
+
+ std::vector<unsigned int> selected;
+ int count = m_listRpt.GetItemCount();
+ for (int i = 0; i < count; ++i) {
+ if (m_listRpt.GetCheck(i)) {
+ selected.push_back((unsigned int)m_listRpt.GetItemData(i));
+ }
+ }
+ if (selected.empty()) {
+ AfxMessageBox(_T("鑷冲皯閫夋嫨涓�涓猂eport"));
+ return;
+ }
+
+ m_strName = name;
+ m_strDesc = desc;
+ m_rptIds.swap(selected);
+ CDialogEx::OnOK();
+}
diff --git a/SourceCode/Bond/Servo/CEventEditDlg.h b/SourceCode/Bond/Servo/CEventEditDlg.h
new file mode 100644
index 0000000..a2a1b96
--- /dev/null
+++ b/SourceCode/Bond/Servo/CEventEditDlg.h
@@ -0,0 +1,37 @@
+锘�#pragma once
+#include "afxdialogex.h"
+#include <vector>
+
+// 浜嬩欢缂栬緫瀵硅瘽妗嗭紙鏂板/缂栬緫鍏辩敤锛屽嬀閫塕eport锛�
+class CEventEditDlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CEventEditDlg)
+
+public:
+ CEventEditDlg(const CString& title, int eventId, const CString& name, const CString& desc, const std::vector<unsigned int>& rptIds, CWnd* pParent = nullptr);
+ virtual ~CEventEditDlg();
+
+ int GetEventId() const { return m_eventId; }
+ CString GetNameText() const { return m_strName; }
+ CString GetDescText() const { return m_strDesc; }
+ const std::vector<unsigned int>& GetSelectedRptIds() const { return m_rptIds; }
+
+protected:
+ virtual BOOL OnInitDialog() override;
+ virtual void DoDataExchange(CDataExchange* pDX) override;
+ afx_msg void OnOK();
+
+ DECLARE_MESSAGE_MAP()
+
+private:
+ CString m_strTitle;
+ int m_eventId;
+ CString m_strName;
+ CString m_strDesc;
+ std::vector<unsigned int> m_rptIds;
+
+ CEdit m_editId;
+ CEdit m_editName;
+ CEdit m_editDesc;
+ CListCtrl m_listRpt;
+};
diff --git a/SourceCode/Bond/Servo/CGlass.cpp b/SourceCode/Bond/Servo/CGlass.cpp
index ae2bca0..9220291 100644
--- a/SourceCode/Bond/Servo/CGlass.cpp
+++ b/SourceCode/Bond/Servo/CGlass.cpp
@@ -1,4 +1,4 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "CGlass.h"
#include "Log.h"
@@ -384,7 +384,7 @@
if (s.size() > maxLen) s.resize(maxLen);
}
- // —— 时间戳 & 工具 ——
+ // 鐘舵�佹椂闂存埑锛氭帓闃�/寮�濮�/缁撴潫
void CGlass::markQueued()
{
m_state = GlsState::Queued;
@@ -432,29 +432,44 @@
return strOut;
}
- // ========== SV数据管理接口实现 ==========
-
+ // ========== SV鏁版嵁鍙h ==========
+ static constexpr size_t MAX_SV_DATA_KEEP = 4800;
void CGlass::addSVData(int machineId, const std::string& dataType, const SVDataItem& dataItem) {
- m_svDatas[machineId][dataType].push_back(dataItem);
+ auto& vec = m_svDatas[machineId][dataType];
+ vec.push_back(dataItem);
+ if (vec.size() > MAX_SV_DATA_KEEP) {
+ vec.erase(vec.begin(), vec.begin() + (vec.size() - MAX_SV_DATA_KEEP));
+ }
}
void CGlass::addSVData(int machineId, const std::string& dataType, double value) {
auto now = std::chrono::system_clock::now();
- m_svDatas[machineId][dataType].emplace_back(now, value);
+ auto& vec = m_svDatas[machineId][dataType];
+ vec.emplace_back(now, value);
+ if (vec.size() > MAX_SV_DATA_KEEP) {
+ vec.erase(vec.begin(), vec.begin() + (vec.size() - MAX_SV_DATA_KEEP));
+ }
}
void CGlass::addSVData(int machineId, const std::string& dataType, int64_t timestamp, double value) {
- // 将int64_t时间戳转换为system_clock::time_point
+ // int64_t鏃堕棿杞垚system_clock::time_point
std::chrono::system_clock::time_point timePoint{
- std::chrono::milliseconds(timestamp) // 假设timestamp是毫秒
- // 如果是秒,使用:std::chrono::seconds(timestamp)
+ std::chrono::milliseconds(timestamp) // timestamp绮惧害锛氭绉�
+ // 濡傛灉闇�瑕佺簿搴︽洿楂橈紝鍙兘瑕佷娇鐢ㄥ叾浠栨椂闂村崟浣嶏紝濡俿td::chrono::seconds(timestamp)
};
- m_svDatas[machineId][dataType].emplace_back(timePoint, value);
+ auto& vec = m_svDatas[machineId][dataType];
+ vec.emplace_back(timePoint, value);
+ if (vec.size() > MAX_SV_DATA_KEEP) {
+ vec.erase(vec.begin(), vec.begin() + (vec.size() - MAX_SV_DATA_KEEP));
+ }
}
void CGlass::addSVData(int machineId, const std::string& dataType, const std::vector<SVDataItem>& dataItems) {
auto& dataList = m_svDatas[machineId][dataType];
dataList.insert(dataList.end(), dataItems.begin(), dataItems.end());
+ if (dataList.size() > MAX_SV_DATA_KEEP) {
+ dataList.erase(dataList.begin(), dataList.begin() + (dataList.size() - MAX_SV_DATA_KEEP));
+ }
}
std::vector<SVDataItem> CGlass::getSVData(int machineId, const std::string& dataType) const {
@@ -515,7 +530,6 @@
auto machineIt = m_svDatas.find(machineId);
if (machineIt != m_svDatas.end()) {
machineIt->second.erase(dataType);
- // 如果该机器没有其他数据了,也清除机器条目
if (machineIt->second.empty()) {
m_svDatas.erase(machineIt);
}
diff --git a/SourceCode/Bond/Servo/CHMPropertyDlg.cpp b/SourceCode/Bond/Servo/CHMPropertyDlg.cpp
index 2542fca..59cce8a 100644
--- a/SourceCode/Bond/Servo/CHMPropertyDlg.cpp
+++ b/SourceCode/Bond/Servo/CHMPropertyDlg.cpp
@@ -5,7 +5,7 @@
#include "Servo.h"
#include "CHMPropertyDlg.h"
#include "afxdialogex.h"
-#include "HmTab.h"
+#include <algorithm>
// CEquipmentDlg 瀵硅瘽妗�
@@ -19,6 +19,7 @@
m_hbrBkgnd = nullptr;
m_nWndWidth = 0;
m_nWndHeight = 0;
+ m_pTab = nullptr;
}
CHMPropertyDlg::CHMPropertyDlg(const char* pszTitle, int width, int height)
@@ -29,6 +30,7 @@
m_nWndWidth = width;
m_nWndHeight = height;
m_strTitle = pszTitle;
+ m_pTab = nullptr;
}
CHMPropertyDlg::~CHMPropertyDlg()
@@ -74,12 +76,13 @@
// Tab
CString strTitle;
- CHmTab* m_pTab = CHmTab::Hook(GetDlgItem(IDC_TAB1)->m_hWnd);
+ m_pTab = CHmTab::Hook(GetDlgItem(IDC_TAB1)->m_hWnd);
m_pTab->SetPaddingLeft(20);
m_pTab->SetItemMarginLeft(18);
for (int i = 0; i < m_pages.size(); i++) {
m_pages[i]->SetParent(this);
m_pages[i]->GetWindowText(strTitle);
+ m_pages[i]->OnCreateBtns();
m_pTab->AddItem(strTitle, i == m_pages.size() - 1);
}
m_pTab->SetCurSel(0);
@@ -125,7 +128,7 @@
void CHMPropertyDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
- if (GetDlgItem(IDC_TAB1) == nullptr) return;
+ if (m_pTab == nullptr) return;
Resize();
}
@@ -157,8 +160,36 @@
pItem->GetWindowRect(&rcItem);
pItem->MoveWindow(x2 - rcItem.Width(), y2 - rcItem.Height(),
rcItem.Width(), rcItem.Height());
- y2 -= rcItem.Height();
- y2 -= 12;
+
+ // 褰撳墠瀛愰〉鎸夐挳锛堝鏋滄湁锛�
+ int btnY = y2 - rcItem.Height();
+ int btnX = 12;
+ y2 -= rcItem.Height() + 12;
+ int curIndex = (m_pTab != nullptr) ? m_pTab->GetCurSel() : 0;
+ if (curIndex >= 0 && curIndex < (int)m_pages.size()) {
+ auto& btnMap = m_pages[curIndex]->getBtns();
+ // 鎸� BTN_ORDER 鎺掑簭
+ std::vector<std::pair<int, CButton*>> ordered;
+ for (auto& kv : btnMap) {
+ CButton* btn = kv.second;
+ if (btn == nullptr || !::IsWindow(btn->GetSafeHwnd())) continue;
+ int order = (int)(INT_PTR)::GetProp(btn->GetSafeHwnd(), _T("BTN_ORDER"));
+ ordered.emplace_back(order, btn);
+ }
+ std::sort(ordered.begin(), ordered.end(), [](const auto& a, const auto& b) {
+ return a.first < b.first;
+ });
+ for (auto& item : ordered) {
+ CButton* btn = item.second;
+ CRect rcBtn;
+ btn->GetWindowRect(&rcBtn);
+ if (rcBtn.Width() <= 0 || rcBtn.Height() <= 0) {
+ rcBtn.SetRect(0, 0, 80, 28);
+ }
+ btn->MoveWindow(btnX, btnY, rcBtn.Width(), rcBtn.Height());
+ btnX += rcBtn.Width() + 8;
+ }
+ }
// 鍒嗛殧绾�
pItem = GetDlgItem(IDC_LINE1);
@@ -185,6 +216,50 @@
for (int i = 0; i < m_pages.size(); i++) {
m_pages[i]->ShowWindow(i == index ? SW_SHOW : SW_HIDE);
}
+
+ // 闅愯棌鎵�鏈夐〉闈㈢殑鎸夐挳
+ for (auto page : m_pages) {
+ for (auto& kv : page->getBtns()) {
+ CButton* btn = kv.second;
+ if (btn != nullptr && ::IsWindow(btn->GetSafeHwnd())) {
+ btn->ShowWindow(SW_HIDE);
+ }
+ }
+ }
+
+ // 鍒涘缓骞舵樉绀哄綋鍓嶉〉闈㈢殑鎸夐挳
+ auto& btns = m_pages[index]->getBtns();
+ if (!btns.empty()) {
+ CRect rcClient;
+ GetClientRect(&rcClient);
+ const int margin = 12;
+ const int spacing = 8;
+ int x = margin;
+ int y = rcClient.bottom - 40; // 棰勭暀搴曢儴鍖哄煙
+
+ // 鎸� BTN_ORDER 鎺掑簭
+ std::vector<std::pair<int, CButton*>> ordered;
+ for (auto& kv : btns) {
+ CButton* btn = kv.second;
+ if (btn == nullptr || !::IsWindow(btn->GetSafeHwnd())) continue;
+ int order = (int)(INT_PTR)::GetProp(btn->GetSafeHwnd(), _T("BTN_ORDER"));
+ ordered.emplace_back(order, btn);
+ }
+ std::sort(ordered.begin(), ordered.end(), [](const auto& a, const auto& b) {
+ return a.first < b.first;
+ });
+ for (auto& item : ordered) {
+ CButton* btn = item.second;
+ CRect rc;
+ btn->GetWindowRect(&rc);
+ if (rc.Width() <= 0 || rc.Height() <= 0) {
+ rc.SetRect(0, 0, 80, 28);
+ }
+ btn->MoveWindow(x, y, rc.Width(), rc.Height());
+ btn->ShowWindow(SW_SHOW);
+ x += rc.Width() + spacing;
+ }
+ }
}
void CHMPropertyDlg::SetWindowSize(int width, int height)
@@ -210,3 +285,22 @@
}
}
}
+
+BOOL CHMPropertyDlg::OnCommand(WPARAM wParam, LPARAM lParam)
+{
+ UINT code = HIWORD(wParam);
+ HWND hCtrl = (HWND)lParam;
+
+ if (code == BN_CLICKED && hCtrl != nullptr) {
+ for (auto page : m_pages) {
+ for (auto& kv : page->getBtns()) {
+ if (kv.second != nullptr && kv.second->GetSafeHwnd() == hCtrl) {
+ page->HandleBtnClick(hCtrl);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return CDialogEx::OnCommand(wParam, lParam);
+}
diff --git a/SourceCode/Bond/Servo/CHMPropertyDlg.h b/SourceCode/Bond/Servo/CHMPropertyDlg.h
index 9ff65b1..43b8f25 100644
--- a/SourceCode/Bond/Servo/CHMPropertyDlg.h
+++ b/SourceCode/Bond/Servo/CHMPropertyDlg.h
@@ -1,6 +1,7 @@
锘�#pragma once
#include <vector>
#include "CHMPropertyPage.h"
+#include "HmTab.h"
// CEquipmentDlg 瀵硅瘽妗�
@@ -30,6 +31,7 @@
std::vector<CHMPropertyPage*> m_pages;
int m_nWndWidth;
int m_nWndHeight;
+ CHmTab* m_pTab;
// 瀵硅瘽妗嗘暟鎹�
@@ -49,4 +51,5 @@
afx_msg void OnTabSelChanged(NMHDR* nmhdr, LRESULT* result);
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedButtonApply();
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
};
diff --git a/SourceCode/Bond/Servo/CHMPropertyPage.cpp b/SourceCode/Bond/Servo/CHMPropertyPage.cpp
index 3591ec4..f5626b6 100644
--- a/SourceCode/Bond/Servo/CHMPropertyPage.cpp
+++ b/SourceCode/Bond/Servo/CHMPropertyPage.cpp
@@ -18,3 +18,75 @@
{
}
+
+void CHMPropertyPage::OnCreateBtns()
+{
+
+}
+
+CButton* CHMPropertyPage::CreateBtn(const char* name, int w, int h, const UINT id)
+{
+ std::string key = std::string(name);
+ auto it = m_btns.find(key);
+ if (it != m_btns.end()) {
+ return it->second;
+ }
+
+ CButton* pBtn = new CButton();
+ pBtn->Create(name, WS_CHILD, CRect(0, 0, w, h), GetParent(), id);
+ // 浣跨敤榛樿GUI瀛椾綋
+ HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
+ if (hFont != nullptr) {
+ pBtn->SetFont(CFont::FromHandle(hFont), FALSE);
+ }
+ ::SetProp(pBtn->GetSafeHwnd(), _T("BTN_ORDER"), (HANDLE)(INT_PTR)m_btnOrderSeq++);
+ m_btns[key] = pBtn;
+ return pBtn;
+}
+
+CButton* CHMPropertyPage::GetBtnByName(const char* name)
+{
+ auto it = m_btns.find(std::string(name));
+ if (it != m_btns.end()) {
+ return it->second;
+ }
+ return nullptr;
+}
+
+std::map<std::string, CButton*>& CHMPropertyPage::getBtns()
+{
+ return m_btns;
+}
+
+void CHMPropertyPage::HandleBtnClick(HWND hBtn)
+{
+ for (auto& kv : m_btns) {
+ if (kv.second != nullptr && kv.second->GetSafeHwnd() == hBtn) {
+ OnClickedBtn(kv.first.c_str());
+ break;
+ }
+ }
+}
+
+BEGIN_MESSAGE_MAP(CHMPropertyPage, CDialogEx)
+ ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+void CHMPropertyPage::OnDestroy()
+{
+ CDialogEx::OnDestroy();
+
+ for (auto& kv : m_btns) {
+ CButton* btn = kv.second;
+ if (btn != nullptr) {
+ if (::IsWindow(btn->GetSafeHwnd())) {
+ ::RemoveProp(btn->GetSafeHwnd(), _T("BTN_ORDER"));
+ }
+ if (::IsWindow(btn->GetSafeHwnd())) {
+ btn->DestroyWindow();
+ }
+ delete btn;
+ }
+ }
+ m_btns.clear();
+}
diff --git a/SourceCode/Bond/Servo/CHMPropertyPage.h b/SourceCode/Bond/Servo/CHMPropertyPage.h
index 8f90c5b..33e53b4 100644
--- a/SourceCode/Bond/Servo/CHMPropertyPage.h
+++ b/SourceCode/Bond/Servo/CHMPropertyPage.h
@@ -1,14 +1,29 @@
-#pragma once
+锘�#pragma once
+#include <map>
+#include <string>
class CHMPropertyPage : public CDialogEx
{
DECLARE_DYNAMIC(CHMPropertyPage)
public:
- CHMPropertyPage(UINT nID, CWnd* pPage); // 标准构造函数
- virtual ~CHMPropertyPage(); // 析构函数
+ CHMPropertyPage(UINT nID, CWnd* pPage); // 鏍囧噯鏋勯�犲嚱鏁�
+ virtual ~CHMPropertyPage(); // 鏋愭瀯鍑芥暟
virtual void OnApply();
+ virtual void OnCreateBtns();
+ afx_msg void OnDestroy();
+ std::map<std::string, CButton*>& getBtns();
+ CButton* GetBtnByName(const char* name);
+ void HandleBtnClick(HWND hBtn);
+protected:
+ // 瀛愮被鍙噸鍐欙細鏂板/鍒犻櫎/缂栬緫鎸夐挳鐐瑰嚮澶勭悊
+ virtual void OnClickedBtn(const char* btnName) {};
+protected:
+ CButton* CreateBtn(const char* name, int w, int h, const UINT id);
+ std::map<std::string, CButton*> m_btns;
+ int m_btnOrderSeq{ 0 };
+
+ DECLARE_MESSAGE_MAP()
};
-
diff --git a/SourceCode/Bond/Servo/CLoadPort.cpp b/SourceCode/Bond/Servo/CLoadPort.cpp
index ebd1690..4b86cf7 100644
--- a/SourceCode/Bond/Servo/CLoadPort.cpp
+++ b/SourceCode/Bond/Servo/CLoadPort.cpp
@@ -1,4 +1,4 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "CLoadPort.h"
#include "CGlassPool.h"
#include "Servo.h"
@@ -17,6 +17,7 @@
m_bAutoChangeEnable = FALSE;
m_nNextCassetteSequenceNo = 0;
m_isCompareMapsBeforeProceeding = FALSE;
+ m_downloadCassetteMap = 0;
}
CLoadPort::~CLoadPort()
@@ -40,16 +41,16 @@
CEquipment::term();
}
- // 必须要实现的虚函数,在此初始化Pin列表
+ // 蹇呴』瑕佸疄鐜扮殑铏氬嚱鏁帮紝鍦ㄦ鍒濆鍖朠in鍒楄〃
void CLoadPort::initPins()
{
- // 加入Pin初始化代码
+ // 鍔犲叆Pin鍒濆鍖栦唬鐮�
LOGD("<CLoadPort>initPins");
addPin(SERVO::PinType::INPUT, _T("In"));
addPin(SERVO::PinType::OUTPUT, _T("Out"));
}
- // 必须要实现的虚函数,在此初始化Slot信息
+ // 蹇呴』瑕佸疄鐜扮殑铏氬嚱鏁帮紝鍦ㄦ鍒濆鍖朣lot淇℃伅
void CLoadPort::initSlots()
{
for (int i = 0; i < SLOT_MAX; i++) {
@@ -351,7 +352,7 @@
CEquipment::onTimer(nTimerid);
- // 从配置读出的enable,初始化时写给efem
+ // 浠庨厤缃鍑虹殑enable锛屽垵濮嬪寲鏃跺啓缁檈fem
static int i_enable[4] = { 0 };
if ((++i_enable[m_nIndex]) == 20 + m_nIndex) {
eablePort(m_bEnable, [&](int code) -> int {
@@ -362,7 +363,7 @@
- // 模拟测试
+ // 妯℃嫙娴嬭瘯
/*
if (m_nIndex == 0) {
static int ii = 0;
@@ -376,6 +377,7 @@
portStatusReport.setCassetteId("CID1001");
int nRet = portStatusReport.serialize(szBuffer, 64);
decodePortStatusReport(pStep, szBuffer, 64);
+ LOGI("<CLoadPort>Port1杞藉叆妯℃嫙鏁版嵁锛� id:CID1001 map: 0xf");
}
}
if (m_nIndex == 1) {
@@ -390,6 +392,7 @@
portStatusReport.setCassetteId("CID1004");
int nRet = portStatusReport.serialize(szBuffer, 64);
decodePortStatusReport(pStep, szBuffer, 64);
+ LOGI("<CLoadPort>Port2杞藉叆妯℃嫙鏁版嵁锛� id:CID1004 map: 0xff");
}
}
*/
@@ -558,6 +561,11 @@
std::string& CLoadPort::getCassetteId()
{
return m_portStatusReport.getCassetteId();
+ }
+
+ void CLoadPort::simulateSetCassetteId(const char* pszCarrierId)
+ {
+ m_portStatusReport.setCassetteId(pszCarrierId);
}
int CLoadPort::getLoadingCassetteType()
@@ -934,27 +942,15 @@
m_portStatusReport.copyEx(portStatusReport);
- // 当port状态为InUse, 比较map
+ // 褰損ort鐘舵�佷负InUse, 姣旇緝map
if (m_portStatusReport.getPortStatus() == PORT_INUSE) {
- if (m_isCompareMapsBeforeProceeding) {
- short scanMap = getScanCassetteMap();
- short downloadMap = getDownloadCassetteMap();
- if (scanMap == downloadMap) {
- generateGlassList(scanMap);
- this->sendCassetteCtrlCmd(CCC_PROCESS_START, nullptr, 0, 0, 0, nullptr, nullptr);
- }
- else {
- this->sendCassetteCtrlCmd(CCC_PROCESS_CANCEL, nullptr, 0, 0, 0, nullptr, nullptr);
+ // 鐢熸垚鐜荤拑鍒楄〃锛氭潵鑷� EFEM 鎵弿鍒扮殑 map
+ generateGlassList(getScanCassetteMap());
- // 抛出到应用层做提示
- if (m_listener.onMapMismatch != nullptr) {
- m_listener.onMapMismatch(this, scanMap, downloadMap);
- }
- }
- }
- else {
- // 抛出到应用层做选择要加工的片子
- generateGlassList(getScanCassetteMap());
+ // CompareMapsBeforeProceeding锛氫笉鍦ㄦ澶勮嚜鍔� Start/Cancel锛屾敼涓虹瓑寰� Host 鍐崇瓥锛圥roceedWithCarrier/ProceedWithSlotMap/CarrierRelease锛�
+ // Host 鍐崇瓥鍏ュ彛锛歋3F17 CarrierAction -> listener.onCarrierAction -> CMaster::proceedWithCarrier()/carrierRelease()
+ if (m_isCompareMapsBeforeProceeding) {
+ // 杩欓噷浠呯瓑寰咃紝鍏蜂綋涓婃姤鐢变笂灞傚湪 PORT_INUSE 浜嬩欢涓Е鍙戯紙S6F11 CheckSlotMap锛�
}
}
if (m_listener.onPortStatusChanged != nullptr) {
@@ -963,7 +959,7 @@
}
- // 缓存Attribute,用于调试时显示信息
+ // 缂撳瓨Attribute锛岀敤浜庤皟璇曟椂鏄剧ず淇℃伅
unsigned int weight = 201;
CAttributeVector& attrubutes = pStep->attributeVector();
m_portStatusReport.getAttributeVector(attrubutes, weight);
@@ -988,17 +984,17 @@
return -1;
}
- LOGI("<CLoadPort-%d>准备设置Port type<%d>", m_nIndex, (int)type);
+ LOGI("<CLoadPort-%d>鍑嗗璁剧疆Port type<%d>", m_nIndex, (int)type);
short value = (short)type;
pStep->writeDataEx((const char*)&value, sizeof(short), [&, onWritedBlock](int code) -> int {
// test
code = WOK;
if (code == WOK) {
m_portType = type;
- LOGI("<CLoadPort-%d>设置Port type成功.", m_nIndex);
+ LOGI("<CLoadPort-%d>璁剧疆Port type鎴愬姛.", m_nIndex);
}
else {
- LOGE("<CLoadPort-%d>设置Port type失败,code:%d", m_nIndex, code);
+ LOGE("<CLoadPort-%d>璁剧疆Port type澶辫触锛宑ode:%d", m_nIndex, code);
}
if (onWritedBlock != nullptr) {
return onWritedBlock(code);
@@ -1018,17 +1014,17 @@
return -1;
}
- LOGI("<CLoadPort-%d>准备%s Port", m_nIndex, bEnable ? _T("启用") : _T("禁用"));
+ LOGI("<CLoadPort-%d>鍑嗗%s Port", m_nIndex, bEnable ? _T("鍚敤") : _T("绂佺敤"));
short value = bEnable ? 1 : 2;
pStep->writeDataEx((const char*)&value, sizeof(short), [&, onWritedBlock](int code) -> int {
// test
code = WOK;
if (code == WOK) {
m_bEnable = bEnable;
- LOGI("<CLoadPort-%d>%s Port成功.", m_nIndex, bEnable ? _T("启用") : _T("禁用"));
+ LOGI("<CLoadPort-%d>%s Port鎴愬姛.", m_nIndex, bEnable ? _T("鍚敤") : _T("绂佺敤"));
}
else {
- LOGE("<CLoadPort-%d>%s Port失败,code:%d", m_nIndex, bEnable ? _T("启用") : _T("禁用"), code);
+ LOGE("<CLoadPort-%d>%s Port澶辫触锛宑ode:%d", m_nIndex, bEnable ? _T("鍚敤") : _T("绂佺敤"), code);
}
if (onWritedBlock != nullptr) {
return onWritedBlock(code);
@@ -1047,17 +1043,17 @@
return -1;
}
- LOGI("<CLoadPort-%d>准备设置Port mode<%d>", m_nIndex, (int)mode);
+ LOGI("<CLoadPort-%d>鍑嗗璁剧疆Port mode<%d>", m_nIndex, (int)mode);
short value = (short)mode;
pStep->writeDataEx((const char*)&value, sizeof(short), [&, onWritedBlock](int code) -> int {
// test
code = WOK;
if (code == WOK) {
m_portMode = mode;
- LOGI("<CLoadPort-%d>设置Port mode成功.", m_nIndex);
+ LOGI("<CLoadPort-%d>璁剧疆Port mode鎴愬姛.", m_nIndex);
}
else {
- LOGE("<CLoadPort-%d>设置Port mode失败,code:%d", m_nIndex, code);
+ LOGE("<CLoadPort-%d>璁剧疆Port mode澶辫触锛宑ode:%d", m_nIndex, code);
}
if (onWritedBlock != nullptr) {
return onWritedBlock(code);
@@ -1077,16 +1073,16 @@
return -1;
}
- LOGI("<CLoadPort-%d>准备设置Cassette Type<%d>", m_nIndex, (int)type);
+ LOGI("<CLoadPort-%d>鍑嗗璁剧疆Cassette Type<%d>", m_nIndex, (int)type);
short value = (short)type;
pStep->writeDataEx((const char*)&value, sizeof(short), [&, onWritedBlock](int code) -> int {
// test
code = WOK;
if (code == WOK) {
- LOGI("<CLoadPort-%d>设置Cassette Type成功.", m_nIndex);
+ LOGI("<CLoadPort-%d>璁剧疆Cassette Type鎴愬姛.", m_nIndex);
}
else {
- LOGE("<CLoadPort-%d>设置Cassette Type失败,code:%d", m_nIndex, code);
+ LOGE("<CLoadPort-%d>璁剧疆Cassette Type澶辫触锛宑ode:%d", m_nIndex, code);
}
if (onWritedBlock != nullptr) {
return onWritedBlock(code);
@@ -1105,17 +1101,17 @@
return -1;
}
- LOGI("<CLoadPort-%d>准备设置Transfer mode<%d>", m_nIndex, (int)mode);
+ LOGI("<CLoadPort-%d>鍑嗗璁剧疆Transfer mode<%d>", m_nIndex, (int)mode);
short value = (short)mode;
pStep->writeDataEx((const char*)&value, sizeof(short), [&, onWritedBlock](int code) -> int {
// test
code = WOK;
if (code == WOK) {
m_transferMode = mode;
- LOGI("<CLoadPort-%d>设置Transfer mode成功.", m_nIndex + 1);
+ LOGI("<CLoadPort-%d>璁剧疆Transfer mode鎴愬姛.", m_nIndex + 1);
}
else {
- LOGE("<CLoadPort-%d>设置Transfer mode失败,code:%d", m_nIndex + 1, code);
+ LOGE("<CLoadPort-%d>璁剧疆Transfer mode澶辫触锛宑ode:%d", m_nIndex + 1, code);
}
if (onWritedBlock != nullptr) {
return onWritedBlock(code);
@@ -1134,17 +1130,17 @@
return -1;
}
- LOGI("<CLoadPort-%d>准备%s Auto Change", m_nIndex, bEnable ? _T("启用") : _T("禁用"));
+ LOGI("<CLoadPort-%d>鍑嗗%s Auto Change", m_nIndex, bEnable ? _T("鍚敤") : _T("绂佺敤"));
short value = bEnable ? 1 : 2;
pStep->writeDataEx((const char*)&value, sizeof(short), [&, onWritedBlock](int code) -> int {
// test
code = WOK;
if (code == WOK) {
m_bAutoChangeEnable = bEnable;
- LOGI("<CLoadPort-%d>%s Auto Change成功.", m_nIndex, bEnable ? _T("启用") : _T("禁用"));
+ LOGI("<CLoadPort-%d>%s Auto Change鎴愬姛.", m_nIndex, bEnable ? _T("鍚敤") : _T("绂佺敤"));
}
else {
- LOGE("<CLoadPort-%d>%s Auto Change失败,code:%d", m_nIndex, bEnable ? _T("启用") : _T("禁用"), code);
+ LOGE("<CLoadPort-%d>%s Auto Change澶辫触锛宑ode:%d", m_nIndex, bEnable ? _T("鍚敤") : _T("绂佺敤"), code);
}
if (onWritedBlock != nullptr) {
return onWritedBlock(code);
@@ -1192,17 +1188,20 @@
short CLoadPort::getDownloadCassetteMap()
{
- // 暂时未实现此功能
- short map = 0;
- return map;
+ return m_downloadCassetteMap;
+ }
+
+ void CLoadPort::setDownloadCassetteMap(short map)
+ {
+ m_downloadCassetteMap = map;
}
/*
- * 生成测试用的玻璃列表
+ * 鐢熸垚娴嬭瘯鐢ㄧ殑鐜荤拑鍒楄〃
*/
int CLoadPort::testGenerateGlassList(MaterialsType type)
{
- // 如果非空就不生成了
+ // 濡傛灉闈炵┖灏变笉鐢熸垚浜�
Lock();
if (hasGlass()) {
Unlock();
@@ -1236,11 +1235,11 @@
}
/*
- * 根据efem扫描到的map,生成玻璃列表
+ * 鏍规嵁efem鎵弿鍒扮殑map锛岀敓鎴愮幓鐠冨垪琛�
*/
int CLoadPort::generateGlassList(short map)
{
- // 先释放较早前的数据
+ // 鍏堥噴鏀捐緝鏃╁墠鐨勬暟鎹�
Lock();
for (int i = 0; i < SLOT_MAX; i++) {
m_slot[i].setContext(nullptr);
@@ -1248,7 +1247,7 @@
Unlock();
- // 根据map生成新的
+ // 鏍规嵁map鐢熸垚鏂扮殑
char szBuffer[64];
for (int i = 0; i < SLOT_MAX; i++) {
if (!m_slot[i].isEnable()) continue;
@@ -1320,4 +1319,9 @@
{
m_isCompareMapsBeforeProceeding = bCompare;
}
+
+ BOOL CLoadPort::isCompareMapsBeforeProceeding() const
+ {
+ return m_isCompareMapsBeforeProceeding;
+ }
}
diff --git a/SourceCode/Bond/Servo/CLoadPort.h b/SourceCode/Bond/Servo/CLoadPort.h
index 523ab69..1fb70d7 100644
--- a/SourceCode/Bond/Servo/CLoadPort.h
+++ b/SourceCode/Bond/Servo/CLoadPort.h
@@ -1,4 +1,4 @@
-#pragma once
+锘�#pragma once
#include "CEquipment.h"
#include "ServoCommo.h"
@@ -39,6 +39,7 @@
void localAutoChangeEnable(BOOL bEnable);
short getScanCassetteMap();
short getDownloadCassetteMap();
+ void setDownloadCassetteMap(short map);
public:
short getNextCassetteSequenceNo();
@@ -55,6 +56,8 @@
int getPortStatus();
int getCassetteSequenceNo();
std::string& getCassetteId();
+ // Simulation helper: allow setting CarrierID when no EFEM is connected.
+ void simulateSetCassetteId(const char* pszCarrierId);
int getLoadingCassetteType();
int getQTimeFlag();
int getCassetteMappingState();
@@ -85,6 +88,7 @@
ONWRITED onWritedBlock);
CStep* getCassetteCtrlCmdStep();
void setCompareMapsBeforeProceeding(BOOL bCompare);
+ BOOL isCompareMapsBeforeProceeding() const;
private:
int decodePortStatusReport(CStep* pStep, const char* pszData, size_t size);
@@ -100,8 +104,8 @@
CPortStatusReport m_portStatusReport;
int m_nNextCassetteSequenceNo;
- // 在开始工艺前是否先需要先比较map
+ // 鍦ㄥ紑濮嬪伐鑹哄墠鏄惁鍏堥渶瑕佸厛姣旇緝map
BOOL m_isCompareMapsBeforeProceeding;
+ short m_downloadCassetteMap;
};
}
-
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index d36f540..bd2e9ab 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -1,12 +1,16 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "Common.h"
#include "CMaster.h"
#include <future>
#include <vector>
+#include <algorithm>
#include "RecipeManager.h"
#include <fstream>
#include "SerializeUtil.h"
#include "CServoUtilsTool.h"
+#include "AlarmManager.h"
+#include "ToolUnits.h"
+#include "Model.h"
namespace SERVO {
@@ -58,6 +62,7 @@
m_ullStartTime = 0;
m_ullRunTime = 0;
m_state = MASTERSTATE::READY;
+ m_curveMode = CurveMode::Production;
m_pActiveRobotTask = nullptr;
m_nLastError = ER_CODE_NOERROR;
m_isCompareMapsBeforeProceeding = FALSE;
@@ -71,13 +76,15 @@
m_nContinuousWorkingPort = 0;
m_nContinuousWorkingSlot = 0;
m_pControlJob = nullptr;
+ m_bPauseAlarmRaised = false;
+ m_pModelCtx = nullptr;
m_nTestFlag = 0;
InitializeCriticalSection(&m_criticalSection);
}
CMaster::~CMaster()
{
- // 释放Job相关
+ // 閲婃斁Job鐩稿叧
for (auto item : m_processJobs) {
delete item;
}
@@ -112,12 +119,18 @@
m_hEventDispatchThreadExit[1] = nullptr;
}
+
DeleteCriticalSection(&m_criticalSection);
}
void CMaster::setListener(MasterListener listener)
{
m_listener = listener;
+ }
+
+ void CMaster::setModelCtx(CModel* pModel)
+ {
+ m_pModelCtx = pModel;
}
CRobotTask* CMaster::getActiveRobotTask()
@@ -127,36 +140,43 @@
int CMaster::init()
{
- LOGI("<Master>正在初始化...");
+ const ULONGLONG boot_master_begin = GetTickCount64();
+ LOGI("<Master>姝e湪鍒濆鍖�...");
+ LOGI("[BOOT][MASTER] init begin");
// cclink
- if (m_cclink.Connect(CC_LINK_IE_CONTROL_CHANNEL(1)) != 0) {
- LOGE("连接CC-Link失败.");
+ const ULONGLONG boot_cclink_begin = GetTickCount64();
+ const int cc_ret = m_cclink.Connect(CC_LINK_IE_CONTROL_CHANNEL(1));
+ LOGI("[BOOT][MASTER] CC-Link connect ret=%d, cost=%llu ms",
+ cc_ret,
+ (unsigned long long)(GetTickCount64() - boot_cclink_begin));
+ if (cc_ret != 0) {
+ LOGE("杩炴帴CC-Link澶辫触.");
}
else {
- LOGI("连接CC-Link成功.");
+ LOGI("杩炴帴CC-Link鎴愬姛.");
BoardVersion version{};
int nRet = m_cclink.GetBoardVersion(version);
if (nRet == 0) {
- LOGD("版本信息:%s.", version.toString().c_str());
+ LOGD("鐗堟湰淇℃伅锛�%s.", version.toString().c_str());
}
else {
- LOGE("获取CC-Link版本信息失败.");
+ LOGE("鑾峰彇CC-Link鐗堟湰淇℃伅澶辫触.");
}
BoardStatus status;
nRet = m_cclink.GetBoardStatus(status);
if (nRet == 0) {
- LOGD("状态:%s.", status.toString().c_str());
+ LOGD("鐘舵�侊細%s.", status.toString().c_str());
}
else {
- LOGE("获取CC-Link状态失败.");
+ LOGE("鑾峰彇CC-Link鐘舵�佸け璐�.");
}
}
- // 初始化添加各子设备
+ // 鍒濆鍖栨坊鍔犲悇瀛愯澶�
CLoadPort* pPort1, * pPort2, * pPort3, * pPort4;
CBonder* pBonder1, * pBonder2;
CEFEM* pEfem;
@@ -219,32 +239,77 @@
- // 读缓存数据
+ // 璇荤紦瀛樻暟鎹�
+ const ULONGLONG boot_cache_begin = GetTickCount64();
+ const ULONGLONG boot_read_begin = GetTickCount64();
readCache();
+ LOGI("[BOOT][MASTER] readCache finished, cost=%llu ms", (unsigned long long)(GetTickCount64() - boot_read_begin));
+
+ const ULONGLONG boot_state_begin = GetTickCount64();
loadState();
+ LOGI("[BOOT][MASTER] loadState finished, cost=%llu ms", (unsigned long long)(GetTickCount64() - boot_state_begin));
+ if (m_listener.onControlJobChanged) {
+ notifyControlJobChanged();
+ }
+
+ LOGI("[BOOT][MASTER] cache/state loaded, cost=%llu ms (since init %llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_cache_begin),
+ (unsigned long long)(GetTickCount64() - boot_master_begin));
- // 定时器
+ // 瀹氭椂鍣�
g_pMaster = this;
SetTimer(NULL, 1, 250, (TIMERPROC)MasterTimerProc);
- // 调度线程
+ // 璋冨害绾跨▼
m_hDispatchThreadHandle = (HANDLE)_beginthreadex(NULL, 0, SERVO::DispatchThreadFunction, this,
0, &m_nDispatchThreadAddr);
- // 监控bit线程
+ // 鐩戞帶bit绾跨▼
m_hReadBitsThreadHandle = (HANDLE)_beginthreadex(NULL, 0, SERVO::ReadBitsThreadFunction, this,
0, &m_nReadBitsThreadAddr);
- // 曲线服务
+ // 鏇茬嚎鏈嶅姟
CreateDAQBridgeServer();
- LOGI("<Master>初始化完成.");
+ LOGI("<Master>鍒濆鍖栧畬鎴�.");
+ LOGI("[BOOT][MASTER] init finished, total cost=%llu ms",
+ (unsigned long long)(GetTickCount64() - boot_master_begin));
return 0;
+ }
+
+ void CMaster::setCurveMode(CurveMode mode)
+ {
+ if (m_curveMode == mode) {
+ return;
+ }
+ m_curveMode = mode;
+ if (m_pCollector != nullptr) {
+ const uint32_t mids[] = {
+ MID_Bonder1, MID_Bonder2,
+ MID_VacuumBakeA, MID_VacuumBakeB,
+ MID_BakeCoolingA, MID_BakeCoolingB
+ };
+ for (uint32_t mid : mids) {
+ if (mode == CurveMode::EmptyChamber) {
+ m_pCollector->batchStart(mid, "EMPTY_CHAMBER", 30 * 60 * 1000ULL); // 绌鸿厰妯″紡锛氬惎鍔ㄩ噰鏍锋壒娆�
+ }
+ else {
+ m_pCollector->batchStop(mid);
+ m_pCollector->buffersClear(mid); // 鍒囧洖鐢熶骇妯″紡锛屾竻鎺夌┖鑵旀暟鎹�
+ }
+ }
+ }
+ LOGI("<Master>CurveMode=%s", mode == CurveMode::EmptyChamber ? "EmptyChamber" : "Production");
+ }
+
+ CurveMode CMaster::getCurveMode() const
+ {
+ return m_curveMode;
}
int CMaster::term()
@@ -254,7 +319,7 @@
::WaitForSingleObject(m_hEventReadBitsThreadExit[1], INFINITE);
::WaitForSingleObject(m_hEventDispatchThreadExit[1], INFINITE);
- LOGI("<Master>正在结束程序.");
+ LOGI("<Master>姝e湪缁撴潫绋嬪簭.");
for (auto item : m_listEquipment) {
item->term();
}
@@ -272,6 +337,13 @@
}
m_listEquipment.clear();
+ // release manual-remove buffer before glass pool is torn down
+ for (auto* pGlass : m_bufGlass) {
+ if (pGlass != nullptr) {
+ pGlass->release();
+ }
+ }
+ m_bufGlass.clear();
if (m_pCollector != nullptr) {
m_pCollector->stopLoop();
@@ -326,7 +398,7 @@
int CMaster::stop(int nErCode/* = ER_CODE_NOERROR*/)
{
- // 运行时间为累加结果,本次停止时刷新;
+ // 杩愯鏃堕棿涓虹疮鍔犵粨鏋滐紝鏈鍋滄鏃跺埛鏂帮紱
lock();
if (m_state != MASTERSTATE::RUNNING && m_state != MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER
&& m_state != MASTERSTATE::RUNNING_BATCH) {
@@ -337,12 +409,12 @@
unlock();
- // 更新状态
+ // 鏇存柊鐘舵��
m_nLastError = nErCode;
setState(MASTERSTATE::STOPPING);
- // ControlJob暂停
+ // ControlJob鏆傚仠
lock();
if (m_pControlJob != nullptr) {
m_pControlJob->pause();
@@ -377,14 +449,14 @@
unsigned CMaster::DispatchProc()
{
- // 优先考虑的类型和次要类型
- // 一种情况,如果不分主次,一直搬G1, 等到Bonder1和Bonder2都放了G1, Aligner也放了G1,
- // Bonder1和Bonder2需要的G2就过不来了
- // 最基本的实现,可以G2和G2轮流搬送,但最好根据Bonder的需求来决定
+ // 浼樺厛鑰冭檻鐨勭被鍨嬪拰娆¤绫诲瀷
+ // 涓�绉嶆儏鍐碉紝濡傛灉涓嶅垎涓绘锛屼竴鐩存惉G1, 绛夊埌Bonder1鍜孊onder2閮芥斁浜咷1, Aligner涔熸斁浜咷1,
+ // Bonder1鍜孊onder2闇�瑕佺殑G2灏辫繃涓嶆潵浜�
+ // 鏈�鍩烘湰鐨勫疄鐜帮紝鍙互G2鍜孏2杞祦鎼�侊紝浣嗘渶濂芥牴鎹瓸onder鐨勯渶姹傛潵鍐冲畾
MaterialsType primaryType, secondaryType;
- // 各种机器
+ // 鍚勭鏈哄櫒
CLoadPort* pLoadPorts[4];
CEFEM* pEFEM = (CEFEM*)getEquipment(EQ_ID_EFEM);
pLoadPorts[0] = (CLoadPort*)getEquipment(EQ_ID_LOADPORT1);
@@ -413,7 +485,7 @@
ASSERT(pMeasurement);
while (1) {
- // 待退出信号或时间到
+ // 寰呴��鍑轰俊鍙锋垨鏃堕棿鍒�
HANDLE hEvents[] = { m_hEventDispatchThreadExit[0], m_hDispatchEvent };
int nRet = WaitForMultipleObjects(2, hEvents, FALSE, 500);
if (nRet == WAIT_OBJECT_0) {
@@ -421,11 +493,11 @@
}
- // 如果状态为STARTING,开始工作并切换到RUNNING状态
+ // 濡傛灉鐘舵�佷负STARTING锛屽紑濮嬪伐浣滃苟鍒囨崲鍒癛UNNING鐘舵��
lock();
if (m_state == MASTERSTATE::STARTING) {
- // 发送indexerOperationModeChange到各个机台,成功后切换到RUNNING状态
- // 否则切换到MSERROR状态
+ // 鍙戦�乮ndexerOperationModeChange鍒板悇涓満鍙帮紝鎴愬姛鍚庡垏鎹㈠埌RUNNING鐘舵��
+ // 鍚﹀垯鍒囨崲鍒癕SERROR鐘舵��
int nRet;
CEquipment* pEq[6] = { pEFEM, pBonder1, pBonder2, pBakeCooling,
pVacuumBake, pMeasurement};
@@ -440,9 +512,9 @@
TRACE("a0001\n", writeCode, retCode);
});
if (nRet != 0) {
- LOGE("<Master>EFEM切换Start状态失败");
+ LOGE("<Master>EFEM鍒囨崲Start鐘舵�佸け璐�");
m_nLastError = ER_CODE_OPERATION_MODE_FAIL;
- m_strLastError = "EFEM切换Start状态失败.";
+ m_strLastError = "EFEM鍒囨崲Start鐘舵�佸け璐�.";
goto WAIT;
}
futures.push_back(promises[0].get_future());
@@ -454,9 +526,9 @@
TRACE("a0002\n");
});
if (nRet != 0) {
- LOGE("<Master>Bonder1切换Start状态失败");
+ LOGE("<Master>Bonder1鍒囨崲Start鐘舵�佸け璐�");
m_nLastError = ER_CODE_BONDER_OPERATION_MODE_FAIL;
- m_strLastError = "Bonder1切换Start状态失败.";
+ m_strLastError = "Bonder1鍒囨崲Start鐘舵�佸け璐�.";
goto WAIT;
}
futures.push_back(promises[1].get_future());
@@ -468,9 +540,9 @@
TRACE("a0003\n");
});
if (nRet != 0) {
- LOGE("<Master>Bonder2切换Start状态失败");
+ LOGE("<Master>Bonder2鍒囨崲Start鐘舵�佸け璐�");
m_nLastError = ER_CODE_BONDER_OPERATION_MODE_FAIL;
- m_strLastError = "Bonder2切换Start状态失败.";
+ m_strLastError = "Bonder2鍒囨崲Start鐘舵�佸け璐�.";
goto WAIT;
}
futures.push_back(promises[2].get_future());
@@ -482,9 +554,9 @@
TRACE("a0004\n");
});
if (nRet != 0) {
- LOGE("<Master>BakeCooling切换Start状态失败");
+ LOGE("<Master>BakeCooling鍒囨崲Start鐘舵�佸け璐�");
m_nLastError = ER_CODE_OPERATION_MODE_FAIL;
- m_strLastError = "BakeCooling切换Start状态失败.";
+ m_strLastError = "BakeCooling鍒囨崲Start鐘舵�佸け璐�.";
goto WAIT;
}
futures.push_back(promises[3].get_future());
@@ -496,9 +568,9 @@
TRACE("a0005\n");
});
if (nRet != 0) {
- LOGE("<Master>VacuumBake切换Start状态失败");
+ LOGE("<Master>VacuumBake鍒囨崲Start鐘舵�佸け璐�");
m_nLastError = ER_CODE_OPERATION_MODE_FAIL;
- m_strLastError = "VacuumBake切换Start状态失败.";
+ m_strLastError = "VacuumBake鍒囨崲Start鐘舵�佸け璐�.";
goto WAIT;
}
futures.push_back(promises[4].get_future());
@@ -510,9 +582,9 @@
TRACE("a0006\n");
});
if (nRet != 0) {
- LOGE("<Master>Measurement切换Start状态失败");
+ LOGE("<Master>Measurement鍒囨崲Start鐘舵�佸け璐�");
m_nLastError = ER_CODE_OPERATION_MODE_FAIL;
- m_strLastError = "Measurement切换Start状态失败.";
+ m_strLastError = "Measurement鍒囨崲Start鐘舵�佸け璐�.";
goto WAIT;
}
futures.push_back(promises[5].get_future());
@@ -520,16 +592,16 @@
WAIT:
for (auto& f : futures) {
- f.wait(); // 阻塞等待对应设备完成
+ f.wait(); // 闃诲绛夊緟瀵瑰簲璁惧瀹屾垚
}
for (int i = 0; i < 6; i++) {
if (!bIomcOk[i]) {
bIomcOk[6] = FALSE;
- LOGE("<Master>%s切换Start状态失败", pEq[i]->getName().c_str());
+ LOGE("<Master>%s鍒囨崲Start鐘舵�佸け璐�", pEq[i]->getName().c_str());
}
}
- // 检查看是否都已经切换到START状态
+ // 妫�鏌ョ湅鏄惁閮藉凡缁忓垏鎹㈠埌START鐘舵��
if (!bIomcOk[6]) {
unlock();
setState(MASTERSTATE::MSERROR);
@@ -548,10 +620,10 @@
}
- // 处理完成当前事务后,切换到停止或就绪状态
+ // 澶勭悊瀹屾垚褰撳墠浜嬪姟鍚庯紝鍒囨崲鍒板仠姝㈡垨灏辩华鐘舵��
else if (m_state == MASTERSTATE::STOPPING) {
unlock();
- LOGI("<Master>开始切换各设备到 Stop 模式...");
+ LOGI("<Master>寮�濮嬪垏鎹㈠悇璁惧鍒� Stop 妯″紡...");
std::vector<std::promise<void>> promises(6);
std::vector<std::future<void>> futures;
@@ -569,23 +641,23 @@
TRACE("s000%d: ret=%d\n", i + 1, retCode);
});
if (nRet != 0) {
- LOGE("<Master>%s切换Stop状态发送失败", pEq[i]->getName().c_str());
+ LOGE("<Master>%s鍒囨崲Stop鐘舵�佸彂閫佸け璐�", pEq[i]->getName().c_str());
m_nLastError = ER_CODE_OPERATION_MODE_FAIL;
- m_strLastError = pEq[i]->getName() + "切换Stop状态发送失败.";
+ m_strLastError = pEq[i]->getName() + "鍒囨崲Stop鐘舵�佸彂閫佸け璐�.";
bIomcOk[i] = FALSE;
- promises[i].set_value(); // 避免 wait 阻塞
+ promises[i].set_value(); // 閬垮厤 wait 闃诲
}
futures.push_back(promises[i].get_future());
}
for (auto& f : futures) {
- f.wait(); // 等待所有完成
+ f.wait(); // 绛夊緟鎵�鏈夊畬鎴�
}
for (int i = 0; i < 6; ++i) {
if (!bIomcOk[i]) {
bIomcOk[6] = FALSE;
- LOGE("<Master>%s切换Stop状态失败", pEq[i]->getName().c_str());
+ LOGE("<Master>%s鍒囨崲Stop鐘舵�佸け璐�", pEq[i]->getName().c_str());
}
}
@@ -594,7 +666,7 @@
continue;
}
- LOGI("<Master>所有设备成功切换到 Stop 模式");
+ LOGI("<Master>鎵�鏈夎澶囨垚鍔熷垏鎹㈠埌 Stop 妯″紡");
if(m_nLastError == ER_CODE_NOERROR)
setState(MASTERSTATE::READY);
else
@@ -604,9 +676,9 @@
}
- // 调度逻辑处理
+ // 璋冨害閫昏緫澶勭悊
else if (m_state == MASTERSTATE::RUNNING) {
- // 检测判断robot状态
+ // 妫�娴嬪垽鏂璻obot鐘舵��
RMDATA& rmd = pEFEM->getRobotMonitoringData();
if (rmd.status != ROBOT_STATUS::Idle && rmd.status != ROBOT_STATUS::Run) {
unlock();
@@ -618,13 +690,13 @@
m_pActiveRobotTask->place();
}
unlock();
- // 检测到当前有正在下午的任务,确保当前任务完成或中止后继续
- // LOGI("检测到当前有正在下午的任务,确保当前任务完成或中止后继续...");
+ // 妫�娴嬪埌褰撳墠鏈夋鍦ㄤ笅鍗堢殑浠诲姟锛岀‘淇濆綋鍓嶄换鍔″畬鎴愭垨涓鍚庣户缁�
+ // LOGI("妫�娴嬪埌褰撳墠鏈夋鍦ㄤ笅鍗堢殑浠诲姟锛岀‘淇濆綋鍓嶄换鍔″畬鎴愭垨涓鍚庣户缁�...");
continue;
}
- // Bonder1、Bonder2、Fliper、VacuumBake、Aligner,统计G2和G1的数量, 配对组数, 多出的类型
+ // Bonder1銆丅onder2銆丗liper銆乂acuumBake銆丄ligner锛岀粺璁2鍜孏1鐨勬暟閲�, 閰嶅缁勬暟, 澶氬嚭鐨勭被鍨�
int nG2Count = 0, nG1Count = 0, nGlassGroup, nExtraType;
if (pBonder1->slotHasGlass(0)) {
nG2Count++;
@@ -691,7 +763,7 @@
// Measurement NG -> LoadPort
- // NG回原位
+ // NG鍥炲師浣�
if (!rmd.armState[1]) {
m_pActiveRobotTask = createTransferTask_restore(pMeasurement, pLoadPorts);
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
@@ -705,7 +777,7 @@
}
- // BakeCooling内部
+ // BakeCooling鍐呴儴
// Bake -> Cooling
if (!rmd.armState[0]) {
m_pActiveRobotTask = createTransferTask_bake_to_cooling(pBakeCooling);
@@ -819,9 +891,9 @@
continue;
}
- // 批处理模式,最终以此为准,但先保留之前的单片模式
+ // 鎵瑰鐞嗘ā寮忥紝鏈�缁堜互姝や负鍑嗭紝浣嗗厛淇濈暀涔嬪墠鐨勫崟鐗囨ā寮�
else if (m_state == MASTERSTATE::RUNNING_BATCH) {
- // 1) 控制作业生命周期保障
+ // 1) 鎺у埗浣滀笟鐢熷懡鍛ㄦ湡淇濋殰
if (m_pControlJob == nullptr) { unlock(); continue; }
CJState cjst = m_pControlJob->state();
if (cjst == CJState::Completed || cjst == CJState::Aborted || cjst == CJState::Failed) {
@@ -829,20 +901,20 @@
continue;
}
if (cjst == CJState::NoState) {
- LOGI("<Master>ControlJob已经进入列队");
+ LOGI("<Master>ControlJob宸茬粡杩涘叆鍒楅槦");
m_pControlJob->queue();
}
if (m_pControlJob->state() == CJState::Queued) {
- LOGI("<Master>ControlJob已经启动");
+ LOGI("<Master>ControlJob宸茬粡鍚姩");
m_pControlJob->start();
if (m_listener.onCjStart) m_listener.onCjStart(this, m_pControlJob);
}
if (m_pControlJob->state() == CJState::Paused) {
- LOGI("<Master>ControlJob已经恢复运行");
+ LOGI("<Master>ControlJob宸茬粡鎭㈠杩愯");
m_pControlJob->resume();
}
- // 2) 若当前无 PJ,则选择一个并上报
+ // 2) 鑻ュ綋鍓嶆棤 PJ锛屽垯閫夋嫨涓�涓苟涓婃姤
if (m_inProcesJobs.empty()) {
if (auto pj = acquireNextProcessJob()) {
m_inProcesJobs.push_back(pj);
@@ -850,35 +922,62 @@
}
}
if (m_inProcesJobs.empty()) {
- LOGE("<Master>选择当前ProcessJob失败!");
+ LOGE("<Master>閫夋嫨褰撳墠ProcessJob澶辫触锛�");
unlock();
continue;
}
- // 3) 若队列无 Glass,拉取到等待队列
+ // 3) 鑻ラ槦鍒楁棤 Glass锛屾媺鍙栧埌绛夊緟闃熷垪
if (m_queueGlasses.empty()) {
int nCount = acquireGlassToQueue();
if (nCount > 0) {
- LOGI("<Master>已加入 %d 块Glass到工艺列队!", nCount);
+ LOGI("<Master>宸插姞鍏� %d 鍧桮lass鍒板伐鑹哄垪闃燂紒", nCount);
}
}
- // 4) 机器人状态
+ // 4) 鏈哄櫒浜虹姸鎬�
RMDATA& rmd = pEFEM->getRobotMonitoringData();
if (rmd.status != ROBOT_STATUS::Idle && rmd.status != ROBOT_STATUS::Run) {
unlock(); continue;
}
- // 5) 正在执行的 RobotTask 先让它跑完一拍
+ // 5) 姝e湪鎵ц鐨� RobotTask 鍏堣瀹冭窇瀹屼竴鎷�
if (m_pActiveRobotTask != nullptr) {
if (m_pActiveRobotTask->isPicked()) {
m_pActiveRobotTask->place();
}
- unlock(); // 等当前任务完成或中止后继续
+ unlock(); // 绛夊綋鍓嶄换鍔″畬鎴愭垨涓鍚庣户缁�
continue;
}
- // 6) ——关键:全局统计 G1/G2 与组数门限(与单片分支对齐)——
+ // 5.5) 鏆傚仠鐘舵�佹鏌ワ細鑻� CJ 鎴栧湪鍒� PJ 澶勪簬 Paused锛屾殏缂撹皟搴︽柊鐨勬惉閫�
+ bool pausedByEvent = false;
+ if (m_pControlJob != nullptr && m_pControlJob->state() == CJState::Paused) {
+ pausedByEvent = true;
+ }
+ for (auto pj : m_inProcesJobs) {
+ if (pj != nullptr && pj->state() == PJState::Paused) {
+ pausedByEvent = true;
+ break;
+ }
+ }
+ if (!pausedByEvent && m_bPauseAlarmRaised) {
+ if (m_pModelCtx != nullptr) {
+ m_pModelCtx->clearSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, 0, 0);
+ }
+ else {
+ AlarmManager& alarmManager = AlarmManager::getInstance();
+ alarmManager.clearAlarmByAttributes(ALID_SOFTWARE_PAUSE_EVENT, 0, 0, CToolUnits::getCurrentTimeString());
+ }
+ m_bPauseAlarmRaised = false;
+ }
+ if (pausedByEvent) {
+ LOGI("<Master>璋冨害鏆傚仠锛欳ontrolJob/ProcessJob 澶勪簬 Paused 鐘舵�侊紙鍙兘鐢� PauseEvent 瑙﹀彂锛�");
+ unlock();
+ continue;
+ }
+
+ // 6) 鈥斺�斿叧閿細鍏ㄥ眬缁熻 G1/G2 涓庣粍鏁伴棬闄愶紙涓庡崟鐗囧垎鏀榻愶級鈥斺��
auto countG1G2 = [&]() {
int g1 = 0, g2 = 0;
if (pBonder1->slotHasGlass(0)) g2++;
@@ -900,20 +999,20 @@
int nGlassGroup = min(g1Count, g2Count);
int nExtraType = (g1Count == g2Count ? 0 : (g1Count > g2Count ? 1 : 2));
- // primary/secondary 统一定义(secondary 默认 G0)
+ // primary/secondary 缁熶竴瀹氫箟锛坰econdary 榛樿 G0锛�
MaterialsType primaryType = MaterialsType::G1;
MaterialsType secondaryType = MaterialsType::G0;
- if (nExtraType == 0) primaryType = MaterialsType::G2; // 与单片分支一致
+ if (nExtraType == 0) primaryType = MaterialsType::G2; // 涓庡崟鐗囧垎鏀竴鑷�
else primaryType = MaterialsType::G1;
- // 组数门限:≥2 组时不再从 LP 上片,避免堆积(与单片一致)
+ // 缁勬暟闂ㄩ檺锛氣墺2 缁勬椂涓嶅啀浠� LP 涓婄墖锛岄伩鍏嶅爢绉紙涓庡崟鐗囦竴鑷达級
bool blockLoadFromLP = (nGlassGroup >= 2);
- // 7) Measurement -> LoadPort(固定:G1 优先回 LP)
+ // 7) Measurement -> LoadPort锛堝浐瀹氾細G1 浼樺厛鍥� LP锛�
if (rmd.armState[0] || rmd.armState[1]) {
LOGD("Arm1 %s, Arm2 %s.",
- rmd.armState[0] ? _T("不可用") : _T("可用"),
- rmd.armState[1] ? _T("不可用") : _T("可用"));
+ rmd.armState[0] ? _T("涓嶅彲鐢�") : _T("鍙敤"),
+ rmd.armState[1] ? _T("涓嶅彲鐢�") : _T("鍙敤"));
}
for (int s = 0; s < 4; s++) {
PortType pt = pLoadPorts[s]->getPortType();
@@ -928,7 +1027,7 @@
BATCH_PORT_PUT:
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
- // 8) Measurement NG -> LoadPort(原位回退)
+ // 8) Measurement NG -> LoadPort锛堝師浣嶅洖閫�锛�
if (!rmd.armState[1]) {
m_pActiveRobotTask = createTransferTask_restore(pMeasurement, pLoadPorts);
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
@@ -940,7 +1039,7 @@
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
- // 10) BakeCooling 内部(Bake -> Cooling)
+ // 10) BakeCooling 鍐呴儴锛圔ake -> Cooling锛�
if (!rmd.armState[0]) {
m_pActiveRobotTask = createTransferTask_bake_to_cooling(pBakeCooling);
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
@@ -956,7 +1055,7 @@
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
- // 12) Fliper(G2) -> Bonder(前置:VacuumBake 有 processed G1;输出 G2 到 Bonder slot0)
+ // 12) Fliper(G2) -> Bonder锛堝墠缃細VacuumBake 鏈� processed G1锛涜緭鍑� G2 鍒� Bonder slot0锛�
if (auto pSrcSlot = pVacuumBake->getProcessedSlot(MaterialsType::G1)) {
if (!rmd.armState[1] && pBonder1->canPlaceGlassInSlot(0)) {
m_pActiveRobotTask = createTransferTask(pFliper, pBonder1, MaterialsType::G2, MaterialsType::G0, 2);
@@ -968,7 +1067,7 @@
}
}
- // 13) VacuumBake(G1) -> Bonder(槽级判定:slot0(G2) 已有且 slot1(G1) 为空)
+ // 13) VacuumBake(G1) -> Bonder锛堟Ы绾у垽瀹氾細slot0(G2) 宸叉湁涓� slot1(G1) 涓虹┖锛�
if (!rmd.armState[0] && pBonder1->slotHasGlass(0) && !pBonder1->slotHasGlass(1)) {
m_pActiveRobotTask = createTransferTask(pVacuumBake, pBonder1, MaterialsType::G1, MaterialsType::G0);
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
@@ -978,7 +1077,7 @@
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
- // 14) Aligner -> Fliper(G2) 以及 -> VacuumBake(G1)(固定映射)
+ // 14) Aligner -> Fliper(G2) 浠ュ強 -> VacuumBake(G1)锛堝浐瀹氭槧灏勶級
if (!rmd.armState[1]) {
m_pActiveRobotTask = createTransferTask(pAligner, pFliper, MaterialsType::G2, MaterialsType::G0);
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
@@ -988,13 +1087,13 @@
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
- // 15) Aligner -> LoadPort(restore)
+ // 15) Aligner -> LoadPort锛坮estore锛�
if (!rmd.armState[1]) {
m_pActiveRobotTask = createTransferTask_restore(pAligner, pLoadPorts);
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
- // 16) LoadPort -> Aligner(受组数门限控制;统一 buddy/状态时序)
+ // 16) LoadPort -> Aligner锛堝彈缁勬暟闂ㄩ檺鎺у埗锛涚粺涓� buddy/鐘舵�佹椂搴忥級
if (blockLoadFromLP) { unlock(); continue; }
for (int s = 0; s < 4; s++) {
@@ -1011,17 +1110,17 @@
continue;
}
- // 统一:queue -> start -> setContext -> move queue→inProcess -> onPanelStart
+ // 缁熶竴锛歲ueue -> start -> setContext -> move queue鈫抜nProcess -> onPanelStart
pGlass->queue();
pGlass->start();
pEFEM->setContext(pGlass);
bool bMoved = glassFromQueueToInPorcess(pGlass);
if (bMoved) {
- LOGI("<Master>Glass(%s)从等待列队到工艺列队转移成功.", pGlass->getID().c_str());
+ LOGI("<Master>Glass(%s)浠庣瓑寰呭垪闃熷埌宸ヨ壓鍒楅槦杞Щ鎴愬姛.", pGlass->getID().c_str());
}
else {
- LOGE("<Master>Glass(%s)从等待列队到工艺列队转移失败.", pGlass->getID().c_str());
+ LOGE("<Master>Glass(%s)浠庣瓑寰呭垪闃熷埌宸ヨ壓鍒楅槦杞Щ澶辫触.", pGlass->getID().c_str());
}
if (m_listener.onPanelStart) m_listener.onPanelStart(this, pGlass);
@@ -1038,9 +1137,9 @@
}
- // 千传模式调度逻辑
+ // 鍗冧紶妯″紡璋冨害閫昏緫
else if (m_state == MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER) {
- // 检测判断robot状态
+ // 妫�娴嬪垽鏂璻obot鐘舵��
RMDATA& rmd = pEFEM->getRobotMonitoringData();
if (rmd.status != ROBOT_STATUS::Idle && rmd.status != ROBOT_STATUS::Run) {
unlock();
@@ -1052,8 +1151,8 @@
m_pActiveRobotTask->place();
}
unlock();
- // 检测到当前有正在下午的任务,确保当前任务完成或中止后继续
- // LOGI("检测到当前有正在下午的任务,确保当前任务完成或中止后继续...");
+ // 妫�娴嬪埌褰撳墠鏈夋鍦ㄤ笅鍗堢殑浠诲姟锛岀‘淇濆綋鍓嶄换鍔″畬鎴愭垨涓鍚庣户缁�
+ // LOGI("妫�娴嬪埌褰撳墠鏈夋鍦ㄤ笅鍗堢殑浠诲姟锛岀‘淇濆綋鍓嶄换鍔″畬鎴愭垨涓鍚庣户缁�...");
continue;
}
@@ -1089,20 +1188,20 @@
3, pMeasurement, 0);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_BakeCooling_Measurement;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(BakeCooling -> Measurement)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(BakeCooling -> Measurement)...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
- // BakeCooling内部
+ // BakeCooling鍐呴儴
if ((m_nContinuousTransferStep == CTStep_Unknow || m_nContinuousTransferStep == CTStep_BakeCooling_BakeCooling2)
&& !rmd.armState[0]) {
m_pActiveRobotTask = createTransferTask_continuous_transfer(pBakeCooling,
2, pBakeCooling, 3);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_BakeCooling_BakeCooling3;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(BakeCooling-2 -> BakeCooling-3)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(BakeCooling-2 -> BakeCooling-3)...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1112,7 +1211,7 @@
1, pBakeCooling, 2);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_BakeCooling_BakeCooling2;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(BakeCooling-1 -> BakeCooling-2)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(BakeCooling-1 -> BakeCooling-2)...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1122,7 +1221,7 @@
0, pBakeCooling, 1);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_BakeCooling_BakeCooling1;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(BakeCooling-0 -> BakeCooling-1)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(BakeCooling-0 -> BakeCooling-1)...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1134,7 +1233,7 @@
1, pBakeCooling, 0);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_VacuumBake_BakeCooling;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(VacuumBake(G1) -> BakeCooling)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(VacuumBake(G1) -> BakeCooling)...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1146,7 +1245,7 @@
0, pVacuumBake, 1);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_VacuumBake_VacuumBake;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(VacuumBake(G1-0) -> VacuumBake(G1-1))...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(VacuumBake(G1-0) -> VacuumBake(G1-1))...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1158,7 +1257,7 @@
1, pVacuumBake, 0);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_Bonder2_VacuumBake;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(Bonder2 -> VacuumBake(G1))...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(Bonder2 -> VacuumBake(G1))...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1170,7 +1269,7 @@
1, pBonder2, 1);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_Bonder1_Bonder2;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(Bonder1 -> Bonder2)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(Bonder1 -> Bonder2)...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1182,7 +1281,7 @@
0, pBonder1, 1);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_Fliper_Bonder1;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(Fliper(G2) -> Bonder1)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(Fliper(G2) -> Bonder1)...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1194,7 +1293,7 @@
0, pFliper, 0);
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_Aligner_Fliper;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(Aligner -> Fliper(G2))...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(Aligner -> Fliper(G2))...");
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
}
@@ -1213,7 +1312,7 @@
m_nContinuousTransferStep = CTStep_LoadPort_Aligner;
m_nContinuousWorkingPort = p;
m_nContinuousWorkingSlot = slot;
- LOGI("<ContinuousTransfer>千传测试,开始搬送任务(LoadPort -> Aligner)...");
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬫惉閫佷换鍔�(LoadPort -> Aligner)...");
pEFEM->setContext(m_pActiveRobotTask->getContext());
goto CT_PORT_GET;
}
@@ -1224,7 +1323,7 @@
CT_PORT_GET:
if (m_pActiveRobotTask != nullptr) {
m_nContinuousTransferStep = CTStep_begin;
- LOGI("<ContinuousTransfer>千传测试,开始第 %d 轮", m_nContinuousTransferCount + 1);
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛屽紑濮嬬 %d 杞�", m_nContinuousTransferCount + 1);
}
CHECK_RUN_ACTIVE_ROBOT_TASK(m_pActiveRobotTask);
@@ -1241,13 +1340,13 @@
// _endthreadex(0);
- TRACE("CMaster::DispatchProc 线程退出\n");
+ TRACE("CMaster::DispatchProc 绾跨▼閫�鍑篭n");
return 0;
}
unsigned CMaster::ReadBitsProc()
{
- // 标志位清0复位
+ // 鏍囧織浣嶆竻0澶嶄綅
{
StationIdentifier station;
station.nNetNo = 0;
@@ -1258,13 +1357,13 @@
while (1) {
- // 待退出信号或时间到
+ // 寰呴��鍑轰俊鍙锋垨鏃堕棿鍒�
int nRet = ::WaitForSingleObject(m_hEventReadBitsThreadExit[0], 1000);
if (nRet == WAIT_OBJECT_0) {
break;
}
- // 读标志位
+ // 璇绘爣蹇椾綅
for (auto item : m_listEquipment) {
const StationIdentifier& station = item->getStation();
MemoryBlock& block = item->getReadBitBlock();
@@ -1282,7 +1381,7 @@
// _endthreadex(0);
- TRACE("CMaster::ReadBitsProc 线程退出\n");
+ TRACE("CMaster::ReadBitsProc 绾跨▼閫�鍑篭n");
return 0;
}
@@ -1317,10 +1416,10 @@
listener.onPreFethedOutJob = [&](void* pEquipment, int port, CJobDataB* pJobDataB) -> BOOL {
CEquipment* p = (CEquipment*)pEquipment;
- // 可能要加这一句
+ // 鍙兘瑕佸姞杩欎竴鍙�
Sleep(750);
- // 取片,更新当前搬送任务
+ // 鍙栫墖锛屾洿鏂板綋鍓嶆惉閫佷换鍔�
BOOL bOk = FALSE;
lock();
if (m_pActiveRobotTask != nullptr) {
@@ -1335,7 +1434,7 @@
&& pJobDataS->getCassetteSequenceNo() == pJobDataB->getCassetteSequenceNo()
&& pJobDataS->getJobSequenceNo() == pJobDataB->getJobSequenceNo()) {
bOk = TRUE;
- LOGD("<CMaster>onPreFethedOutJob, 已校验数据一致性.");
+ LOGD("<CMaster>onPreFethedOutJob, 宸叉牎楠屾暟鎹竴鑷存��.");
}
LOGD("<CMaster>onPreFethedOutJob 0004.");
if (pJobDataS != nullptr) {
@@ -1355,7 +1454,7 @@
unlock();
if (!bOk) {
- LOGE("<CMaster>onPreFethedOutJob, 数据校验失败.");
+ LOGE("<CMaster>onPreFethedOutJob, 鏁版嵁鏍¢獙澶辫触.");
}
return bOk;
@@ -1364,14 +1463,14 @@
listener.onPreStoredJob = [&](void* pEquipment, int port, CJobDataB* pJobDataB, short& slot) -> BOOL {
CEquipment* p = (CEquipment*)pEquipment;
- // 可能要加这一句
+ // 鍙兘瑕佸姞杩欎竴鍙�
Sleep(750);
- // 放片,更新当前搬送任务
+ // 鏀剧墖锛屾洿鏂板綋鍓嶆惉閫佷换鍔�
BOOL bOk = FALSE;
lock();
if (m_pActiveRobotTask != nullptr) {
- // 是否已经进入手臂(即取片完成),进入下一步,放片
+ // 鏄惁宸茬粡杩涘叆鎵嬭噦(鍗冲彇鐗囧畬鎴�),杩涘叆涓嬩竴姝ワ紝鏀剧墖
if (m_pActiveRobotTask->isPicking() &&
((m_pActiveRobotTask->getArmNo() == 1 && p->getID() == EQ_ID_ARM_TRAY1)
|| (m_pActiveRobotTask->getArmNo() == 2 && p->getID() == EQ_ID_ARM_TRAY2))
@@ -1380,32 +1479,32 @@
bOk = TRUE;
}
- // 是否放片完成
+ // 鏄惁鏀剧墖瀹屾垚
else if (m_pActiveRobotTask->isPlacing() &&
m_pActiveRobotTask->getTarPosition() == p->getID()) {
CGlass* pGlass = p->getGlassFromSlot(m_pActiveRobotTask->getTarSlot());
if (pGlass == nullptr) {
bOk = TRUE;
slot = m_pActiveRobotTask->getTarSlot();
- LOGI("<CMaster>onPreStoredJob, 已校验数据一致性.");
+ LOGI("<CMaster>onPreStoredJob, 宸叉牎楠屾暟鎹竴鑷存��.");
}
}
- // 是否回撤
+ // 鏄惁鍥炴挙
else if (m_pActiveRobotTask->isRestoring() &&
m_pActiveRobotTask->getSrcPosition() == p->getID()) {
CGlass* pGlass = p->getGlassFromSlot(m_pActiveRobotTask->getSrcSlot());
if (pGlass == nullptr && m_pActiveRobotTask->getSrcSlot() == port) {
bOk = TRUE;
slot = m_pActiveRobotTask->getSrcSlot();
- LOGI("<CMaster>onPreStoredJob, 已校验数据一致性.");
+ LOGI("<CMaster>onPreStoredJob, 宸叉牎楠屾暟鎹竴鑷存��.");
}
}
}
unlock();
if (!bOk) {
- LOGE("<CMaster>onPreStoredJob, 数据校验失败.");
+ LOGE("<CMaster>onPreStoredJob, 鏁版嵁鏍¢獙澶辫触.");
}
return bOk;
@@ -1418,11 +1517,11 @@
m_listener.onEqDataChanged(this, p, 0);
}
- // 取放片,更新当前搬送任务
+ // 鍙栨斁鐗囷紝鏇存柊褰撳墠鎼�佷换鍔�
if (code == EDCC_FETCHOUT_JOB) {
lock();
if (m_pActiveRobotTask != nullptr && m_pActiveRobotTask->getSrcPosition() == p->getID()) {
- LOGI("开始取片...");
+ LOGI("寮�濮嬪彇鐗�...");
}
unlock();
}
@@ -1433,7 +1532,7 @@
&& ((m_pActiveRobotTask->getArmNo() == 1 && p->getID() == EQ_ID_ARM_TRAY1)
|| (m_pActiveRobotTask->getArmNo() == 2 && p->getID() == EQ_ID_ARM_TRAY2))
) {
- LOGI("取片完成.");
+ LOGI("鍙栫墖瀹屾垚.");
m_pActiveRobotTask->fetchOut();
m_pActiveRobotTask->picked();
}
@@ -1447,17 +1546,17 @@
if (m_state == MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER) {
if (m_nContinuousTransferStep == CTStep_end) {
m_nContinuousTransferCount++;
- LOGI("<ContinuousTransfer>千传测试,第 %d 轮结束", m_nContinuousTransferCount);
+ LOGI("<ContinuousTransfer>鍗冧紶娴嬭瘯锛岀 %d 杞粨鏉�", m_nContinuousTransferCount);
if (m_listener.onCTRoundEnd != nullptr) {
m_listener.onCTRoundEnd(this, m_nContinuousTransferCount);
}
}
}
- LOGI("放片完成...");
- // 完成此条搬送任务,但要把数据和消息上抛应用层
+ LOGI("鏀剧墖瀹屾垚...");
+ // 瀹屾垚姝ゆ潯鎼�佷换鍔★紝浣嗚鎶婃暟鎹拰娑堟伅涓婃姏搴旂敤灞�
- // 如果是搬送回从AOI搬送回Port, 则glass工艺完成
+ // 濡傛灉鏄惉閫佸洖浠嶢OI鎼�佸洖Port, 鍒檊lass宸ヨ壓瀹屾垚
if (m_pActiveRobotTask->getSrcPosition() == EQ_ID_MEASUREMENT) {
CGlass* pGlass = (CGlass*)m_pActiveRobotTask->getContext();
pGlass->complete();
@@ -1466,32 +1565,33 @@
this->saveState();
bool bMoved = glassFromInPorcessToComplete(pGlass);
if (bMoved) {
- LOGI("<Master>Glass(%s)从工艺列队到完成列队转移成功.",
+ LOGI("<Master>Glass(%s)浠庡伐鑹哄垪闃熷埌瀹屾垚鍒楅槦杞Щ鎴愬姛.",
pGlass->getID().c_str());
}
else {
- LOGE("<Master>Glass(%s)从工艺列队到完成列队转移失败.",
+ LOGE("<Master>Glass(%s)浠庡伐鑹哄垪闃熷埌瀹屾垚鍒楅槦杞Щ澶辫触.",
pGlass->getID().c_str());
}
if (m_listener.onPanelEnd != nullptr) {
m_listener.onPanelEnd(this, pGlass);
}
- // 检查PJ是否已经完成
+ // 妫�鏌J鏄惁宸茬粡瀹屾垚
CProcessJob* pJob = getGlassProcessJob((CGlass*)m_pActiveRobotTask->getContext());
if (pJob != nullptr && checkAndUpdatePjComplete(pJob)) {
this->saveState();
- LOGE("<Master>ProcessJob(%s)完成.",
+ LOGE("<Master>ProcessJob(%s)瀹屾垚.",
pJob->id().c_str());
+ processJobFromInPorcessToComplete(pJob);
if (m_listener.onPjEnd != nullptr) {
m_listener.onPjEnd(this, pJob);
}
- // 检查CJ是否已经完成
+ // 妫�鏌J鏄惁宸茬粡瀹屾垚
ASSERT(m_pControlJob);
if (checkAndUpdateCjComplete(m_pControlJob)) {
this->saveState();
- LOGE("<Master>ControlJob(%s)完成.",
+ LOGE("<Master>ControlJob(%s)瀹屾垚.",
m_pControlJob->id().c_str());
if (m_listener.onCjEnd != nullptr) {
m_listener.onCjEnd(this, pJob);
@@ -1520,8 +1620,8 @@
&& m_pActiveRobotTask->getSrcPosition() == p->getID()) {
m_pActiveRobotTask->stored();
m_pActiveRobotTask->restored();
- LOGI("回撤完成...");
- // 完成此条搬送任务,但要把数据和消息上抛应用层
+ LOGI("鍥炴挙瀹屾垚...");
+ // 瀹屾垚姝ゆ潯鎼�佷换鍔★紝浣嗚鎶婃暟鎹拰娑堟伅涓婃姏搴旂敤灞�
unlock();
@@ -1536,27 +1636,38 @@
unlock();
}
};
- listener.onProcessStateChanged = [&](void* pEquipment, int slotNo, PROCESS_STATE state) -> void {
+ listener.onProcessStateChanged = [&](void* pEquipment, int slotNo, PROCESS_STATE prevState, PROCESS_STATE state) -> void {
ASSERT(1 <= slotNo && slotNo <= 8);
int eqid = ((CEquipment*)pEquipment)->getID();
CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(slotNo);
LOGI("<Master>onProcessStateChanged<%d>", (int)state);
if (state == PROCESS_STATE::Processing) {
if (pGlass != nullptr) {
- m_pCollector->batchStart(eqid,
+ m_pCollector->batchStart(SlotToMid(eqid, slotNo),
pGlass->getID().c_str(), 10 * 60 * 1000ULL);
}
}
else if (state == PROCESS_STATE::Complete) {
- m_pCollector->batchStop(eqid);
+ if (pGlass != nullptr) {
+ m_pCollector->batchStop(SlotToMid(eqid, slotNo));
+ }
+ }
+
+ if (m_listener.onProcessStateChanged != nullptr) {
+ m_listener.onProcessStateChanged(this, (CEquipment*)pEquipment, slotNo, prevState, state);
+ }
+ };
+ listener.onProcessDataReport = [&](void* pEquipment, const std::vector<CParam>& params) {
+ if (m_listener.onProcessDataReport != nullptr) {
+ m_listener.onProcessDataReport(this, (CEquipment*)pEquipment, params);
}
};
listener.onMapMismatch = [&](void* pEquipment, short scanMap, short downMap) {
- LOGE("<Master-%s>Port InUse, map(%d!=%d)不一致,请检查。",
+ LOGE("<Master-%s>Port InUse, map(%d!=%d)涓嶄竴鑷达紝璇锋鏌ャ��",
((CEquipment*)pEquipment)->getName().c_str(), scanMap, downMap);
};
listener.onPortStatusChanged = [&](void* pEquipment, short status, __int64 data) {
- LOGE("<Master-%s>onPortStatusChanged。status=%d, data=%lld", ((CEquipment*)pEquipment)->getName().c_str(), status);
+ LOGE("<Master-%s>onPortStatusChanged銆俿tatus=%d, data=%lld", ((CEquipment*)pEquipment)->getName().c_str(), status);
if (status == PORT_INUSE && m_pControlJob != nullptr) {
CLoadPort* pPort = (CLoadPort*)pEquipment;
auto pjs = m_pControlJob->getPjs();
@@ -1622,86 +1733,161 @@
}
};
listener.onSVDataReport = [&](void* pEquipment, void* pData) {
+ const bool allowSvLog =
+ (m_state == MASTERSTATE::RUNNING ||
+ m_state == MASTERSTATE::RUNNING_CONTINUOUS_TRANSFER ||
+ m_state == MASTERSTATE::RUNNING_BATCH ||
+ m_state == MASTERSTATE::STARTING);
+ const bool allowCurve = allowSvLog || (m_curveMode == CurveMode::EmptyChamber);
+ if (!allowCurve) {
+ return;
+ }
CSVData* pSVData = (CSVData*)pData;
auto rawData = pSVData->getSVRawData();
std::vector<CParam> params;
((CEquipment*)pEquipment)->parsingSVData((const char*)rawData.data(), rawData.size(), params);
- // 以下加入到曲线数据中
-
+ // 浠ヤ笅鍔犲叆鍒版洸绾挎暟鎹腑
+ LOGD("<Master>onSVDataReport 001");
const int64_t ts = now_ms_epoch();
int eqid = ((CEquipment*)pEquipment)->getID();
if (eqid == EQ_ID_Bonder1 || eqid == EQ_ID_Bonder2) {
- // 定义 Bonder 的特定映射
+ LOGD("<Master>onSVDataReport 002A");
+ // 瀹氫箟 Bonder 鐨勭壒瀹氭槧灏�
std::vector<std::pair<int, int>> bonderMapping = {
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7},
{8, 8}, {9, 9}, {10, 10}, {11, 11}, {12, 12}, {13, 13}, {14, 14}, {15, 15}, {16, 16}
};
- CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(0);
+ CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(2);
auto& dataTypes = CServoUtilsTool::getEqDataTypes();
- auto& bonderTypes = dataTypes[eqid];
+ auto& bonderTypes = dataTypes[SlotToMid(eqid, 2)];
for (const auto& mapping : bonderMapping) {
int paramIndex = mapping.first;
int channel = mapping.second;
if (paramIndex < params.size() && channel - 1 < bonderTypes.size()) {
if(m_pCollector != nullptr)
- m_pCollector->buffersPush(eqid, channel, ts, params.at(paramIndex).getDoubleValue());
+ m_pCollector->buffersPush(SlotToMid(eqid, 2), channel, ts, params.at(paramIndex).getDoubleValue());
if(pGlass != nullptr)
- pGlass->addSVData(eqid, bonderTypes[channel], ts, params.at(paramIndex).getDoubleValue());
+ pGlass->addSVData(eqid, bonderTypes[channel - 1], ts, params.at(paramIndex).getDoubleValue());
}
}
}
else if (eqid == EQ_ID_VACUUMBAKE) {
- // 定义 VACUUMBAKE 的特定映射
+ LOGD("<Master>onSVDataReport 002");
+ // 瀹氫箟 VACUUMBAKE 鐨勭壒瀹氭槧灏�
std::vector<std::pair<int, int>> vacuumMapping = {
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7},
{10, 8}, {11, 9}, {12, 10}, {13, 11}, {14, 12}, {15, 13}, {16, 14}
};
- CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(0);
+ CGlass* pGlass1 = ((CEquipment*)pEquipment)->getGlassFromSlot(1);
+ CGlass* pGlass2 = ((CEquipment*)pEquipment)->getGlassFromSlot(2);
auto& dataTypes = CServoUtilsTool::getEqDataTypes();
- auto& vacuumbakeTypes = dataTypes[eqid];
+ auto& vacuumbakeTypes = dataTypes[SlotToMid(eqid, 1)];
+ LOGD("<Master>onSVDataReport 003 : %d", vacuumMapping.size());
for (const auto& mapping : vacuumMapping) {
int paramIndex = mapping.first;
int channel = mapping.second;
- if (paramIndex < params.size() && channel - 1 < vacuumbakeTypes.size()) {
- if (m_pCollector != nullptr)
- m_pCollector->buffersPush(eqid, channel, ts, params.at(paramIndex).getDoubleValue());
- if (pGlass != nullptr)
- pGlass->addSVData(eqid, vacuumbakeTypes[channel], ts, params.at(paramIndex).getDoubleValue());
+ if (paramIndex < params.size()) {
+ auto& param = params.at(paramIndex);
+ double value = param.getDoubleValue();
+ const std::string& paramName = param.getName();
+ const char slotTag = !paramName.empty() ? paramName[0] : '\0';
+ const int typeIndex = (slotTag == 'B') ? (channel - 8) : (channel - 1);
+ if (typeIndex < 0 || typeIndex >= (int)vacuumbakeTypes.size()) {
+ continue;
+ }
+ const int pushChannel = typeIndex + 1;
+ const std::string& dataType = vacuumbakeTypes[typeIndex];
+
+ if (m_pCollector != nullptr) {
+ if (slotTag == 'A')
+ m_pCollector->buffersPush(SlotToMid(eqid, 1), pushChannel, ts, value);
+ else if (slotTag == 'B')
+ m_pCollector->buffersPush(SlotToMid(eqid, 2), pushChannel, ts, value);
+ }
+
+ // 鏍规嵁鑵斾綋鍓嶇紑鍐欏叆瀵瑰簲 Slot 鐨勭幓鐠�
+ if (pGlass1 != nullptr && !dataType.empty() && slotTag == 'A')
+ pGlass1->addSVData(eqid, dataType, ts, value);
+ if (pGlass2 != nullptr && !dataType.empty() && slotTag == 'B')
+ pGlass2->addSVData(eqid, dataType, ts, value);
}
}
}
else if (eqid == EQ_ID_BAKE_COOLING) {
- // 定义 BAKE_COOLING 的特定映射
+ LOGD("<Master>onSVDataReport 002B");
+ // 瀹氫箟 BAKE_COOLING 鐨勭壒瀹氭槧灏�
std::vector<std::pair<int, int>> coolingMapping = {
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6},
{11, 7}, {12, 8}, {13, 9}, {14, 10}, {15, 11}, {16, 12}
};
- CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(0);
+ CGlass* pGlass1 = ((CEquipment*)pEquipment)->getGlassFromSlot(1); // A Bake
+ CGlass* pGlass2 = ((CEquipment*)pEquipment)->getGlassFromSlot(2); // A Cool
+ CGlass* pGlass3 = ((CEquipment*)pEquipment)->getGlassFromSlot(3); // B Bake
+ CGlass* pGlass4 = ((CEquipment*)pEquipment)->getGlassFromSlot(4); // B Cool
auto& dataTypes = CServoUtilsTool::getEqDataTypes();
- auto& coolingTypes = dataTypes[eqid];
+ auto& coolingTypes = dataTypes[SlotToMid(eqid, 1)];
+ LOGD("<Master>onSVDataReport 003B : %d", coolingMapping.size());
+ auto addToGlass = [&](CGlass* glass, const std::string& type, double val) {
+ if (glass != nullptr)
+ glass->addSVData(eqid, type, ts, val);
+ };
for (const auto& mapping : coolingMapping) {
int paramIndex = mapping.first;
int channel = mapping.second;
- if (paramIndex < params.size() && channel - 1 < coolingTypes.size()) {
- if (m_pCollector != nullptr)
- m_pCollector->buffersPush(eqid, channel, ts, params.at(paramIndex).getDoubleValue());
- if (pGlass != nullptr)
- pGlass->addSVData(eqid, coolingTypes[channel], ts, params.at(paramIndex).getDoubleValue());
+ if (paramIndex < params.size()) {
+ auto& param = params.at(paramIndex);
+ double value = param.getDoubleValue();
+ const std::string& paramName = param.getName();
+ const char slotTag = !paramName.empty() ? paramName[0] : '\0';
+ const bool paramIsBake = paramName.find("鐑樼儰") != std::string::npos;
+ const bool paramIsCooling = paramName.find("鍐峰嵈") != std::string::npos;
+ const int typeIndex = (slotTag == 'B') ? (channel - 7) : (channel - 1);
+ if (typeIndex < 0 || typeIndex >= (int)coolingTypes.size()) {
+ continue;
+ }
+ const int pushChannel = typeIndex + 1;
+ const std::string& dataType = coolingTypes[typeIndex];
+
+ if (m_pCollector != nullptr && paramIsBake) {
+ if (slotTag == 'A')
+ m_pCollector->buffersPush(SlotToMid(eqid, 1), pushChannel, ts, value);
+ else if (slotTag == 'B')
+ m_pCollector->buffersPush(SlotToMid(eqid, 3), pushChannel, ts, value);
+ }
+
+ if (!dataType.empty()) {
+ switch (slotTag) {
+ case 'A':
+ if (paramIsBake)
+ addToGlass(pGlass1, dataType, value);
+ else if (paramIsCooling)
+ addToGlass(pGlass2, dataType, value);
+ break;
+ case 'B':
+ if (paramIsBake)
+ addToGlass(pGlass3, dataType, value);
+ else if (paramIsCooling)
+ addToGlass(pGlass4, dataType, value);
+ break;
+ default:
+ break;
+ }
+ }
}
}
}
- // 以下是输出测试
+ // 浠ヤ笅鏄緭鍑烘祴璇�
std::string strOut;
char szBuffer[256];
for (auto p : params) {
@@ -1715,6 +1901,10 @@
strOut.append(szBuffer);
}
LOGD("<CMaster-%s>SVDataReport:%s", ((CEquipment*)pEquipment)->getName().c_str(), strOut.c_str());
+
+ if (m_listener.onSVDataReport != nullptr) {
+ m_listener.onSVDataReport(this, (CEquipment*)pEquipment, params);
+ }
};
listener.onPanelDataReport = [&](void* pEquipment, void* pContext) {
LOGD("<CMaster-%s>onPanelDataReport", ((CEquipment*)pEquipment)->getName().c_str());
@@ -1722,18 +1912,28 @@
CEquipment* pEq = (CEquipment*)pEquipment;
CGlass* pGlass = (CGlass*)pContext;
- // 如果AOI检测失败,要停机
+ // 濡傛灉AOI妫�娴嬪け璐ワ紝瑕佸仠鏈�
if (pEq->getID() == EQ_ID_MEASUREMENT) {
LOGD("<CMaster-%s>onPanelDataReport 01", ((CEquipment*)pEquipment)->getName().c_str());
if (pGlass->getAOIInspResult() == InspResult::Fail) {
LOGD("<CMaster-%s>onPanelDataReport 02", ((CEquipment*)pEquipment)->getName().c_str());
if (stop() == 0) {
m_nLastError = ER_CODE_AOI_NG;
- m_strLastError = "AOI检测未通过.";
+ m_strLastError = "AOI妫�娴嬫湭閫氳繃.";
}
}
}
+ };
+ listener.onReceivedJob = [&](void* pEquipment, int port, CJobDataS* pJobDataS) {
+ if (m_listener.onJobReceived != nullptr) {
+ m_listener.onJobReceived(this, (CEquipment*)pEquipment, port, pJobDataS);
+ }
+ };
+ listener.onSentOutJob = [&](void* pEquipment, int port, CJobDataS* pJobDataS) {
+ if (m_listener.onJobSentOut != nullptr) {
+ m_listener.onJobSentOut(this, (CEquipment*)pEquipment, port, pJobDataS);
+ }
};
pEquipment->setListener(listener);
pEquipment->setCcLink(&m_cclink);
@@ -1766,7 +1966,7 @@
}
/*
- * 添加LoadPort1
+ * 娣诲姞LoadPort1
* index -- 0~3
*/
CLoadPort* CMaster::addLoadPort(int index)
@@ -1786,7 +1986,7 @@
pEquipment->init();
- LOGE("已添加“%s”.", pEquipment->getName().c_str());
+ LOGE("宸叉坊鍔犫��%s鈥�.", pEquipment->getName().c_str());
return pEquipment;
@@ -1805,7 +2005,7 @@
pEquipment->init();
- LOGE("已添加“Fliper”.");
+ LOGE("宸叉坊鍔犫�淔liper鈥�.");
return pEquipment;
}
@@ -1822,7 +2022,7 @@
pEquipment->init();
- LOGE("已添加“VacuumBake”.");
+ LOGE("宸叉坊鍔犫�淰acuumBake鈥�.");
return pEquipment;
}
@@ -1840,7 +2040,7 @@
pEquipment->init();
- LOGE("已添加“Aligner”.");
+ LOGE("宸叉坊鍔犫�淎ligner鈥�.");
return pEquipment;
}
@@ -1858,7 +2058,7 @@
pEquipment->init();
- LOGE("已添加“EFEM(ROBOT)”.");
+ LOGE("宸叉坊鍔犫�淓FEM(ROBOT)鈥�.");
return pEquipment;
}
@@ -1874,7 +2074,7 @@
pEquipment->init();
- LOGE("已添加“ARM”.");
+ LOGE("宸叉坊鍔犫�淎RM鈥�.");
return pEquipment;
}
@@ -1890,12 +2090,12 @@
pEquipment->init();
- LOGE("已添加“%s”.", pEquipment->getName().c_str());
+ LOGE("宸叉坊鍔犫��%s鈥�.", pEquipment->getName().c_str());
return pEquipment;
}
- /* 添加bonder1 或 bonder2
+ /* 娣诲姞bonder1 鎴� bonder2
* index -- 0, bonder1
* index -- 1, bonder2
*/
@@ -1914,7 +2114,7 @@
pEquipment->init();
- LOGE("已添加“%s”.", pEquipment->getName().c_str());
+ LOGE("宸叉坊鍔犫��%s鈥�.", pEquipment->getName().c_str());
return pEquipment;
@@ -1932,7 +2132,7 @@
addToEquipmentList(pEquipment);
pEquipment->init();
- LOGE("已添加“Aligner”.");
+ LOGE("宸叉坊鍔犫�淎ligner鈥�.");
return pEquipment;
}
@@ -1949,7 +2149,7 @@
addToEquipmentList(pEquipment);
pEquipment->init();
- LOGE("已添加“Measurement”.");
+ LOGE("宸叉坊鍔犫�淢easurement鈥�.");
return pEquipment;
}
@@ -1964,7 +2164,7 @@
static int i = 0;
i++;
- // 自动保存缓存
+ // 鑷姩淇濆瓨缂撳瓨
if (i % (4 * 2) == 0) {
if (m_bDataModify) {
saveCacheAndBackups();
@@ -1973,8 +2173,203 @@
}
+ // 妯℃嫙娴嬭瘯锛堟棤鏈哄櫒鑱旀満鏃剁敤浜庤仈璋� EAP锛�
+ // 璇诲彇 test.ini锛堝綋鍓嶇洰褰曟垨 exe 鍚岀洰褰曪級
+ {
+ struct SimCfg {
+ bool enabled{ false };
+ DWORD intervalMs{ 5000 };
+ int step{ 0 };
+ };
+ auto loadCfg = [&]() -> SimCfg {
+ SimCfg cfg;
- // 模拟测试
+ // Try INI: current dir, then exe dir
+ char iniPath[MAX_PATH] = { 0 };
+ strcpy_s(iniPath, "test.ini");
+ auto readIni = [&](const char* path) -> bool {
+ const UINT en = GetPrivateProfileIntA("SimEap", "Enabled", 0, path);
+ if (en == 0) return false; // treat as missing/disabled
+ cfg.enabled = (en != 0);
+ cfg.intervalMs = (DWORD)GetPrivateProfileIntA("SimEap", "IntervalMs", 5000, path);
+ cfg.intervalMs = max(500u, cfg.intervalMs);
+ cfg.step = (int)GetPrivateProfileIntA("SimEap", "Step", 0, path);
+ return true;
+ };
+ if (!readIni(iniPath)) {
+ char exePath[MAX_PATH] = { 0 };
+ GetModuleFileNameA(NULL, exePath, MAX_PATH);
+ char* lastSlash = strrchr(exePath, '\\');
+ if (lastSlash != nullptr) {
+ *(lastSlash + 1) = '\0';
+ strcat_s(exePath, "test.ini");
+ readIni(exePath);
+ }
+ }
+ return cfg;
+ };
+
+ const SimCfg cfg = loadCfg();
+ if (cfg.enabled) {
+ static DWORD lastTick = 0;
+ static int lastExecutedStep = -1;
+ static bool inited = false;
+ static SERVO::CGlass simGlass;
+ static SERVO::CVcrEventReport simVcr;
+ static SERVO::CProcessJob simPj("PJ1001");
+ static SERVO::CControlJob simCj("CJ5007");
+
+ if (!inited) {
+ inited = true;
+ simGlass.setID("SIM_PANEL_001");
+ simVcr.getGlassId() = "SIM_PANEL_001";
+ }
+
+ DWORD now = GetTickCount();
+ if (lastTick == 0) lastTick = now;
+ if ((now - lastTick) < cfg.intervalMs) {
+ return;
+ }
+ lastTick = now;
+
+ // 鍗曟瑙﹀彂锛氭瘡涓� Step 鍙墽琛屼竴娆★紱浣犳墜鍔ㄤ慨鏀� ini 鐨� Step 鍊煎悗浼氬啀娆¤Е鍙�
+ const int step = cfg.step;
+ if (step <= 0 || step == lastExecutedStep) {
+ return;
+ }
+ lastExecutedStep = step;
+
+ // 鍙栦竴涓� LoadPort 浣滀负妯℃嫙鐩爣
+ SERVO::CLoadPort* pLpEq = (SERVO::CLoadPort*)getEquipment(EQ_ID_LOADPORT1);
+
+ auto fireLoadPortStatus = [&](short status) {
+ pLpEq->simulateSetCassetteId("Test-Cassette-001");
+ if (m_listener.onLoadPortStatusChanged != nullptr && pLpEq != nullptr) {
+ m_listener.onLoadPortStatusChanged(this, pLpEq, status, 0);
+ }
+ };
+ auto fireProcessState = [&](SERVO::CEquipment* pEq, int slotNo, SERVO::PROCESS_STATE st) {
+ // Drive equipment state so listeners receive prev/current states consistently.
+ if (pEq != nullptr) {
+ pEq->fireSetProcessState(slotNo, st);
+ }
+ };
+
+ LOGI("<Master>SIM_EAP single-step=%d", step);
+ switch (step) {
+ // ===== 涓氬姟娴佺▼姝ラ锛�1~23锛�=====
+ case 1: // E87_06 Material Arrived(TransferBlock) -> Port Blocked
+ fireLoadPortStatus(PORT_BLOCKED);
+ break;
+ case 2: // E87_03 CarrierID Readed -> Port InUse
+ fireLoadPortStatus(PORT_INUSE);
+ break;
+ case 3: // S1F3 Query CJ Space (Host->EQ) - wait host
+ LOGI("<Master>SIM_EAP step3: wait host S1F3");
+ break;
+ case 4: // S16F21 Query PJ Space (Host->EQ) - wait host
+ LOGI("<Master>SIM_EAP step4: wait host S16F21");
+ break;
+ case 5: // S7F19 Query PPID List (Host->EQ) - wait host
+ LOGI("<Master>SIM_EAP step5: wait host S7F19");
+ break;
+ case 6: // S3F17 ProceedWithCarrier (Host->EQ) - wait host
+ LOGI("<Master>SIM_EAP step6: wait host S3F17 ProceedWithCarrier");
+ break;
+ case 7: // E87_14 Check SlotMap (璁惧涓婃姤/杩涘叆 WFH) - 鐢� PORT_INUSE 鍐呴儴瑙﹀彂
+ fireLoadPortStatus(PORT_INUSE);
+ break;
+ case 8: // S3F17 ProceedWithSlotMap (Host->EQ) - wait host
+ LOGI("<Master>SIM_EAP step8: wait host S3F17 ProceedWithSlotMap");
+ break;
+ case 9: // SlotMap Verify OK (鏈」鐩湪鏀跺埌 ProceedWithSlotMap 鍚庝笂鎶�) - wait host
+ LOGI("<Master>SIM_EAP step9: wait host ProceedWithSlotMap to trigger VerifyOK");
+ break;
+ case 10: // Create PJ (Host->EQ) - wait host
+ LOGI("<Master>SIM_EAP step10: wait host S16F15 CreateMultiPJ");
+ break;
+ case 11: // PJ Queued锛堟湰椤圭洰鍦ㄥ垱寤� PJ 鍚庝笂鎶ワ級 - wait host
+ LOGI("<Master>SIM_EAP step11: wait host CreateMultiPJ to trigger PJ_Queued");
+ break;
+ case 12: // Create CJ (Host->EQ) - wait host
+ LOGI("<Master>SIM_EAP step12: wait host S14F9 CreateCJ");
+ break;
+ case 13: // CJ Start
+ if (m_listener.onCjStart != nullptr) m_listener.onCjStart(this, &simCj);
+ break;
+ case 14: // PJ Start
+ if (m_listener.onPjStart != nullptr) m_listener.onPjStart(this, &simPj);
+ break;
+ case 15: // OCR
+ if (m_listener.onEqVcrEventReport != nullptr && pLpEq != nullptr) {
+ m_listener.onEqVcrEventReport(this, pLpEq, &simVcr);
+ }
+ break;
+ case 16: // Panel Start
+ if (m_listener.onPanelStart != nullptr) m_listener.onPanelStart(this, &simGlass);
+ // 鍚屾椂瑙﹀彂涓�娆″瓙鏈哄彴寮�濮嬶紙绀轰緥锛欱onder1, slot 1锛�
+ fireProcessState(getEquipment(EQ_ID_Bonder1), 1, SERVO::PROCESS_STATE::Processing);
+ break;
+ case 17: // Panel End
+ // 鍚屾椂瑙﹀彂涓�娆″瓙鏈哄彴缁撴潫锛堢ず渚嬶細Bonder1, slot 1锛�
+ fireProcessState(getEquipment(EQ_ID_Bonder1), 1, SERVO::PROCESS_STATE::Complete);
+ if (m_listener.onPanelEnd != nullptr) m_listener.onPanelEnd(this, &simGlass);
+ break;
+ case 18: // PJ End
+ if (m_listener.onPjEnd != nullptr) m_listener.onPjEnd(this, &simPj);
+ break;
+ case 19: // CJ End
+ if (m_listener.onCjEnd != nullptr) m_listener.onCjEnd(this, &simCj);
+ break;
+ case 20: // Ready to Release (Port Unload Ready; with prev INUSE will also trigger ReadyToRelease)
+ fireLoadPortStatus(PORT_UNLOAD_READY);
+ break;
+ case 21: // CarrierRelease (Host->EQ) - optional / wait host
+ LOGI("<Master>SIM_EAP step21: wait host S3F17 CarrierRelease");
+ break;
+ case 22: // Ready to Unload
+ fireLoadPortStatus(PORT_UNLOAD_READY);
+ break;
+ case 23: // Material Removed (and ReadyToLoad)
+ fireLoadPortStatus(PORT_LOAD_READY);
+ fireLoadPortStatus(PORT_EMPTY); // will also raise LoadPortNotAssoc via Model
+ break;
+ case 24: { // 妯℃嫙 SV Data锛堢ず渚嬶細Bonder1锛�
+ SERVO::CEquipment* pEq = getEquipment(EQ_ID_Bonder1);
+ if (pEq != nullptr && m_listener.onSVDataReport != nullptr) {
+ static int counter = 0;
+ ++counter;
+ std::vector<CParam> params;
+ params.emplace_back("MockSV_Temp", "1", "C", 25 + (counter % 5));
+ params.emplace_back("MockSV_Pressure", "2", "kPa", 100 + (counter % 3));
+ params.emplace_back("MockSV_Speed", "3", "mm/s", 50 + (counter % 7));
+ m_listener.onSVDataReport(this, pEq, params);
+ LOGI("<Master>SIM_EAP step24: mock SVData (Bonder1), params=%zu", params.size());
+ }
+ break;
+ }
+ case 25: { // 妯℃嫙 Process Data锛堢ず渚嬶細Bonder1锛�
+ SERVO::CEquipment* pEq = getEquipment(EQ_ID_Bonder1);
+ if (pEq != nullptr && m_listener.onProcessDataReport != nullptr) {
+ static int counter = 0;
+ ++counter;
+ std::vector<CParam> params;
+ params.emplace_back("MockProc_CycleTime", "1", "s", 30 + (counter % 4));
+ params.emplace_back("MockProc_MaxTemp", "2", "C", 200 + (counter % 6));
+ params.emplace_back("MockProc_Result", "3", "", (counter % 2) ? 1 : 0);
+ m_listener.onProcessDataReport(this, pEq, params);
+ LOGI("<Master>SIM_EAP step25: mock ProcessData (Bonder1), params=%zu", params.size());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+
+ // 妯℃嫙娴嬭瘯
/*
static int aaa = 0;
aaa++;
@@ -1985,7 +2380,7 @@
glassFromQueueToInPorcess(pGlass);
this->saveState();
- // 这里上报Panel Start事件
+ // 杩欓噷涓婃姤Panel Start浜嬩欢
if (m_listener.onPanelStart != nullptr) {
m_listener.onPanelStart(this, pGlass);
}
@@ -2001,7 +2396,7 @@
glassFromInPorcessToComplete(pGlass);
this->saveState();
- // 这里上报Panel End事件
+ // 杩欓噷涓婃姤Panel End浜嬩欢
if (m_listener.onPanelEnd != nullptr) {
m_listener.onPanelEnd(this, pGlass);
}
@@ -2010,17 +2405,17 @@
if (pJob != nullptr && checkAndUpdatePjComplete(pJob)) {
processJobFromInPorcessToComplete(pJob);
this->saveState();
- LOGE("<Master>ProcessJob(%s)完成.",
+ LOGE("<Master>ProcessJob(%s)瀹屾垚.",
pJob->id().c_str());
if (m_listener.onPjEnd != nullptr) {
m_listener.onPjEnd(this, pJob);
}
- // 检查CJ是否已经完成
+ // 妫�鏌J鏄惁宸茬粡瀹屾垚
ASSERT(m_pControlJob);
if (checkAndUpdateCjComplete(m_pControlJob)) {
this->saveState();
- LOGE("<Master>ControlJob(%s)完成.",
+ LOGE("<Master>ControlJob(%s)瀹屾垚.",
m_pControlJob->id().c_str());
if (m_listener.onCjEnd != nullptr) {
m_listener.onCjEnd(this, pJob);
@@ -2049,63 +2444,63 @@
nRet = pLoadPort1->getPin("Out")->connectPin(pAligner->getPin("In1"));
if (nRet < 0) {
- LOGE("连接LoadPort1-Fliper失败");
+ LOGE("杩炴帴LoadPort1-Fliper澶辫触");
}
nRet = pLoadPort2->getPin("Out")->connectPin(pAligner->getPin("In2"));
if (nRet < 0) {
- LOGE("连接LoadPort1-Fliper失败");
+ LOGE("杩炴帴LoadPort1-Fliper澶辫触");
}
nRet = pAligner->getPin("Out1")->connectPin(pFliper->getPin("In"));
if (nRet < 0) {
- LOGE("连接Aligner-Fliper失败");
+ LOGE("杩炴帴Aligner-Fliper澶辫触");
}
nRet = pAligner->getPin("Out2")->connectPin(pVacuumBake->getPin("In"));
if (nRet < 0) {
- LOGE("连接Aligner-VacuumBake失败");
+ LOGE("杩炴帴Aligner-VacuumBake澶辫触");
}
nRet = pFliper->getPin("Out1")->connectPin(pBonder1->getPin("In1"));
if (nRet < 0) {
- LOGE("连接Fliper-Bonder1失败");
+ LOGE("杩炴帴Fliper-Bonder1澶辫触");
}
nRet = pFliper->getPin("Out2")->connectPin(pBonder2->getPin("In1"));
if (nRet < 0) {
- LOGE("连接Fliper-Bonder2失败");
+ LOGE("杩炴帴Fliper-Bonder2澶辫触");
}
nRet = pVacuumBake->getPin("Out1")->connectPin(pBonder1->getPin("In2"));
if (nRet < 0) {
- LOGE("连接VacuumBake-Bonder1失败");
+ LOGE("杩炴帴VacuumBake-Bonder1澶辫触");
}
nRet = pVacuumBake->getPin("Out2")->connectPin(pBonder2->getPin("In2"));
if (nRet < 0) {
- LOGE("连接VacuumBake-Bonder2失败");
+ LOGE("杩炴帴VacuumBake-Bonder2澶辫触");
}
nRet = pBonder1->getPin("Out")->connectPin(pBakeCooling->getPin("In1"));
if (nRet < 0) {
- LOGE("连接Bonder1-BakeCooling失败");
+ LOGE("杩炴帴Bonder1-BakeCooling澶辫触");
}
nRet = pBonder2->getPin("Out")->connectPin(pBakeCooling->getPin("In2"));
if (nRet < 0) {
- LOGE("连接Bonder2-BakeCooling失败");
+ LOGE("杩炴帴Bonder2-BakeCooling澶辫触");
}
nRet = pBakeCooling->getPin("Out")->connectPin(pMeasurement->getPin("In"));
if (nRet < 0) {
- LOGE("连接BakeCooling-LoadPort3失败");
+ LOGE("杩炴帴BakeCooling-LoadPort3澶辫触");
}
nRet = pMeasurement->getPin("Out1")->connectPin(pLoadPort3->getPin("In"));
if (nRet < 0) {
- LOGE("连接BakeCooling-LoadPort3失败");
+ LOGE("杩炴帴BakeCooling-LoadPort3澶辫触");
}
nRet = pMeasurement->getPin("Out2")->connectPin(pLoadPort4->getPin("In"));
if (nRet < 0) {
- LOGE("连接BakeCooling-LoadPort4失败");
+ LOGE("杩炴帴BakeCooling-LoadPort4澶辫触");
}
}
@@ -2129,7 +2524,7 @@
saveCache();
- // 创建备份目录
+ // 鍒涘缓澶囦唤鐩綍
CString strNewFile;
CString strFileDir = m_strFilepath.c_str();
int index = strFileDir.ReverseFind('\\');
@@ -2383,8 +2778,8 @@
}
unlock();
- // 当前任务手动中止后,停止调度,需要操作员在解决问题后,重新启动
- // 25年7月23日后修改为不停止任务
+ // 褰撳墠浠诲姟鎵嬪姩涓鍚庯紝鍋滄璋冨害锛岄渶瑕佹搷浣滃憳鍦ㄨВ鍐抽棶棰樺悗锛岄噸鏂板惎鍔�
+ // 25骞�7鏈�23鏃ュ悗淇敼涓轰笉鍋滄浠诲姟
// stop();
return 0;
@@ -2542,15 +2937,31 @@
}
m_processJobs = temp;
+ // 閲嶇疆鍚勭鍙� DownloadMap锛圚ost/鏈湴鍕鹃�夌殑鏈熸湜鍔犲伐妲戒綅锛�
+ for (int i = 0; i < 4; i++) {
+ auto* pPort = (CLoadPort*)getEquipment(EQ_ID_LOADPORT1 + i);
+ if (pPort != nullptr) {
+ pPort->setDownloadCassetteMap(0);
+ }
+ }
- // 更新context
- std::vector<uint8_t> newSlots;
- std::vector<void*> newContexts;
+
+ // 鏇存柊context
for (auto pj : m_processJobs) {
for (auto& c : pj->carriers()) {
auto pPort = getPortWithCarrierId(c.carrierId);
if (pPort == nullptr) continue;
+ short downloadMap = 0;
+ for (auto s : c.slots) {
+ if (s >= 1 && s <= 8) {
+ downloadMap |= (short)(1 << (s - 1));
+ }
+ }
+ pPort->setDownloadCassetteMap((short)(pPort->getDownloadCassetteMap() | downloadMap));
+
+ std::vector<uint8_t> newSlots;
+ std::vector<void*> newContexts;
for (auto s : c.slots) {
auto pGlass = pPort->getGlassFromSlot(s);
if (pGlass == nullptr) continue;
@@ -2566,6 +2977,9 @@
this->saveState();
+ if (m_listener.onControlJobChanged) {
+ notifyControlJobChanged();
+ }
return (int)m_processJobs.size();
}
@@ -2586,23 +3000,23 @@
int CMaster::setControlJob(CControlJob& controlJob)
{
- // 回调:是否参创建ControlJob
+ // 鍥炶皟锛氭槸鍚﹀弬鍒涘缓ControlJob
auto canCreateCjFn = [&](uint32_t& cc, std::string& mm) -> bool {
if (m_pControlJob != nullptr) {
cc = 1100;
- mm = "当前ControlJob未结批,不能创建新的ControlJob";
+ mm = "褰撳墠ControlJob鏈粨鎵癸紝涓嶈兘鍒涘缓鏂扮殑ControlJob";
return false;
}
return true;
};
- // 回调:是否存在
+ // 鍥炶皟锛氭槸鍚﹀瓨鍦�
auto pjExists = [&](const std::string& id) -> bool {
return getProcessJob(id) != nullptr;
};
- // 回调:是否可加入 CJ(这里定义:必须是 Queued)
+ // 鍥炶皟锛氭槸鍚﹀彲鍔犲叆 CJ锛堣繖閲屽畾涔夛細蹇呴』鏄� Queued锛�
auto pjJoinable = [&](const std::string& id) -> bool {
auto pj = getProcessJob(id);
if (pj == nullptr) return false;
@@ -2623,6 +3037,9 @@
}
m_pControlJob->setPJs(temps);
this->saveState();
+ if (m_listener.onControlJobChanged) {
+ notifyControlJobChanged();
+ }
return 0;
@@ -2675,7 +3092,98 @@
bool CMaster::ceidDefined(uint32_t ceid) const
{
- return true;
+ if (m_allowedCeids.empty()) return true; // backward compatible: treat as all allowed when not configured
+ return m_allowedCeids.find(ceid) != m_allowedCeids.end();
+ }
+
+ bool CMaster::raiseSoftAlarm(int alarmId,
+ const std::string& desc,
+ int level/* = -1*/,
+ int deviceId/* = 0*/,
+ int unitId/* = 0*/,
+ const char* deviceName/* = "Software"*/,
+ const char* unitName/* = "App"*/)
+ {
+ AlarmManager& alarmManager = AlarmManager::getInstance();
+ const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId);
+
+ int severity = level;
+ if (severity < 0 && info != nullptr) {
+ severity = info->nAlarmLevel;
+ }
+ if (severity < 0) severity = 0; // fallback
+
+ std::string descText = desc;
+ if (descText.empty() && info != nullptr) {
+ descText = !info->strDescription.empty() ? info->strDescription : info->strAlarmText;
+ }
+ if (descText.empty()) {
+ descText = CToolUnits::formatString("Alarm %d", alarmId);
+ }
+
+ AlarmData alarmData;
+ alarmData.nId = alarmId;
+ alarmData.nSeverityLevel = severity;
+ alarmData.nDeviceId = deviceId;
+ alarmData.nUnitId = unitId;
+ alarmData.strDeviceName = deviceName;
+ alarmData.strUnitName = unitName;
+ alarmData.strStartTime = CToolUnits::timeToString2(CToolUnits::getTimestamp());
+ alarmData.strEndTime = "";
+ alarmData.strDescription = descText;
+
+ int nAlarmEventId = 0;
+ return alarmManager.addAlarm(alarmData, nAlarmEventId);
+ }
+
+ void CMaster::handleCollectionEvent(uint32_t ceid)
+ {
+ // 閬嶅巻褰撳墠 PJ锛屽懡涓� pauseEvents 鏃跺彲鍦ㄦ鎵╁睍鏆傚仠鍔ㄤ綔
+ bool pausedAny = false;
+ for (auto pj : m_processJobs) {
+ if (pj == nullptr) continue;
+ const auto& pauseList = pj->pauseEvents();
+ if (std::find(pauseList.begin(), pauseList.end(), ceid) != pauseList.end()) {
+ LOGW("<Master>PauseEvent hit: CEID=%u, PJ=%s, state=%d", ceid, pj->id().c_str(), (int)pj->state());
+ if (pj->pause()) {
+ LOGI("<Master>PJ paused by CEID=%u", ceid);
+ pausedAny = true;
+ }
+ if (m_pControlJob != nullptr && m_pControlJob->state() == CJState::Executing) {
+ if (m_pControlJob->pause()) {
+ LOGI("<Master>ControlJob paused by CEID=%u", ceid);
+ pausedAny = true;
+ }
+ }
+ }
+ }
+ if (pausedAny && m_listener.onControlJobChanged) {
+ // 閫氱煡搴旂敤灞傚埛鏂� UI/鎸夐挳鐘舵��
+ notifyControlJobChanged();
+ }
+ if (pausedAny && !m_bPauseAlarmRaised) {
+ std::string desc = CToolUnits::formatString("<PauseEvent CEID=%u>", ceid);
+ bool raised = false;
+ if (m_pModelCtx != nullptr) {
+ raised = m_pModelCtx->raiseSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, desc);
+ }
+ else {
+ raised = raiseSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, desc);
+ }
+ if (raised) {
+ LOGI("<Master>PauseEvent soft alarm raised, CEID=%u", ceid);
+ m_bPauseAlarmRaised = true;
+ }
+ }
+ }
+
+ void CMaster::setAllowedCeids(const std::vector<unsigned int>& ceids)
+ {
+ m_allowedCeids.clear();
+ m_allowedCeids.reserve(ceids.size());
+ for (auto id : ceids) {
+ m_allowedCeids.insert(id);
+ }
}
bool CMaster::saveState() const
@@ -2683,27 +3191,27 @@
std::ofstream ofs(m_strStatePath, std::ios::binary);
if (!ofs) return false;
- // 文件头
+ // 鏂囦欢澶�
uint32_t magic = 0x4D415354; // 'MAST'
uint16_t version = 1;
ofs.write(reinterpret_cast<const char*>(&magic), sizeof(magic));
ofs.write(reinterpret_cast<const char*>(&version), sizeof(version));
- // 保存 ControlJob
+ // 淇濆瓨 ControlJob
bool hasCJ = (m_pControlJob != nullptr);
ofs.write(reinterpret_cast<const char*>(&hasCJ), sizeof(hasCJ));
if (hasCJ) {
m_pControlJob->serialize(ofs);
}
- // 保存 ProcessJob 列表
+ // 淇濆瓨 ProcessJob 鍒楄〃
uint32_t count = static_cast<uint32_t>(m_processJobs.size());
ofs.write(reinterpret_cast<const char*>(&count), sizeof(count));
for (const auto& job : m_processJobs) {
job->serialize(ofs);
}
- // 以后可以在这里追加新字段
+ // 浠ュ悗鍙互鍦ㄨ繖閲岃拷鍔犳柊瀛楁
return true;
}
@@ -2712,14 +3220,14 @@
std::ifstream ifs(m_strStatePath, std::ios::binary);
if (!ifs) return false;
- // 文件头
+ // 鏂囦欢澶�
uint32_t magic = 0;
uint16_t version = 0;
ifs.read(reinterpret_cast<char*>(&magic), sizeof(magic));
ifs.read(reinterpret_cast<char*>(&version), sizeof(version));
if (magic != 0x4D415354) {
- // 文件不合法
+ // 鏂囦欢涓嶅悎娉�
return false;
}
@@ -2728,7 +3236,7 @@
m_pControlJob = nullptr;
}
- // 读取 ControlJob
+ // 璇诲彇 ControlJob
bool hasCJ = false;
ifs.read(reinterpret_cast<char*>(&hasCJ), sizeof(hasCJ));
if (hasCJ) {
@@ -2739,7 +3247,7 @@
return false;
}
- // 读取 ProcessJob 列表
+ // 璇诲彇 ProcessJob 鍒楄〃
uint32_t count = 0;
ifs.read(reinterpret_cast<char*>(&count), sizeof(count));
m_processJobs.clear();
@@ -2750,7 +3258,7 @@
}
- // 找到CProcessJob指针加入列表中
+ // 鎵惧埌CProcessJob鎸囬拡鍔犲叆鍒楄〃涓�
std::vector<CProcessJob*> tempPjs;
auto ids = m_pControlJob->pjIds();
for (auto id : ids) {
@@ -2762,7 +3270,7 @@
m_pControlJob->setPJs(tempPjs);
- // 更新contexts
+ // 鏇存柊contexts
auto pjs = m_pControlJob->getPjs();
for (auto pj : pjs) {
for (auto& c : pj->carriers()) {
@@ -2780,7 +3288,7 @@
}
- // 如果版本升级,可在这里判断 version 来加载新字段
+ // 濡傛灉鐗堟湰鍗囩骇锛屽彲鍦ㄨ繖閲屽垽鏂� version 鏉ュ姞杞芥柊瀛楁
return true;
@@ -2808,7 +3316,7 @@
CGlass* CMaster::acquireNextGlass()
{
for (auto* pj : m_inProcesJobs) {
- // 遍历 PJ 的 carriers 和 slots
+ // 閬嶅巻 PJ 鐨� carriers 鍜� slots
for (auto& cs : pj->carriers()) {
for (auto ctx : cs.contexts) {
CGlass* pGlass = (CGlass*)ctx;
@@ -2819,14 +3327,14 @@
}
}
}
- return nullptr; // 没有可加工的 Glass
+ return nullptr; // 娌℃湁鍙姞宸ョ殑 Glass
}
int CMaster::acquireGlassToQueue()
{
int nCount = 0;
for (auto* pj : m_inProcesJobs) {
- // 遍历 PJ 的 carriers 和 slots
+ // 閬嶅巻 PJ 鐨� carriers 鍜� slots
if (pj->carriers().empty()) continue;
for (auto& cs : pj->carriers()) {
for (auto ctx : cs.contexts) {
@@ -2946,7 +3454,7 @@
- // 释放Job相关
+ // 閲婃斁Job鐩稿叧
for (auto item : m_processJobs) {
delete item;
}
@@ -2956,7 +3464,7 @@
m_pControlJob = nullptr;
}
- // 注意要释放引用
+ // 娉ㄦ剰瑕侀噴鏀惧紩鐢�
m_inProcesJobs.clear();
m_completeProcessJobs.clear();
m_queueGlasses.clear();
@@ -2965,6 +3473,9 @@
saveState();
+ if (m_listener.onControlJobChanged) {
+ notifyControlJobChanged();
+ }
return true;
}
@@ -2980,7 +3491,7 @@
m_pControlJob->abort(description);
- // 释放Job相关
+ // 閲婃斁Job鐩稿叧
for (auto item : m_processJobs) {
delete item;
}
@@ -2990,7 +3501,7 @@
m_pControlJob = nullptr;
}
- // 注意要释放引用
+ // 娉ㄦ剰瑕侀噴鏀惧紩鐢�
m_inProcesJobs.clear();
m_completeProcessJobs.clear();
m_queueGlasses.clear();
@@ -2999,6 +3510,9 @@
saveState();
+ if (m_listener.onControlJobChanged) {
+ notifyControlJobChanged();
+ }
return true;
}
@@ -3047,7 +3561,7 @@
{
if (stop() == 0) {
m_nLastError = ER_CODE_AOI_NG;
- m_strLastError = "AOI检测未通过.";
+ m_strLastError = "AOI妫�娴嬫湭閫氳繃.";
}
}
@@ -3060,6 +3574,15 @@
if (pSlot == nullptr) return false;
CGlass* pGlass = (CGlass*)pSlot->getContext();
+ if (pGlass == nullptr) return false;
+
+ // Buffer 涓婇檺涓� 1锛氭柊鎼嚭鏃朵涪寮冩棫鐨�
+ if (!m_bufGlass.empty()) {
+ for (auto* oldGlass : m_bufGlass) {
+ if (oldGlass != nullptr) oldGlass->release();
+ }
+ m_bufGlass.clear();
+ }
m_bufGlass.push_back(pGlass);
pGlass->addRef();
pSlot->setContext(nullptr);
@@ -3121,7 +3644,7 @@
};
- // 事件:有人连入/断开就上日志
+ // 浜嬩欢锛氭湁浜鸿繛鍏�/鏂紑灏变笂鏃ュ織
auto clieintEventCallback = [](const std::string& ip, uint16_t port, bool connected) {
LOGI("<DAQBridge>[Client %s] %s:%u", connected ? _T("JOIN") : _T("LEAVE"), ip.c_str(), port);
};
@@ -3134,31 +3657,73 @@
m_pCollector->createServer(8081);
m_pCollector->startLoop(10);
- // 1) 注册机台(推荐:先注册 id + 机器名称)
+ // 1) 娉ㄥ唽鏈哄彴锛堟帹鑽愶細鍏堟敞鍐� id + 鏈哄櫒鍚嶇О锛�
RetentionPolicy defP; defP.mode = RetainMode::ByCount; defP.maxSamples = 200;
- m_pCollector->registryAddMachine(EQ_ID_Bonder1, "Bonder1", defP);
- m_pCollector->registryAddMachine(EQ_ID_Bonder2, "Bonder2", defP);
- m_pCollector->registryAddMachine(EQ_ID_VACUUMBAKE, "前烘烤", defP);
- m_pCollector->registryAddMachine(EQ_ID_BAKE_COOLING, "烘烤冷却", defP);
+ m_pCollector->registryAddMachine(MID_Bonder1, "Bonder1", defP);
+ m_pCollector->registryAddMachine(MID_Bonder2, "Bonder2", defP);
+ m_pCollector->registryAddMachine(MID_VacuumBakeA, "鍓嶇儤-A", defP);
+ m_pCollector->registryAddMachine(MID_VacuumBakeB, "鍓嶇儤-B", defP);
+ m_pCollector->registryAddMachine(MID_BakeCoolingA, "鍚庣儤-A", defP);
+ m_pCollector->registryAddMachine(MID_BakeCoolingB, "鍚庣儤-B", defP);
- // 2) 为通道设置“曲线名称”
+ // 2) 涓洪�氶亾璁剧疆鈥滄洸绾垮悕绉扳��
auto& dataTypes = CServoUtilsTool::getEqDataTypes();
- auto& bonderTypes = dataTypes[EQ_ID_Bonder1];
+ auto& bonderTypes = dataTypes[MID_Bonder1];
for (size_t i = 0; i < bonderTypes.size(); ++i) {
- m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, i + 1, bonderTypes[i].c_str());
- m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, i + 1, bonderTypes[i].c_str());
+ m_pCollector->buffersSetChannelName(MID_Bonder1, (UINT)i + 1, bonderTypes[(UINT)i].c_str());
+ m_pCollector->buffersSetChannelName(MID_Bonder2, (UINT)i + 1, bonderTypes[(UINT)i].c_str());
}
- auto& vacuumbakeTypes = dataTypes[EQ_ID_VACUUMBAKE];
+ auto& vacuumbakeTypes = dataTypes[MID_VacuumBakeA];
for (size_t i = 0; i < vacuumbakeTypes.size(); ++i) {
- m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, i + 1, vacuumbakeTypes[i].c_str());
+ m_pCollector->buffersSetChannelName(MID_VacuumBakeA, (UINT)i + 1, vacuumbakeTypes[(UINT)i].c_str());
+ m_pCollector->buffersSetChannelName(MID_VacuumBakeB, (UINT)i + 1, vacuumbakeTypes[(UINT)i].c_str());
}
- auto& coolingTypes = dataTypes[EQ_ID_BAKE_COOLING];
+ auto& coolingTypes = dataTypes[MID_BakeCoolingA];
for (size_t i = 0; i < coolingTypes.size(); ++i) {
- m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, i + 1, coolingTypes[i].c_str());
+ m_pCollector->buffersSetChannelName(MID_BakeCoolingA, i + 1, coolingTypes[i].c_str());
+ m_pCollector->buffersSetChannelName(MID_BakeCoolingB, i + 1, coolingTypes[i].c_str());
+ }
+
+ if (m_curveMode == CurveMode::EmptyChamber) {
+ const uint32_t mids[] = {
+ MID_Bonder1, MID_Bonder2,
+ MID_VacuumBakeA, MID_VacuumBakeB,
+ MID_BakeCoolingA, MID_BakeCoolingB
+ };
+ for (uint32_t mid : mids) {
+ m_pCollector->batchStart(mid, "EMPTY_CHAMBER", 10 * 60 * 1000ULL);
+ }
}
}
}
+
+ uint32_t CMaster::SlotToMid(int eqid, int slot)
+ {
+ if (eqid == EQ_ID_Bonder1) {
+ return MID_Bonder1;
+ }
+
+ if (eqid == EQ_ID_Bonder2) {
+ return MID_Bonder2;
+ }
+
+ if (eqid == EQ_ID_VACUUMBAKE) {
+ if(slot == 1)
+ return MID_VacuumBakeA;
+ if (slot == 2)
+ return MID_VacuumBakeB;
+ }
+
+ if (eqid == EQ_ID_BAKE_COOLING) {
+ if (slot == 1)
+ return MID_BakeCoolingA;
+ if (slot == 3)
+ return MID_BakeCoolingB;
+ }
+
+ return 0;
+ }
}
diff --git a/SourceCode/Bond/Servo/CMaster.h b/SourceCode/Bond/Servo/CMaster.h
index e2844bd..3db1a19 100644
--- a/SourceCode/Bond/Servo/CMaster.h
+++ b/SourceCode/Bond/Servo/CMaster.h
@@ -1,5 +1,6 @@
锘�#pragma once
#include <list>
+#include <unordered_set>
#include "CEquipment.h"
#include "CEFEM.h"
#include "CBonder.h"
@@ -16,6 +17,9 @@
#include "ProcessJob.h"
#include "CControlJob.h"
#include "../DAQBridge/core/Collector.h"
+#include "CJobDataS.h"
+
+class CModel;
#define CTStep_Unknow 0
@@ -50,6 +54,11 @@
ATHERERROR
};
+ enum class CurveMode {
+ Production = 0,
+ EmptyChamber
+ };
+
typedef std::function<void(void* pMaster, MASTERSTATE state)> ONMASTERSTATECHANGED;
typedef std::function<void(void* pMaster, CEquipment* pEiuipment, BOOL bAlive)> ONEQALIVE;
typedef std::function<void(CStep* pStep, int code, void* pData)> ONEQSTEPEVENT;
@@ -58,8 +67,14 @@
typedef std::function<void(void* pMaster, CEquipment* pEquipment, int code)> ONEQDATACHANGED;
typedef std::function<void(void* pMaster, CRobotTask* pTask, int code)> ONROBOTTASKEVENT;
typedef std::function<void(void* pMaster, CEquipment* pEquipment, short status, __int64 data)> ONLOADPORTSTATUSCHANGED;
+ typedef std::function<void(void* pMaster, CEquipment* pEquipment, int slotNo, PROCESS_STATE prevState, PROCESS_STATE state)> ONPROCESSSTATECHANGED;
+ typedef std::function<void(void* pMaster, CEquipment* pEquipment, const std::vector<CParam>& params)> ONPROCESSDATAREPORTEX;
+ typedef std::function<void(void* pMaster, CEquipment* pEquipment, const std::vector<CParam>& params)> ONSVDATAREPORT;
+ typedef std::function<void(void* pMaster, CEquipment* pEquipment, int port, CJobDataS* pJobDataS)> ONJOBRECEIVED;
+ typedef std::function<void(void* pMaster, CEquipment* pEquipment, int port, CJobDataS* pJobDataS)> ONJOBSENTOUT;
typedef std::function<void(void* pMaster, int round)> ONCTROUNDEND;
typedef std::function<void(void* pMaster, void* pj)> ONPJSTART;
+ typedef std::function<void(void* pMaster)> ONCONTROLJOBCHANGED;
typedef struct _MasterListener
{
ONMASTERSTATECHANGED onMasterStateChanged;
@@ -70,6 +85,11 @@
ONEQDATACHANGED onEqDataChanged;
ONROBOTTASKEVENT onRobotTaskEvent;
ONLOADPORTSTATUSCHANGED onLoadPortStatusChanged;
+ ONPROCESSSTATECHANGED onProcessStateChanged;
+ ONSVDATAREPORT onSVDataReport;
+ ONPROCESSDATAREPORTEX onProcessDataReport;
+ ONJOBRECEIVED onJobReceived;
+ ONJOBSENTOUT onJobSentOut;
ONCTROUNDEND onCTRoundEnd;
ONPJSTART onCjStart;
ONPJSTART onCjEnd;
@@ -77,6 +97,7 @@
ONPJSTART onPjEnd;
ONPJSTART onPanelStart;
ONPJSTART onPanelEnd;
+ ONCONTROLJOBCHANGED onControlJobChanged;
} MasterListener;
class CMaster : public IResourceView
@@ -87,6 +108,7 @@
public:
+ void setModelCtx(CModel* pModel);
void setListener(MasterListener listener);
CRobotTask* getActiveRobotTask();
int init();
@@ -98,6 +120,8 @@
void clearError();
ULONGLONG getRunTime();
MASTERSTATE getState();
+ void setCurveMode(CurveMode mode);
+ CurveMode getCurveMode() const;
unsigned DispatchProc();
unsigned ReadBitsProc();
void onTimer(UINT nTimerid);
@@ -139,6 +163,7 @@
int getPortCassetteSnSeed(int port);
void setPortCassetteSnSeed(int port, int seed);
CGlass* getGlass(int scrPort, int scrSlot);
+ uint32_t SlotToMid(int eqid, int slot);
private:
inline void lock() { EnterCriticalSection(&m_criticalSection); }
@@ -177,6 +202,15 @@
bool carrierPresent(const std::string& carrierId) const override;
bool slotUsable(const std::string& carrierId, uint16_t slot) const override;
bool ceidDefined(uint32_t ceid) const override;
+ void setAllowedCeids(const std::vector<unsigned int>& ceids);
+ void handleCollectionEvent(uint32_t ceid);
+ bool raiseSoftAlarm(int alarmId,
+ const std::string& desc,
+ int level = -1,
+ int deviceId = 0,
+ int unitId = 0,
+ const char* deviceName = "Software",
+ const char* unitName = "App");
public:
int getLastError();
@@ -200,7 +234,7 @@
bool canCompleteControlJob();
bool canDeleteControlJob();
- // DAQ Bridge閻╃鍙�
+ // DAQ Bridge 鐩稿叧
Collector* getCollector() const { return m_pCollector; }
private:
@@ -229,6 +263,7 @@
ULONGLONG m_ullStartTime;
ULONGLONG m_ullRunTime;
MASTERSTATE m_state;
+ CurveMode m_curveMode;
// 褰撳墠浠诲姟鍜屽凡瀹屾垚浠诲姟鍒楄〃
CRobotTask* m_pActiveRobotTask;
@@ -238,10 +273,9 @@
int m_nLastError;
std::string m_strLastError;
- // 鍦ㄥ紑濮嬪伐鑹哄墠鏄惁鍏堥渶瑕佸厛姣旇緝map
+ // 鍦ㄥ紑濮嬪伐鑹哄墠鏄惁闇�瑕佸厛姣旇緝 map
BOOL m_isCompareMapsBeforeProceeding;
BOOL m_bJobMode;
-
// 鍗冧紶鍦堟暟璁℃暟
int m_nContinuousTransferCount;
@@ -249,7 +283,7 @@
int m_nContinuousWorkingPort;
int m_nContinuousWorkingSlot;
- // 鏂板宸茬粡寮�濮嬪鐞嗙殑ProcessJob鍒楄〃
+ // 宸茬粡寮�濮嬪鐞嗙殑 ProcessJob 鍒楄〃
std::vector<CProcessJob*> m_inProcesJobs;
std::vector<CProcessJob*> m_completeProcessJobs;
std::vector<CGlass*> m_queueGlasses;
@@ -259,16 +293,23 @@
private:
bool m_bEnableEventReport;
bool m_bEnableAlarmReport;
+ bool m_bPauseAlarmRaised;
SERVO::CControlJob* m_pControlJob;
std::vector<SERVO::CProcessJob*> m_processJobs;
std::string m_strStatePath;
+ CModel* m_pModelCtx;
int m_nTestFlag;
std::list<CGlass*> m_bufGlass;
+ std::unordered_set<uint32_t> m_allowedCeids;
private:
Collector* m_pCollector = nullptr;
void CreateDAQBridgeServer();
+ inline void notifyControlJobChanged() {
+ if (m_listener.onControlJobChanged) {
+ m_listener.onControlJobChanged(this);
+ }
+ }
};
}
-
diff --git a/SourceCode/Bond/Servo/CMyStatusbar.cpp b/SourceCode/Bond/Servo/CMyStatusbar.cpp
index 589a8cc..7aa389a 100644
--- a/SourceCode/Bond/Servo/CMyStatusbar.cpp
+++ b/SourceCode/Bond/Servo/CMyStatusbar.cpp
@@ -70,6 +70,11 @@
SetDlgItemText(IDC_LABEL_RUNTIME, pszText);
}
+void CMyStatusbar::setJobText(const char* pszText)
+{
+ SetDlgItemText(IDC_LABEL_JOBSTATE, pszText);
+}
+
void CMyStatusbar::setCurTaskBtnText(const char* pszText)
{
SetDlgItemText(IDC_BUTTON_ROBOTTASK, pszText);
@@ -78,6 +83,24 @@
void CMyStatusbar::setCimBtnText(const char* pszText)
{
SetDlgItemText(IDC_BUTTON_CIM, pszText);
+}
+
+void CMyStatusbar::setCurTaskBtnColors(COLORREF face, COLORREF frame, COLORREF text)
+{
+ m_btnCurTask.SetFaceColor(face);
+ m_btnCurTask.SetFrameColor(frame);
+ m_btnCurTask.SetTextColor(text);
+ Invalidate();
+ UpdateWindow();
+}
+
+void CMyStatusbar::setCimBtnColors(COLORREF face, COLORREF frame, COLORREF text)
+{
+ m_btnCim.SetFaceColor(face);
+ m_btnCim.SetFrameColor(frame);
+ m_btnCim.SetTextColor(text);
+ Invalidate();
+ UpdateWindow();
}
BOOL CMyStatusbar::OnInitDialog()
@@ -91,12 +114,14 @@
m_btnCurTask.SetFrameColor(m_crBkgnd);
m_btnCurTask.SetFrameColor(BS_HOVER, RGB(218, 218, 218));
m_btnCurTask.SetFrameColor(BS_PRESS, RGB(168, 168, 168));
+ m_btnCurTask.SetTextColor(m_crForeground);
m_btnCim.SubclassDlgItem(IDC_BUTTON_CIM, this);
m_btnCim.SetFaceColor(m_crBkgnd);
m_btnCim.SetFrameColor(m_crBkgnd);
m_btnCim.SetFrameColor(BS_HOVER, RGB(218, 218, 218));
m_btnCim.SetFrameColor(BS_PRESS, RGB(168, 168, 168));
+ m_btnCim.SetTextColor(m_crForeground);
return TRUE; // return TRUE unless you set the focus to a control
// 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
@@ -192,4 +217,12 @@
pItem->GetClientRect(rcItem);
pItem->MoveWindow(x, (rcClient.Height() - rcItem.Height()) / 2, rcItem.Width(), rcItem.Height());
x += rcItem.Width();
-}
\ No newline at end of file
+
+ x += 8;
+ pItem = GetDlgItem(IDC_LABEL_JOBSTATE);
+ if (pItem != nullptr) {
+ pItem->GetClientRect(rcItem);
+ pItem->MoveWindow(x, (rcClient.Height() - rcItem.Height()) / 2, rcItem.Width(), rcItem.Height());
+ x += rcItem.Width();
+ }
+}
diff --git a/SourceCode/Bond/Servo/CMyStatusbar.h b/SourceCode/Bond/Servo/CMyStatusbar.h
index 4afaaa4..37f039d 100644
--- a/SourceCode/Bond/Servo/CMyStatusbar.h
+++ b/SourceCode/Bond/Servo/CMyStatusbar.h
@@ -20,8 +20,11 @@
void setBackgroundColor(COLORREF color);
void setForegroundColor(COLORREF cr);
void setRunTimeText(const char* pszText);
+ void setJobText(const char* pszText);
void setCurTaskBtnText(const char* pszText);
void setCimBtnText(const char* pszText);
+ void setCurTaskBtnColors(COLORREF face, COLORREF frame, COLORREF text);
+ void setCimBtnColors(COLORREF face, COLORREF frame, COLORREF text);
private:
void Resize();
diff --git a/SourceCode/Bond/Servo/CPageCollectionEvent.cpp b/SourceCode/Bond/Servo/CPageCollectionEvent.cpp
index b7d3322..46f4b71 100644
--- a/SourceCode/Bond/Servo/CPageCollectionEvent.cpp
+++ b/SourceCode/Bond/Servo/CPageCollectionEvent.cpp
@@ -5,6 +5,7 @@
#include "Servo.h"
#include "CPageCollectionEvent.h"
#include "afxdialogex.h"
+#include "CEventEditDlg.h"
// CPageCollectionEvent 瀵硅瘽妗�
@@ -32,6 +33,7 @@
ON_WM_CTLCOLOR()
ON_WM_DESTROY()
ON_WM_SIZE()
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CPageCollectionEvent::OnLvnItemchangedList1)
END_MESSAGE_MAP()
@@ -123,3 +125,109 @@
m_listCtrl.SetItemText(index, 4, item->getReportIdsText().c_str());
}
}
+
+void CPageCollectionEvent::OnCreateBtns()
+{
+ const int BTN_W = 80;
+ const int BTN_H = 28;
+ CreateBtn(_T("鏂板"), BTN_W, BTN_H, 3001);
+ CreateBtn(_T("鍒犻櫎"), BTN_W, BTN_H, 3002)->EnableWindow(FALSE);
+ CreateBtn(_T("缂栬緫"), BTN_W, BTN_H, 3003)->EnableWindow(FALSE);
+}
+
+void CPageCollectionEvent::OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+ int nSelCount = m_listCtrl.GetSelectedCount();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) {
+ pDel->EnableWindow(nSelCount > 0);
+ }
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) {
+ pEdit->EnableWindow(nSelCount > 0);
+ }
+ *pResult = 0;
+}
+
+void CPageCollectionEvent::OnClickedBtn(const char* btnName)
+{
+ ASSERT(btnName);
+ if (_strcmpi(btnName, "鏂板") == 0) {
+ int rc = UX_CanExecute(L"addEvents");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ unsigned int newId = theApp.m_model.m_hsmsPassive.getMaxCollectionEventId() + 1;
+ std::vector<unsigned int> rptIds;
+ CEventEditDlg dlg(_T("鏂板浜嬩欢"), (int)newId, _T(""), _T(""), rptIds, this);
+ if (dlg.DoModal() != IDOK) return;
+
+ int ret = theApp.m_model.m_hsmsPassive.addCollectionEvent(newId, CT2A(dlg.GetNameText()), CT2A(dlg.GetDescText()), dlg.GetSelectedRptIds());
+ if (ret == 0) {
+ UX_RecordAction(L"addEvents");
+ m_listCtrl.DeleteAllItems();
+ loadCollectionEvents();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ else {
+ AfxMessageBox(_T("鏂板浜嬩欢澶辫触锛堝彲鑳絀D閲嶅鎴栧啓鍏ュけ璐ワ級"));
+ }
+ }
+ else if (_strcmpi(btnName, "鍒犻櫎") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pEvent = reinterpret_cast<SERVO::CCollectionEvent*>(m_listCtrl.GetItemData(nItem));
+ if (pEvent == nullptr) return;
+
+ int rc = UX_CanExecute(L"delEvents");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+
+ int ret = theApp.m_model.m_hsmsPassive.deleteCollectionEvent((unsigned short)pEvent->getEventId());
+ if (ret == 0) {
+ UX_RecordAction(L"delEvents");
+ m_listCtrl.DeleteAllItems();
+ loadCollectionEvents();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ else {
+ AfxMessageBox(_T("鍒犻櫎浜嬩欢澶辫触"));
+ }
+ }
+ else if (_strcmpi(btnName, "缂栬緫") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pEvent = reinterpret_cast<SERVO::CCollectionEvent*>(m_listCtrl.GetItemData(nItem));
+ if (pEvent == nullptr) return;
+
+ int rc = UX_CanExecute(L"editEvents");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+
+ CString name = pEvent->getName().c_str();
+ CString desc = pEvent->getDescription().c_str();
+ auto rptIds = pEvent->getReportIds();
+ CEventEditDlg dlg(_T("缂栬緫浜嬩欢"), (int)pEvent->getEventId(), name, desc, rptIds, this);
+ if (dlg.DoModal() != IDOK) return;
+
+ int ret = theApp.m_model.m_hsmsPassive.updateCollectionEvent(pEvent->getEventId(), CT2A(dlg.GetNameText()), CT2A(dlg.GetDescText()), dlg.GetSelectedRptIds());
+ if (ret == 0) {
+ UX_RecordAction(L"editEvents");
+ m_listCtrl.DeleteAllItems();
+ loadCollectionEvents();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ else {
+ AfxMessageBox(_T("缂栬緫浜嬩欢澶辫触锛堝彲鑳藉啓鍏ュけ璐ワ級"));
+ }
+ }
+}
diff --git a/SourceCode/Bond/Servo/CPageCollectionEvent.h b/SourceCode/Bond/Servo/CPageCollectionEvent.h
index 7332a09..836fb6f 100644
--- a/SourceCode/Bond/Servo/CPageCollectionEvent.h
+++ b/SourceCode/Bond/Servo/CPageCollectionEvent.h
@@ -16,6 +16,8 @@
private:
CListCtrlEx m_listCtrl;
+ void OnCreateBtns() override;
+ void OnClickedBtn(const char* btnName) override;
// 瀵硅瘽妗嗘暟鎹�
#ifdef AFX_DESIGN_TIME
@@ -31,4 +33,5 @@
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnDestroy();
afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult);
};
diff --git a/SourceCode/Bond/Servo/CPageCtrlState.cpp b/SourceCode/Bond/Servo/CPageCtrlState.cpp
new file mode 100644
index 0000000..de9972c
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPageCtrlState.cpp
@@ -0,0 +1,157 @@
+锘�// CPageCtrlState.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "CPageCtrlState.h"
+#include "afxdialogex.h"
+#include "Common.h"
+#include "Model.h"
+#include "ColorTransfer.h"
+
+
+// CPageCtrlState 瀵硅瘽妗�
+
+IMPLEMENT_DYNAMIC(CPageCtrlState, CDialogEx)
+
+CPageCtrlState::CPageCtrlState(CWnd* pParent /*=nullptr*/)
+ : CDialogEx(IDD_PROD_CTRL_STATE, pParent)
+{
+
+}
+
+CPageCtrlState::~CPageCtrlState()
+{
+}
+
+void CPageCtrlState::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_BUTTON_OFFLINE, m_btnOffline);
+ DDX_Control(pDX, IDC_BUTTON_ONLINE_LOCAL, m_btnOnlineLocal);
+ DDX_Control(pDX, IDC_BUTTON_ONLINE_REMOTE, m_btnOnlineRemote);
+}
+
+
+BEGIN_MESSAGE_MAP(CPageCtrlState, CDialogEx)
+ ON_WM_CTLCOLOR()
+ ON_WM_SIZE()
+ ON_WM_DESTROY()
+ ON_BN_CLICKED(IDC_BUTTON_OFFLINE, &CPageCtrlState::OnBnClickedOffline)
+ ON_BN_CLICKED(IDC_BUTTON_ONLINE_LOCAL, &CPageCtrlState::OnBnClickedOnlineLocal)
+ ON_BN_CLICKED(IDC_BUTTON_ONLINE_REMOTE, &CPageCtrlState::OnBnClickedOnlineRemote)
+END_MESSAGE_MAP()
+
+
+// CPageCtrlState 娑堟伅澶勭悊绋嬪簭
+
+void CPageCtrlState::InitRxWindows()
+{
+ IRxWindows* pRxWindows = RX_GetRxWindows();
+ if (m_pObserver == nullptr) {
+ m_pObserver = pRxWindows->allocObserver([this](IAny* pAny) -> void {
+ pAny->addRef();
+ const int code = pAny->getCode();
+ if (code == RX_CODE_CONTROL_STATE_CHANGED && ::IsWindow(m_hWnd)) {
+ UpdateButtonStyles();
+ }
+ pAny->release();
+ }, [&]() -> void {
+ // onComplete
+ }, [&](IThrowable* pThrowable) -> void {
+ // onError
+ pThrowable->printf();
+ });
+
+ theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())
+ ->subscribe(m_pObserver);
+ }
+}
+
+void CPageCtrlState::ApplyButtonTheme(CBlButton& btn, bool active)
+{
+ const COLORREF text = active ? RGB(255, 255, 255) : RGB(0, 0, 0);
+ const COLORREF normal = active ? RGB(34, 177, 76) : RGB(222, 222, 222);
+ const COLORREF hover = CColorTransfer::ApproximateColor(normal, active ? 0.08 : 0.05);
+ const COLORREF press = CColorTransfer::ApproximateColor(normal, active ? -0.10 : -0.12);
+ const COLORREF frame = active ? CColorTransfer::ApproximateColor(normal, -0.18) : RGB(168, 168, 168);
+
+ btn.SetRoundWidth(6);
+ btn.SetTextColor(BS_NORMAL, text);
+ btn.SetTextColor(BS_HOVER, text);
+ btn.SetTextColor(BS_PRESS, text);
+ btn.SetTextColor(BS_DISABLE, RGB(120, 120, 120));
+
+ btn.SetBkgndColor(BS_NORMAL, normal);
+ btn.SetBkgndColor(BS_HOVER, hover);
+ btn.SetBkgndColor(BS_PRESS, press);
+ btn.SetBkgndColor(BS_DISABLE, RGB(210, 210, 210));
+
+ btn.SetFrameColor(BS_NORMAL, frame);
+ btn.SetFrameColor(BS_HOVER, frame);
+ btn.SetFrameColor(BS_PRESS, frame);
+ btn.SetFrameColor(BS_DISABLE, RGB(180, 180, 180));
+}
+
+void CPageCtrlState::UpdateButtonStyles()
+{
+ const auto state = theApp.m_model.getControlState();
+ ApplyButtonTheme(m_btnOffline, state == ControlState::OfflineEquipment || state == ControlState::OfflineHost);
+ ApplyButtonTheme(m_btnOnlineLocal, state == ControlState::OnlineLocal);
+ ApplyButtonTheme(m_btnOnlineRemote, state == ControlState::OnlineRemote);
+
+ Invalidate();
+ UpdateWindow();
+}
+
+BOOL CPageCtrlState::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ // TODO: 鍦ㄦ娣诲姞棰濆鐨勫垵濮嬪寲
+ InitRxWindows();
+ UpdateButtonStyles();
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
+}
+
+
+HBRUSH CPageCtrlState::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
+
+ // TODO: 鍦ㄦ鏇存敼 DC 鐨勪换浣曠壒鎬�
+
+ // TODO: 濡傛灉榛樿鐨勪笉鏄墍闇�鐢荤瑪锛屽垯杩斿洖鍙︿竴涓敾绗�
+ return hbr;
+}
+
+void CPageCtrlState::OnSize(UINT nType, int cx, int cy)
+{
+ CDialogEx::OnSize(nType, cx, cy);
+
+ // TODO: 鍦ㄦ澶勬坊鍔犳秷鎭鐞嗙▼搴忎唬鐮�
+}
+
+void CPageCtrlState::OnDestroy()
+{
+ CDialogEx::OnDestroy();
+
+ // TODO: 鍦ㄦ澶勬坊鍔犳秷鎭鐞嗙▼搴忎唬鐮�
+}
+
+void CPageCtrlState::OnBnClickedOffline()
+{
+ theApp.m_model.setControlState(ControlState::OfflineEquipment);
+}
+
+void CPageCtrlState::OnBnClickedOnlineLocal()
+{
+ theApp.m_model.setControlState(ControlState::OnlineLocal);
+}
+
+void CPageCtrlState::OnBnClickedOnlineRemote()
+{
+ theApp.m_model.setControlState(ControlState::OnlineRemote);
+}
diff --git a/SourceCode/Bond/Servo/CPageCtrlState.h b/SourceCode/Bond/Servo/CPageCtrlState.h
new file mode 100644
index 0000000..2ed7343
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPageCtrlState.h
@@ -0,0 +1,41 @@
+锘�#pragma once
+#include "BlButton.h"
+
+// CPageCtrlState 瀵硅瘽妗�
+
+class CPageCtrlState : public CDialogEx
+{
+ DECLARE_DYNAMIC(CPageCtrlState)
+
+public:
+ CPageCtrlState(CWnd* pParent = nullptr); // 鏍囧噯鏋勯�犲嚱鏁�
+ virtual ~CPageCtrlState();
+
+private:
+ void InitRxWindows();
+ void UpdateButtonStyles();
+ void ApplyButtonTheme(CBlButton& btn, bool active);
+
+ CBlButton m_btnOffline;
+ CBlButton m_btnOnlineLocal;
+ CBlButton m_btnOnlineRemote;
+ IObserver* m_pObserver{ nullptr };
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_PROD_CTRL_STATE };
+#endif
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 鏀寔
+
+ DECLARE_MESSAGE_MAP()
+public:
+ virtual BOOL OnInitDialog();
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnDestroy();
+ afx_msg void OnBnClickedOffline();
+ afx_msg void OnBnClickedOnlineLocal();
+ afx_msg void OnBnClickedOnlineRemote();
+};
diff --git a/SourceCode/Bond/Servo/CPageDataVarialbles.cpp b/SourceCode/Bond/Servo/CPageDataVarialbles.cpp
new file mode 100644
index 0000000..cd68a1b
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPageDataVarialbles.cpp
@@ -0,0 +1,220 @@
+锘�// CPageDataVarialbles.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "CPageDataVarialbles.h"
+#include "afxdialogex.h"
+#include "CVariableEditDlg2.h"
+
+IMPLEMENT_DYNAMIC(CPageDataVarialbles, CHMPropertyPage)
+
+CPageDataVarialbles::CPageDataVarialbles(CWnd* pParent /*=nullptr*/)
+ : CHMPropertyPage(IDD_PAGE_VARIABLE, pParent)
+{
+}
+
+CPageDataVarialbles::~CPageDataVarialbles()
+{
+}
+
+void CPageDataVarialbles::DoDataExchange(CDataExchange* pDX)
+{
+ CHMPropertyPage::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_LIST1, m_listCtrl);
+}
+
+BEGIN_MESSAGE_MAP(CPageDataVarialbles, CHMPropertyPage)
+ ON_WM_CTLCOLOR()
+ ON_WM_DESTROY()
+ ON_WM_SIZE()
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CPageDataVarialbles::OnLvnItemchangedList1)
+END_MESSAGE_MAP()
+
+BOOL CPageDataVarialbles::OnInitDialog()
+{
+ CHMPropertyPage::OnInitDialog();
+
+ // 璇诲嚭鍒楀锛堢嫭绔嬬殑 ini 鍒嗚妭锛岄伩鍏嶄笌 SVID 椤甸潰鍐茬獊锛�
+ CString strIniFile, strItem;
+ strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+ int width[8] = { 0, 218, 180, 180, 180, 180, 180, 180 };
+ for (int i = 0; i < 8; i++) {
+ strItem.Format(_T("Col_%d_Width"), i);
+ width[i] = GetPrivateProfileInt("PageDataVariableListCtrl", strItem, width[i], strIniFile);
+ }
+
+ DWORD dwStyle = m_listCtrl.GetExtendedStyle();
+ dwStyle |= LVS_EX_FULLROWSELECT;
+ dwStyle |= LVS_EX_GRIDLINES;
+ m_listCtrl.SetExtendedStyle(dwStyle);
+
+ HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
+ ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL);
+ m_listCtrl.InsertColumn(0, _T(""), LVCFMT_RIGHT, width[0]);
+ m_listCtrl.InsertColumn(1, _T("DV ID"), LVCFMT_LEFT, width[1]);
+ m_listCtrl.InsertColumn(2, _T("DV Name"), LVCFMT_LEFT, width[2]);
+ m_listCtrl.InsertColumn(3, _T("DV Format"), LVCFMT_LEFT, width[3]);
+ m_listCtrl.InsertColumn(4, _T("DV Remark"), LVCFMT_LEFT, width[4]);
+
+ loadDataVariables();
+
+ return TRUE;
+}
+
+HBRUSH CPageDataVarialbles::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ return CHMPropertyPage::OnCtlColor(pDC, pWnd, nCtlColor);
+}
+
+void CPageDataVarialbles::OnDestroy()
+{
+ CHMPropertyPage::OnDestroy();
+
+ // 淇濆瓨鍒楀
+ CString strIniFile, strItem, strTemp;
+ strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+ CHeaderCtrl* pHeader = m_listCtrl.GetHeaderCtrl();
+ for (int i = 0; i < pHeader->GetItemCount(); i++) {
+ RECT rect;
+ pHeader->GetItemRect(i, &rect);
+ strItem.Format(_T("Col_%d_Width"), i);
+ strTemp.Format(_T("%d"), rect.right - rect.left);
+ WritePrivateProfileString("PageDataVariableListCtrl", strItem, strTemp, strIniFile);
+ }
+}
+
+void CPageDataVarialbles::OnSize(UINT nType, int cx, int cy)
+{
+ CHMPropertyPage::OnSize(nType, cx, cy);
+ if (GetDlgItem(IDC_LIST1) == nullptr) return;
+
+ CRect rcClient;
+ GetClientRect(&rcClient);
+ m_listCtrl.MoveWindow(12, 12, rcClient.Width() - 24, rcClient.Height() - 24);
+}
+
+void CPageDataVarialbles::OnApply()
+{
+ __super::OnApply();
+}
+
+void CPageDataVarialbles::loadDataVariables()
+{
+ auto& dvars = theApp.m_model.m_hsmsPassive.getDataVariables();
+ for (auto item : dvars) {
+ int index = m_listCtrl.InsertItem(m_listCtrl.GetItemCount(), _T(""));
+ m_listCtrl.SetItemData(index, (DWORD_PTR)item);
+ m_listCtrl.SetItemText(index, 1, std::to_string(item->getVarialbleId()).c_str());
+ m_listCtrl.SetItemText(index, 2, item->getName().c_str());
+ m_listCtrl.SetItemText(index, 3, SERVO::CVariable::formatToString(item->getFormat()).c_str());
+ m_listCtrl.SetItemText(index, 4, item->getRemark().c_str());
+ }
+}
+
+void CPageDataVarialbles::OnCreateBtns()
+{
+ const int BTN_W = 80;
+ const int BTN_H = 28;
+ CreateBtn(_T("鏂板"), BTN_W, BTN_H, 1001);
+ CreateBtn(_T("鍒犻櫎"), BTN_W, BTN_H, 1002)->EnableWindow(FALSE);
+ CreateBtn(_T("缂栬緫"), BTN_W, BTN_H, 1003)->EnableWindow(FALSE);
+}
+
+void CPageDataVarialbles::OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+ int nSelCount = m_listCtrl.GetSelectedCount();
+
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) {
+ pDel->EnableWindow(nSelCount > 0);
+ }
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) {
+ pEdit->EnableWindow(nSelCount > 0);
+ }
+
+ *pResult = 0;
+}
+
+void CPageDataVarialbles::OnClickedBtn(const char* btnName)
+{
+ ASSERT(btnName);
+ if (_strcmpi(btnName, "鏂板") == 0) {
+ int rc = UX_CanExecute(L"addVarialbles");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ unsigned int newId = theApp.m_model.m_hsmsPassive.getMaxDataVariableId();
+ int newIdInt = static_cast<int>(newId + 1);
+ CVariableEditDlg2 dlg(_T("鏂板鏁版嵁鍙橀噺"), newIdInt, _T("U1"), _T(""), _T(""), this);
+ if (dlg.DoModal() != IDOK) return;
+ CString name = dlg.GetNameText();
+ CString fmt = dlg.GetTypeText();
+ CString remark = dlg.GetRemark();
+
+ int ret = theApp.m_model.m_hsmsPassive.addDataVariable(CT2A(name), CT2A(fmt), CT2A(remark), newIdInt);
+ if (ret == 0) {
+ UX_RecordAction(L"addVarialbles");
+ m_listCtrl.DeleteAllItems();
+ loadDataVariables();
+ }
+ else {
+ AfxMessageBox(_T("鏂板鏁版嵁鍙橀噺澶辫触锛屾牸寮忔槸鍚︽纭紵(U1/U2/I2/A20/A50/L)"));
+ }
+ }
+ else if (_strcmpi(btnName, "鍒犻櫎") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pVar = reinterpret_cast<SERVO::CDataVariable*>(m_listCtrl.GetItemData(nItem));
+ if (pVar == nullptr) return;
+
+ int rc = UX_CanExecute(L"delVarialbles");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ int ret = theApp.m_model.m_hsmsPassive.deleteDataVariable(static_cast<int>(pVar->getVarialbleId()));
+ if (ret == 0) {
+ UX_RecordAction(L"delVarialbles");
+ m_listCtrl.DeleteAllItems();
+ loadDataVariables();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ }
+ else if (_strcmpi(btnName, "缂栬緫") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pVar = reinterpret_cast<SERVO::CDataVariable*>(m_listCtrl.GetItemData(nItem));
+ if (pVar == nullptr) return;
+
+ int rc = UX_CanExecute(L"editVarialbles");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ CVariableEditDlg2 dlg(_T("缂栬緫鏁版嵁鍙橀噺"),
+ pVar->getVarialbleId(),
+ CString(CA2T(SERVO::CVariable::formatToString(pVar->getFormat()).c_str())),
+ CString(CA2T(pVar->getName().c_str())),
+ CString(CA2T(pVar->getRemark().c_str())),
+ this);
+ if (dlg.DoModal() != IDOK) return;
+ CString name = dlg.GetNameText();
+ CString fmt = dlg.GetTypeText();
+ CString remark = dlg.GetRemark();
+
+ int ret = theApp.m_model.m_hsmsPassive.updateDataVariable(static_cast<int>(pVar->getVarialbleId()), CT2A(name), CT2A(fmt), CT2A(remark));
+ if (ret == 0) {
+ UX_RecordAction(L"editVarialbles");
+ m_listCtrl.DeleteAllItems();
+ loadDataVariables();
+ }
+ else {
+ AfxMessageBox(_T("缂栬緫鏁版嵁鍙橀噺澶辫触锛屾牸寮忔槸鍚︽纭紵(U1/U2/I2/A20/A50/L)"));
+ }
+ }
+}
diff --git a/SourceCode/Bond/Servo/CPageDataVarialbles.h b/SourceCode/Bond/Servo/CPageDataVarialbles.h
new file mode 100644
index 0000000..9abcac4
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPageDataVarialbles.h
@@ -0,0 +1,37 @@
+锘�#pragma once
+#include "CHMPropertyPage.h"
+#include "ListCtrlEx.h"
+
+// CPageDataVarialbles 瀵硅瘽妗嗭紙DVID 缂栬緫/鏌ョ湅锛�
+class CPageDataVarialbles : public CHMPropertyPage
+{
+ DECLARE_DYNAMIC(CPageDataVarialbles)
+
+public:
+ CPageDataVarialbles(CWnd* pParent = nullptr);
+ virtual ~CPageDataVarialbles();
+ virtual void OnApply();
+ void loadDataVariables();
+ virtual void OnCreateBtns();
+
+private:
+ CListCtrlEx m_listCtrl;
+
+protected:
+ virtual void OnClickedBtn(const char* btnName) override;
+
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_PAGE_VARIABLE };
+#endif
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+
+ DECLARE_MESSAGE_MAP()
+public:
+ virtual BOOL OnInitDialog();
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg void OnDestroy();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult);
+};
diff --git a/SourceCode/Bond/Servo/CPageGlassList.cpp b/SourceCode/Bond/Servo/CPageGlassList.cpp
index cbe79ed..29b3b23 100644
--- a/SourceCode/Bond/Servo/CPageGlassList.cpp
+++ b/SourceCode/Bond/Servo/CPageGlassList.cpp
@@ -13,6 +13,7 @@
#include <unordered_map>
#include <vector>
#include <string>
+#include <algorithm>
#include "CProcessDataListDlg.h"
#define PAGE_SIZE 50
@@ -1087,9 +1088,10 @@
{
CDialogEx::OnInitDialog();
- // 瀹氭椂鍣細1=鍒濆鍖栬闃咃紝2=鍛ㄦ湡鍒锋柊锛堝彧澧為噺锛�
+ // 瀹氭椂鍣細1=鍒濆鍖栬闃咃紝2=鍛ㄦ湡鍒锋柊锛堝彧澧為噺锛夛紝3=寤惰繜鍔犺浇棣栧睆鏁版嵁
SetTimer(1, 3000, nullptr);
SetTimer(2, 2000, nullptr);
+ SetTimer(3, 10, nullptr);
// 涓嬫媺妗嗘帶浠�
InitStatusCombo();
@@ -1139,7 +1141,6 @@
m_listCtrl.SetPopupFullTextColumns({ 11, 12 });
Resize();
- OnBnClickedButtonSearch(); // 瑙﹀彂涓�娆℃煡璇笌棣栧睆濉厖
return TRUE; // return TRUE unless you set the focus to a control
}
@@ -1199,6 +1200,10 @@
else if (nIDEvent == 2) {
UpdateWipData(); // 鍙仛澧為噺锛屼笉閲嶅缓
}
+ else if (nIDEvent == 3) {
+ KillTimer(3);
+ OnBnClickedButtonSearch(); // 寤惰繜棣栧睆鏌ヨ锛岄伩鍏嶅崱浣� OnInitDialog
+ }
CDialogEx::OnTimer(nIDEvent);
}
@@ -1228,6 +1233,8 @@
void CPageGlassList::OnBnClickedButtonSearch()
{
+ CWaitCursor wait; // 鏄剧ず绛夊緟鍏夋爣锛屾彁绀烘鍦ㄥ姞杞�
+
// 鑾峰彇鍏抽敭瀛楄緭鍏ユ鍐呭
CString strKeyword;
GetDlgItemText(IDC_EDIT_KEYWORD, strKeyword);
@@ -1363,7 +1370,7 @@
if (!row.pretty.empty()) {
CFile file;
if (file.Open(filePath, CFile::modeCreate | CFile::modeWrite)) {
- file.Write(row.pretty.c_str(), row.pretty.length());
+ file.Write(row.pretty.c_str(), (UINT)row.pretty.length());
file.Close();
CString strSuccess;
@@ -1481,69 +1488,59 @@
// 瀵规瘡涓満鍣ㄧ敓鎴愯〃鏍�
for (const auto& machinePair : tempGlass.getAllSVData()) {
int machineId = machinePair.first;
+ const auto& dataByType = machinePair.second;
CString machineName = CString(SERVO::CServoUtilsTool::getEqName(machineId).c_str());
csvContent += _T("\n[") + machineName + _T("]\n");
- // 鑾峰彇璇ユ満鍣ㄧ殑棰勫畾涔夊垪椤哄簭
- auto columnOrder = getMachineColumnOrder(machineId);
-
- if (columnOrder.empty()) {
- csvContent += _T("鏃犻瀹氫箟鍒楅厤缃甛n");
+ if (dataByType.empty()) {
+ csvContent += _T("No sensor data\n");
continue;
}
- // 鏋勫缓琛ㄥご - 鐩存帴浣跨敤涓枃鍒楀悕
- CString header = _T("鏃堕棿鎴�(ms),鏈湴鏃堕棿");
+ auto columnOrder = getMachineColumnOrder(machineId, &dataByType);
+ if (columnOrder.empty()) {
+ csvContent += _T("No exportable columns\n");
+ continue;
+ }
+
+ CString header = _T("Timestamp(ms),LocalTime");
for (const auto& dataType : columnOrder) {
header += _T(",");
- header += CString(dataType.c_str()); // 鐩存帴浣跨敤涓枃鍒楀悕
+ header += CString(dataType.c_str());
}
header += _T("\n");
csvContent += header;
- // 妫�鏌ユ槸鍚︽湁鏁版嵁
- if (machinePair.second.empty()) {
- csvContent += _T("鏃犱紶鎰熷櫒鏁版嵁\n");
+ auto baselineIt = std::find_if(columnOrder.begin(), columnOrder.end(),
+ [&](const std::string& type) {
+ auto dataIt = dataByType.find(type);
+ return dataIt != dataByType.end() && !dataIt->second.empty();
+ });
+ if (baselineIt == columnOrder.end()) {
+ csvContent += _T("No usable time series\n");
continue;
}
- // 浣跨敤绗竴涓暟鎹被鍨嬬殑鏃堕棿搴忓垪浣滀负鍩哄噯
- const std::string& firstDataType = columnOrder[0];
- auto firstDataTypeIt = machinePair.second.find(firstDataType);
- if (firstDataTypeIt == machinePair.second.end() || firstDataTypeIt->second.empty()) {
- csvContent += _T("鏃犲熀鍑嗘暟鎹被鍨嬫暟鎹甛n");
- continue;
- }
-
- const auto& timeSeries = firstDataTypeIt->second;
-
- // 瀵逛簬姣忎釜鏃堕棿鐐癸紝杈撳嚭涓�琛屾暟鎹�
- for (size_t i = 0; i < timeSeries.size(); i++) {
+ const auto& timeSeries = dataByType.at(*baselineIt);
+ for (size_t i = 0; i < timeSeries.size(); ++i) {
auto timestamp = timeSeries[i].timestamp;
-
- // 鏃堕棿鎴筹紙姣锛�
auto ms = timePointToMs(timestamp);
CString row;
row.Format(_T("%lld,"), ms);
- // 鏈湴鏃堕棿瀛楃涓�
CString localTime = CString(timePointToString(timestamp).c_str());
row += localTime;
- // 鎸夌収棰勫畾涔夌殑鍒楅『搴忚緭鍑烘暟鎹�
for (const auto& dataType : columnOrder) {
row += _T(",");
-
- auto dataTypeIt = machinePair.second.find(dataType);
- if (dataTypeIt != machinePair.second.end() && i < dataTypeIt->second.size()) {
- // 鐩存帴鎸夌储寮曡幏鍙栨暟鎹�
+ auto dataTypeIt = dataByType.find(dataType);
+ if (dataTypeIt != dataByType.end() && i < dataTypeIt->second.size()) {
CString valueStr;
valueStr.Format(_T("%.3f"), dataTypeIt->second[i].value);
row += valueStr;
}
else {
- // 鐞嗚涓婁笉搴旇鍙戠敓锛屽洜涓烘偍璇存病鏈夌┖鍊�
row += _T("N/A");
}
}
@@ -1582,9 +1579,14 @@
auto* p = reinterpret_cast<NMC_ELC_SHOWFULLTEXT*>(pNMHDR);
// 瀵硅瘽妗嗘樉绀哄伐鑹哄弬鏁�
- CProcessDataListDlg dlg;
- dlg.setRawText(p->text);
- dlg.DoModal();
+ if (p->iSubItem == 12) {
+ CProcessDataListDlg dlg;
+ dlg.setRawText(p->text);
+ dlg.DoModal();
+ }
+ else {
+ AfxMessageBox(p->text);
+ }
*pResult = 0;
}
@@ -1919,11 +1921,33 @@
}
// 鑾峰彇鏈哄櫒棰勫畾涔夌殑鍒楅『搴�
-std::vector<std::string> CPageGlassList::getMachineColumnOrder(int machineId)
+std::vector<std::string> CPageGlassList::getMachineColumnOrder(int machineId,
+ const std::unordered_map<std::string, std::vector<SERVO::SVDataItem>>* actualData)
{
+ std::vector<std::string> columnOrder;
auto dataTypes = SERVO::CServoUtilsTool::getEqDataTypes();
auto it = dataTypes.find(machineId);
- return it != dataTypes.end() ? it->second : std::vector<std::string>();
+
+ if (actualData != nullptr) {
+ if (it != dataTypes.end()) {
+ for (const auto& name : it->second) {
+ if (actualData->find(name) != actualData->end()) {
+ columnOrder.push_back(name);
+ }
+ }
+ }
+ for (const auto& kv : *actualData) {
+ if (std::find(columnOrder.begin(), columnOrder.end(), kv.first) == columnOrder.end()) {
+ columnOrder.push_back(kv.first);
+ }
+ }
+ return columnOrder;
+ }
+
+ if (it != dataTypes.end()) {
+ columnOrder = it->second;
+ }
+ return columnOrder;
}
// 鏃堕棿鎴宠浆鎹负瀛楃涓�
@@ -1953,13 +1977,25 @@
for (const auto& machinePair : dataTypes) {
int machineId = machinePair.first;
const auto& dataTypeList = machinePair.second;
+ std::vector<std::string> filteredTypes;
+
+ if (machineId == EQ_ID_VACUUMBAKE || machineId == EQ_ID_BAKE_COOLING) {
+ const char activePrefix = 'A';
+ for (const auto& dataType : dataTypeList) {
+ if (!dataType.empty() && dataType[0] == activePrefix) {
+ filteredTypes.push_back(dataType);
+ }
+ }
+ }
+
+ const auto& typeList = filteredTypes.empty() ? dataTypeList : filteredTypes;
// 鐢熸垚鏃堕棿搴忓垪锛氫粠褰撳墠鏃堕棿寰�鍓嶆帹10鍒嗛挓锛屾瘡1绉掍竴涓暟鎹偣
auto now = std::chrono::system_clock::now();
auto startTime = now - std::chrono::minutes(10);
// 涓烘瘡涓暟鎹被鍨嬬敓鎴愭ā鎷熸暟鎹�
- for (const auto& dataType : dataTypeList) {
+ for (const auto& dataType : typeList) {
std::vector<SERVO::SVDataItem> mockData;
// 鐢熸垚600涓暟鎹偣锛�10鍒嗛挓 * 60涓偣/鍒嗛挓锛�
@@ -2029,4 +2065,4 @@
double randomNoise = (rand() % 100 - 50) / 100.0 * variation * 0.3; // 闅忔満鍣0
return baseValue + timeTrend + randomNoise;
-}
\ No newline at end of file
+}
diff --git a/SourceCode/Bond/Servo/CPageGlassList.h b/SourceCode/Bond/Servo/CPageGlassList.h
index 09e548a..38a35c7 100644
--- a/SourceCode/Bond/Servo/CPageGlassList.h
+++ b/SourceCode/Bond/Servo/CPageGlassList.h
@@ -1,6 +1,7 @@
锘�#pragma once
#include "CExpandableListCtrl.h"
#include "GlassLogDb.h"
+#include <unordered_map>
// ====== 缂栬瘧寮�鍏宠鏄� ======
// USE_MOCK_SENSOR_DATA: 1=鍚敤妯℃嫙浼犳劅鍣ㄦ暟鎹敓鎴愶紱0=浣跨敤鐪熷疄鏁版嵁
@@ -65,7 +66,7 @@
void ExportBasicInfo(CString& csvContent, const GlassLogDb::Row& row);
void ExportProcessParams(CString& csvContent, const GlassLogDb::Row& row);
void ExportSensorData(CString& csvContent, const GlassLogDb::Row& row);
- static std::vector<std::string> getMachineColumnOrder(int machineId);
+ static std::vector<std::string> getMachineColumnOrder(int machineId, const std::unordered_map<std::string, std::vector<SERVO::SVDataItem>>* actualData = nullptr);
static std::string timePointToString(const std::chrono::system_clock::time_point& tp);
static int64_t timePointToMs(const std::chrono::system_clock::time_point& tp);
void GenerateMockSVData(SERVO::CGlass& glass);
diff --git a/SourceCode/Bond/Servo/CPageGraph1.cpp b/SourceCode/Bond/Servo/CPageGraph1.cpp
index 60f2ac4..2ae613e 100644
--- a/SourceCode/Bond/Servo/CPageGraph1.cpp
+++ b/SourceCode/Bond/Servo/CPageGraph1.cpp
@@ -180,7 +180,7 @@
{
CDialogEx::OnInitDialog();
InitRxWindows();
- SetTimer(TIMER_ID_DEVICE_STATUS, 3000, nullptr);
+ SetTimer(TIMER_ID_DEVICE_STATUS, 800, nullptr);
SetTimer(TIMER_ID_ROBOT_STATUS, 1000, nullptr); // 姣� 1000ms 鏇存柊涓�娆$姸鎬�
// 鍥剧ず
@@ -196,73 +196,73 @@
// Bonder
m_pGraph->AddIndicateBox(INDICATE_BONDER1, 220, 172, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_BONDER1, "10", "Bonder 1");
+ m_pGraph->SetBoxText(INDICATE_BONDER1, "", "Bonder 1");
m_pGraph->AddIndicateBox(INDICATE_BONDER2, 220, 516, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_BONDER2, "11", "Bonder 2");
+ m_pGraph->SetBoxText(INDICATE_BONDER2, "", "Bonder 2");
// 缈昏浆
m_pGraph->AddIndicateBox(INDICATE_FLIPER, 338, 172, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_FLIPER, "8", "Fliper");
+ m_pGraph->SetBoxText(INDICATE_FLIPER, "", "Fliper");
// 瀵逛綅
m_pGraph->AddIndicateBox(INDICATE_ALIGNER, 428, 172, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_ALIGNER, "7", "Aligner");
+ m_pGraph->SetBoxText(INDICATE_ALIGNER, "", "Aligner");
// Load port 4
m_pGraph->AddIndicateBox(INDICATE_LPORT4, 518, 172, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_LPORT4, "4", "LPort4");
+ m_pGraph->SetBoxText(INDICATE_LPORT4, "", "LPort4");
// Load port 3
m_pGraph->AddIndicateBox(INDICATE_LPORT3, 606, 172, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_LPORT3, "3", "LPort3");
+ m_pGraph->SetBoxText(INDICATE_LPORT3, "", "LPort3");
// Load port 2
m_pGraph->AddIndicateBox(INDICATE_LPORT2, 690, 172, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_LPORT2, "2", "LPort2");
+ m_pGraph->SetBoxText(INDICATE_LPORT2, "", "LPort2");
// Load port 1
m_pGraph->AddIndicateBox(INDICATE_LPORT1, 774, 172, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_LPORT1, "1", "LPort1");
+ m_pGraph->SetBoxText(INDICATE_LPORT1, "", "LPort1");
// Robot
m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 190, 294, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "5", "Robot");
+ m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "", "Robot");
m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 243, 294, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "6", "Robot");
+ m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "", "Robot");
// Vacuum bake
m_pGraph->AddIndicateBox(INDICATE_VACUUM_BAKE, 396, 516, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_VACUUM_BAKE, "9", "Vacuum bake");
+ m_pGraph->SetBoxText(INDICATE_VACUUM_BAKE, "", "Vacuum bake");
// Bake cooling
m_pGraph->AddIndicateBox(INDICATE_BAKE_COOLING, 566, 516, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_BAKE_COOLING, "12", "Bake cooling");
+ m_pGraph->SetBoxText(INDICATE_BAKE_COOLING, "", "Bake cooling");
// 绮惧害妫�
m_pGraph->AddIndicateBox(INDICATE_MEASUREMENT, 737, 516, 48, RGB(22, 22, 22),
RGB(255, 127, 39), EQ_BOX_OFFLINE);
- m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "13", "Measurement");
+ m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "", "Measurement");
return TRUE; // return TRUE unless you set the focus to a control
@@ -560,37 +560,7 @@
// 绉诲姩鍒版寚瀹氫綅缃� (娴嬭瘯浣跨敤)
if (pGraphNmhdr->dwData == INDICATE_LPORT1) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Port1);
- }
- else if (pGraphNmhdr->dwData == INDICATE_LPORT2) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Port2);
- }
- else if (pGraphNmhdr->dwData == INDICATE_LPORT3) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Port3);
- }
- else if (pGraphNmhdr->dwData == INDICATE_LPORT4) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Port4);
- }
- else if (pGraphNmhdr->dwData == INDICATE_ALIGNER) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Aligner);
- }
- else if (pGraphNmhdr->dwData == INDICATE_FLIPER) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Fliper);
- }
- else if (pGraphNmhdr->dwData == INDICATE_BONDER1) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Bonder1);
- }
- else if (pGraphNmhdr->dwData == INDICATE_BONDER2) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Bonder2);
- }
- else if (pGraphNmhdr->dwData == INDICATE_VACUUM_BAKE) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Bake);
- }
- else if (pGraphNmhdr->dwData == INDICATE_BAKE_COOLING) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Cooling);
- }
- else if (pGraphNmhdr->dwData == INDICATE_MEASUREMENT) {
- StartRobotMoveToPosition(SERVO::ROBOT_POSITION::Measurement);
+
}
*pResult = 0;
diff --git a/SourceCode/Bond/Servo/CPageGraph2.cpp b/SourceCode/Bond/Servo/CPageGraph2.cpp
index cb4d449..1156425 100644
--- a/SourceCode/Bond/Servo/CPageGraph2.cpp
+++ b/SourceCode/Bond/Servo/CPageGraph2.cpp
@@ -64,10 +64,8 @@
if (RX_CODE_EQ_DATA_CHANGED == code) {
// 閫氱煡璁惧鐘舵��
SERVO::CEquipment* pEquipment = nullptr;
- if (pAny->getPtrValue("ptr", (void*&)pEquipment)) {
- if (pEquipment != nullptr) {
- m_pEqsGraphWnd->ShowItemIndicator((DWORD_PTR)pEquipment, pEquipment->hasGlass());
- }
+ if (pAny->getPtrValue("ptr", (void*&)pEquipment) && pEquipment != nullptr) {
+ UpdateItemIndicators(pEquipment);
}
}
@@ -305,6 +303,12 @@
m_pEqsGraphWnd->SetBkgndColor(m_crBkgnd);
m_pEqsGraphWnd->SetOnListener(listener);
+ CString strIniFile, strItem;
+ strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+ int nIndicatorSize = GetPrivateProfileInt("PageGraph2", _T("IndicatorSize"), 10, strIniFile);
+ int nIndicatorMargin = GetPrivateProfileInt("PageGraph2", _T("IndicatorMargin"), 0, strIniFile);
+ m_pEqsGraphWnd->SetIndicatorSize(nIndicatorSize);
+ m_pEqsGraphWnd->SetIndicatorMargin(nIndicatorMargin);
return TRUE; // return TRUE unless you set the focus to a control
// 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
@@ -377,7 +381,20 @@
m_pEqsGraphWnd->AddPin(pItem, OUTPIN, outPin->getName().c_str(), (DWORD_PTR)outPin);
}
- m_pEqsGraphWnd->ShowItemIndicator((DWORD_PTR)pEquipment, pEquipment->hasGlass());
+ UpdateItemIndicators(pEquipment);
+}
+
+void CPageGraph2::UpdateItemIndicators(SERVO::CEquipment* pEquipment)
+{
+ for (int i = 0; i < SLOT_MAX; i++) {
+ auto pSlot = pEquipment->getSlot(i);
+
+ int state = 0;
+ if (pSlot->isEnable()) {
+ state = pSlot->getContext() != nullptr ? 1 : 2;
+ }
+ m_pEqsGraphWnd->ShowItemIndicator((DWORD_PTR)pEquipment, state, i);
+ }
}
void CPageGraph2::OnTimer(UINT_PTR nIDEvent)
diff --git a/SourceCode/Bond/Servo/CPageGraph2.h b/SourceCode/Bond/Servo/CPageGraph2.h
index 736fb65..3c3e5e5 100644
--- a/SourceCode/Bond/Servo/CPageGraph2.h
+++ b/SourceCode/Bond/Servo/CPageGraph2.h
@@ -16,6 +16,7 @@
private:
void InitRxWindows();
void AddEqToGraphWnd(SERVO::CEquipment* pEquipment);
+ void UpdateItemIndicators(SERVO::CEquipment* pEquipment);
void SaveEqsGraphData();
void GetItemDataFormIni(const char* pszItemName, int& left, int& top);
diff --git a/SourceCode/Bond/Servo/CPageProdOverview.cpp b/SourceCode/Bond/Servo/CPageProdOverview.cpp
new file mode 100644
index 0000000..11813f4
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPageProdOverview.cpp
@@ -0,0 +1,165 @@
+锘�// CPageProOverview.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "CPageProdOverview.h"
+#include "afxdialogex.h"
+#include "CPanelProduction.h"
+
+namespace
+{
+ constexpr UINT_PTR kTimerRefreshId = 2001;
+ constexpr UINT kTimerRefreshIntervalMs = 10000;
+}
+
+IMPLEMENT_DYNAMIC(CPageProdOverview, CDialogEx)
+
+CPageProdOverview::CPageProdOverview(CWnd* pParent /*=nullptr*/)
+ : CDialogEx(IDD_PROD_OVERVIEW, pParent)
+ , m_clrBackground(RGB(240, 240, 240))
+{
+}
+
+CPageProdOverview::~CPageProdOverview()
+{
+}
+
+void CPageProdOverview::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+}
+
+void CPageProdOverview::SetBackgroundColor(COLORREF color)
+{
+ m_clrBackground = color;
+ m_brushBackground.DeleteObject();
+ m_brushBackground.CreateSolidBrush(m_clrBackground);
+ if (::IsWindow(m_hWnd)) {
+ Invalidate();
+ }
+}
+
+BEGIN_MESSAGE_MAP(CPageProdOverview, CDialogEx)
+ ON_WM_CTLCOLOR()
+ ON_WM_DESTROY()
+ ON_WM_SIZE()
+ ON_WM_TIMER()
+END_MESSAGE_MAP()
+
+BOOL CPageProdOverview::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+
+ // 浣跨敤鑷畾涔夋爣绛�
+ if (CWnd* pDay = GetDlgItem(IDC_PROD_DAY_OUTPUT)) {
+ m_labelDayOut.SubclassWindow(pDay->GetSafeHwnd());
+ m_labelDayOut.setFontSize(28);
+ m_labelDayOut.setNoteTextColor(RGB(128, 128, 128));
+ m_labelDayOut.setNote1(_T("鐧界彮浜у嚭"));
+ m_labelDayOut.setBackground(m_clrBackground);
+ m_labelDayOut.setForeground(RGB(18, 18, 18), TRUE);
+ }
+ if (CWnd* pNight = GetDlgItem(IDC_PROD_NIGHT_OUTPUT)) {
+ m_labelNightOut.SubclassWindow(pNight->GetSafeHwnd());
+ m_labelNightOut.setFontSize(28);
+ m_labelNightOut.setNoteTextColor(RGB(128, 128, 128));
+ m_labelNightOut.setNote1(_T("澶滅彮浜у嚭"));
+ m_labelNightOut.setBackground(m_clrBackground);
+ m_labelNightOut.setForeground(RGB(18, 18, 18), TRUE);
+ }
+ if (CWnd* pDayTakt = GetDlgItem(IDC_PROD_DAY_TAKT)) {
+ m_labelDayTakt.SubclassWindow(pDayTakt->GetSafeHwnd());
+ m_labelDayTakt.setFontSize(28);
+ m_labelDayTakt.setNoteTextColor(RGB(128, 128, 128));
+ m_labelDayTakt.setNote1(_T("鐧界彮骞冲潎TT"));
+ m_labelDayTakt.setBackground(m_clrBackground);
+ m_labelDayTakt.setForeground(RGB(18, 18, 18), TRUE);
+ }
+ if (CWnd* pNightTakt = GetDlgItem(IDC_PROD_NIGHT_TAKT)) {
+ m_labelNightTakt.SubclassWindow(pNightTakt->GetSafeHwnd());
+ m_labelNightTakt.setFontSize(28);
+ m_labelNightTakt.setNoteTextColor(RGB(128, 128, 128));
+ m_labelNightTakt.setNote1(_T("澶滅彮骞冲潎TT"));
+ m_labelNightTakt.setBackground(m_clrBackground);
+ m_labelNightTakt.setForeground(RGB(18, 18, 18), TRUE);
+ }
+
+ RefreshData();
+ m_timerId = SetTimer(kTimerRefreshId, kTimerRefreshIntervalMs, nullptr);
+ return TRUE;
+}
+
+HBRUSH CPageProdOverview::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
+
+ if (nCtlColor == CTLCOLOR_DLG || nCtlColor == CTLCOLOR_STATIC) {
+ if (m_brushBackground.GetSafeHandle() == NULL) {
+ m_brushBackground.CreateSolidBrush(m_clrBackground);
+ }
+ pDC->SetBkMode(TRANSPARENT);
+ return (HBRUSH)m_brushBackground.GetSafeHandle();
+ }
+
+ return hbr;
+}
+
+void CPageProdOverview::OnDestroy()
+{
+ if (m_timerId != 0) {
+ KillTimer(m_timerId);
+ m_timerId = 0;
+ }
+ CDialogEx::OnDestroy();
+}
+
+void CPageProdOverview::OnSize(UINT nType, int cx, int cy)
+{
+ CDialogEx::OnSize(nType, cx, cy);
+}
+
+void CPageProdOverview::OnTimer(UINT_PTR nIDEvent)
+{
+ if (nIDEvent == kTimerRefreshId) {
+ RefreshData();
+ }
+ CDialogEx::OnTimer(nIDEvent);
+}
+
+void CPageProdOverview::RefreshData()
+{
+ auto* pPanel = dynamic_cast<CPanelProduction*>(GetParent());
+ if (pPanel == nullptr) {
+ pPanel = dynamic_cast<CPanelProduction*>(GetParent() ? GetParent()->GetParent() : nullptr);
+ }
+ if (pPanel == nullptr) {
+ m_labelDayOut.setText(_T("--"));
+ m_labelNightOut.setText(_T("--"));
+ m_labelDayTakt.setText(_T("--"));
+ m_labelNightTakt.setText(_T("--"));
+ return;
+ }
+
+ ProductionShiftSummary day;
+ ProductionShiftSummary night;
+ if (!pPanel->TryGetDayNightSummaries(day, night)) {
+ m_labelDayOut.setText(_T("--"));
+ m_labelNightOut.setText(_T("--"));
+ m_labelDayTakt.setText(_T("--"));
+ m_labelNightTakt.setText(_T("--"));
+ return;
+ }
+
+ CString text;
+ text.Format(_T("%lld"), day.output.pairsTotal);
+ m_labelDayOut.setText(text);
+ text.Format(_T("%lld"), night.output.pairsTotal);
+ m_labelNightOut.setText(text);
+
+ text.Format(_T("%.1fs"), day.output.avgTaktSeconds);
+ m_labelDayTakt.setText(text);
+ text.Format(_T("%.1fs"), night.output.avgTaktSeconds);
+ m_labelNightTakt.setText(text);
+}
diff --git a/SourceCode/Bond/Servo/CPageProdOverview.h b/SourceCode/Bond/Servo/CPageProdOverview.h
new file mode 100644
index 0000000..6198d90
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPageProdOverview.h
@@ -0,0 +1,37 @@
+锘�#pragma once
+#include <chrono>
+#include "HmLabel.h"
+
+// CPageProOverview 瀵硅瘽妗�
+class CPageProdOverview : public CDialogEx
+{
+ DECLARE_DYNAMIC(CPageProdOverview)
+
+public:
+ CPageProdOverview(CWnd* pParent = nullptr); // 鏍囧噯鏋勯�犲嚱鏁�
+ virtual ~CPageProdOverview();
+ void SetBackgroundColor(COLORREF color);
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 鏀寔
+
+ DECLARE_MESSAGE_MAP()
+public:
+ virtual BOOL OnInitDialog();
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg void OnDestroy();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnTimer(UINT_PTR nIDEvent);
+
+private:
+ void RefreshData();
+
+private:
+ COLORREF m_clrBackground{ RGB(240, 240, 240) };
+ CBrush m_brushBackground;
+ UINT_PTR m_timerId = 0;
+ CHmLabel m_labelDayOut;
+ CHmLabel m_labelNightOut;
+ CHmLabel m_labelDayTakt;
+ CHmLabel m_labelNightTakt;
+};
diff --git a/SourceCode/Bond/Servo/CPageReport.cpp b/SourceCode/Bond/Servo/CPageReport.cpp
index 0066c9b..4e3fd2e 100644
--- a/SourceCode/Bond/Servo/CPageReport.cpp
+++ b/SourceCode/Bond/Servo/CPageReport.cpp
@@ -5,6 +5,8 @@
#include "Servo.h"
#include "CPageReport.h"
#include "afxdialogex.h"
+#include "CReportEditDlg.h"
+#include <algorithm>
// CPageReport 瀵硅瘽妗�
@@ -32,6 +34,7 @@
ON_WM_CTLCOLOR()
ON_WM_DESTROY()
ON_WM_SIZE()
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CPageReport::OnLvnItemchangedList1)
END_MESSAGE_MAP()
@@ -123,3 +126,112 @@
m_listCtrl.SetItemText(index, 2, item->getVariablesIdsText().c_str());
}
}
+
+void CPageReport::OnCreateBtns()
+{
+ const int BTN_W = 80;
+ const int BTN_H = 28;
+ CreateBtn(_T("鏂板"), BTN_W, BTN_H, 2001);
+ CreateBtn(_T("鍒犻櫎"), BTN_W, BTN_H, 2002)->EnableWindow(FALSE);
+ CreateBtn(_T("缂栬緫"), BTN_W, BTN_H, 2003)->EnableWindow(FALSE);
+}
+
+void CPageReport::OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+ int nSelCount = m_listCtrl.GetSelectedCount();
+
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) {
+ pDel->EnableWindow(nSelCount > 0);
+ }
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) {
+ pEdit->EnableWindow(nSelCount > 0);
+ }
+
+ *pResult = 0;
+}
+
+void CPageReport::OnClickedBtn(const char* btnName)
+{
+ ASSERT(btnName);
+ if (_strcmpi(btnName, "鏂板") == 0) {
+ int rc = UX_CanExecute(L"addReports");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+
+ unsigned int newId = theApp.m_model.m_hsmsPassive.getMaxReportId() + 1;
+ std::vector<unsigned int> initVids;
+ CReportEditDlg dlg(_T("鏂板鎶ュ憡"), static_cast<int>(newId), initVids, this);
+ if (dlg.DoModal() != IDOK) return;
+ const auto& vids = dlg.GetSelectedVids();
+
+ int ret = theApp.m_model.m_hsmsPassive.addReport(static_cast<int>(newId), vids);
+ if (ret == 0) {
+ UX_RecordAction(L"addReports");
+ m_listCtrl.DeleteAllItems();
+ loadReports();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ else {
+ AfxMessageBox(_T("鏂板鎶ュ憡澶辫触锛堝彲鑳絀D閲嶅鎴栨枃浠跺啓鍏ュけ璐ワ級"));
+ }
+ }
+ else if (_strcmpi(btnName, "鍒犻櫎") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pRpt = reinterpret_cast<SERVO::CReport*>(m_listCtrl.GetItemData(nItem));
+ if (pRpt == nullptr) return;
+
+ int rc = UX_CanExecute(L"delReports");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+
+ int ret = theApp.m_model.m_hsmsPassive.deleteReport((int)pRpt->getReportId());
+ if (ret == 0) {
+ UX_RecordAction(L"delReports");
+ m_listCtrl.DeleteAllItems();
+ loadReports();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ else {
+ AfxMessageBox(_T("鍒犻櫎鎶ュ憡澶辫触"));
+ }
+ }
+ else if (_strcmpi(btnName, "缂栬緫") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pRpt = reinterpret_cast<SERVO::CReport*>(m_listCtrl.GetItemData(nItem));
+ if (pRpt == nullptr) return;
+
+ int rc = UX_CanExecute(L"editReports");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+
+ std::vector<unsigned int> vidsExisting = pRpt->getVids();
+ CReportEditDlg dlg(_T("缂栬緫鎶ュ憡"), (int)pRpt->getReportId(), vidsExisting, this);
+ if (dlg.DoModal() != IDOK) return;
+ const auto& vids = dlg.GetSelectedVids();
+
+ int ret = theApp.m_model.m_hsmsPassive.updateReport((int)pRpt->getReportId(), vids);
+ if (ret == 0) {
+ UX_RecordAction(L"editReports");
+ m_listCtrl.DeleteAllItems();
+ loadReports();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ else {
+ AfxMessageBox(_T("缂栬緫鎶ュ憡澶辫触锛堝彲鑳芥枃浠跺啓鍏ュけ璐ワ級"));
+ }
+ }
+}
diff --git a/SourceCode/Bond/Servo/CPageReport.h b/SourceCode/Bond/Servo/CPageReport.h
index 716ac0b..bec5588 100644
--- a/SourceCode/Bond/Servo/CPageReport.h
+++ b/SourceCode/Bond/Servo/CPageReport.h
@@ -16,6 +16,8 @@
private:
CListCtrlEx m_listCtrl;
+ void OnCreateBtns() override;
+ void OnClickedBtn(const char* btnName) override;
// 瀵硅瘽妗嗘暟鎹�
#ifdef AFX_DESIGN_TIME
@@ -31,4 +33,5 @@
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnDestroy();
afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult);
};
diff --git a/SourceCode/Bond/Servo/CPageVarialbles.cpp b/SourceCode/Bond/Servo/CPageVarialbles.cpp
index 5f4f5cc..f2c2587 100644
--- a/SourceCode/Bond/Servo/CPageVarialbles.cpp
+++ b/SourceCode/Bond/Servo/CPageVarialbles.cpp
@@ -5,6 +5,7 @@
#include "Servo.h"
#include "CPageVarialbles.h"
#include "afxdialogex.h"
+#include "CVariableEditDlg2.h"
// CPageVarialbles 瀵硅瘽妗�
@@ -32,6 +33,7 @@
ON_WM_CTLCOLOR()
ON_WM_DESTROY()
ON_WM_SIZE()
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CPageVarialbles::OnLvnItemchangedList1)
END_MESSAGE_MAP()
@@ -130,3 +132,112 @@
m_listCtrl.SetItemText(index, 4, item->getRemark().c_str());
}
}
+
+void CPageVarialbles::OnCreateBtns()
+{
+ const int BTN_W = 80;
+ const int BTN_H = 28;
+ CreateBtn(_T("鏂板"), BTN_W, BTN_H, 1001);
+ CreateBtn(_T("鍒犻櫎"), BTN_W, BTN_H, 1002)->EnableWindow(FALSE);
+ CreateBtn(_T("缂栬緫"), BTN_W, BTN_H, 1003)->EnableWindow(FALSE);
+}
+
+void CPageVarialbles::OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+ int nSelCount = m_listCtrl.GetSelectedCount();
+
+ // 鏍规嵁閫変腑鐘舵�佸惎鐢�/绂佺敤鎸夐挳
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) {
+ pDel->EnableWindow(nSelCount > 0);
+ }
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) {
+ pEdit->EnableWindow(nSelCount > 0);
+ }
+
+ *pResult = 0;
+}
+
+void CPageVarialbles::OnClickedBtn(const char* btnName)
+{
+ ASSERT(btnName);
+ if (_strcmpi(btnName, "鏂板") == 0) {
+ int rc = UX_CanExecute(L"addVarialbles");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ unsigned int newId = theApp.m_model.m_hsmsPassive.getMaxVariableId();
+ int newIdInt = static_cast<int>(newId + 1);
+ CVariableEditDlg2 dlg(_T("鏂板鍙橀噺"), newIdInt, _T("U1"), _T(""), _T(""), this);
+ if (dlg.DoModal() != IDOK) return;
+ CString name = dlg.GetNameText();
+ CString fmt = dlg.GetTypeText();
+ CString remark = dlg.GetRemark();
+
+ int ret = theApp.m_model.m_hsmsPassive.addVariable(CT2A(name), CT2A(fmt), CT2A(remark), newIdInt);
+ if (ret == 0) {
+ UX_RecordAction(L"addVarialbles");
+ m_listCtrl.DeleteAllItems();
+ loadVariables();
+ }
+ else {
+ AfxMessageBox(_T("鏂板鍙橀噺澶辫触锛屾牸寮忔槸鍚︽纭紵(U1/U2/I2/A20/A50/L)"));
+ }
+ }
+ else if (_strcmpi(btnName, "鍒犻櫎") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pVar = reinterpret_cast<SERVO::CVariable*>(m_listCtrl.GetItemData(nItem));
+ if (pVar == nullptr) return;
+
+ int rc = UX_CanExecute(L"delVarialbles");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ int ret = theApp.m_model.m_hsmsPassive.deleteVariable(pVar->getVarialbleId());
+ if (ret == 0) {
+ UX_RecordAction(L"delVarialbles");
+
+ m_listCtrl.DeleteAllItems();
+ loadVariables();
+ if (CButton* pDel = GetBtnByName("鍒犻櫎")) pDel->EnableWindow(FALSE);
+ if (CButton* pEdit = GetBtnByName("缂栬緫")) pEdit->EnableWindow(FALSE);
+ }
+ }
+ else if (_strcmpi(btnName, "缂栬緫") == 0) {
+ POSITION pos = m_listCtrl.GetFirstSelectedItemPosition();
+ if (pos == nullptr) return;
+ int nItem = m_listCtrl.GetNextSelectedItem(pos);
+ auto pVar = reinterpret_cast<SERVO::CVariable*>(m_listCtrl.GetItemData(nItem));
+ if (pVar == nullptr) return;
+
+ int rc = UX_CanExecute(L"editVarialbles");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ CVariableEditDlg2 dlg(_T("缂栬緫鍙橀噺"),
+ pVar->getVarialbleId(),
+ CString(CA2T(SERVO::CVariable::formatToString(pVar->getFormat()).c_str())),
+ CString(CA2T(pVar->getName().c_str())),
+ CString(CA2T(pVar->getRemark().c_str())),
+ this);
+ if (dlg.DoModal() != IDOK) return;
+ CString name = dlg.GetNameText();
+ CString fmt = dlg.GetTypeText();
+ CString remark = dlg.GetRemark();
+
+ int ret = theApp.m_model.m_hsmsPassive.updateVariable(pVar->getVarialbleId(), CT2A(name), CT2A(fmt), CT2A(remark));
+ if (ret == 0) {
+ UX_RecordAction(L"editVarialbles");
+ m_listCtrl.DeleteAllItems();
+ loadVariables();
+ }
+ else {
+ AfxMessageBox(_T("缂栬緫鍙橀噺澶辫触锛屾牸寮忔槸鍚︽纭紵(U1/U2/I2/A20/A50/L)"));
+ }
+ }
+}
diff --git a/SourceCode/Bond/Servo/CPageVarialbles.h b/SourceCode/Bond/Servo/CPageVarialbles.h
index 3ccbdc0..3898e68 100644
--- a/SourceCode/Bond/Servo/CPageVarialbles.h
+++ b/SourceCode/Bond/Servo/CPageVarialbles.h
@@ -14,9 +14,13 @@
virtual ~CPageVarialbles();
virtual void OnApply();
void loadVariables();
+ virtual void OnCreateBtns();
private:
CListCtrlEx m_listCtrl;
+
+protected:
+ virtual void OnClickedBtn(const char* btnName) override;
// 瀵硅瘽妗嗘暟鎹�
@@ -33,4 +37,5 @@
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnDestroy();
afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnLvnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult);
};
diff --git a/SourceCode/Bond/Servo/CPanelMaster.cpp b/SourceCode/Bond/Servo/CPanelMaster.cpp
index 82c5361..4fd3dbb 100644
--- a/SourceCode/Bond/Servo/CPanelMaster.cpp
+++ b/SourceCode/Bond/Servo/CPanelMaster.cpp
@@ -50,6 +50,11 @@
return m_nPanelWidth;
}
+void CPanelMaster::setPanelWidth(int width)
+{
+ m_nPanelWidth = width;
+}
+
BOOL CPanelMaster::OnInitDialog()
{
CDialogEx::OnInitDialog();
@@ -59,13 +64,6 @@
pLine1->SetBkgndColor(RGB(225, 225, 225));
pLine1->SetLineColor(RGB(198, 198, 198));
pLine1->EnableResize();
-
-
- // 璇诲彇闈㈡澘瀹�
- CString strIniFile;
- strIniFile.Format(_T("%s\\%s.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir, (LPTSTR)(LPCTSTR)theApp.m_strAppFile);
- m_nPanelWidth = GetPrivateProfileInt(_T("App"), _T("MasterPanelWidth"),
- int((double)GetSystemMetrics(SM_CXSCREEN) * 0.25), (LPTSTR)(LPCTSTR)strIniFile);
// treectrl
@@ -132,12 +130,6 @@
m_nPanelWidth = max(m_nPanelWidth, MASTER_PANEL_MIN_WIDTH);
m_nPanelWidth = min(m_nPanelWidth, MASTER_PANEL_MAX_WIDTH);
GetParent()->SendMessage(ID_MSG_PANEL_RESIZE, m_nPanelWidth, 0);
-
- CString strIniFile, strValue;
- strIniFile.Format(_T("%s\\%s.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir, (LPTSTR)(LPCTSTR)theApp.m_strAppFile);
- strValue.Format(_T("%d"), m_nPanelWidth);
- WritePrivateProfileString(_T("App"), _T("MasterPanelWidth"),
- (LPTSTR)(LPCTSTR)strValue, (LPTSTR)(LPCTSTR)strIniFile);
OnSize(0, 0, 0);
* result = 0;
diff --git a/SourceCode/Bond/Servo/CPanelMaster.h b/SourceCode/Bond/Servo/CPanelMaster.h
index 15bd2db..481683a 100644
--- a/SourceCode/Bond/Servo/CPanelMaster.h
+++ b/SourceCode/Bond/Servo/CPanelMaster.h
@@ -12,6 +12,7 @@
CPanelMaster(CWnd* pParent = nullptr); // 鏍囧噯鏋勯�犲嚱鏁�
virtual ~CPanelMaster();
int getPanelWidth();
+ void setPanelWidth(int width);
void loadEquipmentList();
void loadSteps(SERVO::CEquipment* pEquipment, HTREEITEM hItemEq);
SERVO::CEquipment* GetActiveEquipment();
diff --git a/SourceCode/Bond/Servo/CPanelProduction.cpp b/SourceCode/Bond/Servo/CPanelProduction.cpp
new file mode 100644
index 0000000..f3a5ab2
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPanelProduction.cpp
@@ -0,0 +1,236 @@
+锘�// CPanelProduction.cpp
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "CPanelProduction.h"
+#include "afxdialogex.h"
+#include "Common.h"
+#include "VerticalLine.h"
+
+
+// CPanelProduction dialog
+
+IMPLEMENT_DYNAMIC(CPanelProduction, CDialogEx)
+
+CPanelProduction::CPanelProduction(CWnd* pParent /*=nullptr*/)
+ : CDialogEx(IDD_PANEL_PRODUCTION, pParent)
+{
+ m_crBkgnd = PANEL_PRODUCTION_BACKGROUND_COLOR;
+ m_hbrBkgnd = nullptr;
+ m_nPanelWidth = 288;
+ m_hPlaceholder = nullptr;
+ m_bShiftSummaryValid = FALSE;
+ m_pStatsThread = nullptr;
+ m_pAccordionWnd = nullptr;
+ m_pPageProdOverview = nullptr;
+ m_pPageCtrlState = nullptr;
+}
+
+CPanelProduction::~CPanelProduction()
+{
+}
+
+void CPanelProduction::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+}
+
+
+BEGIN_MESSAGE_MAP(CPanelProduction, CDialogEx)
+ ON_WM_CTLCOLOR()
+ ON_WM_DESTROY()
+ ON_WM_SIZE()
+ ON_NOTIFY(BYVERTICALLINE_MOVEX, IDC_LINE1, &CPanelProduction::OnVLineMoveX)
+ ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CPanelProduction::OnBnClickedButtonClose)
+ ON_WM_TIMER()
+END_MESSAGE_MAP()
+
+int CPanelProduction::getPanelWidth()
+{
+ return m_nPanelWidth;
+}
+
+void CPanelProduction::setPanelWidth(int width)
+{
+ m_nPanelWidth = width;
+}
+
+BOOL CPanelProduction::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ CVerticalLine* pLine1 = CVerticalLine::Hook(GetDlgItem(IDC_LINE1)->GetSafeHwnd());
+ pLine1->SetBkgndColor(RGB(225, 225, 225));
+ pLine1->SetLineColor(RGB(198, 198, 198));
+ pLine1->EnableResize();
+
+ CString strExpandIcon, strCloseIcon;
+ strExpandIcon.Format(_T("%s\\res\\arrow_down.ico"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+ strCloseIcon.Format(_T("%s\\res\\arrow_right.ico"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+
+ m_pAccordionWnd = CAccordionWnd::FromHandle(GetDlgItem(IDC_ACCORDION_WND1)->m_hWnd);
+ m_pAccordionWnd->SetBkgndColor(m_crBkgnd);
+ m_pAccordionWnd->SetFrameColor(RGB(220, 220, 200), TRUE);
+ m_pAccordionWnd->Setpadding(PADDING_LEFT, 2);
+ m_pAccordionWnd->Setpadding(PADDING_TOP, 2);
+ m_pAccordionWnd->Setpadding(PADDING_RIGHT, 2);
+ m_pAccordionWnd->Setpadding(PADDING_BOTTOM, 2);
+ m_pAccordionWnd->LoadExpandIcon(strExpandIcon, strCloseIcon);
+
+ m_pPageCtrlState = new CPageCtrlState();
+ m_pPageCtrlState->SetBackgroundColor(m_crBkgnd);
+ m_pPageCtrlState->Create(IDD_PROD_CTRL_STATE, GetDlgItem(IDC_ACCORDION_WND1));
+ m_pPageCtrlState->ShowWindow(SW_HIDE);
+ m_pAccordionWnd->AddItem("鐘舵��", m_pPageCtrlState, 120, TRUE, TRUE);
+
+ m_pPageProdOverview = new CPageProdOverview();
+ m_pPageProdOverview->SetBackgroundColor(m_crBkgnd);
+ m_pPageProdOverview->Create(IDD_PROD_OVERVIEW, GetDlgItem(IDC_ACCORDION_WND1));
+ m_pPageProdOverview->ShowWindow(SW_HIDE);
+ m_pAccordionWnd->AddItem("鐢熶骇鎬昏", m_pPageProdOverview, 280, TRUE, TRUE);
+
+ SetTimer(1, 1000 * 10, nullptr);
+ StartStatsThread();
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // Exception: OCX property pages should return FALSE
+}
+
+HBRUSH CPanelProduction::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
+
+ if (nCtlColor == CTLCOLOR_STATIC) {
+ pDC->SetBkColor(m_crBkgnd);
+ pDC->SetTextColor(RGB(0, 0, 0));
+ }
+
+ if (m_hbrBkgnd == nullptr) {
+ m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
+ }
+
+ return m_hbrBkgnd;
+}
+
+void CPanelProduction::OnDestroy()
+{
+ StopStatsThread();
+
+ CDialogEx::OnDestroy();
+
+ if (m_pPageCtrlState != nullptr) {
+ m_pPageCtrlState->DestroyWindow();
+ delete m_pPageCtrlState;
+ m_pPageCtrlState = nullptr;
+ }
+ if (m_pPageProdOverview != nullptr) {
+ m_pPageProdOverview->DestroyWindow();
+ delete m_pPageProdOverview;
+ m_pPageProdOverview = nullptr;
+ }
+
+ if (m_hbrBkgnd != nullptr) {
+ ::DeleteObject(m_hbrBkgnd);
+ }
+}
+
+void CPanelProduction::OnSize(UINT nType, int cx, int cy)
+{
+ CDialogEx::OnSize(nType, cx, cy);
+ if (GetDlgItem(IDC_LINE1) == nullptr) return;
+
+ CWnd* pItem;
+ CRect rcClient, rcItem;
+
+ GetClientRect(&rcClient);
+ pItem = GetDlgItem(IDC_LINE1);
+ pItem->MoveWindow(rcClient.right - 3, 0, 3, rcClient.Height());
+
+ pItem = GetDlgItem(IDC_ACCORDION_WND1);
+ pItem->MoveWindow(5, 5, rcClient.Width() - 10, rcClient.Height() - 10);
+}
+
+#define PRODUCTION_PANEL_MIN_WIDTH 88
+#define PRODUCTION_PANEL_MAX_WIDTH 588
+void CPanelProduction::OnVLineMoveX(NMHDR* nmhdr, LRESULT* result)
+{
+ BYVERTICALLINE_NMHDR* pNmhdrex = (BYVERTICALLINE_NMHDR*)nmhdr;
+ int x = pNmhdrex->dwData;
+ m_nPanelWidth += x;
+ m_nPanelWidth = max(m_nPanelWidth, PRODUCTION_PANEL_MIN_WIDTH);
+ m_nPanelWidth = min(m_nPanelWidth, PRODUCTION_PANEL_MAX_WIDTH);
+ GetParent()->SendMessage(ID_MSG_PANEL_RESIZE, m_nPanelWidth, 0);
+ OnSize(0, 0, 0);
+
+ *result = 0;
+}
+
+void CPanelProduction::OnBnClickedButtonClose()
+{
+ CWnd* pParent = GetParent();
+ if (pParent != nullptr) {
+ pParent->PostMessage(WM_COMMAND, ID_MENU_WND_TEST_PANEL, 0);
+ }
+}
+
+BOOL CPanelProduction::TryGetDayNightSummaries(ProductionShiftSummary& outDay, ProductionShiftSummary& outNight)
+{
+ CSingleLock lock(&m_csShiftSummary, TRUE);
+ if (!m_bShiftSummaryValid) return FALSE;
+ outDay = m_daySummary;
+ outNight = m_nightSummary;
+ return TRUE;
+}
+
+void CPanelProduction::StartStatsThread()
+{
+ if (m_pStatsThread != nullptr) return;
+
+ m_evStopStats.ResetEvent();
+
+ m_pStatsThread = AfxBeginThread(&CPanelProduction::StatsThreadProc, this, THREAD_PRIORITY_BELOW_NORMAL, 0, 0);
+ if (m_pStatsThread != nullptr) {
+ m_pStatsThread->m_bAutoDelete = FALSE;
+ }
+}
+
+void CPanelProduction::StopStatsThread()
+{
+ if (m_pStatsThread == nullptr) return;
+
+ m_evStopStats.SetEvent();
+ const DWORD rc = WaitForSingleObject(m_pStatsThread->m_hThread, 5000);
+ if (rc == WAIT_OBJECT_0) {
+ delete m_pStatsThread;
+ }
+ m_pStatsThread = nullptr;
+}
+
+UINT CPanelProduction::StatsThreadProc(LPVOID pParam)
+{
+ CPanelProduction* self = reinterpret_cast<CPanelProduction*>(pParam);
+ if (self == nullptr) return 0;
+
+ const DWORD intervalMs = 5000;
+ for (;;) {
+ if (self->m_evStopStats.Lock(intervalMs)) break;
+
+ ProductionShiftSummary daySummary;
+ ProductionShiftSummary nightSummary;
+ if (ProductionStats::ComputeDayNightSummaries(theApp.m_model.m_configuration, daySummary, nightSummary)) {
+ CSingleLock lock(&self->m_csShiftSummary, TRUE);
+ self->m_daySummary = std::move(daySummary);
+ self->m_nightSummary = std::move(nightSummary);
+ self->m_bShiftSummaryValid = TRUE;
+ }
+ }
+
+ return 0;
+}
+
+void CPanelProduction::OnTimer(UINT_PTR nIDEvent)
+{
+ // TODO: 鍦ㄦ娣诲姞娑堟伅澶勭悊绋嬪簭浠g爜鍜�/鎴栬皟鐢ㄩ粯璁ゅ��
+ CDialogEx::OnTimer(nIDEvent);
+}
diff --git a/SourceCode/Bond/Servo/CPanelProduction.h b/SourceCode/Bond/Servo/CPanelProduction.h
new file mode 100644
index 0000000..a4bd705
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPanelProduction.h
@@ -0,0 +1,64 @@
+锘�#pragma once
+#include "BlButton.h"
+#include <afxmt.h>
+#include "AccordionWnd.h"
+#include "ProductionStats.h"
+#include "CPageProdOverview.h"
+#include "CPageCtrlState.h"
+
+// CPanelProduction dialog
+class CPanelProduction : public CDialogEx
+{
+ DECLARE_DYNAMIC(CPanelProduction)
+
+public:
+ CPanelProduction(CWnd* pParent = nullptr); // standard constructor
+ virtual ~CPanelProduction();
+ int getPanelWidth();
+ void setPanelWidth(int width);
+
+private:
+ COLORREF m_crBkgnd;
+ HBRUSH m_hbrBkgnd;
+ int m_nPanelWidth;
+ CBlButton m_btnClose;
+ HWND m_hPlaceholder;
+ CAccordionWnd* m_pAccordionWnd;
+
+ // Production shift summaries (updated by background thread)
+ ProductionShiftSummary m_daySummary;
+ ProductionShiftSummary m_nightSummary;
+ BOOL m_bShiftSummaryValid;
+ CCriticalSection m_csShiftSummary;
+ CWinThread* m_pStatsThread;
+ CEvent m_evStopStats;
+ CPageProdOverview* m_pPageProdOverview;
+ CPageCtrlState* m_pPageCtrlState;
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_PANEL_PRODUCTION };
+#endif
+
+ DECLARE_MESSAGE_MAP()
+public:
+ virtual BOOL OnInitDialog();
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg void OnDestroy();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnVLineMoveX(NMHDR* nmhdr, LRESULT* result);
+ afx_msg void OnBnClickedButtonClose();
+
+ // Thread-safe snapshots for UI timer display
+ BOOL TryGetDayNightSummaries(ProductionShiftSummary& outDay, ProductionShiftSummary& outNight);
+
+private:
+ static UINT AFX_CDECL StatsThreadProc(LPVOID pParam);
+ void StartStatsThread();
+ void StopStatsThread();
+public:
+ afx_msg void OnTimer(UINT_PTR nIDEvent);
+};
diff --git a/SourceCode/Bond/Servo/CParam.cpp b/SourceCode/Bond/Servo/CParam.cpp
index 57a8f0c..9162b12 100644
--- a/SourceCode/Bond/Servo/CParam.cpp
+++ b/SourceCode/Bond/Servo/CParam.cpp
@@ -53,7 +53,7 @@
return m_nValueType;
}
-int CParam::getIntValue()
+int CParam::getIntValue() const
{
return m_nValue;
}
@@ -63,7 +63,7 @@
m_nValue = value;
}
-double CParam::getDoubleValue()
+double CParam::getDoubleValue() const
{
if(m_nValueType == PVT_DOUBLE)
return m_fValue;
diff --git a/SourceCode/Bond/Servo/CParam.h b/SourceCode/Bond/Servo/CParam.h
index f8562a1..3e8dd1c 100644
--- a/SourceCode/Bond/Servo/CParam.h
+++ b/SourceCode/Bond/Servo/CParam.h
@@ -17,9 +17,9 @@
std::string& getName();
std::string& getUnit();
int getValueType();
- int getIntValue();
+ int getIntValue() const;
void setIntValue(int value);
- double getDoubleValue();
+ double getDoubleValue() const;
void setDoubleValue(double value);
void Serialize(CArchive& ar);
diff --git a/SourceCode/Bond/Servo/CReadStep.cpp b/SourceCode/Bond/Servo/CReadStep.cpp
index c6aa36f..06181b8 100644
--- a/SourceCode/Bond/Servo/CReadStep.cpp
+++ b/SourceCode/Bond/Servo/CReadStep.cpp
@@ -95,6 +95,7 @@
ASSERT(m_pEquipment);
m_pEquipment->onStepEvent(this, STEP_EVENT_READDATA);
}
+ if (m_bReadContinue) continue;
// 0426新增
// 1.1,写return code or data
diff --git a/SourceCode/Bond/Servo/CReadStep.h b/SourceCode/Bond/Servo/CReadStep.h
index dcb3c56..8d048a4 100644
--- a/SourceCode/Bond/Servo/CReadStep.h
+++ b/SourceCode/Bond/Servo/CReadStep.h
@@ -11,6 +11,7 @@
public:
unsigned WorkingProc();
+ virtual void setReadContinue(BOOL bContinue) { m_bReadContinue = bContinue; };
virtual void setWriteSignalDev(int dev);
virtual void setReturnDev(int dev);
virtual void onReadSignal(int nSignalType);
@@ -40,6 +41,7 @@
char m_szReturnBuf[1024];
int m_nReturnDataSize;
int m_nReturnDevNo;
+ BOOL m_bReadContinue{ FALSE };
};
}
diff --git a/SourceCode/Bond/Servo/CReport.cpp b/SourceCode/Bond/Servo/CReport.cpp
index 1ab15fa..29d57d3 100644
--- a/SourceCode/Bond/Servo/CReport.cpp
+++ b/SourceCode/Bond/Servo/CReport.cpp
@@ -8,12 +8,10 @@
m_nReportId = 0;
}
- CReport::CReport(unsigned int reportId, std::vector<unsigned int>& vids)
+ CReport::CReport(unsigned int reportId, const std::vector<unsigned int>& vids)
{
m_nReportId = reportId;
- for (auto vid : vids) {
- m_vids.push_back(vid);
- }
+ m_vids = vids;
}
CReport::~CReport()
diff --git a/SourceCode/Bond/Servo/CReport.h b/SourceCode/Bond/Servo/CReport.h
index 191d539..41952df 100644
--- a/SourceCode/Bond/Servo/CReport.h
+++ b/SourceCode/Bond/Servo/CReport.h
@@ -7,7 +7,7 @@
{
public:
CReport();
- CReport(unsigned int reportId, std::vector<unsigned int>& vids);
+ CReport(unsigned int reportId, const std::vector<unsigned int>& vids);
virtual ~CReport();
public:
@@ -18,6 +18,7 @@
std::vector<CVariable*>& getVariables();
std::string getVariablesIdsText();
bool getVariableName(unsigned int vid, std::string& strName);
+ const std::vector<unsigned int>& getVids() const { return m_vids; }
private:
unsigned int m_nReportId;
diff --git a/SourceCode/Bond/Servo/CReportEditDlg.cpp b/SourceCode/Bond/Servo/CReportEditDlg.cpp
new file mode 100644
index 0000000..5dd368b
--- /dev/null
+++ b/SourceCode/Bond/Servo/CReportEditDlg.cpp
@@ -0,0 +1,78 @@
+锘�#include "stdafx.h"
+#include "CReportEditDlg.h"
+#include "Servo.h"
+#include "resource.h"
+#include <algorithm>
+
+IMPLEMENT_DYNAMIC(CReportEditDlg, CDialogEx)
+
+CReportEditDlg::CReportEditDlg(const CString& title, int rptId, const std::vector<unsigned int>& vids, CWnd* pParent)
+ : CDialogEx(IDD_DIALOG_REPORT_EDIT, pParent)
+ , m_strTitle(title)
+ , m_rptId(rptId)
+ , m_vids(vids)
+{
+}
+
+CReportEditDlg::~CReportEditDlg()
+{
+}
+
+void CReportEditDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_EDIT_RPT_ID, m_editId);
+ DDX_Control(pDX, IDC_LIST_RPT_VARS, m_listVars);
+}
+
+BEGIN_MESSAGE_MAP(CReportEditDlg, CDialogEx)
+END_MESSAGE_MAP()
+
+BOOL CReportEditDlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+ SetWindowText(m_strTitle);
+
+ CString strId;
+ strId.Format(_T("%d"), m_rptId);
+ m_editId.SetWindowText(strId);
+ m_editId.SetReadOnly(TRUE);
+
+ // 鍒濆鍖栧垪琛�
+ m_listVars.SetExtendedStyle(m_listVars.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_CHECKBOXES);
+ m_listVars.InsertColumn(0, _T("ID"), LVCFMT_LEFT, 60);
+ m_listVars.InsertColumn(1, _T("鍚嶇О"), LVCFMT_LEFT, 140);
+ m_listVars.InsertColumn(2, _T("鏍煎紡"), LVCFMT_LEFT, 80);
+
+ auto& vars = theApp.m_model.m_hsmsPassive.getVariables();
+ for (int i = 0; i < (int)vars.size(); ++i) {
+ auto v = vars[i];
+ if (v == nullptr) continue;
+ int idx = m_listVars.InsertItem(m_listVars.GetItemCount(), std::to_string(v->getVarialbleId()).c_str());
+ m_listVars.SetItemText(idx, 1, v->getName().c_str());
+ m_listVars.SetItemText(idx, 2, SERVO::CVariable::formatToString(v->getFormat()).c_str());
+ m_listVars.SetItemData(idx, (DWORD_PTR)v->getVarialbleId());
+ if (std::find(m_vids.begin(), m_vids.end(), v->getVarialbleId()) != m_vids.end()) {
+ m_listVars.SetCheck(idx, TRUE);
+ }
+ }
+
+ return TRUE;
+}
+
+void CReportEditDlg::OnOK()
+{
+ std::vector<unsigned int> selected;
+ int count = m_listVars.GetItemCount();
+ for (int i = 0; i < count; ++i) {
+ if (m_listVars.GetCheck(i)) {
+ selected.push_back((unsigned int)m_listVars.GetItemData(i));
+ }
+ }
+ if (selected.empty()) {
+ AfxMessageBox(_T("鑷冲皯閫夋嫨涓�涓彉閲�"));
+ return;
+ }
+ m_vids.swap(selected);
+ CDialogEx::OnOK();
+}
diff --git a/SourceCode/Bond/Servo/CReportEditDlg.h b/SourceCode/Bond/Servo/CReportEditDlg.h
new file mode 100644
index 0000000..00fc4dc
--- /dev/null
+++ b/SourceCode/Bond/Servo/CReportEditDlg.h
@@ -0,0 +1,30 @@
+锘�#pragma once
+#include "afxdialogex.h"
+
+// 鎶ュ憡缂栬緫瀵硅瘽妗嗭紙鏂板/缂栬緫鍏辩敤锛�
+class CReportEditDlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CReportEditDlg)
+
+public:
+ CReportEditDlg(const CString& title, int rptId, const std::vector<unsigned int>& vids, CWnd* pParent = nullptr);
+ virtual ~CReportEditDlg();
+
+ int GetReportId() const { return m_rptId; }
+ const std::vector<unsigned int>& GetSelectedVids() const { return m_vids; }
+
+protected:
+ virtual BOOL OnInitDialog() override;
+ virtual void DoDataExchange(CDataExchange* pDX) override;
+ afx_msg void OnOK();
+
+ DECLARE_MESSAGE_MAP()
+
+private:
+ CString m_strTitle;
+ int m_rptId;
+ std::vector<unsigned int> m_vids;
+
+ CEdit m_editId;
+ CListCtrl m_listVars;
+};
diff --git a/SourceCode/Bond/Servo/CSVData.cpp b/SourceCode/Bond/Servo/CSVData.cpp
index fbda14e..a60a7f6 100644
--- a/SourceCode/Bond/Servo/CSVData.cpp
+++ b/SourceCode/Bond/Servo/CSVData.cpp
@@ -26,31 +26,35 @@
int CSVData::serialize(char* pszBuffer, int nBufferSize)
{
- if (nBufferSize < 133) return -1;
+ if (nBufferSize < 133 * 2) return -1;
int index = 0;
- CToolUnits::convertString(&pszBuffer[index], 8, m_strTime);
- index += 8;
+ CToolUnits::convertString(&pszBuffer[index], 8 * 2, m_strTime);
+ index += 8 * 2;
- memcpy(&pszBuffer[index], m_svRawData.data(), 125);
- index += 125;
+ memcpy(&pszBuffer[index], m_svRawData.data(), 125 * 2);
+ index += 125 * 2;
- return 133;
+ return 133 * 2;
}
int CSVData::unserialize(const char* pszBuffer, int nBufferSize)
{
- if (nBufferSize < 133) return -1;
+ if (pszBuffer == nullptr) return -1;
+ if (nBufferSize < 133 * 2) return -1;
int index = 0;
- CSVData svData;
CToolUnits::convertString(&pszBuffer[index], 8 * 2, m_strTime);
index += 8 * 2;
m_svRawData.clear();
- m_svRawData.insert(m_svRawData.end(), (uint8_t*)(&pszBuffer[index]), (uint8_t*)(pszBuffer)+(125 * 2));
+ if (nBufferSize < index + 125 * 2) return -1;
+ m_svRawData.insert(
+ m_svRawData.end(),
+ (const uint8_t*)&pszBuffer[index],
+ (const uint8_t*)&pszBuffer[index + 125 * 2]);
index += 125 * 2;
- return 133;
+ return 133 * 2;
}
}
diff --git a/SourceCode/Bond/Servo/CServoUtilsTool.cpp b/SourceCode/Bond/Servo/CServoUtilsTool.cpp
index c9d06f6..94bb8f7 100644
--- a/SourceCode/Bond/Servo/CServoUtilsTool.cpp
+++ b/SourceCode/Bond/Servo/CServoUtilsTool.cpp
@@ -1,32 +1,37 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "CServoUtilsTool.h"
#include "Common.h"
namespace SERVO {
static std::unordered_map<int, std::vector<std::string>> EQ_DATA_TYPES = {
- {EQ_ID_Bonder1, {
- "气囊压力", "上腔压力", "管道真空规值", "腔体真空规值",
- "上腔温度1", "上腔温度2", "上腔温度3", "上腔温度4",
- "上腔温度5", "上腔温度6", "下腔温度1", "下腔温度2",
- "下腔温度3", "下腔温度4", "下腔温度5", "下腔温度6"
+ {MID_Bonder1, {
+ "姘斿泭鍘嬪姏", "涓婅厰鍘嬪姏", "绠¢亾鐪熺┖瑙勫��", "鑵斾綋鐪熺┖瑙勫��",
+ "涓婅厰娓╁害1", "涓婅厰娓╁害2", "涓婅厰娓╁害3", "涓婅厰娓╁害4",
+ "涓婅厰娓╁害5", "涓婅厰娓╁害6", "涓嬭厰娓╁害1", "涓嬭厰娓╁害2",
+ "涓嬭厰娓╁害3", "涓嬭厰娓╁害4", "涓嬭厰娓╁害5", "涓嬭厰娓╁害6"
}},
- {EQ_ID_Bonder2, {
- "气囊压力", "上腔压力", "管道真空规值", "腔体真空规值",
- "上腔温度1", "上腔温度2", "上腔温度3", "上腔温度4",
- "上腔温度5", "上腔温度6", "下腔温度1", "下腔温度2",
- "下腔温度3", "下腔温度4", "下腔温度5", "下腔温度6"
+ {MID_Bonder2, {
+ "姘斿泭鍘嬪姏", "涓婅厰鍘嬪姏", "绠¢亾鐪熺┖瑙勫��", "鑵斾綋鐪熺┖瑙勫��",
+ "涓婅厰娓╁害1", "涓婅厰娓╁害2", "涓婅厰娓╁害3", "涓婅厰娓╁害4",
+ "涓婅厰娓╁害5", "涓婅厰娓╁害6", "涓嬭厰娓╁害1", "涓嬭厰娓╁害2",
+ "涓嬭厰娓╁害3", "涓嬭厰娓╁害4", "涓嬭厰娓╁害5", "涓嬭厰娓╁害6"
}},
- {EQ_ID_VACUUMBAKE, {
- "A腔真空规值", "A腔温控1", "A腔温控2", "A腔温控4",
- "A腔温控5", "A腔温控6", "A腔温控7", "B腔真空规值",
- "B腔温控1", "B腔温控2", "B腔温控4", "B腔温控5",
- "B腔温控6", "B腔温控7"
+ {MID_VacuumBakeA, {
+ "鐪熺┖瑙勫��", "娓╂帶1", "娓╂帶2", "娓╂帶4",
+ "娓╂帶5", "娓╂帶6", "娓╂帶7"
}},
- {EQ_ID_BAKE_COOLING, {
- "A烘烤温控1", "A烘烤温控2", "A烘烤温控4", "A烘烤温控5",
- "A烘烤温控6", "A烘烤温控7", "B烘烤温控1", "B烘烤温控2",
- "B烘烤温控4", "B烘烤温控5", "B烘烤温控6", "B烘烤温控7"
+ {MID_VacuumBakeB, {
+ "鐪熺┖瑙勫��", "娓╂帶1", "娓╂帶2", "娓╂帶4",
+ "娓╂帶5", "娓╂帶6", "娓╂帶7"
+ }},
+ {MID_BakeCoolingA, {
+ "鐑樼儰娓╂帶1", "鐑樼儰娓╂帶2", "鐑樼儰娓╂帶4", "鐑樼儰娓╂帶5",
+ "鐑樼儰娓╂帶6", "鐑樼儰娓╂帶7"
+ }},
+ {MID_BakeCoolingB, {
+ "鐑樼儰娓╂帶1", "鐑樼儰娓╂帶2", "鐑樼儰娓╂帶4", "鐑樼儰娓╂帶5",
+ "鐑樼儰娓╂帶6", "鐑樼儰娓╂帶7"
}}
};
@@ -98,8 +103,8 @@
}
if (eqid == EQ_ID_VACUUMBAKE) {
- if (unit == 0) return "烘烤A腔";
- if (unit == 1) return "烘烤B腔";
+ if (unit == 0) return "鐑樼儰A鑵�";
+ if (unit == 1) return "鐑樼儰B鑵�";
}
if (eqid == EQ_ID_Bonder1) {
@@ -112,10 +117,10 @@
if (eqid == EQ_ID_BAKE_COOLING) {
- if (unit == 0) return "后烘烤A腔";
- if (unit == 1) return "冷却A";
- if (unit == 2) return "后烘烤B腔";
- if (unit == 3) return "冷却B";
+ if (unit == 0) return "鍚庣儤鐑鑵�";
+ if (unit == 1) return "鍐峰嵈A";
+ if (unit == 2) return "鍚庣儤鐑鑵�";
+ if (unit == 3) return "鍐峰嵈B";
}
if (eqid == EQ_ID_MEASUREMENT) {
@@ -156,11 +161,11 @@
if (eqid == EQ_ID_VACUUMBAKE) {
if (unit == 0) {
- sprintf_s(szBuffer, 256, "烘烤A腔(Slot%d)", slot);
+ sprintf_s(szBuffer, 256, "鐑樼儰A鑵�(Slot%d)", slot);
return std::string(szBuffer);
}
if (unit == 1) {
- sprintf_s(szBuffer, 256, "烘烤B腔(Slot%d)", slot);
+ sprintf_s(szBuffer, 256, "鐑樼儰B鑵�(Slot%d)", slot);
return std::string(szBuffer);
}
}
@@ -177,10 +182,10 @@
if (eqid == EQ_ID_BAKE_COOLING) {
- if (slot == 0) return "后烘烤A腔";
- if (slot == 1) return "冷却A";
- if (slot == 2) return "后烘烤B腔";
- if (slot == 3) return "冷却B";
+ if (slot == 0) return "鍚庣儤鐑鑵�";
+ if (slot == 1) return "鍐峰嵈A";
+ if (slot == 2) return "鍚庣儤鐑鑵�";
+ if (slot == 3) return "鍐峰嵈B";
}
if (eqid == EQ_ID_MEASUREMENT) {
diff --git a/SourceCode/Bond/Servo/CServoUtilsTool.h b/SourceCode/Bond/Servo/CServoUtilsTool.h
index 2945984..06b167c 100644
--- a/SourceCode/Bond/Servo/CServoUtilsTool.h
+++ b/SourceCode/Bond/Servo/CServoUtilsTool.h
@@ -1,9 +1,16 @@
-#pragma once
+锘�#pragma once
#include "ServoCommo.h"
#include "CGlass.h"
namespace SERVO {
+ constexpr uint32_t MID_Bonder1 = 1001;
+ constexpr uint32_t MID_Bonder2 = 1002;
+ constexpr uint32_t MID_VacuumBakeA = 1003;
+ constexpr uint32_t MID_VacuumBakeB = 1004;
+ constexpr uint32_t MID_BakeCoolingA = 1005;
+ constexpr uint32_t MID_BakeCoolingB = 1006;
+
class CServoUtilsTool
{
public:
diff --git a/SourceCode/Bond/Servo/CUserEdit2Dlg.cpp b/SourceCode/Bond/Servo/CUserEdit2Dlg.cpp
new file mode 100644
index 0000000..7237f5d
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserEdit2Dlg.cpp
@@ -0,0 +1,125 @@
+锘�#include "stdafx.h"
+#include "CUserEdit2Dlg.h"
+#include "CUserManager2.h"
+#include "resource.h"
+
+IMPLEMENT_DYNAMIC(CUserEdit2Dlg, CDialogEx)
+
+CUserEdit2Dlg::CUserEdit2Dlg(bool editMode, CWnd* pParent /*=nullptr*/)
+ : CDialogEx(IDD_DIALOG_USER_EDIT2, pParent)
+{
+ m_bEditMode = editMode;
+}
+
+CUserEdit2Dlg::~CUserEdit2Dlg()
+{
+}
+
+void CUserEdit2Dlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+ DDX_Text(pDX, IDC_EDIT_USER_ACCOUNT, m_strUsername);
+ DDX_Text(pDX, IDC_EDIT_USER_DISPLAY, m_strDisplayName);
+ DDX_Text(pDX, IDC_EDIT_USER_PASSWORD, m_strPassword);
+ DDX_CBString(pDX, IDC_COMBO_USER_ROLE, m_strRole);
+ DDX_Check(pDX, IDC_CHECK_USER_ENABLED, m_bEnabled);
+}
+
+BEGIN_MESSAGE_MAP(CUserEdit2Dlg, CDialogEx)
+END_MESSAGE_MAP()
+
+BOOL CUserEdit2Dlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ if (m_bEditMode) {
+ if (auto pEdit = GetDlgItem(IDC_EDIT_USER_ACCOUNT)) {
+ pEdit->EnableWindow(FALSE);
+ }
+ }
+
+ UpdateData(FALSE);
+
+ auto roles = CUserManager2::getInstance().getRoles();
+ CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO_USER_ROLE);
+ if (pCombo) {
+ int selected = -1;
+ for (const auto& role : roles) {
+ CString text(role.name.c_str());
+ int idx = pCombo->AddString(text);
+ if (selected == -1 && m_strRole.CompareNoCase(text) == 0) {
+ selected = idx;
+ }
+ }
+
+ if (selected >= 0) {
+ pCombo->SetCurSel(selected);
+ }
+ else if (pCombo->GetCount() > 0) {
+ pCombo->SetCurSel(0);
+ CString text;
+ pCombo->GetLBText(0, text);
+ if (m_strRole.IsEmpty()) {
+ m_strRole = text;
+ }
+ }
+ }
+
+ if (auto pPwd = GetDlgItem(IDC_EDIT_USER_PASSWORD)) {
+ pPwd->EnableWindow(!m_bEditMode);
+ if (m_bEditMode) {
+ pPwd->SetWindowText(_T(""));
+ }
+ }
+
+ return TRUE;
+}
+
+void CUserEdit2Dlg::OnOK()
+{
+ UpdateData(TRUE);
+
+ CString user = m_strUsername;
+ user.Trim();
+ CString role = m_strRole;
+ role.Trim();
+
+ CString password = m_strPassword;
+ password.Trim();
+
+ if (m_bEditMode) {
+ password.Empty();
+ }
+
+ if (!m_bEditMode) {
+ if (user.IsEmpty()) {
+ AfxMessageBox(_T("璇疯緭鍏ヨ处鍙�"));
+ return;
+ }
+
+ if (password.IsEmpty()) {
+ AfxMessageBox(_T("璇疯緭鍏ュ瘑鐮�"));
+ return;
+ }
+ }
+
+ if (role.IsEmpty()) {
+ AfxMessageBox(_T("璇烽�夋嫨瑙掕壊"));
+ return;
+ }
+
+ if (auto pCombo = (CComboBox*)GetDlgItem(IDC_COMBO_USER_ROLE)) {
+ int sel = pCombo->GetCurSel();
+ if (sel != CB_ERR) {
+ CString text;
+ pCombo->GetLBText(sel, text);
+ if (!text.IsEmpty()) {
+ m_strRole = text;
+ }
+ }
+ }
+
+ m_strUsername = user;
+ m_strPassword = password;
+ CDialogEx::OnOK();
+}
diff --git a/SourceCode/Bond/Servo/CUserEdit2Dlg.h b/SourceCode/Bond/Servo/CUserEdit2Dlg.h
new file mode 100644
index 0000000..e61e745
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserEdit2Dlg.h
@@ -0,0 +1,23 @@
+#pragma once
+
+class CUserEdit2Dlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CUserEdit2Dlg)
+
+public:
+ CUserEdit2Dlg(bool editMode = false, CWnd* pParent = nullptr);
+ virtual ~CUserEdit2Dlg();
+
+ CString m_strUsername;
+ CString m_strDisplayName;
+ CString m_strPassword;
+ CString m_strRole;
+ BOOL m_bEnabled = TRUE;
+ bool m_bEditMode = false;
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+ virtual BOOL OnInitDialog();
+ afx_msg void OnOK();
+ DECLARE_MESSAGE_MAP()
+};
diff --git a/SourceCode/Bond/Servo/CUserManager2.cpp b/SourceCode/Bond/Servo/CUserManager2.cpp
new file mode 100644
index 0000000..402b2f6
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserManager2.cpp
@@ -0,0 +1,250 @@
+锘�#include "stdafx.h"
+#include "CUserManager2.h"
+#include "ToolUnits.h"
+#include <vector>
+#include <map>
+#include <utility>
+#include <algorithm>
+#include <sstream>
+#include <cwchar>
+
+std::vector<std::wstring> SplitLines(const std::wstring& text)
+{
+ std::wstringstream ss(text); std::vector<std::wstring> v; std::wstring line; while (std::getline(ss, line)) v.push_back(line); return v;
+}
+
+std::vector<std::wstring> SplitByDelimiter(const std::wstring& text, wchar_t delimiter)
+{
+ std::vector<std::wstring> parts;
+ size_t start = 0;
+ while (start <= text.length()) {
+ size_t pos = text.find(delimiter, start);
+ if (pos == std::wstring::npos) {
+ parts.push_back(text.substr(start));
+ break;
+ }
+
+ parts.push_back(text.substr(start, pos - start));
+ start = pos + 1;
+ }
+
+ return parts;
+}
+
+template<typename Fn>
+std::wstring ReadBufferVia(Fn fn)
+{
+ int need = fn(nullptr, 0); if (need <= 0) return L"";
+ std::wstring buf; buf.resize((size_t)need);
+ int rc = fn(buf.data(), need);
+ if (rc == 0) { if (!buf.empty() && buf.back() == L'\0') buf.pop_back(); return buf; }
+ return L"";
+}
+
+// 鑾峰彇鍗曚緥瀹炰緥
+CUserManager2& CUserManager2::getInstance() {
+ static CUserManager2 instance;
+ return instance;
+}
+
+CUserManager2::CUserManager2()
+{
+
+}
+
+CUserManager2::~CUserManager2()
+{
+
+}
+
+void CUserManager2::init(const char* pszDir)
+{
+ std::wstring dir = CToolUnits::AnsiToWString(std::string(pszDir));
+ UX_Init(dir.c_str());
+
+ wchar_t buffer[1024];
+ UX_GetUsers(buffer, 1024);
+ bool hasAny = false;
+ for (auto& ln : SplitLines(buffer)) { if (!ln.empty()) { hasAny = true; break; } }
+ if (!hasAny) {
+ const wchar_t* roles = L"Admin:100\nEE:80\nPE:50\nOperator:10\n";
+ (void)UX_SetRoleDefinitions(roles);
+ (void)UX_AddUser(L"admin", L"Administrator", L"admin123", L"Admin");
+
+ UX_DefineAction(L"start", L"鍚姩鏈哄彴", L"Operator");
+ UX_DefineAction(L"stop", L"鍋滄満", L"Operator");
+ UX_DefineAction(L"recipe", L"缂栬緫閰嶆柟", L"PE");
+ UX_DefineAction(L"delVarialbles", L"鍒犻櫎鍙橀噺", L"PE");
+ UX_DefineAction(L"addVarialbles", L"鏂板鍙橀噺", L"PE");
+ UX_DefineAction(L"editVarialbles", L"缂栬緫鍙橀噺", L"PE");
+ UX_DefineAction(L"addReports", L"鏂板Report", L"PE");
+ UX_DefineAction(L"editReports", L"缂栬緫Report", L"PE");
+ UX_DefineAction(L"delReports", L"鍒犻櫎Report", L"PE");
+ UX_DefineAction(L"addEvents", L"鏂板Event", L"PE");
+ UX_DefineAction(L"editEvents", L"缂栬緫Event", L"PE");
+ UX_DefineAction(L"delEvents", L"鍒犻櫎Event", L"PE");
+ }
+ // 纭繚鏉冮檺瀹氫箟瀛樺湪锛堝箓绛夛級
+ UX_DefineAction(L"addVarialbles", L"鏂板鍙橀噺", L"PE");
+ UX_DefineAction(L"editVarialbles", L"缂栬緫鍙橀噺", L"PE");
+ UX_DefineAction(L"delVarialbles", L"鍒犻櫎鍙橀噺", L"PE");
+ UX_DefineAction(L"addReports", L"鏂板Report", L"PE");
+ UX_DefineAction(L"editReports", L"缂栬緫Report", L"PE");
+ UX_DefineAction(L"delReports", L"鍒犻櫎Report", L"PE");
+ UX_DefineAction(L"delEvents", L"鍒犻櫎Event", L"PE");
+ UX_DefineAction(L"addEvents", L"鏂板Event", L"PE");
+ UX_DefineAction(L"editEvents", L"缂栬緫Event", L"PE");
+}
+
+bool CUserManager2::login(const char* pszAccount, const char* pszPwd)
+{
+ std::wstring strUser, strPwd;
+ strUser = CToolUnits::AnsiToWString(std::string(pszAccount));
+ strPwd = CToolUnits::AnsiToWString(std::string(pszPwd));
+ int rc = UX_Login(strUser.c_str(), strPwd.c_str());
+ return rc == UX_OK;
+}
+
+bool CUserManager2::isLoggedIn()
+{
+ return UX_IsLoggedIn();
+}
+
+std::string CUserManager2::getCurrentUserName()
+{
+ std::string strName;
+
+ int need = UX_GetCurrentUser(nullptr, 0);
+ std::wstring buf; buf.resize((size_t)need);
+ if (UX_GetCurrentUser(buf.data(), need) == UX_OK) {
+ if (!buf.empty() && buf.back() == L'\0')
+ buf.pop_back();
+
+ strName = CToolUnits::WStringToAnsi(buf);
+ }
+
+ return strName;
+}
+
+bool CUserManager2::IsAdminCurrent()
+{
+ if (UX_IsLoggedIn() != 1) return false;
+ int need = UX_GetCurrentUser(nullptr, 0); if (need <= 0) return false;
+ std::wstring user; user.resize((size_t)need);
+ if (UX_GetCurrentUser(user.data(), need) != 0) return false;
+ if (!user.empty() && user.back() == L'\0') user.pop_back();
+ int maxLvl = 0; auto rolesTxt = ReadBufferVia([](wchar_t* b, int n) { return UX_GetRoles(b, n); });
+ for (auto& ln : SplitLines(rolesTxt)) { size_t p = ln.find(L':'); if (p != std::wstring::npos) { int lvl = _wtoi(ln.substr(p + 1).c_str()); if (lvl > maxLvl) maxLvl = lvl; } }
+ int myLvl = 0; auto usersTxt = ReadBufferVia([](wchar_t* b, int n) { return UX_GetUsers(b, n); });
+ for (auto& ln : SplitLines(usersTxt)) {
+ if (ln.empty()) continue; size_t p1 = ln.find(L','), p2 = ln.find(L',', p1 == std::wstring::npos ? 0 : p1 + 1), p3 = ln.find(L',', p2 == std::wstring::npos ? 0 : p2 + 1);
+ std::wstring name = (p1 == std::wstring::npos ? ln : ln.substr(0, p1)); if (name == user) { if (p2 != std::wstring::npos) { std::wstring lvlS = ln.substr(p2 + 1, (p3 == std::wstring::npos ? ln.size() : p3) - (p2 + 1)); myLvl = _wtoi(lvlS.c_str()); } break; }
+ }
+
+ return (maxLvl > 0) && (myLvl >= maxLvl);
+}
+
+std::vector<CUserManager2::RoleInfo> CUserManager2::getRoles()
+{
+ std::vector<RoleInfo> roles;
+ auto txt = ReadBufferVia([](wchar_t* b, int n) { return UX_GetRoles(b, n); });
+ if (txt.empty()) {
+ return roles;
+ }
+
+ for (auto& line : SplitLines(txt)) {
+ if (line.empty()) continue;
+ size_t pos = line.find(L':');
+ RoleInfo info;
+ info.name = (pos == std::wstring::npos) ? line : line.substr(0, pos);
+ if (pos != std::wstring::npos) {
+ info.level = _wtoi(line.substr(pos + 1).c_str());
+ }
+
+ if (!info.name.empty()) {
+ roles.push_back(std::move(info));
+ }
+ }
+
+ std::sort(roles.begin(), roles.end(), [](const RoleInfo& a, const RoleInfo& b) {
+ if (a.level == b.level) {
+ return a.name < b.name;
+ }
+ return a.level > b.level;
+ });
+
+ return roles;
+}
+std::vector<CUserManager2::UserInfo> CUserManager2::getUsers()
+{
+ std::vector<UserInfo> users;
+ auto txt = ReadBufferVia([](wchar_t* b, int n) { return UX_GetUsers(b, n); });
+ if (txt.empty()) {
+ return users;
+ }
+
+ std::map<int, std::wstring> roleMap;
+ for (auto& role : getRoles()) {
+ roleMap[role.level] = role.name;
+ }
+
+ for (auto& line : SplitLines(txt)) {
+ if (line.empty()) continue;
+ auto parts = SplitByDelimiter(line, L',');
+ UserInfo info;
+ if (!parts.empty()) info.userName = parts[0];
+ if (parts.size() > 1) info.displayName = parts[1];
+ if (parts.size() > 2) info.roleLevel = _wtoi(parts[2].c_str());
+ if (parts.size() > 3) info.enabled = (_wtoi(parts[3].c_str()) != 0);
+ auto it = roleMap.find(info.roleLevel);
+ if (it != roleMap.end()) {
+ info.roleName = it->second;
+ }
+ users.push_back(std::move(info));
+ }
+
+ return users;
+}
+
+int CUserManager2::addUser(const std::wstring& userName, const std::wstring& displayName,
+ const std::wstring& password, const std::wstring& roleName, bool enabled)
+{
+ int rc = UX_AddUser(userName.c_str(), displayName.c_str(), password.c_str(), roleName.c_str());
+ if (rc == UX_OK && !enabled) {
+ UX_EnableUser(userName.c_str(), 0);
+ }
+
+ return rc;
+}
+
+int CUserManager2::updateUser(const std::wstring& userName, const std::wstring& displayName,
+ const std::wstring& password, const std::wstring& roleName, bool enabled)
+{
+ const wchar_t* disp = displayName.empty() ? nullptr : displayName.c_str();
+ const wchar_t* pwd = password.empty() ? nullptr : password.c_str();
+ const wchar_t* role = roleName.empty() ? nullptr : roleName.c_str();
+ return UX_UpdateUser(userName.c_str(), disp, pwd, role, enabled ? 1 : 0);
+}
+
+int CUserManager2::deleteUser(const std::wstring& userName)
+{
+ return UX_DeleteUser(userName.c_str());
+}
+
+int CUserManager2::setUserEnabled(const std::wstring& userName, bool enabled)
+{
+ return UX_EnableUser(userName.c_str(), enabled ? 1 : 0);
+}
+
+int CUserManager2::resetPassword(const std::wstring& userName, const std::wstring& password)
+{
+ return UX_ResetPassword(userName.c_str(), password.c_str());
+}
+
+
+
+
+
+
+
+
diff --git a/SourceCode/Bond/Servo/CUserManager2.h b/SourceCode/Bond/Servo/CUserManager2.h
new file mode 100644
index 0000000..794f454
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserManager2.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+class CUserManager2
+{
+public:
+ static CUserManager2& getInstance();
+ CUserManager2(const CUserManager2&) = delete;
+ CUserManager2& operator=(const CUserManager2&) = delete;
+
+ struct RoleInfo
+ {
+ std::wstring name;
+ int level = 0;
+ };
+
+ struct UserInfo
+ {
+ std::wstring userName;
+ std::wstring displayName;
+ std::wstring roleName;
+ int roleLevel = 0;
+ bool enabled = false;
+ };
+
+public:
+ void init(const char* pszDir);
+ bool login(const char* pszAccount, const char* pszPwd);
+ bool isLoggedIn();
+ std::string getCurrentUserName();
+ bool IsAdminCurrent();
+ std::vector<RoleInfo> getRoles();
+ std::vector<UserInfo> getUsers();
+ int addUser(const std::wstring& userName, const std::wstring& displayName,
+ const std::wstring& password, const std::wstring& roleName, bool enabled);
+ int updateUser(const std::wstring& userName, const std::wstring& displayName,
+ const std::wstring& password, const std::wstring& roleName, bool enabled);
+ int deleteUser(const std::wstring& userName);
+ int setUserEnabled(const std::wstring& userName, bool enabled);
+ int resetPassword(const std::wstring& userName, const std::wstring& password);
+
+private:
+ CUserManager2();
+ ~CUserManager2();
+};
+
diff --git a/SourceCode/Bond/Servo/CUserManager2Dlg.cpp b/SourceCode/Bond/Servo/CUserManager2Dlg.cpp
new file mode 100644
index 0000000..84be473
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserManager2Dlg.cpp
@@ -0,0 +1,322 @@
+锘�// CUserManager2Dlg.cpp
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "CUserManager2Dlg.h"
+#include "afxdialogex.h"
+#include "CUserEdit2Dlg.h"
+#include "InputDialog.h"
+#include "resource.h"
+#include "ToolUnits.h"
+
+IMPLEMENT_DYNAMIC(CUserManager2Dlg, CDialogEx)
+
+CUserManager2Dlg::CUserManager2Dlg(CWnd* pParent)
+ : CDialogEx(IDD_DIALOG_USER_MANAGER2, pParent)
+{
+}
+
+CUserManager2Dlg::~CUserManager2Dlg()
+{
+}
+
+void CUserManager2Dlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_LIST1, m_listUsers);
+}
+
+BEGIN_MESSAGE_MAP(CUserManager2Dlg, CDialogEx)
+ ON_WM_SIZE()
+ ON_BN_CLICKED(IDC_BUTTON_ADD, &CUserManager2Dlg::OnBnClickedButtonAdd)
+ ON_BN_CLICKED(IDC_BUTTON_EDIT, &CUserManager2Dlg::OnBnClickedButtonEdit)
+ ON_BN_CLICKED(IDC_BUTTON_DEL, &CUserManager2Dlg::OnBnClickedButtonDel)
+ ON_BN_CLICKED(IDC_BUTTON_RESET_PWD, &CUserManager2Dlg::OnBnClickedButtonResetPwd)
+ ON_BN_CLICKED(IDC_BUTTON_ENABLE, &CUserManager2Dlg::OnBnClickedButtonEnable)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CUserManager2Dlg::OnLvnItemchangedUsers)
+END_MESSAGE_MAP()
+
+BOOL CUserManager2Dlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ InitList();
+ RefreshUserList();
+ UpdateButtonState();
+
+ return TRUE;
+}
+
+void CUserManager2Dlg::OnSize(UINT nType, int cx, int cy)
+{
+ CDialogEx::OnSize(nType, cx, cy);
+}
+
+void CUserManager2Dlg::InitList()
+{
+ DWORD dwStyle = m_listUsers.GetExtendedStyle();
+ m_listUsers.SetExtendedStyle(dwStyle | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
+ m_listUsers.InsertColumn(0, _T("璐﹀彿"), LVCFMT_LEFT, 90);
+ m_listUsers.InsertColumn(1, _T("鏄剧ず鍚�"), LVCFMT_LEFT, 100);
+ m_listUsers.InsertColumn(2, _T("瑙掕壊"), LVCFMT_LEFT, 80);
+ m_listUsers.InsertColumn(3, _T("绾у埆"), LVCFMT_LEFT, 60);
+ m_listUsers.InsertColumn(4, _T("鐘舵��"), LVCFMT_LEFT, 70);
+}
+
+void CUserManager2Dlg::RefreshUserList()
+{
+ CString selectedName;
+ int currentIndex = GetSelectedIndex();
+ if (currentIndex >= 0 && currentIndex < static_cast<int>(m_users.size())) {
+ selectedName = m_users[currentIndex].userName.c_str();
+ }
+
+ m_users = CUserManager2::getInstance().getUsers();
+ m_listUsers.DeleteAllItems();
+
+ for (size_t i = 0; i < m_users.size(); ++i) {
+ const auto& user = m_users[i];
+ CString account(user.userName.c_str());
+ CString display(user.displayName.c_str());
+ CString role(user.roleName.empty() ? L"-" : user.roleName.c_str());
+ CString level;
+ level.Format(_T("%d"), user.roleLevel);
+ CString state = user.enabled ? _T("鍚敤") : _T("绂佺敤");
+
+ int row = m_listUsers.InsertItem(static_cast<int>(i), account);
+ m_listUsers.SetItemText(row, 1, display);
+ m_listUsers.SetItemText(row, 2, role);
+ m_listUsers.SetItemText(row, 3, level);
+ m_listUsers.SetItemText(row, 4, state);
+ }
+
+ if (!selectedName.IsEmpty()) {
+ for (int i = 0; i < m_listUsers.GetItemCount(); ++i) {
+ if (selectedName.CompareNoCase(m_listUsers.GetItemText(i, 0)) == 0) {
+ m_listUsers.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
+ m_listUsers.EnsureVisible(i, FALSE);
+ break;
+ }
+ }
+ }
+ UpdateButtonState();
+}
+
+void CUserManager2Dlg::UpdateButtonState()
+{
+ int index = GetSelectedIndex();
+ BOOL hasSelection = (index >= 0);
+
+ auto enable = [&](int id, BOOL enableFlag) {
+ if (CWnd* p = GetDlgItem(id)) {
+ p->EnableWindow(enableFlag);
+ }
+ };
+
+ enable(IDC_BUTTON_EDIT, hasSelection);
+ enable(IDC_BUTTON_DEL, hasSelection);
+ enable(IDC_BUTTON_RESET_PWD, hasSelection);
+ enable(IDC_BUTTON_ENABLE, hasSelection);
+
+ CString toggleText = _T("绂佺敤/鍚敤");
+ if (const auto* user = GetSelectedUser()) {
+ toggleText = user->enabled ? _T("绂佺敤") : _T("鍚敤");
+ if (IsCurrentUser(*user)) {
+ enable(IDC_BUTTON_DEL, FALSE);
+ enable(IDC_BUTTON_ENABLE, FALSE);
+ }
+ }
+
+ SetDlgItemText(IDC_BUTTON_ENABLE, toggleText);
+}
+
+int CUserManager2Dlg::GetSelectedIndex() const
+{
+ if (!::IsWindow(m_listUsers.GetSafeHwnd())) {
+ return -1;
+ }
+ return m_listUsers.GetNextItem(-1, LVNI_SELECTED);
+}
+
+const CUserManager2::UserInfo* CUserManager2Dlg::GetSelectedUser() const
+{
+ int index = GetSelectedIndex();
+ if (index < 0 || index >= static_cast<int>(m_users.size())) {
+ return nullptr;
+ }
+ return &m_users[index];
+}
+
+std::wstring CUserManager2Dlg::ToWString(const CString& text) const
+{
+ CString trimmed(text);
+ trimmed.Trim();
+
+ std::string str((LPTSTR)(LPCTSTR)trimmed);
+ return CToolUnits::AnsiToWString(str);
+}
+
+void CUserManager2Dlg::ShowErrorMessage(const CString& action, int code)
+{
+ const wchar_t* detail = UX_ErrorMessage(code);
+ CString msg;
+ msg.Format(_T("%s: %s"), action.GetString(), detail ? detail : L"Unknown");
+ AfxMessageBox(msg, MB_ICONERROR);
+}
+
+bool CUserManager2Dlg::IsCurrentUser(const CUserManager2::UserInfo& info) const
+{
+ CString current(CUserManager2::getInstance().getCurrentUserName().c_str());
+ CString account(info.userName.c_str());
+ current.Trim();
+ account.Trim();
+ return !current.IsEmpty() && current.CompareNoCase(account) == 0;
+}
+
+void CUserManager2Dlg::OnBnClickedButtonAdd()
+{
+ CUserEdit2Dlg dlg(false, this);
+ if (dlg.DoModal() != IDOK) {
+ return;
+ }
+
+ CString account = dlg.m_strUsername;
+ account.Trim();
+ CString display = dlg.m_strDisplayName;
+ display.Trim();
+ if (display.IsEmpty()) {
+ display = account;
+ }
+ CString role = dlg.m_strRole;
+ role.Trim();
+ CString password = dlg.m_strPassword;
+ password.Trim();
+
+ int rc = CUserManager2::getInstance().addUser(ToWString(account), ToWString(display), ToWString(password), ToWString(role), dlg.m_bEnabled == TRUE);
+ if (rc == UX_OK) {
+ RefreshUserList();
+ AfxMessageBox(_T("鏂板鐢ㄦ埛鎴愬姛"));
+ }
+ else {
+ ShowErrorMessage(_T("鏂板鐢ㄦ埛澶辫触"), rc);
+ }
+}
+
+void CUserManager2Dlg::OnBnClickedButtonEdit()
+{
+ const auto* user = GetSelectedUser();
+ if (!user) {
+ AfxMessageBox(_T("璇烽�夋嫨鐢ㄦ埛"));
+ return;
+ }
+
+ CUserEdit2Dlg dlg(true, this);
+ dlg.m_strUsername = user->userName.c_str();
+ dlg.m_strDisplayName = user->displayName.c_str();
+ dlg.m_strRole = user->roleName.c_str();
+ dlg.m_bEnabled = user->enabled ? TRUE : FALSE;
+ if (dlg.DoModal() != IDOK) {
+ return;
+ }
+
+ CString display = dlg.m_strDisplayName;
+ display.Trim();
+ CString password = dlg.m_strPassword;
+ password.Trim();
+ CString role = dlg.m_strRole;
+ role.Trim();
+
+ int rc = CUserManager2::getInstance().updateUser(user->userName, ToWString(display), ToWString(password), ToWString(role), dlg.m_bEnabled == TRUE);
+ if (rc == UX_OK) {
+ RefreshUserList();
+ AfxMessageBox(_T("淇濆瓨鎴愬姛"));
+ }
+ else {
+ ShowErrorMessage(_T("淇濆瓨澶辫触"), rc);
+ }
+}
+
+void CUserManager2Dlg::OnBnClickedButtonDel()
+{
+ const auto* user = GetSelectedUser();
+ if (!user) {
+ AfxMessageBox(_T("璇烽�夋嫨鐢ㄦ埛"));
+ return;
+ }
+
+ if (IsCurrentUser(*user)) {
+ AfxMessageBox(_T("涓嶈兘鍒犻櫎褰撳墠鐧诲綍鐢ㄦ埛"));
+ return;
+ }
+
+ CString prompt;
+ prompt.Format(_T("纭畾鍒犻櫎鐢ㄦ埛 %s ?"), CString(user->userName.c_str()));
+ if (AfxMessageBox(prompt, MB_ICONQUESTION | MB_YESNO) != IDYES) {
+ return;
+ }
+
+ int rc = CUserManager2::getInstance().deleteUser(user->userName);
+ if (rc == UX_OK) {
+ RefreshUserList();
+ AfxMessageBox(_T("鍒犻櫎鎴愬姛"));
+ }
+ else {
+ ShowErrorMessage(_T("鍒犻櫎澶辫触"), rc);
+ }
+}
+
+void CUserManager2Dlg::OnBnClickedButtonResetPwd()
+{
+ const auto* user = GetSelectedUser();
+ if (!user) {
+ AfxMessageBox(_T("璇烽�夋嫨鐢ㄦ埛"));
+ return;
+ }
+
+ CInputDialog dlg(_T("閲嶇疆瀵嗙爜"), _T("璇疯緭鍏ユ柊瀵嗙爜:"), this);
+ if (dlg.DoModal() != IDOK) {
+ return;
+ }
+
+ CString password = dlg.GetInputText();
+ password.Trim();
+ if (password.IsEmpty()) {
+ AfxMessageBox(_T("瀵嗙爜涓嶈兘涓虹┖"));
+ return;
+ }
+
+ int rc = CUserManager2::getInstance().resetPassword(user->userName, ToWString(password));
+ if (rc == UX_OK) {
+ AfxMessageBox(_T("瀵嗙爜宸查噸缃�"));
+ }
+ else {
+ ShowErrorMessage(_T("閲嶇疆澶辫触"), rc);
+ }
+}
+
+void CUserManager2Dlg::OnBnClickedButtonEnable()
+{
+ const auto* user = GetSelectedUser();
+ if (!user) {
+ AfxMessageBox(_T("璇烽�夋嫨鐢ㄦ埛"));
+ return;
+ }
+
+ bool enable = !user->enabled;
+ int rc = CUserManager2::getInstance().setUserEnabled(user->userName, enable);
+ if (rc == UX_OK) {
+ RefreshUserList();
+ CString msg = enable ? _T("鐢ㄦ埛宸插惎鐢�") : _T("鐢ㄦ埛宸茬鐢�");
+ AfxMessageBox(msg);
+ }
+ else {
+ ShowErrorMessage(_T("鎿嶄綔澶辫触"), rc);
+ }
+}
+
+void CUserManager2Dlg::OnLvnItemchangedUsers(NMHDR* /*pNMHDR*/, LRESULT* pResult)
+{
+ UpdateButtonState();
+ *pResult = 0;
+}
diff --git a/SourceCode/Bond/Servo/CUserManager2Dlg.h b/SourceCode/Bond/Servo/CUserManager2Dlg.h
new file mode 100644
index 0000000..2a2507c
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserManager2Dlg.h
@@ -0,0 +1,45 @@
+锘�#pragma once
+
+#include "CUserManager2.h"
+#include <string>
+#include <vector>
+
+class CUserManager2Dlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CUserManager2Dlg)
+
+public:
+ CUserManager2Dlg(CWnd* pParent = nullptr);
+ virtual ~CUserManager2Dlg();
+
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_DIALOG_USER_MANAGER2 };
+#endif
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+
+ DECLARE_MESSAGE_MAP()
+public:
+ virtual BOOL OnInitDialog();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnBnClickedButtonAdd();
+ afx_msg void OnBnClickedButtonEdit();
+ afx_msg void OnBnClickedButtonDel();
+ afx_msg void OnBnClickedButtonResetPwd();
+ afx_msg void OnBnClickedButtonEnable();
+ afx_msg void OnLvnItemchangedUsers(NMHDR* pNMHDR, LRESULT* pResult);
+
+private:
+ CListCtrl m_listUsers;
+ std::vector<CUserManager2::UserInfo> m_users;
+
+ void InitList();
+ void RefreshUserList();
+ void UpdateButtonState();
+ int GetSelectedIndex() const;
+ const CUserManager2::UserInfo* GetSelectedUser() const;
+ std::wstring ToWString(const CString& text) const;
+ void ShowErrorMessage(const CString& action, int code);
+ bool IsCurrentUser(const CUserManager2::UserInfo& info) const;
+};
diff --git a/SourceCode/Bond/Servo/CUserXLogDlg.cpp b/SourceCode/Bond/Servo/CUserXLogDlg.cpp
new file mode 100644
index 0000000..58e8950
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserXLogDlg.cpp
@@ -0,0 +1,178 @@
+锘�#include "stdafx.h"
+#include "Servo.h"
+#include "CUserXLogDlg.h"
+#include "afxdialogex.h"
+#include <functional>
+#include <vector>
+#include <sstream>
+
+namespace
+{
+ std::wstring ReadBufferVia(const std::function<int(wchar_t*, int)>& fn)
+ {
+ int need = fn(nullptr, 0);
+ if (need <= 0) {
+ return L"";
+ }
+
+ std::wstring buffer;
+ buffer.resize(static_cast<size_t>(need));
+ if (fn(buffer.data(), need) == UX_OK) {
+ if (!buffer.empty() && buffer.back() == L'\0') {
+ buffer.pop_back();
+ }
+ return buffer;
+ }
+
+ return L"";
+ }
+
+ std::vector<std::wstring> SplitLines(const std::wstring& text)
+ {
+ std::vector<std::wstring> lines;
+ std::wstringstream ss(text);
+ std::wstring line;
+ while (std::getline(ss, line)) {
+ lines.push_back(line);
+ }
+ return lines;
+ }
+}
+
+IMPLEMENT_DYNAMIC(CUserXLogDlg, CDialogEx)
+
+CUserXLogDlg::CUserXLogDlg(CWnd* pParent)
+ : CDialogEx(IDD_DIALOG_USERX_LOG, pParent)
+{
+}
+
+CUserXLogDlg::~CUserXLogDlg()
+{
+}
+
+void CUserXLogDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_LIST1, m_listLogs);
+}
+
+BEGIN_MESSAGE_MAP(CUserXLogDlg, CDialogEx)
+ ON_WM_SIZE()
+ ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+BOOL CUserXLogDlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ InitListCtrl();
+ RefreshLogs();
+ AdjustLayout();
+
+ return TRUE;
+}
+
+void CUserXLogDlg::InitListCtrl()
+{
+ DWORD dwStyle = m_listLogs.GetExtendedStyle();
+ m_listLogs.SetExtendedStyle(dwStyle | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
+ m_listLogs.InsertColumn(0, _T("鏃堕棿"), LVCFMT_LEFT, 180);
+ m_listLogs.InsertColumn(1, _T("鐢ㄦ埛"), LVCFMT_LEFT, 120);
+ m_listLogs.InsertColumn(2, _T("鍔ㄤ綔"), LVCFMT_LEFT, 120);
+ m_listLogs.InsertColumn(3, _T("鎻忚堪"), LVCFMT_LEFT, 200);
+}
+
+void CUserXLogDlg::RefreshLogs()
+{
+ m_logs.clear();
+ m_listLogs.DeleteAllItems();
+
+ auto allLogs = ReadBufferVia([](wchar_t* buffer, int size) {
+ return UX_QueryLogs(200, buffer, size);
+ });
+
+ for (auto& rawLine : SplitLines(allLogs)) {
+ if (rawLine.empty()) continue;
+
+ auto trim = [](std::wstring value) {
+ size_t first = value.find_first_not_of(L" \t\r\n");
+ size_t last = value.find_last_not_of(L" \t\r\n");
+ if (first == std::wstring::npos || last == std::wstring::npos) {
+ return std::wstring();
+ }
+ return value.substr(first, last - first + 1);
+ };
+
+ auto takeField = [&](size_t& cursor) {
+ if (cursor == std::wstring::npos || cursor >= rawLine.length()) {
+ return std::wstring();
+ }
+ size_t pos = rawLine.find(L',', cursor);
+ std::wstring part = (pos == std::wstring::npos) ? rawLine.substr(cursor) : rawLine.substr(cursor, pos - cursor);
+ cursor = (pos == std::wstring::npos) ? std::wstring::npos : pos + 1;
+ return trim(part);
+ };
+
+ size_t cursor = 0;
+ LogItem item;
+ item.time = takeField(cursor).c_str();
+ item.user = takeField(cursor).c_str();
+ item.action = takeField(cursor).c_str();
+ if (cursor != std::wstring::npos && cursor < rawLine.length()) {
+ item.detail = trim(rawLine.substr(cursor)).c_str();
+ }
+
+ m_logs.push_back(item);
+ }
+
+ for (size_t i = 0; i < m_logs.size(); ++i) {
+ const auto& log = m_logs[i];
+ int row = m_listLogs.InsertItem(static_cast<int>(i), log.time);
+ m_listLogs.SetItemText(row, 1, log.user);
+ m_listLogs.SetItemText(row, 2, log.action);
+ m_listLogs.SetItemText(row, 3, log.detail);
+ }
+}
+
+void CUserXLogDlg::AdjustLayout()
+{
+ if (!::IsWindow(m_listLogs.GetSafeHwnd())) {
+ return;
+ }
+
+ CRect rcClient;
+ GetClientRect(&rcClient);
+ const int margin = 7;
+
+ CRect rcList(margin, margin, rcClient.right - margin, rcClient.bottom - 40);
+ m_listLogs.MoveWindow(rcList);
+
+ auto moveButton = [&](int id, int order) {
+ if (CWnd* pBtn = GetDlgItem(id)) {
+ CRect rc;
+ pBtn->GetWindowRect(&rc);
+ ScreenToClient(&rc);
+ int width = rc.Width();
+ int height = rc.Height();
+ rc.left = rcClient.right - margin - width - order * (width + margin);
+ rc.right = rc.left + width;
+ rc.top = rcClient.bottom - margin - height;
+ rc.bottom = rc.top + height;
+ pBtn->MoveWindow(rc);
+ }
+ };
+
+ moveButton(IDOK, 1);
+ moveButton(IDCANCEL, 0);
+}
+
+void CUserXLogDlg::OnSize(UINT nType, int cx, int cy)
+{
+ CDialogEx::OnSize(nType, cx, cy);
+ AdjustLayout();
+}
+
+void CUserXLogDlg::OnDestroy()
+{
+ CDialogEx::OnDestroy();
+}
diff --git a/SourceCode/Bond/Servo/CUserXLogDlg.h b/SourceCode/Bond/Servo/CUserXLogDlg.h
new file mode 100644
index 0000000..e28a810
--- /dev/null
+++ b/SourceCode/Bond/Servo/CUserXLogDlg.h
@@ -0,0 +1,42 @@
+锘�#pragma once
+
+#include <vector>
+#include <string>
+
+class CUserXLogDlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CUserXLogDlg)
+
+public:
+ CUserXLogDlg(CWnd* pParent = nullptr);
+ virtual ~CUserXLogDlg();
+
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_DIALOG_USERX_LOG };
+#endif
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+
+ DECLARE_MESSAGE_MAP()
+public:
+ virtual BOOL OnInitDialog();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnDestroy();
+
+private:
+ struct LogItem
+ {
+ CString time;
+ CString user;
+ CString action;
+ CString detail;
+ };
+
+ CListCtrl m_listLogs;
+ std::vector<LogItem> m_logs;
+
+ void InitListCtrl();
+ void RefreshLogs();
+ void AdjustLayout();
+};
diff --git a/SourceCode/Bond/Servo/CVariable.h b/SourceCode/Bond/Servo/CVariable.h
index 597b84d..aa16b34 100644
--- a/SourceCode/Bond/Servo/CVariable.h
+++ b/SourceCode/Bond/Servo/CVariable.h
@@ -3,7 +3,7 @@
namespace SERVO {
- // 变量格式
+ // 鍙橀噺绫诲瀷
enum class SVFromat {
U1 = 0,
U2,
@@ -33,6 +33,9 @@
std::string getValue();
__int64 getIntValue();
std::vector<CVariable>& getVarsValue();
+ void setName(const char* pszName) { m_strName = pszName; }
+ void setFormat(const char* pszFmt) { m_format = toFormat(pszFmt); }
+ void setRemark(const char* pszRemark) { m_strRemark = pszRemark; }
private:
unsigned int m_nVarialbeId;
diff --git a/SourceCode/Bond/Servo/CVariableEditDlg2.cpp b/SourceCode/Bond/Servo/CVariableEditDlg2.cpp
new file mode 100644
index 0000000..657ec41
--- /dev/null
+++ b/SourceCode/Bond/Servo/CVariableEditDlg2.cpp
@@ -0,0 +1,89 @@
+锘�#include "stdafx.h"
+#include "CVariableEditDlg2.h"
+#include "resource.h"
+
+IMPLEMENT_DYNAMIC(CVariableEditDlg2, CDialogEx)
+
+CVariableEditDlg2::CVariableEditDlg2(const CString& title, int varId, const CString& type, const CString& name, const CString& remark, CWnd* pParent)
+ : CDialogEx(IDD_DIALOG_VARIABLE_EDIT2, pParent),
+ m_strTitle(title),
+ m_varId(varId),
+ m_strType(type),
+ m_strName(name),
+ m_strRemark(remark)
+{
+}
+
+CVariableEditDlg2::~CVariableEditDlg2()
+{
+}
+
+void CVariableEditDlg2::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_EDIT_VAR_ID, m_editId);
+ DDX_Control(pDX, IDC_COMBO_VAR_TYPE, m_cbType);
+ DDX_Control(pDX, IDC_EDIT_VAR_NAME, m_editName);
+ DDX_Control(pDX, IDC_EDIT_VAR_REMARK, m_editRemark);
+}
+
+BEGIN_MESSAGE_MAP(CVariableEditDlg2, CDialogEx)
+END_MESSAGE_MAP()
+
+BOOL CVariableEditDlg2::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+ SetWindowText(m_strTitle);
+
+ CString strId;
+ strId.Format(_T("%d"), m_varId);
+ m_editId.SetWindowText(strId);
+ m_editId.SetReadOnly(TRUE);
+
+ m_cbType.ResetContent();
+ const TCHAR* fmts[] = { _T("U1"), _T("U2"), _T("I2"), _T("A20"), _T("A50"), _T("L") };
+ for (auto f : fmts) {
+ m_cbType.AddString(f);
+ }
+ if (!m_strType.IsEmpty()) {
+ m_cbType.SelectString(-1, m_strType);
+ }
+ else {
+ m_cbType.SetCurSel(0);
+ }
+
+ m_editName.SetWindowText(m_strName);
+ m_editRemark.SetWindowText(m_strRemark);
+
+ return TRUE;
+}
+
+void CVariableEditDlg2::OnOK()
+{
+ CString name, fmt, remark;
+ m_editName.GetWindowText(name);
+ m_cbType.GetWindowText(fmt);
+ m_editRemark.GetWindowText(remark);
+
+ fmt.MakeUpper();
+ if (name.IsEmpty()) {
+ AfxMessageBox(_T("鍚嶇О涓嶈兘涓虹┖"));
+ return;
+ }
+ if (fmt.IsEmpty()) {
+ AfxMessageBox(_T("绫诲瀷涓嶈兘涓虹┖"));
+ return;
+ }
+ if (fmt != _T("U1") && fmt != _T("U2") && fmt != _T("I2")
+ && fmt != _T("A20") && fmt != _T("A50") && fmt != _T("L")) {
+ AfxMessageBox(_T("绫诲瀷蹇呴』鏄� U1/U2/I2/A20/A50/L"));
+ return;
+ }
+
+ m_strName = name;
+ m_strType = fmt;
+ m_strRemark = remark;
+
+ CDialogEx::OnOK();
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/CVariableEditDlg2.h b/SourceCode/Bond/Servo/CVariableEditDlg2.h
new file mode 100644
index 0000000..4d09c05
--- /dev/null
+++ b/SourceCode/Bond/Servo/CVariableEditDlg2.h
@@ -0,0 +1,34 @@
+锘�#pragma once
+#include "afxdialogex.h"
+
+// 鍙橀噺缂栬緫瀵硅瘽妗嗭紙鏂板/缂栬緫鍏辩敤锛屼娇鐢ㄨ祫婧愭ā鏉匡級
+class CVariableEditDlg2 : public CDialogEx
+{
+ DECLARE_DYNAMIC(CVariableEditDlg2)
+
+public:
+ CVariableEditDlg2(const CString& title, int varId, const CString& type, const CString& name, const CString& remark, CWnd* pParent = nullptr);
+ virtual ~CVariableEditDlg2();
+
+ int GetVarId() const { return m_varId; }
+ CString GetTypeText() const { return m_strType; }
+ CString GetNameText() const { return m_strName; }
+ CString GetRemark() const { return m_strRemark; }
+
+protected:
+ virtual BOOL OnInitDialog() override;
+ virtual void DoDataExchange(CDataExchange* pDX) override;
+ afx_msg void OnOK();
+
+ DECLARE_MESSAGE_MAP()
+
+private:
+ CString m_strTitle;
+ int m_varId;
+ CString m_strType;
+ CString m_strName;
+ CString m_strRemark;
+
+ CEdit m_editId, m_editName, m_editRemark;
+ CComboBox m_cbType;
+};
diff --git a/SourceCode/Bond/Servo/ClientListDlg.cpp b/SourceCode/Bond/Servo/ClientListDlg.cpp
index 500062e..1e8cd71 100644
--- a/SourceCode/Bond/Servo/ClientListDlg.cpp
+++ b/SourceCode/Bond/Servo/ClientListDlg.cpp
@@ -132,7 +132,7 @@
{
const ClientInfo& client = clients[i];
- int nItem = m_listClients.InsertItem(i, CString(client.ip.c_str()));
+ int nItem = m_listClients.InsertItem((int)i, CString(client.ip.c_str()));
m_listClients.SetItemText(nItem, 1, CString(std::to_string(client.port).c_str()));
m_listClients.SetItemText(nItem, 2, client.versionOk ? _T("姝e父") : _T("寮傚父"));
m_listClients.SetItemText(nItem, 3, CString(client.status.c_str()));
diff --git a/SourceCode/Bond/Servo/Common.h b/SourceCode/Bond/Servo/Common.h
index 3031752..41b56e7 100644
--- a/SourceCode/Bond/Servo/Common.h
+++ b/SourceCode/Bond/Servo/Common.h
@@ -19,7 +19,12 @@
#define RX_CODE_MASTER_STATE_CHANGED 1011
#define RX_CODE_EQ_ROBOT_TASK 1012
#define RX_CODE_LOADPORT_STATUS_CHANGED 1014
+#define RX_CODE_CONTROL_STATE_CHANGED 1015
+#define RX_CODE_CONTROLJOB_CHANGED 1016
+/* 软件侧 ALID */
+#define ALID_SOFTWARE_PAUSE_EVENT 9000
+#define ALID_SOFTWARE_TEST_ALARM 9099
/* Channel Name */
#define MC_CHANNEL1_NAME "McChannel1"
@@ -29,6 +34,7 @@
#define APPDLG_BACKGROUND_COLOR RGB(255, 255, 255)
#define LOGDLG_BACKGROUND_COLOR RGB(255, 255, 255)
#define PANEL_MASTER_BACKGROUND_COLOR RGB(255, 255, 255)
+#define PANEL_PRODUCTION_BACKGROUND_COLOR RGB(255, 255, 255)
#define PANEL_ATTRIBUTES_BACKGROUND_COLOR RGB(255, 255, 255)
#define PANEL_EQUIPMENT_BACKGROUND_COLOR RGB(255, 255, 255)
#define PAGE_GRPAH1_BACKGROUND_COLOR RGB(255, 255, 255)
@@ -46,6 +52,8 @@
#define STATUSBAR_BK_STARTING RGB(58, 127, 78)
#define STATUSBAR_BK_RUNNING RGB(34, 177, 76)
#define STATUSBAR_BK_ALARM RGB(255, 127, 39)
+#define CIM_STATUS_BK_SELECTED STATUSBAR_BK_RUNNING
+#define CIM_STATUS_BK_DISCONNECTED STATUSBAR_BK_NORMAL
/* LOG BTN */
#define BTN_LOG_FRAME_NORMAL RGB(88, 88, 88)
@@ -552,4 +560,25 @@
/* PPID名字最大长度 */
-#define PPID_NAME_MAX 80
\ No newline at end of file
+#define PPID_NAME_MAX 80
+
+
+/* 解除警告 按钮 */
+#define BTN_ALARM_OFF_FRAME_NORMAL RGB(88, 88, 88)
+#define BTN_ALARM_OFF_FRAME_HOVER RGB(88, 88, 88)
+#define BTN_ALARM_OFF_FRAME_PRESS RGB(88, 88, 88)
+#define BTN_ALARM_OFF_BKGND_NORMAL RGB(255, 127, 39)
+#define BTN_ALARM_OFF_BKGND_HOVER RGB(255, 157, 59)
+#define BTN_ALARM_OFF_BKGND_PRESS RGB(255, 100, 29)
+
+/* 静音按钮 */
+#define BTN_SOUND_OFF_FRAME_NORMAL RGB(88, 88, 88)
+#define BTN_SOUND_OFF_FRAME_HOVER RGB(88, 88, 88)
+#define BTN_SOUND_OFF_FRAME_PRESS RGB(88, 88, 88)
+#define BTN_SOUND_OFF_BKGND_NORMAL RGB(255, 127, 39)
+#define BTN_SOUND_OFF_BKGND_HOVER RGB(255, 157, 59)
+#define BTN_SOUND_OFF_BKGND_PRESS RGB(255, 100, 29)
+#define BTN_SOUND_ON_BKGND_NORMAL RGB(100, 200, 100)
+#define BTN_SOUND_ON_BKGND_HOVER RGB(150, 250, 150)
+#define BTN_SOUND_ON_BKGND_PRESS RGB(50, 150, 50)
+
diff --git a/SourceCode/Bond/Servo/Configuration.h b/SourceCode/Bond/Servo/Configuration.h
index 0bf2628..9c6aaf7 100644
--- a/SourceCode/Bond/Servo/Configuration.h
+++ b/SourceCode/Bond/Servo/Configuration.h
@@ -34,6 +34,13 @@
int getPortCassetteSnSeed(int port);
void setPortCassetteSnSeed(int port, int seed);
+ // Production shift settings
+ // Reads shift start times from ini.
+ // - [Production] DayShiftStart=HH:MM (default 08:00)
+ // - [Production] NightShiftStart=HH:MM (default DayShiftStart+12h)
+ // Returns TRUE if both values are valid (or derived); otherwise FALSE and falls back to defaults.
+ BOOL getProductionShiftStartMinutes(int& dayStartMinutes, int& nightStartMinutes);
+
public:
void setP2RemoteEqReconnectInterval(int second);
int getP2RemoteEqReconnectInterval();
diff --git a/SourceCode/Bond/Servo/ConfigurationProduction.cpp b/SourceCode/Bond/Servo/ConfigurationProduction.cpp
new file mode 100644
index 0000000..3aa9bcc
--- /dev/null
+++ b/SourceCode/Bond/Servo/ConfigurationProduction.cpp
@@ -0,0 +1,75 @@
+#include "stdafx.h"
+#include "Configuration.h"
+
+#include <mutex>
+#include <string>
+#include <unordered_map>
+
+static bool TryParseHHMM(const std::string& text, int& outMinutes)
+{
+ int hour = 0;
+ int minute = 0;
+ if (sscanf_s(text.c_str(), "%d:%d", &hour, &minute) != 2) return false;
+ if (hour < 0 || hour >= 24) return false;
+ if (minute < 0 || minute >= 60) return false;
+ outMinutes = hour * 60 + minute;
+ return true;
+}
+
+BOOL CConfiguration::getProductionShiftStartMinutes(int& dayStartMinutes, int& nightStartMinutes)
+{
+ struct CachedShift {
+ BOOL ok = FALSE;
+ int day = 0;
+ int night = 0;
+ bool inited = false;
+ };
+
+ static std::mutex s_mtx;
+ static std::unordered_map<std::string, CachedShift> s_cache;
+
+ const std::string filePath((LPCSTR)(LPCTSTR)m_strFilepath);
+ {
+ std::lock_guard<std::mutex> g(s_mtx);
+ auto it = s_cache.find(filePath);
+ if (it != s_cache.end() && it->second.inited) {
+ dayStartMinutes = it->second.day;
+ nightStartMinutes = it->second.night;
+ return it->second.ok;
+ }
+ }
+
+ char buf[64] = {};
+ GetPrivateProfileStringA("Production", "DayShiftStart", "08:00", buf, (DWORD)sizeof(buf), m_strFilepath);
+ std::string dayStr(buf);
+
+ GetPrivateProfileStringA("Production", "NightShiftStart", "", buf, (DWORD)sizeof(buf), m_strFilepath);
+ std::string nightStr(buf);
+
+ const int kDefaultDay = 8 * 60;
+ const int kDefaultNight = 20 * 60;
+
+ bool okDay = TryParseHHMM(dayStr, dayStartMinutes);
+ bool okNight = false;
+ if (!nightStr.empty()) okNight = TryParseHHMM(nightStr, nightStartMinutes);
+
+ if (!okDay) dayStartMinutes = kDefaultDay;
+ if (!okNight) nightStartMinutes = (dayStartMinutes + 12 * 60) % (24 * 60);
+
+ if (dayStartMinutes == nightStartMinutes) {
+ dayStartMinutes = kDefaultDay;
+ nightStartMinutes = kDefaultNight;
+ {
+ std::lock_guard<std::mutex> g(s_mtx);
+ s_cache[filePath] = CachedShift{ FALSE, dayStartMinutes, nightStartMinutes, true };
+ }
+ return FALSE;
+ }
+
+ const BOOL ok = (okDay && (nightStr.empty() ? TRUE : okNight)) ? TRUE : FALSE;
+ {
+ std::lock_guard<std::mutex> g(s_mtx);
+ s_cache[filePath] = CachedShift{ ok, dayStartMinutes, nightStartMinutes, true };
+ }
+ return ok;
+}
diff --git a/SourceCode/Bond/Servo/EqsGraphWnd.cpp b/SourceCode/Bond/Servo/EqsGraphWnd.cpp
index 9aa534d..9e68a4c 100644
--- a/SourceCode/Bond/Servo/EqsGraphWnd.cpp
+++ b/SourceCode/Bond/Servo/EqsGraphWnd.cpp
@@ -1,4 +1,4 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "EqsGraphWnd.h"
#include "ColorTransfer.h"
#include "MapPosWnd.h"
@@ -64,6 +64,8 @@
m_nMagneticLinHoz = 0;
m_nMagneticLinVer = 0;
m_hFontTitle = nullptr;
+ m_nIndicatorSize = 10;
+ m_nIndicatorMargin = 3;
}
@@ -86,7 +88,7 @@
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
- // 注册窗口类
+ // 娉ㄥ唽绐楀彛绫�
return (::RegisterClass(&wc) != 0);
}
@@ -157,6 +159,20 @@
m_crItemIdText[0] = CColorTransfer::ApproximateColor(m_crItemNameText[0], -0.3f);
m_crItemIdText[1] = CColorTransfer::ApproximateColor(m_crItemNameText[1], -0.3f);
+}
+
+void CEqsGraphWnd::SetIndicatorSize(int nSize)
+{
+ if (nSize > 0) {
+ m_nIndicatorSize = nSize;
+ }
+}
+
+void CEqsGraphWnd::SetIndicatorMargin(int nMargin)
+{
+ if (nMargin >= 0) {
+ m_nIndicatorMargin = nMargin;
+ }
}
void CEqsGraphWnd::EnableScroll(BOOL bEnable)
@@ -235,7 +251,7 @@
}
/*
- * 计算磁力线位置
+ * 璁$畻纾佸姏绾夸綅缃�
*/
void CEqsGraphWnd::CalculateMagneticLine(EQITEM* pItem, LPRECT lprcItemRect, int &hoz, int &ver)
{
@@ -243,7 +259,7 @@
ver = 0;
#define MAGNETIC_DIS 10
- // 检测是否接近或对齐
+ // 妫�娴嬫槸鍚︽帴杩戞垨瀵归綈
for (int i = 0; i < m_arItem.GetSize(); i++) {
EQITEM *pTemp = (EQITEM*)m_arItem.GetAt(i);
if (pTemp != pItem) {
@@ -287,10 +303,10 @@
}
/*
- * 取得In Pin的区域
+ * 鍙栧緱In Pin鐨勫尯鍩�
* pItem -- EQITEM
- * lpRect -- 得到的Rect
- * 返回是否成功
+ * lpRect -- 寰楀埌鐨凴ect
+ * 杩斿洖鏄惁鎴愬姛
*/
BOOL CEqsGraphWnd::GetItemRect(EQITEM* pItem, LPRECT lpRect)
{
@@ -321,11 +337,11 @@
}
/*
- * 取得In Pin的区域
+ * 鍙栧緱In Pin鐨勫尯鍩�
* pItem -- EQITEM
- * nPinIndex -- in pin索引
- * lpRect -- 得到的Rect
- * 返回是否成功
+ * nPinIndex -- in pin绱㈠紩
+ * lpRect -- 寰楀埌鐨凴ect
+ * 杩斿洖鏄惁鎴愬姛
*/
BOOL CEqsGraphWnd::GetInPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect)
{
@@ -345,11 +361,11 @@
}
/*
- * 取得Out Pin的区域
+ * 鍙栧緱Out Pin鐨勫尯鍩�
* pItem -- EQITEM
- * nPinIndex -- in pin索引
- * lpRect -- 得到的Rect
- * 返回是否成功
+ * nPinIndex -- in pin绱㈠紩
+ * lpRect -- 寰楀埌鐨凴ect
+ * 杩斿洖鏄惁鎴愬姛
*/
BOOL CEqsGraphWnd::GetOutPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect)
{
@@ -368,11 +384,11 @@
}
/*
- * 取得Pin的Point
+ * 鍙栧緱Pin鐨凱oint
* pItem -- EQITEM
- * nPinIndex -- in pin索引
- * lpRect -- 得到的Rect
- * 返回是否成功
+ * nPinIndex -- in pin绱㈠紩
+ * lpRect -- 寰楀埌鐨凴ect
+ * 杩斿洖鏄惁鎴愬姛
*/
BOOL CEqsGraphWnd::GetPinPoint(PIN *pPin, LPPOINT lpPoint)
{
@@ -453,7 +469,7 @@
}
/*
- * 清空PIN连接线缓存点,以便重新计算和绘制
+ * 娓呯┖PIN杩炴帴绾跨紦瀛樼偣锛屼互渚块噸鏂拌绠楀拰缁樺埗
*/
void CEqsGraphWnd::ClearConnectedLinePoint(EQITEM*& pItem)
{
@@ -479,14 +495,7 @@
void CEqsGraphWnd::SetOnListener(EqsGraphListener& listener)
{
- m_listener.onConnectPin = listener.onConnectPin;
- m_listener.onCheckConnectPin = listener.onCheckConnectPin;
- m_listener.onDisconnectPin = listener.onDisconnectPin;
- m_listener.onDeleteEqItem = listener.onDeleteEqItem;
- m_listener.onEqItemPosChanged = listener.onEqItemPosChanged;
- m_listener.onDblckEqItem = listener.onDblckEqItem;
- m_listener.onRclickEqItem = listener.onRclickEqItem;
- m_listener.onSelectEqItem = listener.onSelectEqItem;
+ m_listener = listener;
}
BOOL CEqsGraphWnd::SetCurSel(int nSel)
@@ -555,7 +564,7 @@
*/
EQITEM* CEqsGraphWnd::AddItem(int id, CString strText, DWORD_PTR dwData, int nType/* = ITEM_NORMAL*/)
{
- // 需要计算一个新位置,不然全部重叠在一起
+ // 闇�瑕佽绠椾竴涓柊浣嶇疆锛屼笉鐒跺叏閮ㄩ噸鍙犲湪涓�璧�
int x, y;
x = (m_arItem.GetCount() % 4) * 218;
y = (m_arItem.GetCount() / 4) * 168;
@@ -755,7 +764,7 @@
return 0;
}
-// 删除Item, 如果pin有连接,注意先断开
+// 鍒犻櫎Item, 濡傛灉pin鏈夎繛鎺ワ紝娉ㄦ剰鍏堟柇寮�
int CEqsGraphWnd::DeleteItem(EQITEM* pItem)
{
for (int i = 0; i < m_arItem.GetSize(); i++) {
@@ -795,7 +804,7 @@
}
/*
- * 设置子项的选中状态
+ * 璁剧疆瀛愰」鐨勯�変腑鐘舵��
*/
void CEqsGraphWnd::SetItemSelectState(int nIndex, BOOL bSelect)
{
@@ -828,14 +837,14 @@
}
/*
- * 检测坐标点所在的项
- * 返回项类型, 如HT_ITEM, HT_PIN, HT_LINE
- * pItem - 所在的EQITEM
- * pPin --所在的pin, 如果在连线上,表示所属pin, out pin;
+ * 妫�娴嬪潗鏍囩偣鎵�鍦ㄧ殑椤�
+ * 杩斿洖椤圭被鍨�, 濡侶T_ITEM, HT_PIN, HT_LINE
+ * pItem - 鎵�鍦ㄧ殑EQITEM
+ * pPin --鎵�鍦ㄧ殑pin, 濡傛灉鍦ㄨ繛绾夸笂锛岃〃绀烘墍灞瀙in, out pin锛�
*/
int CEqsGraphWnd::HighTest(POINT pt, OUT EQITEM*& pItem, OUT PIN *& pPin)
{
- // 检测是否在某个子项
+ // 妫�娴嬫槸鍚﹀湪鏌愪釜瀛愰」
int nRet = HT_NOWHERE;
pItem = NULL;
pPin = NULL;
@@ -844,7 +853,7 @@
EQITEM *pTempItem = (EQITEM*)m_arItem.GetAt(i);
GetItemRect(pTempItem, &rcItem);
if (::PtInRect(&rcItem, pt)) {
- // 在Item
+ // 鍦↖tem
pItem = pTempItem;
nRet = HT_ITEM;
break;
@@ -854,7 +863,7 @@
CPtrArray * pPins = (CPtrArray *)pTempItem->pInPins;
for (int j = 0; j < pPins->GetSize(); j++) {
if (GetInPinRect(pTempItem, j, &rcPin) && ::PtInRect(&rcPin, pt)) {
- // 在in pin上
+ // 鍦╥n pin涓�
pPin = (PIN *)pPins->GetAt(j);
pItem = pTempItem;
nRet = HT_PIN;
@@ -866,15 +875,15 @@
pPins = (CPtrArray *)pTempItem->pOutPins;
for (int j = 0; j < pPins->GetSize(); j++) {
if (GetOutPinRect(pTempItem, j, &rcPin) && ::PtInRect(&rcPin, pt)) {
- // 在out pin
+ // 鍦╫ut pin
pPin = (PIN *)pPins->GetAt(j);
pItem = pTempItem;
nRet = HT_PIN;
break;
}
else {
- // 是否在pin连接线上,即判断点是否在线上
- // 点到直线的距离公式(先通过p1,p2用两点式求出直线的表达式,再套距离公式);abs()为取绝对值函数,sqrt()为开根号函数
+ // 鏄惁鍦╬in杩炴帴绾夸笂,鍗冲垽鏂偣鏄惁鍦ㄧ嚎涓�
+ // 鐐瑰埌鐩寸嚎鐨勮窛绂诲叕寮忥紙鍏堥�氳繃p1,p2鐢ㄤ袱鐐瑰紡姹傚嚭鐩寸嚎鐨勮〃杈惧紡锛屽啀濂楄窛绂诲叕寮忥級锛沘bs()涓哄彇缁濆鍊煎嚱鏁帮紝sqrt()涓哄紑鏍瑰彿鍑芥暟
PIN *pTempPin = (PIN *)pPins->GetAt(j);
if (pTempPin->pConnectedPin != NULL && pTempPin->nLinePtCount > 1) {
for (int i = 0; i < pTempPin->nLinePtCount - 1; i++) {
@@ -907,7 +916,7 @@
}
/*
- * 绘制虚线框,代表正在拖动的item
+ * 缁樺埗铏氱嚎妗嗭紝浠h〃姝e湪鎷栧姩鐨刬tem
*/
void CEqsGraphWnd::DrawDropItemRectangle(LPRECT lpRect1, LPRECT lpRect2)
{
@@ -935,7 +944,7 @@
}
/*
- * 绘制磁吸线
+ * 缁樺埗纾佸惛绾�
*/
void CEqsGraphWnd::DrawMagneticLine(LPRECT lprcClient, int nHozLine1, int nHozLine2, int nVerLine1, int nVerLine2)
{
@@ -969,16 +978,16 @@
}
/*
- * 缓制Pin连接线
- * pBrush -- 画刷
- * pPen - 画笔
- * lpPt1, lpPt2 -- Pin脚的位置
- * lpRect1, lpRect2 -- 两个Item的Rect
+ * 缂撳埗Pin杩炴帴绾�
+ * pBrush -- 鐢诲埛
+ * pPen - 鐢荤瑪
+ * lpPt1, lpPt2 -- Pin鑴氱殑浣嶇疆
+ * lpRect1, lpRect2 -- 涓や釜Item鐨凴ect
*/
void CEqsGraphWnd::DrawPinConnectedLine(Gdiplus::Graphics *pGraphics, Gdiplus::Brush *pBrush, Gdiplus::Pen *pPen, LPPOINT lpPt1, LPPOINT lpPt2,
LPRECT lpRect1, LPRECT lpRect2, PIN *pOwnerPin)
{
- // 如果没有缓存线条的POINT,则先计算并缓存
+ // 濡傛灉娌℃湁缂撳瓨绾挎潯鐨凱OINT锛屽垯鍏堣绠楀苟缂撳瓨
ASSERT(pOwnerPin);
int nPinCount = ((CPtrArray*)pOwnerPin->pItem->pOutPins)->GetSize();
@@ -987,10 +996,10 @@
int nMargin = 12;
int x1, x2, y1;
- if (pOwnerPin->nLinePtCount == 0) { // 第一个点的最小折线长
+ if (pOwnerPin->nLinePtCount == 0) { // 绗竴涓偣鐨勬渶灏忔姌绾块暱
::OffsetRect(lpRect1, +m_nOffsetX, +m_nOffsetY);
::OffsetRect(lpRect2, +m_nOffsetX, +m_nOffsetY);
- lpPt1->x += m_nOffsetX; // 消除偏移
+ lpPt1->x += m_nOffsetX; // 娑堥櫎鍋忕Щ
lpPt1->y += m_nOffsetY;
lpPt2->x += m_nOffsetX;
lpPt2->y += m_nOffsetY;
@@ -1099,7 +1108,7 @@
}
/*
- * WindowProc,窗口过程
+ * WindowProc锛岀獥鍙h繃绋�
*/
LRESULT CALLBACK CEqsGraphWnd::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
@@ -1110,7 +1119,7 @@
}
- // 处理窗口消息
+ // 澶勭悊绐楀彛娑堟伅
ASSERT(hWnd);
switch (uMsg)
{
@@ -1174,7 +1183,7 @@
/*
* WM_NCCREATE
- * 窗口创建
+ * 绐楀彛鍒涘缓
*/
LRESULT CEqsGraphWnd::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
@@ -1187,7 +1196,7 @@
/*
* WM_DESTROY
- * 窗口销毁
+ * 绐楀彛閿�姣�
*/
LRESULT CEqsGraphWnd::OnDestroy(WPARAM wParam, LPARAM lParam)
{
@@ -1250,7 +1259,7 @@
/*
* WM_MOUSEMOVE
- * 鼠标滚动
+ * 榧犳爣婊氬姩
*/
LRESULT CEqsGraphWnd::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
@@ -1259,7 +1268,7 @@
/*
* WM_LBUTTONDOWN
- * 鼠标左键按下
+ * 榧犳爣宸﹂敭鎸変笅
*/
LRESULT CEqsGraphWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
@@ -1276,7 +1285,7 @@
int nLastVerLine = 0;
- // 检测点击坐标是否在某一子项上,如是,则高亮显示
+ // 妫�娴嬬偣鍑诲潗鏍囨槸鍚﹀湪鏌愪竴瀛愰」涓婏紝濡傛槸锛屽垯楂樹寒鏄剧ず
EQITEM* pLastItem = m_pCurItem;
PIN *pLastPin = m_pCurPin;
PIN *pLastSelLineOutPin = m_pSelLineOutPin;
@@ -1323,14 +1332,14 @@
bChanged = pLastItem != m_pCurItem || pLastPin != m_pCurPin || pLastSelLineOutPin != m_pSelLineOutPin;
- // 刷新
+ // 鍒锋柊
SetFocus(m_hWnd);
if (bChanged) {
::InvalidateRect(m_hWnd, &rcClient, TRUE);
}
- // 捕捉鼠标消息,检测是否拖动
+ // 鎹曟崏榧犳爣娑堟伅锛屾娴嬫槸鍚︽嫋鍔�
if (nRet == HT_ITEM && m_pCurItem != NULL) {
GetItemRect(m_pCurItem, &rcItem);
@@ -1400,7 +1409,7 @@
}
- // 捕捉鼠标消息,检测是否连接引脚
+ // 鎹曟崏榧犳爣娑堟伅锛屾娴嬫槸鍚﹁繛鎺ュ紩鑴�
else if (nRet == HT_PIN && m_pCurPin != NULL) {
if (::GetCapture() == NULL) {
BOOL bLast = FALSE;
@@ -1427,12 +1436,12 @@
ptNew = msg.pt;
::ScreenToClient(m_hWnd, &ptNew);
- // 擦除上一次
+ // 鎿﹂櫎涓婁竴娆�
if (bLast) {
DrawPinWillConnectLine(lineColor, &ptPin, &ptLast);
}
- // 检测是否可以连接
+ // 妫�娴嬫槸鍚﹀彲浠ヨ繛鎺�
bCanConnect = false;
nRet = HighTest(ptNew, pHitItem, pHitPin);
if (nRet == HT_PIN) {
@@ -1458,12 +1467,12 @@
ptNew = msg.pt;
::ScreenToClient(m_hWnd, &ptNew);
- // 擦除上一次
+ // 鎿﹂櫎涓婁竴娆�
if (bLast) {
DrawPinWillConnectLine(lineColor, &ptPin, &ptLast);
}
- // 检测是否可以连接
+ // 妫�娴嬫槸鍚﹀彲浠ヨ繛鎺�
bCanConnect = false;
nRet = HighTest(ptNew, pHitItem, pHitPin);
if (nRet == HT_PIN) {
@@ -1498,7 +1507,7 @@
}
- // 检测鼠标消息,检测是否移动画布
+ // 妫�娴嬮紶鏍囨秷鎭紝妫�娴嬫槸鍚︾Щ鍔ㄧ敾甯�
else if (nRet == HT_NOWHERE) {
if (::GetCapture() == NULL) {
int nLastOffsetX = m_nOffsetX;
@@ -1569,7 +1578,7 @@
/*
* WM_LBUTTONDBLCLK
- * 鼠标左键双击
+ * 榧犳爣宸﹂敭鍙屽嚮
*/
LRESULT CEqsGraphWnd::OnLButtonDblclk(WPARAM wParam, LPARAM lParam)
{
@@ -1581,7 +1590,7 @@
GetClientRect(m_hWnd, &rcClient);
rcLast = { 0, 0, 0, 0 };
- // 检测点击坐标是否在某一子项上,如是,则高亮显示
+ // 妫�娴嬬偣鍑诲潗鏍囨槸鍚﹀湪鏌愪竴瀛愰」涓婏紝濡傛槸锛屽垯楂樹寒鏄剧ず
EQITEM* pLastItem = m_pCurItem;
BOOL bChanged = FALSE;
EQITEM* pHitItem = NULL;
@@ -1601,7 +1610,7 @@
/*
* WM_MOUSEWHEEL
- * 鼠标滚动
+ * 榧犳爣婊氬姩
*/
LRESULT CEqsGraphWnd::OnMouseWheel(WPARAM wParam, LPARAM lParam)
{
@@ -1628,7 +1637,7 @@
/*
* WM_MOUSEHWHEEL
-* 鼠标滚动
+* 榧犳爣婊氬姩
*/
LRESULT CEqsGraphWnd::OnMouseHWheel(WPARAM wParam, LPARAM lParam)
{
@@ -1655,7 +1664,7 @@
/*
* WM_RBUTTONDOWN
- * 鼠标左键按下
+ * 榧犳爣宸﹂敭鎸変笅
*/
LRESULT CEqsGraphWnd::OnRButtonDown(WPARAM wParam, LPARAM lParam)
{
@@ -1667,7 +1676,7 @@
GetClientRect(m_hWnd, &rcClient);
rcLast = { 0, 0, 0, 0 };
- // 检测点击坐标是否在某一子项上,如是,则高亮显示
+ // 妫�娴嬬偣鍑诲潗鏍囨槸鍚﹀湪鏌愪竴瀛愰」涓婏紝濡傛槸锛屽垯楂樹寒鏄剧ず
EQITEM* pLastItem = m_pCurItem;
PIN *pLastPin = m_pCurPin;
PIN *pLastSelLineOutPin = m_pSelLineOutPin;
@@ -1710,14 +1719,14 @@
bChanged = pLastItem != m_pCurItem || pLastPin != m_pCurPin || pLastSelLineOutPin != m_pSelLineOutPin;
- // 刷新
+ // 鍒锋柊
SetFocus(m_hWnd);
if (bChanged) {
::InvalidateRect(m_hWnd, &rcClient, TRUE);
}
- // 捕捉鼠标消息,检测是否拖动
+ // 鎹曟崏榧犳爣娑堟伅锛屾娴嬫槸鍚︽嫋鍔�
if (nRet == HT_ITEM && m_pCurItem != NULL) {
CopyRect(&rcItem, &m_pCurItem->rect);
@@ -1770,13 +1779,13 @@
/*
* WM_KEYDOWN
- * 键盘消息,按下按键
+ * 閿洏娑堟伅锛屾寜涓嬫寜閿�
*/
LRESULT CEqsGraphWnd::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
BOOL bChanged = FALSE;
if (wParam == VK_DELETE) {
- // 如果当前选择为线,则断开连接
+ // 濡傛灉褰撳墠閫夋嫨涓虹嚎锛屽垯鏂紑杩炴帴
if (m_pSelLineOutPin != NULL) {
if (m_listener.onDisconnectPin != nullptr) {
if (m_listener.onDisconnectPin(m_pSelLineOutPin)) {
@@ -1866,7 +1875,6 @@
CString strText;
HBRUSH hBrushBK;
-
// BeginPaint
PAINTSTRUCT ps;
hDC = BeginPaint(m_hWnd, &ps);
@@ -1877,14 +1885,12 @@
rcClient.bottom - rcClient.top);
::SelectObject(hMemDC, hBitmap);
-
- // 背景颜色
+ // 鑳屾櫙棰滆壊
hBrushBK = CreateSolidBrush(m_crBkgnd);
::FillRect(hMemDC, &rcClient, hBrushBK);
DeleteObject(hBrushBK);
-
- // 标题
+ // 鏍囬瀛椾綋
if (m_hFontTitle == nullptr) {
LOGFONT lf;
HFONT hFontDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
@@ -1894,8 +1900,9 @@
m_hFontTitle = CreateFontIndirect(&lf);
}
+ // 缁樺埗鏍囬鏂囨湰
{
- char szTitle[256];
+ char szTitle[256] = { 0 };
GetWindowText(m_hWnd, szTitle, 256);
RECT rcTitle;
rcTitle.left = rcClient.left + 5;
@@ -1906,21 +1913,23 @@
::DrawText(hMemDC, szTitle, (int)strlen(szTitle), &rcTitle, DT_LEFT | DT_TOP);
}
-
- // 绘制子项
+ // 缁樺埗瀛愰」
HBRUSH hbrItemBackground[2];
HBRUSH hbrItemFrame[2];
HBRUSH hbrPinBackground[3];
HBRUSH hbrIndicator;
+ HBRUSH hbrIndicatorGray;
+
hbrItemBackground[0] = CreateSolidBrush(m_crItemBackground[0]);
hbrItemBackground[1] = CreateSolidBrush(m_crItemBackground[1]);
hbrItemFrame[0] = CreateSolidBrush(m_crItemFrame[0]);
hbrItemFrame[1] = CreateSolidBrush(m_crItemFrame[1]);
- hbrIndicator = CreateSolidBrush(RGB(34, 177, 76));
+ hbrIndicator = CreateSolidBrush(RGB(34, 177, 76)); // 缁胯壊
+ hbrIndicatorGray = CreateSolidBrush(RGB(192, 192, 192)); // 鐏拌壊
+
for (int i = 0; i < 3; i++) {
hbrPinBackground[i] = CreateSolidBrush(m_crPinBkgnd[i]);
}
-
// gdi+
Gdiplus::Graphics graphics(hMemDC);
@@ -1931,21 +1940,22 @@
graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
}
-
SetBkMode(hMemDC, TRANSPARENT);
{
RECT rcItem;
int nPinState;
int nItemCount = (int)m_arItem.GetCount();
+
+ // 鍏堢敾 item銆佹枃鏈�乸in 鍜屾寚绀虹伅
for (int i = 0; i < nItemCount; i++) {
EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
if (pItem->nFlashFlag == 1) {
continue;
}
+
GetItemRect(pItem, &rcItem);
-
- // 子项背景和边框
+ // 瀛愰」鑳屾櫙鍜岃竟妗�
if (m_nItemRound == 0) {
::FillRect(hMemDC, &rcItem, pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]);
::FrameRect(hMemDC, &rcItem, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
@@ -1957,27 +1967,37 @@
::DeleteObject(hRgn);
}
-
- // name和id
+ // name
HFONT hFontOld = (HFONT)::SelectObject(hMemDC, m_hFontName);
::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemNameText[1] : m_crItemNameText[0]);
- ::DrawText(hMemDC, pItem->text, (int)strlen(pItem->text), &rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+ ::DrawText(hMemDC, pItem->text, (int)strlen(pItem->text), &rcItem,
+ DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
-
- // 添加一个小绿点指示器
- if(pItem->bShowIndicator[0]){
+ // indicators vertical column layout
+ const int indicatorSize = m_nIndicatorSize;
+ const int indicatorMargin = m_nIndicatorMargin;
+ const int indicatorX = rcItem.left + 5;
+
+ for (int k = 0; k < EQITEM_INDICATOR_COUNT; ++k) {
+ BYTE indicatorState = pItem->nIndicatorState[k];
+ if (indicatorState == INDICATOR_STATE_HIDDEN) {
+ continue;
+ }
+
RECT rcIndicator;
- rcIndicator.left = rcItem.left + 5;
- rcIndicator.top = rcItem.top + 5;
- rcIndicator.right = rcIndicator.left + 12;
- rcIndicator.bottom = rcIndicator.top + 12;
- HRGN hRgn = CreateRoundRectRgn(rcIndicator.left, rcIndicator.top, rcIndicator.right, rcIndicator.bottom, 2, 2);
- ::FillRgn(hMemDC, hRgn, hbrIndicator);
- ::FrameRgn(hMemDC, hRgn, hbrItemFrame[0], 1, 1);
- ::DeleteObject(hRgn);
+ rcIndicator.left = indicatorX;
+ rcIndicator.top = rcItem.top + 5 + k * (indicatorSize + indicatorMargin);
+ rcIndicator.right = rcIndicator.left + indicatorSize;
+ rcIndicator.bottom = rcIndicator.top + indicatorSize;
+
+ RECT rcInner = rcIndicator;
+ ::InflateRect(&rcInner, -1, -1);
+ ::FillRect(hMemDC, &rcInner, indicatorState == INDICATOR_STATE_HIGHLIGHT
+ ? hbrIndicator : hbrIndicatorGray);
+ ::FrameRect(hMemDC, &rcIndicator, hbrItemFrame[0]);
}
-
+ // ID 鏂囨湰锛堥潪灏忓彿 item锛�
if (pItem->nShowType != ITEM_SMALL) {
RECT rcId = rcItem;
rcId.left += 5;
@@ -1986,29 +2006,28 @@
strId.Format(_T("ID:%d"), pItem->id);
::SelectObject(hMemDC, m_hFontId);
::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
- ::DrawText(hMemDC, strId, (int)strId.GetLength(), &rcId, DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS);
+ ::DrawText(hMemDC, strId, (int)strId.GetLength(), &rcId,
+ DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS);
}
-
- // 动画效果不绘pin
+ // 鍔ㄧ敾鏁堟灉鏃朵笉缁樺埗 pin
if (m_pAnimationItem == pItem) {
+ ::SelectObject(hMemDC, hFontOld);
continue;
}
-
- // 绘制pin
+ // 缁樺埗 pin
RECT rcPin, rcPin2, rcPinText;
- CPtrArray *pPins;
+ CPtrArray* pPins;
rcPinText.left = rcItem.left + 8;
rcPinText.right = rcItem.right - 8;
-
// in pins
- PIN *pPin = NULL;
- pPins = (CPtrArray *)pItem->pInPins;
+ PIN* pPin = NULL;
+ pPins = (CPtrArray*)pItem->pInPins;
for (int j = 0; j < pPins->GetSize(); j++) {
if (GetInPinRect(pItem, j, &rcPin)) {
- pPin = (PIN *)pPins->GetAt(j);
+ pPin = (PIN*)pPins->GetAt(j);
::FrameRect(hMemDC, &rcPin, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
rcPin2.left = rcPin.left + 1;
@@ -2016,22 +2035,25 @@
rcPin2.top = rcPin.top + 1;
rcPin2.bottom = rcPin.bottom - 1;
nPinState = GetPinState(pPin);
- ::FillRect(hMemDC, &rcPin2, nPinState == 0 ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]) : hbrPinBackground[nPinState]);
+ ::FillRect(hMemDC, &rcPin2,
+ nPinState == 0
+ ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0])
+ : hbrPinBackground[nPinState]);
if (pItem->nShowType != ITEM_SMALL) {
rcPinText.top = rcPin.top - 12;
rcPinText.bottom = rcPin.bottom + 12;
::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
- ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+ ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText,
+ DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
}
}
-
// out pins
- pPins = (CPtrArray *)pItem->pOutPins;
+ pPins = (CPtrArray*)pItem->pOutPins;
for (int j = 0; j < pPins->GetSize(); j++) {
- pPin = (PIN *)pPins->GetAt(j);
+ pPin = (PIN*)pPins->GetAt(j);
if (GetOutPinRect(pItem, j, &rcPin)) {
::FrameRect(hMemDC, &rcPin, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
@@ -2040,50 +2062,53 @@
rcPin2.top = rcPin.top + 1;
rcPin2.bottom = rcPin.bottom - 1;
nPinState = GetPinState(pPin);
- ::FillRect(hMemDC, &rcPin2, nPinState == 0 ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]) : hbrPinBackground[nPinState]);
+ ::FillRect(hMemDC, &rcPin2,
+ nPinState == 0
+ ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0])
+ : hbrPinBackground[nPinState]);
if (pItem->nShowType != ITEM_SMALL) {
rcPinText.top = rcPin.top - 12;
rcPinText.bottom = rcPin.bottom + 12;
::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
- ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText, DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+ ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText,
+ DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
}
}
- ::DeleteObject(hbrItemFrame);
::SelectObject(hMemDC, hFontOld);
}
-
- // 绘制连接线,保存线条在最后绘制
+ // 鍐嶇粯鍒惰繛鎺ョ嚎
for (int i = 0; i < nItemCount; i++) {
- EQITEM *pItem = (EQITEM*)m_arItem.GetAt(i);
+ EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
if (pItem->nFlashFlag == 1) {
continue;
}
- PIN *pPin = NULL;
- CPtrArray *pPins;
-
- // out pins边线
+ PIN* pPin = NULL;
+ CPtrArray* pPins;
RECT rcItem1, rcItem2;
- pPins = (CPtrArray *)pItem->pOutPins;
+
+ // out pins 杈圭嚎
+ pPins = (CPtrArray*)pItem->pOutPins;
for (int j = 0; j < pPins->GetSize(); j++) {
- pPin = (PIN *)pPins->GetAt(j);
+ pPin = (PIN*)pPins->GetAt(j);
if (pPin->pConnectedPin != NULL) {
POINT pt1, pt2;
if (GetPinPoint(pPin, &pt1) && GetPinPoint(pPin->pConnectedPin, &pt2)) {
GetItemRect(pItem, &rcItem1);
GetItemRect(pPin->pConnectedPin->pItem, &rcItem2);
- DrawPinConnectedLine(&graphics, &brush1, pPin == m_pSelLineOutPin ? &pen2 : &pen1,
+ DrawPinConnectedLine(&graphics, &brush1,
+ pPin == m_pSelLineOutPin ? &pen2 : &pen1,
&pt1, &pt2, &rcItem1, &rcItem2, pPin);
}
}
}
}
-
+ // 鍒犻櫎 brush
for (int i = 0; i < 3; i++) {
::DeleteObject(hbrPinBackground[i]);
}
@@ -2091,11 +2116,9 @@
::DeleteObject(hbrItemBackground[1]);
::DeleteObject(hbrItemFrame[0]);
::DeleteObject(hbrItemFrame[1]);
- ::DeleteObject(hbrIndicator);
+ ::DeleteObject(hbrIndicator);
+ ::DeleteObject(hbrIndicatorGray);
}
-
-
-
// EndPaint
::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
@@ -2104,9 +2127,9 @@
::DeleteObject(hBitmap);
::DeleteDC(hMemDC);
-
return 1;
}
+
/*
* WM_SIZE
@@ -2354,8 +2377,8 @@
/*
- * 设置背景颜色
- * color -- 背景色
+ * 璁剧疆鑳屾櫙棰滆壊
+ * color -- 鑳屾櫙鑹�
*/
void CEqsGraphWnd::SetBkgndColor(COLORREF color)
{
@@ -2363,8 +2386,8 @@
}
/*
- * 边框颜色
- * color -- 边框色
+ * 杈规棰滆壊
+ * color -- 杈规鑹�
*/
void CEqsGraphWnd::SetFrameColor(COLORREF color)
{
@@ -2408,11 +2431,15 @@
SetTimer(m_hWnd, TIMER_ANIMATION_RECT, uElpase, NULL);
}
-void CEqsGraphWnd::ShowItemIndicator(DWORD_PTR dwItemData, BOOL bShow)
+void CEqsGraphWnd::ShowItemIndicator(DWORD_PTR dwItemData, int state, int nIndex)
{
+ if (nIndex < 0 || nIndex >= EQITEM_INDICATOR_COUNT) {
+ return;
+ }
+
EQITEM* pItem = GetItem(dwItemData);
if (pItem != nullptr) {
- pItem->bShowIndicator[0] = bShow;
+ pItem->nIndicatorState[nIndex] = (BYTE)state;
::InvalidateRect(m_hWnd, nullptr, TRUE);
}
}
diff --git a/SourceCode/Bond/Servo/EqsGraphWnd.h b/SourceCode/Bond/Servo/EqsGraphWnd.h
index c54d768..aaf9804 100644
--- a/SourceCode/Bond/Servo/EqsGraphWnd.h
+++ b/SourceCode/Bond/Servo/EqsGraphWnd.h
@@ -1,4 +1,4 @@
-#pragma once
+锘�#pragma once
#include <functional>
@@ -35,6 +35,16 @@
#define MAX(X,Y) (((X)>(Y))?(X):(Y))
#endif
+#define EQITEM_INDICATOR_COUNT 8
+
+
+enum EIndicatorState
+{
+ INDICATOR_STATE_HIDDEN = 0,
+ INDICATOR_STATE_HIGHLIGHT = 1,
+ INDICATOR_STATE_GRAY = 2,
+};
+
typedef struct tagEQSGRAPHWND_NMHDR
{
NMHDR nmhdr;
@@ -54,7 +64,7 @@
DWORD_PTR pInPins;
DWORD_PTR pOutPins;
int nFlashFlag;
- BOOL bShowIndicator[2];
+ BYTE nIndicatorState[EQITEM_INDICATOR_COUNT]; // 0=闅愯棌, 1=楂樹寒, 2=鐏拌壊
} EQITEM;
typedef struct tagPIN
@@ -144,7 +154,9 @@
void SetItemPos(EQITEM* pItem, int x, int y);
void FlashItem(EQITEM* pItem);
void AnimationItem(EQITEM*pItem);
- void ShowItemIndicator(DWORD_PTR dwItemData, BOOL bShow);
+ void ShowItemIndicator(DWORD_PTR dwItemData, int state, int nIndex = 0);
+ void SetIndicatorSize(int nSize);
+ void SetIndicatorMargin(int nMargin);
private:
void Init();
@@ -192,7 +204,7 @@
EQITEM* m_pFlashItem;
EQITEM* m_pAnimationItem;
PIN * m_pCurPin;
- PIN * m_pSelLineOutPin; // 选中的连线的两个pin中的out pin
+ PIN * m_pSelLineOutPin; // 閫変腑鐨勮繛绾跨殑涓や釜pin涓殑out pin
private:
HWND m_hWnd;
@@ -201,12 +213,12 @@
HFONT m_hFontTitle;
private:
- BOOL m_bUseGdiPlus; // 使用GDI+绘图?
- COLORREF m_crItemBackground[2]; // item的颜色,normal, active
- COLORREF m_crItemFrame[2]; // item的边框,normal, active
+ BOOL m_bUseGdiPlus; // 浣跨敤GDI+缁樺浘锛�
+ COLORREF m_crItemBackground[2]; // item鐨勯鑹诧紝normal锛� active
+ COLORREF m_crItemFrame[2]; // item鐨勮竟妗嗭紝normal锛� active
COLORREF m_crItemNameText[2];
COLORREF m_crItemIdText[2];
- COLORREF m_crPinBkgnd[3]; // pin的颜色,normal, active, enable connect
+ COLORREF m_crPinBkgnd[3]; // pin鐨勯鑹诧紝normal, active, enable connect
int m_nCurSel;
EqsGraphListener m_listener;
CPtrArray m_arItem;
@@ -214,18 +226,20 @@
int m_nItemRound;
private:
- int m_nStageCx; // 画布大小
+ int m_nStageCx; // 鐢诲竷澶у皬
int m_nStageCy;
int m_nOffsetX;
int m_nOffsetY;
+ int m_nIndicatorSize;
+ int m_nIndicatorMargin;
- // 动画
+ // 鍔ㄧ敾
RECTF m_rcAnimation;
RECTF m_rcAninationStep;
int m_nAninationStep;
int m_nAninationDuration; // ms
- // 字体
+ // 瀛椾綋
HFONT m_hFontName;
HFONT m_hFontId;
diff --git a/SourceCode/Bond/Servo/HmLabel.cpp b/SourceCode/Bond/Servo/HmLabel.cpp
new file mode 100644
index 0000000..c60cacb
--- /dev/null
+++ b/SourceCode/Bond/Servo/HmLabel.cpp
@@ -0,0 +1,177 @@
+#include "stdafx.h"
+#include "HmLabel.h"
+
+
+CHmLabel::CHmLabel()
+{
+ m_crFrame = RGB(128, 128, 128);
+ m_crBackground = RGB(255, 0, 0);
+ m_crForeground = RGB(255, 255, 255);
+ m_hFont = NULL;
+ m_hFontNote = NULL;
+}
+
+
+CHmLabel::~CHmLabel()
+{
+ if (m_hFont != NULL) {
+ ::DeleteObject(m_hFont);
+ m_hFont = NULL;
+ }
+
+ if (m_hFontNote != NULL) {
+ ::DeleteObject(m_hFontNote);
+ m_hFontNote = NULL;
+ }
+}
+
+BEGIN_MESSAGE_MAP(CHmLabel, CStatic)
+ ON_WM_PAINT()
+ ON_WM_NCPAINT()
+END_MESSAGE_MAP()
+
+void CHmLabel::setText(CString strText)
+{
+ SetWindowText(strText);
+ Invalidate();
+}
+
+void CHmLabel::setNote1(CString strNote1)
+{
+ m_strNote1 = strNote1;
+}
+
+void CHmLabel::setFontSize(int size)
+{
+ if (m_hFont != NULL) {
+ ::DeleteObject(m_hFont);
+ m_hFont = NULL;
+ }
+
+ m_hFont = CreateFont(size, 0, 0, 0, FW_MEDIUM,
+ FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS,
+ CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, _T("宋体"));
+}
+
+void CHmLabel::setNoteFontSize(int size)
+{
+ if (m_hFontNote != NULL) {
+ ::DeleteObject(m_hFontNote);
+ m_hFontNote = NULL;
+ }
+
+ m_hFontNote = CreateFont(size, 0, 0, 0, FW_MEDIUM,
+ FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS,
+ CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, _T("宋体"));
+}
+
+void CHmLabel::setNoteTextColor(COLORREF color, BOOL bInvalidate/* = FALSE*/)
+{
+ m_crNote = color;
+ if (bInvalidate) Invalidate(TRUE);
+}
+
+void CHmLabel::setBackground(COLORREF color, BOOL bInvalidate/* = FALSE*/)
+{
+ m_crBackground = color;
+ if (bInvalidate) Invalidate(TRUE);
+}
+
+void CHmLabel::setForeground(COLORREF color, BOOL bInvalidate/* = FALSE*/)
+{
+ m_crForeground = color;
+ if (bInvalidate) Invalidate(TRUE);
+}
+
+void CHmLabel::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+ // TODO: 在此处添加消息处理程序代码
+ // 不为绘图消息调用 CStatic::OnPaint()
+
+
+ HDC hMemDC;
+ HBITMAP hBitmap;
+ RECT rcClient;
+ CString strText;
+ HBRUSH hBrushBK;
+
+
+ GetClientRect(&rcClient);
+ hMemDC = ::CreateCompatibleDC(dc.m_hDC);
+ hBitmap = ::CreateCompatibleBitmap(dc.m_hDC, rcClient.right - rcClient.left,
+ rcClient.bottom - rcClient.top);
+ ::SelectObject(hMemDC, hBitmap);
+
+
+ // 背景颜色
+ hBrushBK = CreateSolidBrush(m_crBackground);
+ ::FillRect(hMemDC, &rcClient, hBrushBK);
+ DeleteObject(hBrushBK);
+
+
+ // 文字
+ char szText[256];
+ GetWindowText(szText, 256);
+ RECT rcText = rcClient;
+ SetBkMode(hMemDC, TRANSPARENT);
+ SetTextColor(hMemDC, m_crForeground);
+ ::SelectObject(hMemDC, m_hFont == NULL ? (HFONT)GetStockObject(DEFAULT_GUI_FONT) : m_hFont);
+ ::DrawTextA(hMemDC, szText, (int)strlen(szText), &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+
+
+ // Note1
+ SetTextColor(hMemDC, m_crNote);
+ ::SelectObject(hMemDC, m_hFontNote == NULL ? (HFONT)GetStockObject(DEFAULT_GUI_FONT) : m_hFontNote);
+ ::DrawText(hMemDC, m_strNote1, m_strNote1.GetLength(), &rcClient, DT_CENTER | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS);
+
+
+ // EndPaint
+ ::BitBlt(dc.m_hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
+ hMemDC, 0, 0, SRCCOPY);
+ ::DeleteObject(hBitmap);
+ ::DeleteDC(hMemDC);
+}
+
+
+void CHmLabel::OnNcPaint()
+{
+ // TODO: 在此处添加消息处理程序代码
+ // 不为绘图消息调用 CStatic::OnNcPaint()
+ long styleEx = GetWindowLong(m_hWnd, GWL_EXSTYLE);
+ if ((styleEx & WS_EX_CLIENTEDGE) == WS_EX_CLIENTEDGE) {
+
+ RECT rect, rcClient;
+ GetClientRect(&rcClient);
+ ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.left);
+ ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.right);
+ GetWindowRect(&rect);
+ ::OffsetRect(&rcClient, -rect.left, -rect.top);
+
+ rect.right -= rect.left;
+ rect.bottom -= rect.top;
+ rect.left = 0;
+ rect.top = 0;
+
+ HRGN hRgnWnd = CreateRectRgnIndirect(&rect);
+ HRGN hRgnClient = CreateRectRgnIndirect(&rcClient);
+
+ HBRUSH hBrushBK, hBrushFrame;
+ HDC hDC = ::GetWindowDC(m_hWnd);
+ ::SelectClipRgn(hDC, hRgnWnd);
+ ::ExtSelectClipRgn(hDC, hRgnClient, RGN_DIFF);
+
+ hBrushBK = CreateSolidBrush(m_crBackground);
+ ::FillRect(hDC, &rect, hBrushBK);
+ DeleteObject(hBrushBK);
+
+ hBrushFrame = CreateSolidBrush(m_crFrame);
+ ::FrameRect(hDC, &rect, hBrushFrame);
+
+ ::DeleteObject(hRgnWnd);
+ ::DeleteObject(hRgnClient);
+ DeleteObject(hBrushFrame);
+ ::ReleaseDC(m_hWnd, hDC);
+ }
+}
+
diff --git a/SourceCode/Bond/Servo/HmLabel.h b/SourceCode/Bond/Servo/HmLabel.h
new file mode 100644
index 0000000..d7623e1
--- /dev/null
+++ b/SourceCode/Bond/Servo/HmLabel.h
@@ -0,0 +1,33 @@
+#pragma once
+class CHmLabel : public CStatic
+{
+public:
+ CHmLabel();
+ ~CHmLabel();
+
+public:
+ void setText(CString strText);
+ void setNote1(CString strNote1);
+ void setFontSize(int size);
+ void setNoteFontSize(int size);
+ void setBackground(COLORREF color, BOOL bInvalidate = FALSE);
+ void setForeground(COLORREF color, BOOL bInvalidate = FALSE);
+ void setNoteTextColor(COLORREF color, BOOL bInvalidate = FALSE);
+
+private:
+ COLORREF m_crFrame;
+ COLORREF m_crBackground;
+ COLORREF m_crForeground;
+ COLORREF m_crNote;
+ HFONT m_hFont;
+ HFONT m_hFontNote;
+
+private:
+ CString m_strNote1;
+
+public:
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnPaint();
+ afx_msg void OnNcPaint();
+};
+
diff --git a/SourceCode/Bond/Servo/HsmsAction.cpp b/SourceCode/Bond/Servo/HsmsAction.cpp
index 1c0d71c..1257696 100644
--- a/SourceCode/Bond/Servo/HsmsAction.cpp
+++ b/SourceCode/Bond/Servo/HsmsAction.cpp
@@ -9,6 +9,7 @@
m_nTimeout = 45;
m_nResponseTime = 0;
m_pContext = NULL;
+ m_pSendMessage = NULL;
m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}
@@ -19,6 +20,7 @@
m_nTimeout = nTimeout;
m_nResponseTime = 0;
m_pContext = NULL;
+ m_pSendMessage = NULL;
m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}
@@ -41,6 +43,10 @@
m_bNeedWaitReply = FALSE;
m_nTimeout = 45;
m_nResponseTime = 0;
+ if (m_pSendMessage != NULL) {
+ HSMS_Destroy1Message(m_pSendMessage);
+ m_pSendMessage = NULL;
+ }
::ResetEvent(m_hEvent);
}
@@ -73,6 +79,9 @@
void CHsmsAction::setSendMessage(IMessage* pMessage)
{
+ if (m_pSendMessage != NULL && m_pSendMessage != pMessage) {
+ HSMS_Destroy1Message(m_pSendMessage);
+ }
m_pSendMessage = pMessage;
}
@@ -110,12 +119,15 @@
int CHsmsAction::serialize(char* pszBuffer, int nBufferSize)
{
int index = 0;
+ if (m_pSendMessage == NULL) {
+ return 0;
+ }
if (pszBuffer == nullptr) {
index += sizeof(int);
index += sizeof(m_nTimeout);
index += sizeof(int);
index += sizeof(BOOL);
- index += m_pSendMessage->serialize(pszBuffer, nBufferSize);
+ index += m_pSendMessage->serialize(nullptr, 0);
return index;
}
@@ -157,7 +169,13 @@
memcpy(&m_bNeedWaitReply, &pszBuffer[index], sizeof(BOOL));
index += sizeof(BOOL);
- HSMS_Create1Message(m_pSendMessage, 1, 1 | REPLY, 1, 1);
+ if (m_pSendMessage != NULL) {
+ HSMS_Destroy1Message(m_pSendMessage);
+ m_pSendMessage = NULL;
+ }
+ if (HSMS_Create1Message(m_pSendMessage, 1, 1 | REPLY, 1, 1) != 0 || m_pSendMessage == NULL) {
+ return -1;
+ }
int nRet = m_pSendMessage->unserialize(&pszBuffer[index], nBufferSize - index);
if (nRet < 0) return nRet;
diff --git a/SourceCode/Bond/Servo/HsmsPassive.cpp b/SourceCode/Bond/Servo/HsmsPassive.cpp
index 1b122ec..98d6a85 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.cpp
+++ b/SourceCode/Bond/Servo/HsmsPassive.cpp
@@ -3,17 +3,78 @@
#include "Log.h"
#include "Model.h"
#include "Common.h"
+#include "RecipeManager.h"
#include <time.h>
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <string.h>
+#include <algorithm>
+#include <set>
#include <regex>
+#include <sstream>
+
+// ---- Encoding helpers ----
+static bool hasUtf8Bom(const std::string& s)
+{
+ return s.size() >= 3 &&
+ static_cast<unsigned char>(s[0]) == 0xEF &&
+ static_cast<unsigned char>(s[1]) == 0xBB &&
+ static_cast<unsigned char>(s[2]) == 0xBF;
+}
+
+static bool isLikelyUtf8(const std::string& s)
+{
+ // Simple heuristic: try to convert; if success without errors, treat as UTF-8.
+ int wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), (int)s.size(), nullptr, 0);
+ return wlen > 0;
+}
+
+static CStringW Utf8ToWide(const char* psz)
+{
+ if (psz == nullptr) return L"";
+ int wlen = MultiByteToWideChar(CP_UTF8, 0, psz, -1, nullptr, 0);
+ if (wlen <= 0) return L"";
+ CStringW ws;
+ LPWSTR buf = ws.GetBufferSetLength(wlen - 1);
+ MultiByteToWideChar(CP_UTF8, 0, psz, -1, buf, wlen);
+ ws.ReleaseBuffer();
+ return ws;
+}
+
+static CStringW AnsiToWide(const char* psz)
+{
+ if (psz == nullptr) return L"";
+ int wlen = MultiByteToWideChar(CP_ACP, 0, psz, -1, nullptr, 0);
+ if (wlen <= 0) return L"";
+ CStringW ws;
+ LPWSTR buf = ws.GetBufferSetLength(wlen - 1);
+ MultiByteToWideChar(CP_ACP, 0, psz, -1, buf, wlen);
+ ws.ReleaseBuffer();
+ return ws;
+}
+// ---- End helpers ----
+
+// ControlState values (keep in sync with Model::ControlState / VariableList.txt)
+static constexpr uint8_t kControlStateOnlineRemote = 5;
const char ACK[2] = {0, 1};
const char* ACK0 = &ACK[0];
const char* ACK1 = &ACK[1];
+
+// Log SECS-II message briefly to avoid huge strings causing issues.
+static void LogSecsMessageBrief(const char* tag, IMessage* pMessage, size_t maxLen = 1024)
+{
+ if (pMessage == nullptr) return;
+ const char* msgStr = pMessage->toString();
+ if (msgStr == nullptr) return;
+ std::string buf(msgStr);
+ if (buf.size() > maxLen) {
+ buf = buf.substr(0, maxLen) + "...<truncated>";
+ }
+ LOGI("%s%s", tag, buf.c_str());
+}
unsigned __stdcall CimWorkThreadFunction(LPVOID lpParam)
{
@@ -46,8 +107,6 @@
m_listener.onEQOffLine = nullptr;
m_listener.onEQOnLine = nullptr;
m_listener.onCommand = nullptr;
- m_listener.onEQConstantRequest = nullptr;
- m_listener.onEQConstantSend = nullptr;
m_pActiveAction = nullptr;
InitializeCriticalSection(&m_criticalSection);
}
@@ -108,23 +167,35 @@
ASSERT(pParent);
ASSERT(pVariable);
+ std::string svNote("SV");
+ {
+ SERVO::CVariable* pDef = getVariable((int)pVariable->getVarialbleId());
+ if (pDef == nullptr) {
+ pDef = pVariable;
+ }
+ auto& name = pDef->getName();
+ if (!name.empty()) {
+ svNote += " -> ";
+ svNote += name;
+ }
+ }
ISECS2Item* pItemList;
SERVO::SVFromat format = pVariable->getFormat();
switch (format)
{
case SERVO::SVFromat::U1:
- pParent->addU1Item((unsigned char)pVariable->getIntValue(), "SV");
+ pParent->addU1Item((unsigned char)pVariable->getIntValue(), svNote.c_str());
break;
case SERVO::SVFromat::U2:
- pParent->addU2Item((unsigned char)pVariable->getIntValue(), "SV");
+ pParent->addU2Item((unsigned char)pVariable->getIntValue(), svNote.c_str());
break;
case SERVO::SVFromat::I2:
- pParent->addI2Item((unsigned char)pVariable->getIntValue(), "SV");
+ pParent->addI2Item((unsigned char)pVariable->getIntValue(), svNote.c_str());
break;
case SERVO::SVFromat::A20:
case SERVO::SVFromat::A50:
- pParent->addItem(pVariable->getValue().c_str(), "SV");
+ pParent->addItem(pVariable->getValue().c_str(), svNote.c_str());
break;
case SERVO::SVFromat::L:
pItemList = pParent->addItem();
@@ -151,23 +222,50 @@
void CHsmsPassive::unlinkEventReport(unsigned int CEID)
{
+ LOGI("<CHsmsPassive>unlinkEventReport enter");
SERVO::CCollectionEvent* pEvent = getEvent(CEID);
if (pEvent != nullptr) {
pEvent->setReport(nullptr);
+ LOGI("<CHsmsPassive>unlink Event Report.CEID=%d", CEID);
}
+}
+
+bool CHsmsPassive::shouldSpool(uint8_t streamId, uint8_t functionId) const
+{
+ // Comment: stream 1 is not spooled
+ if (streamId == 1) return false;
+
+ // Comment: m=0 turns off all streams and fns
+ if (!m_spoolingEnabled) return false;
+
+ // Blacklist semantics: in map => do NOT spool/cache.
+ // Not in map => allow spooling by default.
+ auto it = m_spoolBlacklistByStream.find(streamId);
+ if (it == m_spoolBlacklistByStream.end()) return true;
+
+ // Empty set => all functions in this stream
+ if (it->second.empty()) return true;
+
+ return it->second.find(functionId) == it->second.end();
}
SERVO::CReport* CHsmsPassive::defineReport(unsigned int RPTID, std::vector<unsigned int>& vids)
{
+ LOGI("<CHsmsPassive>defineReport enter");
// 娣诲姞瀹氫箟report
SERVO::CReport* pReport = new SERVO::CReport(RPTID, vids);
for (auto vid : vids) {
SERVO::CVariable* pVariable = getVariable(vid);
+ if (pVariable == nullptr) {
+ pVariable = getDataVariable(vid);
+ }
if (pVariable != nullptr) {
pReport->addVariable(pVariable);
+ LOGI("<CHsmsPassive>defineReport RPTID=%d", RPTID);
}
}
m_reports.push_back(pReport);
+ writeReportsToFile(m_strReportFilepath);
return pReport;
}
@@ -191,7 +289,7 @@
int CHsmsPassive::onRecvMsg(IMessage* pMessage)
{
- LOGI("onRecvMsg:%s", pMessage->toString());
+ // LOGI("onRecvMsg:%s", pMessage->toString());
Lock();
if (m_pActiveAction != nullptr &&
(m_pActiveAction->getSendMessage()->getHeader()->systemBytes == pMessage->getHeader()->systemBytes)) {
@@ -228,40 +326,133 @@
int CHsmsPassive::loadVarialbles(const char* pszFilepath)
{
- CStdioFile file;
- if (!file.Open(pszFilepath, CFile::modeRead)) {
+ m_strVariableFilepath = pszFilepath;
+ m_bVariableUtf8 = false;
+ m_bVariableUtf8Bom = false;
+ // 鍏堣鍘熷瀛楄妭锛屽悗缁啀鎸� UTF-8/BOM 鎴栨湰鍦扮紪鐮佽浆鎹�
+ CFile file;
+ if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
return -1;
}
- std::regex pattern("^\\d+,.*"); // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
+ const ULONGLONG nLen = file.GetLength();
+ if (nLen == 0) {
+ return -1;
+ }
+
+ std::string buffer;
+ buffer.resize(static_cast<size_t>(nLen));
+ file.Read(buffer.data(), static_cast<UINT>(nLen));
+ file.Close();
+
+ const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buffer.data());
+ size_t offset = 0;
+ CStringW content;
+
+ // UTF-8 BOM
+ if (nLen >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
+ offset = 3;
+ m_bVariableUtf8 = true;
+ m_bVariableUtf8Bom = true;
+ }
+
+ // UTF-16 LE BOM
+ if (nLen >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
+ const wchar_t* wdata = reinterpret_cast<const wchar_t*>(buffer.data() + 2);
+ const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+ content.SetString(wdata, static_cast<int>(wlen));
+ }
+ // UTF-16 BE BOM
+ else if (nLen >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
+ const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+ std::wstring temp;
+ temp.reserve(wlen);
+ for (size_t i = 0; i < wlen; ++i) {
+ wchar_t ch = static_cast<wchar_t>(bytes[2 + i * 2] << 8 | bytes[2 + i * 2 + 1]);
+ temp.push_back(ch);
+ }
+ content = temp.c_str();
+ }
+ // 灏濊瘯 UTF-8锛堝惈鏃� BOM锛�
+ else {
+ auto tryUtf8 = [&](size_t off) -> bool {
+ int need = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buffer.data() + off,
+ static_cast<int>(buffer.size() - off), nullptr, 0);
+ if (need <= 0) return false;
+ std::wstring temp;
+ temp.resize(need);
+ MultiByteToWideChar(CP_UTF8, 0, buffer.data() + off,
+ static_cast<int>(buffer.size() - off), temp.data(), need);
+ content = temp.c_str();
+ m_bVariableUtf8 = true;
+ return true;
+ };
+
+ if (!tryUtf8(offset)) {
+ // 鍥為��鍒版湰鍦颁唬鐮侀〉
+ int need = MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), nullptr, 0);
+ if (need > 0) {
+ std::wstring temp;
+ temp.resize(need);
+ MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), temp.data(), need);
+ content = temp.c_str();
+ }
+ }
+ }
+
+ if (content.IsEmpty()) {
+ return -1;
+ }
+
+ std::wregex pattern(L"^\\d+,.+"); // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
std::vector<SERVO::CVariable*> variables;
int index, last;
- CString strLine;
- CString strId, strName, strFormat, strRemark;
- while (file.ReadString(strLine)) {
- if (!std::regex_match((LPTSTR)(LPCTSTR)strLine, pattern)) {
+ CStringW strLine;
+ CStringW strId, strName, strFormat, strRemark;
+ std::wstringstream ss(content.GetString());
+ auto narrowFromW = [](const CStringW& s) -> std::string {
+ int need = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, nullptr, nullptr);
+ if (need <= 0) return {};
+ std::string out(static_cast<size_t>(need - 1), '\0');
+ WideCharToMultiByte(CP_ACP, 0, s, -1, out.data(), need, nullptr, nullptr);
+ return out;
+ };
+ std::wstring line;
+ while (std::getline(ss, line, L'\n')) {
+ strLine = line.c_str();
+ strLine.Trim();
+ if (strLine.IsEmpty()) continue;
+ if (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
continue;
}
last = 0;
- index = strLine.Find(",", last);
+ index = strLine.Find(L",", last);
if (index < 0) continue;
strId = strLine.Left(index);
last = index + 1;
- index = strLine.Find(",", last);
+ index = strLine.Find(L",", last);
if (index < 0) continue;
strName = strLine.Mid(last, index - last);
last = index + 1;
- index = strLine.Find(",", last);
+ index = strLine.Find(L",", last);
if (index < 0) continue;
strFormat = strLine.Mid(last, index - last);
strRemark = strLine.Right(strLine.GetLength() - index - 1);
- strRemark.Replace(_T("\\r\\n"), _T("\r\n"));
+ strRemark.Replace(L"\\r\\n", L"\r\n");
+
+ std::string sId = narrowFromW(strId);
+ std::string sName = narrowFromW(strName);
+ std::string sFormat = narrowFromW(strFormat);
+ std::string sRemark = narrowFromW(strRemark);
SERVO::CVariable* pVarialble = new SERVO::CVariable(
- (LPTSTR)(LPCTSTR)strId, (LPTSTR)(LPCTSTR)strName, (LPTSTR)(LPCTSTR)strFormat, (LPTSTR)(LPCTSTR)strRemark);
+ sId.c_str(),
+ sName.c_str(),
+ sFormat.c_str(),
+ sRemark.c_str());
variables.push_back(pVarialble);
}
@@ -272,14 +463,230 @@
}
}
+ return 0;
+}
+int CHsmsPassive::loadDataVarialbles(const char* pszFilepath)
+{
+ if (pszFilepath == NULL) {
+ return -1;
+ }
+ m_strDataVariableFilepath = pszFilepath;
+ m_bDataVariableUtf8 = false;
+ m_bDataVariableUtf8Bom = false;
+ CFile file;
+ if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
+ return -1;
+ }
+ const ULONGLONG nLen = file.GetLength();
+ if (nLen == 0) {
+ return -1;
+ }
+ std::string buffer;
+ buffer.resize(static_cast<size_t>(nLen));
+ file.Read(buffer.data(), static_cast<UINT>(nLen));
file.Close();
+
+ if (hasUtf8Bom(buffer)) {
+ m_bDataVariableUtf8 = true;
+ m_bDataVariableUtf8Bom = true;
+ buffer = buffer.substr(3);
+ }
+ else if (isLikelyUtf8(buffer)) {
+ m_bDataVariableUtf8 = true;
+ }
+ CStringW content = m_bDataVariableUtf8 ? Utf8ToWide(buffer.c_str()) : AnsiToWide(buffer.c_str());
+
+ // Regex: DVID,DV Name,DV Format,DV Remark
+ std::wregex pattern(L"^\\d+,[^,]*,[^,]*,.*");
+ std::vector<SERVO::CDataVariable*> dataVars;
+ int index;
+ CStringW strLine, strId, strName, strFormat, strRemark;
+ std::wstringstream ss(content.GetString());
+ auto narrowFromW = [](const CStringW& s) -> std::string {
+ int need = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, nullptr, nullptr);
+ if (need <= 0) return {};
+ std::string out(static_cast<size_t>(need - 1), '\0');
+ WideCharToMultiByte(CP_ACP, 0, s, -1, out.data(), need, nullptr, nullptr);
+ return out;
+ };
+ std::wstring line;
+ while (std::getline(ss, line, L'\n')) {
+ strLine = line.c_str();
+ strLine.Trim();
+ if (strLine.IsEmpty()) continue;
+ if (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
+ continue;
+ }
+ index = strLine.Find(L",", 0);
+ if (index < 0) continue;
+ strId = strLine.Left(index);
+
+ strLine = strLine.Right(strLine.GetLength() - index - 1);
+ index = strLine.Find(L",", 0);
+ if (index < 0) continue;
+ strName = strLine.Left(index);
+
+ strLine = strLine.Right(strLine.GetLength() - index - 1);
+ index = strLine.Find(L",", 0);
+ if (index < 0) continue;
+ strFormat = strLine.Left(index);
+
+ strRemark = strLine.Right(strLine.GetLength() - index - 1);
+ strRemark.Replace(L"\\r\\n", L"\r\n");
+
+ std::string sId = narrowFromW(strId);
+ std::string sName = narrowFromW(strName);
+ std::string sFormat = narrowFromW(strFormat);
+ std::string sRemark = narrowFromW(strRemark);
+
+ SERVO::CDataVariable* pVarialble = new SERVO::CDataVariable(
+ sId.c_str(),
+ sName.c_str(),
+ sFormat.c_str(),
+ sRemark.c_str());
+ dataVars.push_back(pVarialble);
+ }
+
+ if (!dataVars.empty()) {
+ clearAllDataVariabel();
+ for (auto item : dataVars) {
+ m_dataVariabels.push_back(item);
+ }
+ }
+
+ return 0;
+}
+
+int CHsmsPassive::loadEquipmentConstants(const char* pszFilepath)
+{
+ if (pszFilepath == NULL) return -1;
+ m_strEquipmentConstantFilepath = pszFilepath;
+ m_bEquipmentConstantUtf8 = false;
+ m_bEquipmentConstantUtf8Bom = false;
+
+ CFile file;
+ if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
+ return -1;
+ }
+ const ULONGLONG nLen = file.GetLength();
+ if (nLen == 0) {
+ return -1;
+ }
+ std::string buffer;
+ buffer.resize(static_cast<size_t>(nLen));
+ file.Read(buffer.data(), static_cast<UINT>(nLen));
+ file.Close();
+
+ if (hasUtf8Bom(buffer)) {
+ m_bEquipmentConstantUtf8 = true;
+ m_bEquipmentConstantUtf8Bom = true;
+ buffer = buffer.substr(3);
+ }
+ else if (isLikelyUtf8(buffer)) {
+ m_bEquipmentConstantUtf8 = true;
+ }
+ CStringW content = m_bEquipmentConstantUtf8 ? Utf8ToWide(buffer.c_str()) : AnsiToWide(buffer.c_str());
+ if (content.IsEmpty()) return -1;
+
+ std::wregex pattern(L"^\\d+,[^,]*,[^,]*,([^,]*),.*");
+ std::vector<EquipmentConstantEntry> constants;
+ CStringW strLine, strId, strName, strFormat, strRemark, strDefault;
+ std::wstringstream ss(content.GetString());
+ auto narrowFromW = [](const CStringW& s) -> std::string {
+ int need = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, nullptr, nullptr);
+ if (need <= 0) return {};
+ std::string out(static_cast<size_t>(need - 1), '\0');
+ WideCharToMultiByte(CP_ACP, 0, s, -1, out.data(), need, nullptr, nullptr);
+ return out;
+ };
+ std::wstring line;
+ while (std::getline(ss, line, L'\n')) {
+ strLine = line.c_str();
+ strLine.Trim();
+ if (strLine.IsEmpty()) continue;
+ if (strLine.Find(L"ECID") == 0) continue; // skip header
+ if (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
+ continue;
+ }
+ int last = 0;
+ int idx = strLine.Find(L",", last);
+ if (idx < 0) continue;
+ strId = strLine.Left(idx);
+ last = idx + 1;
+
+ idx = strLine.Find(L",", last);
+ if (idx < 0) continue;
+ strName = strLine.Mid(last, idx - last);
+ last = idx + 1;
+
+ idx = strLine.Find(L",", last);
+ if (idx < 0) continue;
+ strFormat = strLine.Mid(last, idx - last);
+ last = idx + 1;
+
+ idx = strLine.Find(L",", last);
+ if (idx < 0) continue;
+ strRemark = strLine.Mid(last, idx - last);
+ last = idx + 1;
+
+ strDefault = strLine.Right(strLine.GetLength() - last);
+
+ EquipmentConstantEntry entry;
+ entry.id = _wtoi(strId);
+ entry.name = narrowFromW(strName);
+ entry.format = narrowFromW(strFormat);
+ entry.remark = narrowFromW(strRemark);
+ entry.value = narrowFromW(strDefault);
+ constants.push_back(entry);
+ }
+
+ if (!constants.empty()) {
+ m_equipmentConstants = std::move(constants);
+ }
return 0;
}
std::vector<SERVO::CVariable*>& CHsmsPassive::getVariables()
{
return m_variabels;
+}
+
+std::vector<SERVO::CDataVariable*>& CHsmsPassive::getDataVariables()
+{
+ return m_dataVariabels;
+}
+
+unsigned int CHsmsPassive::getMaxVariableId() const
+{
+ unsigned int maxId = 0;
+ for (auto item : m_variabels) {
+ if (item && item->getVarialbleId() > maxId) {
+ maxId = item->getVarialbleId();
+ }
+ }
+ for (auto item : m_dataVariabels) {
+ if (item && item->getVarialbleId() > maxId) {
+ maxId = item->getVarialbleId();
+ }
+ }
+ return maxId;
+}
+
+unsigned int CHsmsPassive::getMaxDataVariableId() const
+{
+ unsigned int maxId = 0;
+ for (auto item : m_variabels) {
+ if (item && item->getVarialbleId() > maxId) {
+ maxId = item->getVarialbleId();
+ }
+ }
+ for (auto item : m_dataVariabels) {
+ if (item && item->getVarialbleId() > maxId) {
+ maxId = item->getVarialbleId();
+ }
+ }
+ return maxId;
}
SERVO::CVariable* CHsmsPassive::getVariable(int variableId)
@@ -300,8 +707,46 @@
return item;
}
}
+ // try numeric id string
+ if (pszName != nullptr && *pszName) {
+ const int id = atoi(pszName);
+ if (id > 0) {
+ return getVariable(id);
+ }
+ }
return nullptr;
+}
+
+SERVO::CDataVariable* CHsmsPassive::getDataVariable(int dvid)
+{
+ for (auto item : m_dataVariabels) {
+ if (item->getVarialbleId() == (unsigned int)dvid) return item;
+ }
+ return nullptr;
+}
+
+SERVO::CDataVariable* CHsmsPassive::getDataVariable(const char* pszName)
+{
+ for (auto item : m_dataVariabels) {
+ if (item->getName().compare(pszName) == 0) return item;
+ }
+ return nullptr;
+}
+
+int CHsmsPassive::getCurrentControlState()
+{
+ auto v = getVariable("CurrentControlState");
+ if (v != nullptr) {
+ return static_cast<int>(v->getIntValue());
+ }
+ return 0;
+}
+
+bool CHsmsPassive::isHostCommandAllowed()
+{
+ // Only allow host control commands in OnlineRemote.
+ return getCurrentControlState() == kControlStateOnlineRemote;
}
void CHsmsPassive::clearAllVariabel()
@@ -312,58 +757,447 @@
m_variabels.clear();
}
+void CHsmsPassive::clearAllDataVariabel()
+{
+ for (auto item : m_dataVariabels) {
+ delete item;
+ }
+ m_dataVariabels.clear();
+}
+
+CStringA WideToUtf8(const CStringW& ws)
+{
+ int need = WideCharToMultiByte(CP_UTF8, 0, ws, -1, nullptr, 0, nullptr, nullptr);
+ if (need <= 0) return "";
+ CStringA out;
+ LPSTR buf = out.GetBufferSetLength(need - 1);
+ WideCharToMultiByte(CP_UTF8, 0, ws, -1, buf, need, nullptr, nullptr);
+ out.ReleaseBuffer();
+ return out;
+}
+
+CStringA WideToAnsi(const CStringW& ws)
+{
+ int need = WideCharToMultiByte(CP_ACP, 0, ws, -1, nullptr, 0, nullptr, nullptr);
+ if (need <= 0) return "";
+ CStringA out;
+ LPSTR buf = out.GetBufferSetLength(need - 1);
+ WideCharToMultiByte(CP_ACP, 0, ws, -1, buf, need, nullptr, nullptr);
+ out.ReleaseBuffer();
+ return out;
+}
+
+static CStringA AnsiToUtf8(const std::string& s)
+{
+ int wlen = MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, nullptr, 0);
+ if (wlen <= 0) return "";
+ CStringW ws;
+ LPWSTR wbuf = ws.GetBufferSetLength(wlen - 1);
+ MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, wbuf, wlen);
+ ws.ReleaseBuffer();
+ return WideToUtf8(ws);
+}
+
+int CHsmsPassive::deleteVariable(int variableId)
+{
+ Lock();
+ auto it = std::find_if(m_variabels.begin(), m_variabels.end(), [=](SERVO::CVariable* v) {
+ return v != nullptr && v->getVarialbleId() == variableId;
+ });
+ if (it == m_variabels.end()) {
+ Unlock();
+ return -1;
+ }
+ delete *it;
+ m_variabels.erase(it);
+ auto filepath = m_strVariableFilepath;
+ Unlock();
+
+ if (filepath.empty()) return -2;
+
+ return writeVariablesToFile(filepath);
+}
+
void CHsmsPassive::setVariableValue(const char* pszName, __int64 value)
{
- auto v = getVariable(pszName);
- if (v != nullptr) {
+ // Protect variable list updates; multiple threads may set SVs.
+ Lock();
+ if (auto v = getVariable(pszName)) {
v->setValue(value);
}
+ else if (auto dv = getDataVariable(pszName)) {
+ dv->setValue(value);
+ }
+ Unlock();
}
void CHsmsPassive::setVariableValue(const char* pszName, const char* value)
{
- auto v = getVariable(pszName);
- if (v != nullptr) {
+ Lock();
+ if (auto v = getVariable(pszName)) {
v->setValue(value);
}
+ else if (auto dv = getDataVariable(pszName)) {
+ dv->setValue(value);
+ }
+ Unlock();
}
void CHsmsPassive::setVariableValue(const char* pszName, std::vector<SERVO::CVariable>& vars)
{
- auto v = getVariable(pszName);
- if (v != nullptr) {
+ Lock();
+ if (auto v = getVariable(pszName)) {
v->setValue(vars);
}
+ else if (auto dv = getDataVariable(pszName)) {
+ dv->setValue(vars);
+ }
+ Unlock();
+}
+
+void CHsmsPassive::withVariableLock(const std::function<void()>& fn)
+{
+ Lock();
+ if (fn) fn();
+ Unlock();
+}
+
+static bool isValidFormat(const std::string& fmt)
+{
+ static const std::set<std::string> allow = { "U1","U2","I2","A20","A50","L" };
+ return allow.count(fmt) > 0;
+}
+
+int CHsmsPassive::addVariable(const char* pszName, const char* pszFormat, const char* pszRemark, int& outId)
+{
+ if (pszName == nullptr || pszFormat == nullptr) return -1;
+ std::string fmt = pszFormat;
+ std::transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
+ if (!isValidFormat(fmt)) return -2;
+
+ Lock();
+ int maxId = 0;
+ for (auto v : m_variabels) {
+ if (v != nullptr && static_cast<int>(v->getVarialbleId()) > maxId) {
+ maxId = static_cast<int>(v->getVarialbleId());
+ }
+ }
+ outId = maxId + 1;
+
+ SERVO::CVariable* pNew = new SERVO::CVariable(std::to_string(outId).c_str(), pszName, fmt.c_str(), pszRemark ? pszRemark : "");
+ m_variabels.push_back(pNew);
+ auto filepath = m_strVariableFilepath;
+ Unlock();
+
+ if (filepath.empty()) return -3;
+ return writeVariablesToFile(filepath);
+}
+
+int CHsmsPassive::updateVariable(int variableId, const char* pszName, const char* pszFormat, const char* pszRemark)
+{
+ if (pszName == nullptr || pszFormat == nullptr) return -1;
+ std::string fmt = pszFormat;
+ std::transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
+ if (!isValidFormat(fmt)) return -2;
+
+ Lock();
+ auto it = std::find_if(m_variabels.begin(), m_variabels.end(), [=](SERVO::CVariable* v) {
+ return v != nullptr && v->getVarialbleId() == variableId;
+ });
+ if (it == m_variabels.end()) {
+ Unlock();
+ return -4;
+ }
+ (*it)->setName(pszName);
+ (*it)->setFormat(fmt.c_str());
+ (*it)->setRemark(pszRemark ? pszRemark : "");
+ auto filepath = m_strVariableFilepath;
+ Unlock();
+
+ if (filepath.empty()) return -3;
+ return writeVariablesToFile(filepath);
+}
+
+int CHsmsPassive::deleteDataVariable(int dvid)
+{
+ Lock();
+ auto it = std::find_if(m_dataVariabels.begin(), m_dataVariabels.end(), [=](SERVO::CDataVariable* v) {
+ return v != nullptr && v->getVarialbleId() == (unsigned int)dvid;
+ });
+ if (it == m_dataVariabels.end()) {
+ Unlock();
+ return -1;
+ }
+ delete *it;
+ m_dataVariabels.erase(it);
+ auto filepath = m_strDataVariableFilepath;
+ Unlock();
+
+ if (filepath.empty()) return -2;
+ return writeDataVariablesToFile(filepath);
+}
+
+int CHsmsPassive::addDataVariable(const char* pszName, const char* pszFormat, const char* pszRemark, int& outId)
+{
+ if (pszName == nullptr || pszFormat == nullptr) return -1;
+ std::string fmt = pszFormat;
+ std::transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
+ if (!isValidFormat(fmt)) return -2;
+
+ Lock();
+ int maxId = 0;
+ for (auto v : m_dataVariabels) {
+ if (v != nullptr && static_cast<int>(v->getVarialbleId()) > maxId) {
+ maxId = static_cast<int>(v->getVarialbleId());
+ }
+ }
+ outId = maxId + 1;
+
+ SERVO::CDataVariable* pNew = new SERVO::CDataVariable(std::to_string(outId).c_str(), pszName, fmt.c_str(), pszRemark ? pszRemark : "");
+ m_dataVariabels.push_back(pNew);
+ auto filepath = m_strDataVariableFilepath;
+ Unlock();
+
+ if (filepath.empty()) return -3;
+ return writeDataVariablesToFile(filepath);
+}
+
+int CHsmsPassive::updateDataVariable(int dvid, const char* pszName, const char* pszFormat, const char* pszRemark)
+{
+ if (pszName == nullptr || pszFormat == nullptr) return -1;
+ std::string fmt = pszFormat;
+ std::transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
+ if (!isValidFormat(fmt)) return -2;
+
+ Lock();
+ auto it = std::find_if(m_dataVariabels.begin(), m_dataVariabels.end(), [=](SERVO::CDataVariable* v) {
+ return v != nullptr && v->getVarialbleId() == (unsigned int)dvid;
+ });
+ if (it == m_dataVariabels.end()) {
+ Unlock();
+ return -4;
+ }
+ (*it)->setName(pszName);
+ (*it)->setFormat(fmt.c_str());
+ (*it)->setRemark(pszRemark ? pszRemark : "");
+ auto filepath = m_strDataVariableFilepath;
+ Unlock();
+
+ if (filepath.empty()) return -3;
+ return writeDataVariablesToFile(filepath);
+}
+
+int CHsmsPassive::writeVariablesToFile(const std::string& filepath)
+{
+ if (filepath.empty()) return -3;
+
+ CFile file;
+ if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) {
+ return -3;
+ }
+
+ // header
+ const std::string headerAnsi = "SVID,SV Name,SV Format,SV Remark\r\n";
+ if (m_bVariableUtf8) {
+ if (m_bVariableUtf8Bom) {
+ const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
+ file.Write(bom, 3);
+ }
+ CStringA header = AnsiToUtf8(headerAnsi);
+ file.Write(header.GetString(), header.GetLength());
+ }
+ else {
+ file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
+ }
+
+ for (auto v : m_variabels) {
+ if (v == nullptr) continue;
+ std::string lineAnsi;
+ lineAnsi.reserve(256);
+ lineAnsi += std::to_string(v->getVarialbleId());
+ lineAnsi.push_back(',');
+ lineAnsi += v->getName();
+ lineAnsi.push_back(',');
+ lineAnsi += SERVO::CVariable::formatToString(v->getFormat());
+ lineAnsi.push_back(',');
+ lineAnsi += v->getRemark();
+ lineAnsi.append("\r\n");
+
+ if (m_bVariableUtf8) {
+ CStringA outLine = AnsiToUtf8(lineAnsi);
+ file.Write(outLine.GetString(), outLine.GetLength());
+ }
+ else {
+ file.Write(lineAnsi.data(), (UINT)lineAnsi.size());
+ }
+ }
+ file.Close();
+
+ return 0;
+}
+
+int CHsmsPassive::writeDataVariablesToFile(const std::string& filepath)
+{
+ if (filepath.empty()) return -3;
+
+ CFile file;
+ if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) {
+ return -3;
+ }
+
+ const std::string headerAnsi = "DVID,DV Name,DV Format,DV Remark\r\n";
+ if (m_bDataVariableUtf8) {
+ if (m_bDataVariableUtf8Bom) {
+ const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
+ file.Write(bom, 3);
+ }
+ CStringA header = AnsiToUtf8(headerAnsi);
+ file.Write(header.GetString(), header.GetLength());
+ }
+ else {
+ file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
+ }
+
+ for (auto v : m_dataVariabels) {
+ if (v == nullptr) continue;
+ std::string lineAnsi;
+ lineAnsi.reserve(256);
+ lineAnsi += std::to_string(v->getVarialbleId());
+ lineAnsi.push_back(',');
+ lineAnsi += v->getName();
+ lineAnsi.push_back(',');
+ lineAnsi += SERVO::CVariable::formatToString(v->getFormat());
+ lineAnsi.push_back(',');
+ lineAnsi += v->getRemark();
+ lineAnsi.append("\r\n");
+
+ if (m_bDataVariableUtf8) {
+ CStringA outLine = AnsiToUtf8(lineAnsi);
+ file.Write(outLine.GetString(), outLine.GetLength());
+ }
+ else {
+ file.Write(lineAnsi.data(), (UINT)lineAnsi.size());
+ }
+ }
+ file.Close();
+ return 0;
}
int CHsmsPassive::loadReports(const char* pszFilepath)
{
- CStdioFile file;
- if (!file.Open(pszFilepath, CFile::modeRead)) {
+ m_strReportFilepath = pszFilepath;
+ m_bReportUtf8 = false;
+ m_bReportUtf8Bom = false;
+ // 鍏煎 UTF-8/BOM 涓庢湰鍦扮紪鐮佽鍙�
+ CFile file;
+ if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
return -1;
}
- std::regex pattern("^\\d+,\\(\\d+(,\\d+)*\\).*"); // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
+ const ULONGLONG nLen = file.GetLength();
+ if (nLen == 0) {
+ return -1;
+ }
+
+ std::string buffer;
+ buffer.resize(static_cast<size_t>(nLen));
+ file.Read(buffer.data(), static_cast<UINT>(nLen));
+ file.Close();
+
+ const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buffer.data());
+ size_t offset = 0;
+ CStringW content;
+
+ // UTF-8 BOM
+ if (nLen >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
+ offset = 3;
+ m_bReportUtf8 = true;
+ m_bReportUtf8Bom = true;
+ }
+
+ // UTF-16 LE BOM
+ if (nLen >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
+ const wchar_t* wdata = reinterpret_cast<const wchar_t*>(buffer.data() + 2);
+ const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+ content.SetString(wdata, static_cast<int>(wlen));
+ }
+ // UTF-16 BE BOM
+ else if (nLen >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
+ const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+ std::wstring temp;
+ temp.reserve(wlen);
+ for (size_t i = 0; i < wlen; ++i) {
+ wchar_t ch = static_cast<wchar_t>(bytes[2 + i * 2] << 8 | bytes[2 + i * 2 + 1]);
+ temp.push_back(ch);
+ }
+ content = temp.c_str();
+ }
+ else {
+ auto tryUtf8 = [&](size_t off) -> bool {
+ int need = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buffer.data() + off,
+ static_cast<int>(buffer.size() - off), nullptr, 0);
+ if (need <= 0) return false;
+ std::wstring temp;
+ temp.resize(need);
+ MultiByteToWideChar(CP_UTF8, 0, buffer.data() + off,
+ static_cast<int>(buffer.size() - off), temp.data(), need);
+ content = temp.c_str();
+ m_bReportUtf8 = true;
+ return true;
+ };
+
+ if (!tryUtf8(offset)) {
+ int need = MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), nullptr, 0);
+ if (need > 0) {
+ std::wstring temp;
+ temp.resize(need);
+ MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), temp.data(), need);
+ content = temp.c_str();
+ }
+ }
+ }
+
+ if (content.IsEmpty()) {
+ return -1;
+ }
+
+ std::wregex pattern(L"^\\d+,\\(\\d+(,\\d+)*\\).*"); // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
std::vector<SERVO::CReport*> reports;
int index;
- CString strLine, strVariable;
- CString strId;
- while (file.ReadString(strLine)) {
- if (!std::regex_match((LPTSTR)(LPCTSTR)strLine, pattern)) {
+ CStringW strLine, strVariable;
+ CStringW strId;
+ std::wstringstream ss(content.GetString());
+ auto narrowFromW = [](const CStringW& s) -> std::string {
+ int need = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, nullptr, nullptr);
+ if (need <= 0) return {};
+ std::string out(static_cast<size_t>(need - 1), '\0');
+ WideCharToMultiByte(CP_ACP, 0, s, -1, out.data(), need, nullptr, nullptr);
+ return out;
+ };
+ std::wstring line;
+ while (std::getline(ss, line, L'\n')) {
+ strLine = line.c_str();
+ strLine.Trim();
+ if (strLine.IsEmpty()) continue;
+ if (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
continue;
}
- index = strLine.Find(",", 0);
+ index = strLine.Find(L",", 0);
if (index < 0) continue;
strId = strLine.Left(index);
strVariable = strLine.Right(strLine.GetLength() - index - 1);
strVariable.Delete(0);
strVariable.Delete(strVariable.GetLength() - 1);
- auto vids = parseVidList(strVariable);
+ CString strVariableA(narrowFromW(strVariable).c_str());
+ auto vids = parseVidList(strVariableA);
- SERVO::CReport* pReport = new SERVO::CReport(atoi((LPTSTR)(LPCTSTR)strId), vids);
+ SERVO::CReport* pReport = new SERVO::CReport(_wtoi(strId), vids);
for (auto vid : vids) {
SERVO::CVariable* pVariable = getVariable(vid);
+ if (pVariable == nullptr) {
+ pVariable = getDataVariable(vid);
+ }
if (pVariable != nullptr) {
pReport->addVariable(pVariable);
}
@@ -380,13 +1214,23 @@
}
- file.Close();
return 0;
}
std::vector<SERVO::CReport*>& CHsmsPassive::getReports()
{
return m_reports;
+}
+
+unsigned int CHsmsPassive::getMaxReportId() const
+{
+ unsigned int maxId = 0;
+ for (auto item : m_reports) {
+ if (item && item->getReportId() > maxId) {
+ maxId = item->getReportId();
+ }
+ }
+ return maxId;
}
SERVO::CReport* CHsmsPassive::getReport(int rptid)
@@ -413,52 +1257,248 @@
return false;
}
-void CHsmsPassive::clearAllReport()
+int CHsmsPassive::deleteReport(int rptid)
{
+ LOGI("<CHsmsPassive>deleteReport enter");
+ if (!removeReport(rptid)) {
+ return -1;
+ }
+ LOGI("<CHsmsPassive>delete Report. rptid=%d", rptid);
+
+ return writeReportsToFile(m_strReportFilepath);
+}
+
+int CHsmsPassive::addReport(int rptid, const std::vector<unsigned int>& vids)
+{
+ if (getReport(rptid) != nullptr) {
+ return -1;
+ }
+ SERVO::CReport* pReport = new SERVO::CReport(rptid, vids);
+ for (auto vid : vids) {
+ SERVO::CVariable* pVariable = getVariable((int)vid);
+ if (pVariable == nullptr) {
+ pVariable = getDataVariable((int)vid);
+ }
+ if (pVariable != nullptr) {
+ pReport->addVariable(pVariable);
+ }
+ }
+ m_reports.push_back(pReport);
+ return writeReportsToFile(m_strReportFilepath);
+}
+
+int CHsmsPassive::updateReport(int rptid, const std::vector<unsigned int>& vids)
+{
+ for (auto iter = m_reports.begin(); iter != m_reports.end(); ++iter) {
+ if ((*iter)->getReportId() == rptid) {
+ delete (*iter);
+ SERVO::CReport* pReport = new SERVO::CReport(rptid, vids);
+ for (auto vid : vids) {
+ SERVO::CVariable* pVariable = getVariable((int)vid);
+ if (pVariable == nullptr) {
+ pVariable = getDataVariable((int)vid);
+ }
+ if (pVariable != nullptr) {
+ pReport->addVariable(pVariable);
+ }
+ }
+ *iter = pReport;
+ return writeReportsToFile(m_strReportFilepath);
+ }
+ }
+ return -1;
+}
+
+void CHsmsPassive::clearAllReport(BOOL bSave/* = FALSE*/)
+{
+ LOGI("<CHsmsPassive>clearAllReport enter");
for (auto item : m_reports) {
delete item;
}
m_reports.clear();
+
+ if(bSave)
+ writeReportsToFile(m_strReportFilepath);
+}
+
+int CHsmsPassive::writeReportsToFile(const std::string& filepath)
+{
+ if (filepath.empty()) return -1;
+
+ CFile file;
+ if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite)) {
+ return -1;
+ }
+
+ if (m_bReportUtf8 && m_bReportUtf8Bom) {
+ const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
+ file.Write(bom, 3);
+ }
+
+ // header
+ const std::string headerAnsi = "RPTID,(VID1,VID2,...)\r\n";
+ if (m_bReportUtf8) {
+ CStringA header = AnsiToUtf8(headerAnsi);
+ file.Write(header.GetString(), header.GetLength());
+ }
+ else {
+ file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
+ }
+
+ for (auto rpt : m_reports) {
+ if (rpt == nullptr) continue;
+ std::string line;
+ line.reserve(64);
+ line += std::to_string(rpt->getReportId());
+ line += ",(";
+
+ const auto& vids = rpt->getVids();
+ for (size_t i = 0; i < vids.size(); ++i) {
+ line += std::to_string(vids[i]);
+ if (i + 1 < vids.size()) {
+ line.push_back(',');
+ }
+ }
+ line += ")\r\n";
+
+ if (m_bReportUtf8) {
+ CStringA out = AnsiToUtf8(line);
+ file.Write(out.GetString(), out.GetLength());
+ }
+ else {
+ file.Write(line.data(), (UINT)line.size());
+ }
+ }
+
+ file.Close();
+ return 0;
}
int CHsmsPassive::loadCollectionEvents(const char* pszFilepath)
{
- CStdioFile file;
- if (!file.Open(pszFilepath, CFile::modeRead)) {
+ m_strCollectionEventFilepath = pszFilepath;
+ m_bCollectionUtf8 = false;
+ m_bCollectionUtf8Bom = false;
+ CFile file;
+ if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
return -1;
}
- std::regex pattern("^\\d+,[^,]*,[^,]*,\\(\\d+(,\\d+)*\\).*"); // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
+ const ULONGLONG nLen = file.GetLength();
+ if (nLen == 0) {
+ return -1;
+ }
+
+ std::string buffer;
+ buffer.resize(static_cast<size_t>(nLen));
+ file.Read(buffer.data(), static_cast<UINT>(nLen));
+ file.Close();
+
+ const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buffer.data());
+ size_t offset = 0;
+ CStringW content;
+
+ // UTF-8 BOM
+ if (nLen >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
+ offset = 3;
+ m_bCollectionUtf8 = true;
+ m_bCollectionUtf8Bom = true;
+ }
+
+ // UTF-16 LE BOM
+ if (nLen >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
+ const wchar_t* wdata = reinterpret_cast<const wchar_t*>(buffer.data() + 2);
+ const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+ content.SetString(wdata, static_cast<int>(wlen));
+ }
+ // UTF-16 BE BOM
+ else if (nLen >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
+ const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+ std::wstring temp;
+ temp.reserve(wlen);
+ for (size_t i = 0; i < wlen; ++i) {
+ wchar_t ch = static_cast<wchar_t>(bytes[2 + i * 2] << 8 | bytes[2 + i * 2 + 1]);
+ temp.push_back(ch);
+ }
+ content = temp.c_str();
+ }
+ else {
+ auto tryUtf8 = [&](size_t off) -> bool {
+ int need = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buffer.data() + off,
+ static_cast<int>(buffer.size() - off), nullptr, 0);
+ if (need <= 0) return false;
+ std::wstring temp;
+ temp.resize(need);
+ MultiByteToWideChar(CP_UTF8, 0, buffer.data() + off,
+ static_cast<int>(buffer.size() - off), temp.data(), need);
+ content = temp.c_str();
+ m_bCollectionUtf8 = true;
+ return true;
+ };
+
+ if (!tryUtf8(offset)) {
+ int need = MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), nullptr, 0);
+ if (need > 0) {
+ std::wstring temp;
+ temp.resize(need);
+ MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), temp.data(), need);
+ content = temp.c_str();
+ }
+ }
+ }
+
+ if (content.IsEmpty()) {
+ return -1;
+ }
+
+ // 鍏佽 Attached RPTID 涓虹┖锛�()
+ std::wregex pattern(L"^\\d+,[^,]*,[^,]*,\\(\\d*(,\\d+)*\\).*"); // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
std::vector<SERVO::CCollectionEvent*> events;
int index, last;
- CString strLine, strRPTIDs;
- CString strId, strName, strDescription;
- while (file.ReadString(strLine)) {
- if (!std::regex_match((LPTSTR)(LPCTSTR)strLine, pattern)) {
+ CStringW strLine, strRPTIDs;
+ CStringW strId, strName, strDescription;
+ std::wstringstream ss(content.GetString());
+ auto narrowFromW = [](const CStringW& s) -> std::string {
+ int need = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, nullptr, nullptr);
+ if (need <= 0) return {};
+ std::string out(static_cast<size_t>(need - 1), '\0');
+ WideCharToMultiByte(CP_ACP, 0, s, -1, out.data(), need, nullptr, nullptr);
+ return out;
+ };
+ std::wstring line;
+ while (std::getline(ss, line, L'\n')) {
+ strLine = line.c_str();
+ strLine.Trim();
+ if (strLine.IsEmpty()) continue;
+ if (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
continue;
}
last = 0;
- index = strLine.Find(",", last);
+ index = strLine.Find(L",", last);
if (index < 0) continue;
strId = strLine.Left(index);
last = index + 1;
- index = strLine.Find(",", last);
+ index = strLine.Find(L",", last);
if (index < 0) continue;
strName = strLine.Mid(last, index - last);
last = index + 1;
- index = strLine.Find(",", last);
+ index = strLine.Find(L",", last);
if (index < 0) continue;
strDescription = strLine.Mid(last, index - last);
strRPTIDs = strLine.Right(strLine.GetLength() - index - 1);
strRPTIDs.Delete(0);
strRPTIDs.Delete(strRPTIDs.GetLength() - 1);
- auto prtids = parseVidList(strRPTIDs);
+ CString strRPTIDsA(narrowFromW(strRPTIDs).c_str());
+ auto prtids = parseVidList(strRPTIDsA);
+
+ std::string sName = narrowFromW(strName);
+ std::string sDesc = narrowFromW(strDescription);
SERVO::CCollectionEvent* pEvent = new SERVO::CCollectionEvent(
- atoi(strId), (LPTSTR)(LPCTSTR)strName, (LPTSTR)(LPCTSTR)strDescription, prtids);
+ _wtoi(strId), sName.c_str(), sDesc.c_str(), prtids);
for (auto rptid : prtids) {
SERVO::CReport* pReport = getReport(rptid);
if (pReport != nullptr) {
@@ -474,15 +1514,70 @@
m_collectionEvents.push_back(item);
}
}
-
-
- file.Close();
return 0;
}
std::vector<SERVO::CCollectionEvent*>& CHsmsPassive::getCollectionEvents()
{
return m_collectionEvents;
+}
+
+unsigned int CHsmsPassive::getMaxCollectionEventId() const
+{
+ unsigned int maxId = 0;
+ for (auto item : m_collectionEvents) {
+ if (item && item->getEventId() > maxId) {
+ maxId = item->getEventId();
+ }
+ }
+ return maxId;
+}
+
+int CHsmsPassive::deleteCollectionEvent(unsigned short CEID)
+{
+ for (auto iter = m_collectionEvents.begin(); iter != m_collectionEvents.end(); ++iter) {
+ if ((*iter)->getEventId() == CEID) {
+ delete (*iter);
+ m_collectionEvents.erase(iter);
+ return writeCollectionEventsToFile(m_strCollectionEventFilepath);
+ }
+ }
+ return -1;
+}
+
+int CHsmsPassive::addCollectionEvent(unsigned int CEID, const char* name, const char* desc, const std::vector<unsigned int>& rptids)
+{
+ if (getEvent((unsigned short)CEID) != nullptr) {
+ return -1;
+ }
+ auto* pEvent = new SERVO::CCollectionEvent(CEID, name, desc, const_cast<std::vector<unsigned int>&>(rptids));
+ for (auto rptid : rptids) {
+ SERVO::CReport* pReport = getReport((int)rptid);
+ if (pReport != nullptr) {
+ pEvent->addReport(pReport);
+ }
+ }
+ m_collectionEvents.push_back(pEvent);
+ return writeCollectionEventsToFile(m_strCollectionEventFilepath);
+}
+
+int CHsmsPassive::updateCollectionEvent(unsigned int CEID, const char* name, const char* desc, const std::vector<unsigned int>& rptids)
+{
+ for (auto iter = m_collectionEvents.begin(); iter != m_collectionEvents.end(); ++iter) {
+ if ((*iter)->getEventId() == CEID) {
+ delete (*iter);
+ auto* pEvent = new SERVO::CCollectionEvent(CEID, name, desc, const_cast<std::vector<unsigned int>&>(rptids));
+ for (auto rptid : rptids) {
+ SERVO::CReport* pReport = getReport((int)rptid);
+ if (pReport != nullptr) {
+ pEvent->addReport(pReport);
+ }
+ }
+ *iter = pEvent;
+ return writeCollectionEventsToFile(m_strCollectionEventFilepath);
+ }
+ }
+ return -1;
}
void CHsmsPassive::clearAllCollectionEvent()
@@ -526,6 +1621,62 @@
return result;
}
+int CHsmsPassive::writeCollectionEventsToFile(const std::string& filepath)
+{
+ if (filepath.empty()) return -1;
+
+ CFile file;
+ if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite)) {
+ return -1;
+ }
+
+ if (m_bCollectionUtf8 && m_bCollectionUtf8Bom) {
+ const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
+ file.Write(bom, 3);
+ }
+
+ const std::string headerAnsi = "CEID,CE Name,Descriptions,Attached RPTID\r\n";
+ if (m_bCollectionUtf8) {
+ CStringA header = AnsiToUtf8(headerAnsi);
+ file.Write(header.GetString(), header.GetLength());
+ }
+ else {
+ file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
+ }
+
+ for (auto ev : m_collectionEvents) {
+ if (ev == nullptr) continue;
+ std::string line;
+ line.reserve(128);
+ line += std::to_string(ev->getEventId());
+ line.push_back(',');
+ line += ev->getName();
+ line.push_back(',');
+ line += ev->getDescription();
+ line.push_back(',');
+ line.push_back('(');
+ auto rptIds = ev->getReportIds();
+ for (size_t i = 0; i < rptIds.size(); ++i) {
+ line += std::to_string(rptIds[i]);
+ if (i + 1 < rptIds.size()) {
+ line.push_back(',');
+ }
+ }
+ line += ")\r\n";
+
+ if (m_bCollectionUtf8) {
+ CStringA out = AnsiToUtf8(line);
+ file.Write(out.GetString(), out.GetLength());
+ }
+ else {
+ file.Write(line.data(), (UINT)line.size());
+ }
+ }
+
+ file.Close();
+ return 0;
+}
+
int CHsmsPassive::init(CModel* pModel, const char* pszName, unsigned int port)
{
m_pModel = pModel;
@@ -549,7 +1700,7 @@
*/
};
auto onRecvSysMessage = [&](void* pFrom, IMessage* pMessage) -> void {
- LOGI("<HSMS>onRecvSysMessage:sessionId:%d, sType:%d systemBytes:%d",
+ LOGI("<HSMS>[Received]sessionId:%d, sType:%d systemBytes:%d",
pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
onRecvMsg(pMessage);
if (MSG_LINKTEST_REQ == pMessage->getHeader()->sType) {
@@ -576,13 +1727,22 @@
HEADER* pHeader = pMessage->getHeader();
int nStream = (pHeader->stream & 0x7F);
- LOGI("<HSMS>鏀跺埌娑堟伅 S%dF%d", nStream, pHeader->function);
+ LogSecsMessageBrief("<HSMS>[Received]", pMessage);
if (nStream == 1 && pHeader->function == 1) {
// S1F1
replyAreYouThere(pMessage);
}
else if (nStream == 1 && pHeader->function == 3) {
replySelectedEquipmentStatusData(pMessage);
+ }
+ else if (nStream == 1 && pHeader->function == 11) {
+ replyStatusVariableNamelistRequest(pMessage);
+ }
+ else if (nStream == 1 && pHeader->function == 21) {
+ replyDataVariableNamelistRequest(pMessage);
+ }
+ else if (nStream == 1 && pHeader->function == 23) {
+ replyCollectionEventNamelistRequest(pMessage);
}
else if (nStream == 1 && pHeader->function == 13) {
replyEstablishCommunications(pMessage);
@@ -629,6 +1789,12 @@
else if (nStream == 7 && pHeader->function == 19) {
replyQueryPPIDList(pMessage);
}
+ else if (nStream == 7 && pHeader->function == 17) {
+ replyDeletePPID(pMessage);
+ }
+ else if (nStream == 7 && pHeader->function == 5) {
+ replyProcessProgramRequest(pMessage);
+ }
else if (nStream == 10 && pHeader->function == 3) {
replyTerminalDisplay(pMessage);
}
@@ -668,7 +1834,12 @@
return -1;
}
- int nBufSize = file.GetLength();
+ ULONGLONG len = file.GetLength();
+ if (len > INT_MAX) {
+ file.Close();
+ return -1;
+ }
+ int nBufSize = static_cast<int>(len);
char* pszBuffer = new char[nBufSize];
file.Read(pszBuffer, nBufSize);
file.Close();
@@ -721,25 +1892,93 @@
int CHsmsPassive::serialize(char* pszBuffer, int nBufferSize)
{
int index = 0;
+ const auto calcSpoolCfgSize = [&]() -> int {
+ // magic(4) + ver(2) + enabled(1) + mapSize(4) + entries...
+ int sz = 0;
+ sz += 4; // 'SPOL'
+ sz += 2; // version
+ sz += 1; // enabled
+ sz += 4; // map size
+ for (const auto& kv : m_spoolBlacklistByStream) {
+ sz += 2; // streamId (U16)
+ sz += 4; // fn count (U32)
+ sz += static_cast<int>(kv.second.size()) * 2; // fn ids (U16 each)
+ }
+ return sz;
+ };
if (pszBuffer == nullptr) {
index += sizeof(int);
for (auto item : m_listActionSpooling) {
- index += item->serialize(pszBuffer, nBufferSize);
+ if (item == nullptr || item->getSendMessage() == nullptr) {
+ LOGE("<HSMS>skip spooling item: null send message");
+ continue;
+ }
+ int nRet = item->serialize(nullptr, 0);
+ if (nRet <= 0) {
+ LOGE("<HSMS>skip spooling item: serialize failed");
+ continue;
+ }
+ index += nRet;
}
+
+ index += calcSpoolCfgSize();
return index;
}
else {
- int nTemp, nRet;
+ int nTemp = 0;
+ int nRet = 0;
- nTemp = (int)m_listActionSpooling.size();
+ for (auto item : m_listActionSpooling) {
+ if (item == nullptr || item->getSendMessage() == nullptr) {
+ continue;
+ }
+ if (item->serialize(nullptr, 0) > 0) {
+ ++nTemp;
+ }
+ }
+
memcpy(&pszBuffer[index], &nTemp, sizeof(int));
index += sizeof(int);
for (auto item : m_listActionSpooling) {
+ if (item == nullptr || item->getSendMessage() == nullptr) {
+ LOGE("<HSMS>skip spooling item: null send message");
+ continue;
+ }
nRet = item->serialize(&pszBuffer[index], nBufferSize);
- if (nRet <= 0) break;
+ if (nRet <= 0) {
+ LOGE("<HSMS>skip spooling item: serialize failed");
+ continue;
+ }
index += nRet;
+ }
+
+ // Append spooling config (backward compatible via magic+version)
+ auto writeU32 = [&](uint32_t v) {
+ memcpy(&pszBuffer[index], &v, sizeof(v));
+ index += sizeof(v);
+ };
+ auto writeU16 = [&](uint16_t v) {
+ memcpy(&pszBuffer[index], &v, sizeof(v));
+ index += sizeof(v);
+ };
+ auto writeU8 = [&](uint8_t v) {
+ memcpy(&pszBuffer[index], &v, sizeof(v));
+ index += sizeof(v);
+ };
+
+ const uint32_t magic = 0x4C4F5053; // 'SPOL' little-endian
+ writeU32(magic);
+ writeU16(1); // version
+ writeU8(m_spoolingEnabled ? 1 : 0);
+ writeU32(static_cast<uint32_t>(m_spoolBlacklistByStream.size()));
+ for (const auto& kv : m_spoolBlacklistByStream) {
+ writeU16(static_cast<uint16_t>(kv.first));
+ writeU32(static_cast<uint32_t>(kv.second.size()));
+ for (const auto& fn : kv.second) {
+ writeU16(static_cast<uint16_t>(fn));
+ }
}
return index;
@@ -757,12 +1996,61 @@
for (int i = 0; i < nTemp; i++) {
CHsmsAction* pAction = new CHsmsAction();
nRet = pAction->unserialize(&pszBuffer[index], nBufferSize - index);
- if (nRet <= 0) break;
+ if (nRet <= 0 || pAction->getSendMessage() == nullptr) {
+ delete pAction;
+ break;
+ }
index += nRet;
m_listActionSpooling.push_back(pAction);
}
- return index + nRet;
+ // Parse optional spooling config tail (magic+version). If absent, keep defaults.
+ const auto remaining = nBufferSize - index;
+ if (remaining >= 4) {
+ uint32_t magic = 0;
+ memcpy(&magic, &pszBuffer[index], sizeof(magic));
+ if (magic == 0x4C4F5053) { // 'SPOL'
+ index += 4;
+ if (nBufferSize - index >= 2 + 1 + 4) {
+ uint16_t ver = 0;
+ memcpy(&ver, &pszBuffer[index], sizeof(ver));
+ index += 2;
+ if (ver >= 1) {
+ uint8_t enabled = 1;
+ memcpy(&enabled, &pszBuffer[index], sizeof(enabled));
+ index += 1;
+ m_spoolingEnabled = (enabled != 0);
+
+ uint32_t mapSize = 0;
+ memcpy(&mapSize, &pszBuffer[index], sizeof(mapSize));
+ index += 4;
+
+ m_spoolBlacklistByStream.clear();
+ for (uint32_t mi = 0; mi < mapSize; ++mi) {
+ if (nBufferSize - index < 2 + 4) break;
+ uint16_t streamId = 0;
+ memcpy(&streamId, &pszBuffer[index], sizeof(streamId));
+ index += 2;
+ uint32_t fnCount = 0;
+ memcpy(&fnCount, &pszBuffer[index], sizeof(fnCount));
+ index += 4;
+
+ auto& setRef = m_spoolBlacklistByStream[streamId];
+ setRef.clear();
+ for (uint32_t fi = 0; fi < fnCount; ++fi) {
+ if (nBufferSize - index < 2) break;
+ uint16_t fn = 0;
+ memcpy(&fn, &pszBuffer[index], sizeof(fn));
+ index += 2;
+ setRef.insert(fn);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return index;
}
unsigned CHsmsPassive::OnCimWork()
@@ -781,27 +2069,57 @@
Unlock();
while (!list.empty()) {
+ CHsmsAction* pAction = nullptr;
Lock();
- CHsmsAction* pAction = list.front();
- if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
- m_listActionSpooling.push_back(pAction);
- Unlock();
- continue;
- }
+ pAction = list.front();
Unlock();
list.pop_front();
+
+ Lock();
+ const bool selected = (m_pPassive != NULL && STATE::SELECTED == m_pPassive->getState());
+ Unlock();
+ if (!selected) {
+ IMessage* pMsg = pAction->getSendMessage();
+ if (pMsg == NULL) {
+ LOGE("<HSMS>spooling drop: null send message");
+ delete pAction;
+ continue;
+ }
+ uint8_t streamId = 0;
+ uint8_t functionId = 0;
+ if (pMsg && pMsg->getHeader()) {
+ streamId = static_cast<uint8_t>(pMsg->getHeader()->stream & 0x7F);
+ functionId = static_cast<uint8_t>(pMsg->getHeader()->function & 0xFF);
+ }
+ if (shouldSpool(streamId, functionId)) {
+ Lock();
+ m_listActionSpooling.push_back(pAction);
+ Unlock();
+ }
+ else {
+ LOGI("<HSMS>spooling disabled for S%dF%d, drop action", (int)streamId, (int)functionId);
+ delete pAction;
+ }
+ continue;
+ }
TRACE("OnCimWork 004.\n");
if (pAction->isNeedWaitReply()) {
// 濡傛灉闇�瑕佺瓑寰呭洖澶�
+ IMessage* pMessage = pAction->getSendMessage();
+ if (pMessage == NULL) {
+ LOGE("<HSMS>drop action: null send message");
+ delete pAction;
+ continue;
+ }
Lock();
m_pActiveAction = pAction;
- IMessage* pMessage = pAction->getSendMessage();
Unlock();
ASSERT(pMessage);
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS> [SEND] SysByte=%u sessionId:%d", pMessage->getHeader()->systemBytes, pMessage->getHeader()->sessionId);
+ LOGI("<HSMS>[SEND]SysByte=%u sessionId:%d", pMessage->getHeader()->systemBytes, pMessage->getHeader()->sessionId);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
int nRet = WaitForSingleObject(pAction->getEvent(), pAction->getTimeout() * 1000);
if (nRet == WAIT_TIMEOUT) {
@@ -819,14 +2137,20 @@
Unlock();
}
else {
+ IMessage* pMessage = pAction->getSendMessage();
+ if (pMessage == NULL) {
+ LOGE("<HSMS>drop action: null send message");
+ delete pAction;
+ continue;
+ }
Lock();
m_listActionSent.push_back(pAction);
- IMessage* pMessage = pAction->getSendMessage();
Unlock();
ASSERT(pMessage);
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS> [SEND] SysByte=%u sessionId:%d", pMessage->getHeader()->systemBytes, pMessage->getHeader()->sessionId);
+ LOGI("<HSMS>[SEND]SysByte=%u sessionId:%d", pMessage->getHeader()->systemBytes, pMessage->getHeader()->sessionId);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
}
}
@@ -848,7 +2172,9 @@
ISECS2Item* pItem = pMessage->getBody();
pItem->setBinary((const char*)&ack, 1, pszAckName);
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]S%dF%d (SysByte=%u)", s, f, systemBytes);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
}
@@ -861,11 +2187,15 @@
Lock();
CHsmsAction* pAction = new CHsmsAction(ACTION_HELLO, FALSE, m_nActionTimeout);
- m_listAction.push_back(pAction);
IMessage* pMessage = NULL;
- HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 1, ++m_nSystemByte);
- ASSERT(pMessage);
+ if (HSMS_Create1Message(pMessage, m_nSessionId, 1 | REPLY, 1, ++m_nSystemByte) != 0 || pMessage == NULL) {
+ LOGE("<HSMS>S1F1 create message failed");
+ delete pAction;
+ Unlock();
+ return ER_CREATED_MESSAGE;
+ }
pAction->setSendMessage(pMessage);
+ m_listAction.push_back(pAction);
SetEvent(m_hCimWorkEvent);
Unlock();
@@ -889,7 +2219,9 @@
pItem->addItem(m_strEquipmentModelType.c_str(), "MDLN");
pItem->addItem(m_strSoftRev.c_str(), "SOFTREV");
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]S1F2 (SysByte=%u)", pMessage->getHeader()->systemBytes);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
return 0;
@@ -950,7 +2282,9 @@
pList->addItem(m_strEquipmentModelType.c_str(), "MDLN");
pList->addItem(m_strSoftRev.c_str(), "SOFTREV");
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]%s", pMessage->toString());
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
return 0;
@@ -968,18 +2302,25 @@
ASSERT(pMessage);
unsigned char SVU1 = 0;
- unsigned int SVID = 0;
+ unsigned short SVID = 0;
ISECS2Item* pBody = pRecv->getBody();
if (pBody == nullptr || pBody->getType() != SITYPE::L) {
pMessage->getBody()->addU1Item(SVU1, "SV");
goto MYREPLY;
}
- if (!pBody->getSubItemU4(0, SVID)) {
- pMessage->getBody()->addU1Item(SVU1, "SV");
- goto MYREPLY;
+ if (!pBody->getSubItemU2(0, SVID)) {
+ // also accept I2 or U4 to be tolerant with host implementations
+ if (!pBody->getSubItemI2(0, (short&)SVID)) {
+ unsigned int svidU4 = 0;
+ if (!pBody->getSubItemU4(0, svidU4)) {
+ pMessage->getBody()->addU1Item(SVU1, "SV");
+ goto MYREPLY;
+ }
+ SVID = static_cast<unsigned short>(svidU4);
+ }
}
- SERVO::CVariable* pVariable = getVariable(SVID);
+ SERVO::CVariable* pVariable = getVariable((int)SVID);
if (pVariable == nullptr) {
pMessage->getBody()->addU1Item(SVU1, "SV");
goto MYREPLY;
@@ -988,12 +2329,273 @@
MYREPLY:
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]%s", pMessage->toString());
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
return 0;
+}
+
+// S1F11
+int CHsmsPassive::replyStatusVariableNamelistRequest(IMessage* pRecv)
+{
+ if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
+ return ER_NOTSELECT;
+ }
+
+ std::vector<unsigned short> reqIds;
+ ISECS2Item* pBody = pRecv->getBody();
+ if (pBody != nullptr && pBody->getType() == SITYPE::L) {
+ const int sz = pBody->getSubItemSize();
+ for (int i = 0; i < sz; ++i) {
+ unsigned short id = 0;
+ if (pBody->getSubItemU2(i, id)) {
+ reqIds.push_back(id);
+ }
+ }
+ }
+
+ // Build response list items: {L:3 SVID, SVNAME, UNITS}
+ std::vector<unsigned short> svids;
+ std::set<unsigned short> requested(reqIds.begin(), reqIds.end());
+ Lock();
+ if (reqIds.empty()) {
+ for (auto v : m_variabels) {
+ svids.push_back(static_cast<unsigned short>(v->getVarialbleId()));
+ }
+ }
+ else {
+ // include requested IDs (existing + unknown marker)
+ for (auto id : requested) {
+ svids.push_back(id);
+ }
+ }
+ Unlock();
+
+ IMessage* pMessage = NULL;
+ HSMS_Create1Message(pMessage, m_nSessionId, 1, 12, pRecv->getHeader()->systemBytes);
+ ASSERT(pMessage);
+
+ ISECS2Item* pList = pMessage->getBody(); // Body is L[n] of {SVID, SVNAME, UNITS}
+ for (auto id : svids) {
+ ISECS2Item* pEntry = pList->addItem();
+ pEntry->addU2Item(id, "SVID");
+ SERVO::CVariable* v = getVariable((int)id);
+ if (v != nullptr) {
+ pEntry->addItem(v->getName().c_str(), "SVNAME");
+ // Use remark as UNITS if provided; empty string if none.
+ pEntry->addItem(v->getRemark().c_str(), "UNITS");
+ }
+ else {
+ // Unknown SVID: A:0 for name/units
+ pEntry->addItem("", "SVNAME");
+ pEntry->addItem("", "UNITS");
+ }
+ }
+
+ m_pPassive->sendMessage(pMessage);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
+ HSMS_Destroy1Message(pMessage);
+
+ return ER_NOERROR;
+}
+
+int CHsmsPassive::writeEquipmentConstantsToFile(const std::string& filepath)
+{
+ if (filepath.empty()) return -1;
+ CFile file;
+ if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) {
+ return -1;
+ }
+ const std::string headerAnsi = "ECID,EC Name,EC Format,EC Remark,Default Value\r\n";
+ if (m_bEquipmentConstantUtf8) {
+ if (m_bEquipmentConstantUtf8Bom) {
+ const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
+ file.Write(bom, 3);
+ }
+ CStringA header = AnsiToUtf8(headerAnsi);
+ file.Write(header.GetString(), header.GetLength());
+ }
+ else {
+ file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
+ }
+ for (const auto& e : m_equipmentConstants) {
+ std::string line;
+ line.reserve(128);
+ line += std::to_string(e.id);
+ line.push_back(',');
+ line += e.name;
+ line.push_back(',');
+ line += e.format;
+ line.push_back(',');
+ line += e.remark;
+ line.push_back(',');
+ line += e.value;
+ line.append("\r\n");
+ if (m_bEquipmentConstantUtf8) {
+ CStringA out = AnsiToUtf8(line);
+ file.Write(out.GetString(), out.GetLength());
+ }
+ else {
+ file.Write(line.data(), (UINT)line.size());
+ }
+ }
+ file.Close();
+ return 0;
+}
+// S1F21/S1F22 - Data Variable Namelist
+int CHsmsPassive::replyDataVariableNamelistRequest(IMessage* pRecv)
+{
+ if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
+ return ER_NOTSELECT;
+ }
+
+ std::vector<unsigned short> reqIds;
+ ISECS2Item* pBody = pRecv->getBody();
+ if (pBody != nullptr && pBody->getType() == SITYPE::L) {
+ const int sz = pBody->getSubItemSize();
+ for (int i = 0; i < sz; ++i) {
+ unsigned short id = 0;
+ if (pBody->getSubItemU2(i, id)) {
+ reqIds.push_back(id);
+ }
+ }
+ }
+
+ std::vector<unsigned short> dvids;
+ std::set<unsigned short> requested(reqIds.begin(), reqIds.end());
+ Lock();
+ if (reqIds.empty()) {
+ for (auto v : m_dataVariabels) {
+ if (v) dvids.push_back(static_cast<unsigned short>(v->getVarialbleId()));
+ }
+ }
+ else {
+ for (auto id : requested) dvids.push_back(id);
+ }
+ Unlock();
+
+ IMessage* pMessage = NULL;
+ HSMS_Create1Message(pMessage, m_nSessionId, 1, 22, pRecv->getHeader()->systemBytes);
+ ASSERT(pMessage);
+
+ ISECS2Item* pList = pMessage->getBody(); // L[n] of {DVID, DVNAME, UNITS}
+ for (auto id : dvids) {
+ ISECS2Item* pEntry = pList->addItem();
+ pEntry->addU2Item(id, "DVID");
+ SERVO::CDataVariable* v = getDataVariable((int)id);
+ if (v != nullptr) {
+ pEntry->addItem(v->getName().c_str(), "DVNAME");
+ pEntry->addItem(v->getRemark().c_str(), "UNITS");
+ }
+ else {
+ pEntry->addItem("", "DVNAME");
+ pEntry->addItem("", "UNITS");
+ }
+ }
+
+ m_pPassive->sendMessage(pMessage);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
+ HSMS_Destroy1Message(pMessage);
+
+ return ER_NOERROR;
+}
+
+// S1F23
+int CHsmsPassive::replyCollectionEventNamelistRequest(IMessage* pRecv)
+{
+ if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
+ return ER_NOTSELECT;
+ }
+
+ std::vector<unsigned short> reqIds;
+ ISECS2Item* pBody = pRecv->getBody();
+ if (pBody != nullptr && pBody->getType() == SITYPE::L) {
+ const int sz = pBody->getSubItemSize();
+ for (int i = 0; i < sz; ++i) {
+ unsigned short id = 0;
+ if (pBody->getSubItemU2(i, id)) {
+ reqIds.push_back(id);
+ }
+ }
+ }
+
+ struct CEInfo {
+ unsigned short id{ 0 };
+ std::string name;
+ std::vector<unsigned short> vids;
+ };
+ std::vector<CEInfo> ceInfos;
+ {
+ Lock();
+ if (reqIds.empty()) {
+ for (auto e : m_collectionEvents) {
+ if (e == nullptr) continue;
+ CEInfo info;
+ info.id = static_cast<unsigned short>(e->getEventId());
+ info.name = e->getName();
+ std::set<unsigned short> vidSet;
+ for (auto rpt : e->getReports()) {
+ if (rpt == nullptr) continue;
+ for (auto vid : rpt->getVids()) {
+ vidSet.insert(static_cast<unsigned short>(vid));
+ }
+ }
+ info.vids.assign(vidSet.begin(), vidSet.end());
+ ceInfos.push_back(std::move(info));
+ }
+ }
+ else {
+ for (auto id : reqIds) {
+ CEInfo info;
+ info.id = id;
+ SERVO::CCollectionEvent* e = getEvent(id);
+ if (e != nullptr) {
+ info.name = e->getName();
+ std::set<unsigned short> vidSet;
+ for (auto rpt : e->getReports()) {
+ if (rpt == nullptr) continue;
+ for (auto vid : rpt->getVids()) {
+ vidSet.insert(static_cast<unsigned short>(vid));
+ }
+ }
+ info.vids.assign(vidSet.begin(), vidSet.end());
+ }
+ ceInfos.push_back(std::move(info));
+ }
+ }
+ Unlock();
+ }
+
+ IMessage* pMessage = NULL;
+ HSMS_Create1Message(pMessage, m_nSessionId, 1, 24, pRecv->getHeader()->systemBytes);
+ ASSERT(pMessage);
+
+ ISECS2Item* pList = pMessage->getBody(); // Body is L[n] of {CEID, CENAME, L[VIDs]}
+ for (const auto& info : ceInfos) {
+ ISECS2Item* pEntry = pList->addItem();
+ pEntry->addU2Item(info.id, "CEID");
+ pEntry->addItem(info.name.c_str(), "CENAME"); // empty if unknown
+ ISECS2Item* pVidList = pEntry->addItem();
+ for (auto vid : info.vids) {
+ pVidList->addU2Item(vid, "VID");
+ }
+ }
+
+ m_pPassive->sendMessage(pMessage);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
+ HSMS_Destroy1Message(pMessage);
+
+ return ER_NOERROR;
}
// S2F13
@@ -1004,32 +2606,42 @@
}
- // 瑕佽幏鍙栫殑甯搁噺琛ㄨ〃
- BOOL bCheckData = FALSE;
std::vector<EQConstant> eqcs;
{
ISECS2Item* pItem = pRecv->getBody();
- int ecidSize = pItem->getSubItemSize();
+ const int ecidSize = pItem ? pItem->getSubItemSize() : 0;
for (int i = 0; i < ecidSize; i++) {
- EQConstant eqc;
- unsigned short id;
- if (pItem->getSubItemU2(i, id)) {
+ EQConstant eqc{};
+ unsigned short id = 0;
+ if (pItem && pItem->getSubItemU2(i, id)) {
eqc.id = id;
eqcs.push_back(eqc);
}
}
}
-
-
- // 浜ょ敱涓婂眰搴旂敤鏉ヨ幏鍙栨満鍣ㄥ父閲忓��
- if (m_listener.onEQConstantRequest != nullptr) {
- m_listener.onEQConstantRequest(this, eqcs);
+ // 绌哄垪琛ㄨ〃绀鸿姹傚叏閮� ECID
+ if (eqcs.empty()) {
+ for (const auto& e : m_equipmentConstants) {
+ EQConstant eqc{};
+ eqc.id = e.id;
+ strcpy_s(eqc.szValue, EQCONSTANT_VALUE_MAX, e.value.c_str());
+ eqcs.push_back(eqc);
+ }
+ } else {
+ for (auto& item : eqcs) {
+ auto it = std::find_if(m_equipmentConstants.begin(), m_equipmentConstants.end(),
+ [&](const EquipmentConstantEntry& e) { return e.id == item.id; });
+ if (it != m_equipmentConstants.end()) {
+ strcpy_s(item.szValue, EQCONSTANT_VALUE_MAX, it->value.c_str());
+ } else {
+ item.szValue[0] = '\0'; // unknown -> empty
+ }
+ }
}
-
// 鍥炲
IMessage* pMessage = NULL;
- HSMS_Create1Message(pMessage, m_nSessionId, 1, 14, pRecv->getHeader()->systemBytes);
+ HSMS_Create1Message(pMessage, m_nSessionId, 2, 14, pRecv->getHeader()->systemBytes);
ASSERT(pMessage);
ISECS2Item* pItem = pMessage->getBody();
for (auto& item : eqcs) {
@@ -1037,7 +2649,9 @@
}
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]S2F14 (SysByte=%u)", pMessage->getHeader()->systemBytes);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
return 0;
@@ -1051,33 +2665,38 @@
}
- // 瑕佽缃殑甯搁噺琛ㄨ〃
- BOOL bCheckData = FALSE;
+ // 瑕佽缃殑甯搁噺琛�
std::vector<EQConstant> eqcs;
{
ISECS2Item* pItem = pRecv->getBody();
- int ecidSize = pItem->getSubItemSize();
+ int ecidSize = pItem ? pItem->getSubItemSize() : 0;
for (int i = 0; i < ecidSize; i++) {
- ISECS2Item* pItemEqc = pItem->getSubItem(i);
- if (pItemEqc != nullptr) {
- EQConstant eqc;
- unsigned short eqcid;
- const char* pszValue;
- if (pItemEqc->getSubItemU2(0, eqcid)
- && pItemEqc->getSubItemString(1, pszValue)) {
- eqc.id = eqcid;
- strcpy_s(eqc.szValue, EQCONSTANT_VALUE_MAX, pszValue);
- eqcs.push_back(eqc);
- }
+ ISECS2Item* pItemEqc = pItem ? pItem->getSubItem(i) : nullptr;
+ if (pItemEqc == nullptr) continue;
+ EQConstant eqc{};
+ unsigned short eqcid = 0;
+ const char* pszValue = nullptr;
+ if (pItemEqc->getSubItemU2(0, eqcid)
+ && pItemEqc->getSubItemString(1, pszValue)) {
+ eqc.id = eqcid;
+ strcpy_s(eqc.szValue, EQCONSTANT_VALUE_MAX, pszValue);
+ eqcs.push_back(eqc);
}
}
}
-
- // 浜ょ敱涓婂眰搴旂敤鏉ヤ繚瀛樺拰璁剧疆鏈哄櫒甯搁噺鍊�
- std::vector<unsigned int> ecvs;
- if (m_listener.onEQConstantSend != nullptr) {
- m_listener.onEQConstantSend(this, eqcs);
+ // 鏇存柊鍐呭瓨琛ㄥ苟钀界洏
+ bool changed = false;
+ for (auto& item : eqcs) {
+ auto it = std::find_if(m_equipmentConstants.begin(), m_equipmentConstants.end(),
+ [&](const EquipmentConstantEntry& e) { return e.id == item.id; });
+ if (it != m_equipmentConstants.end()) {
+ it->value = item.szValue;
+ changed = true;
+ }
+ }
+ if (changed && !m_strEquipmentConstantFilepath.empty()) {
+ writeEquipmentConstantsToFile(m_strEquipmentConstantFilepath);
}
@@ -1133,13 +2752,14 @@
ISECS2Item* pBody = pRecv->getBody();
ISECS2Item* defineItem, *rptListItem, * vidListItem;
- unsigned int dataId, rptid, vid;
+ unsigned short dataId;
+ unsigned int rptid, vid;
- if (!pBody->getSubItemU4(0, dataId)) goto MYREPLY;
+ if (!pBody->getSubItemU2(0, dataId)) goto MYREPLY;
rptListItem = pBody->getSubItem(1);
if (rptListItem == nullptr) goto MYREPLY;
if (rptListItem->getSubItemSize() == 0) {
- clearAllReport();
+ clearAllReport(TRUE);
goto MYREPLY;
}
@@ -1159,8 +2779,10 @@
}
}
- removeReport(rptid);
- if (!vids.empty()) {
+ if (vids.empty()) {
+ deleteReport(rptid);
+ } else {
+ removeReport(rptid);
pReport = defineReport(rptid, vids);
}
@@ -1189,8 +2811,10 @@
ISECS2Item* pBody = pRecv->getBody();
ISECS2Item* linkItem, *ceidListItem, *rptListItem;
- unsigned int dataId, ceid, rptid;
- if (!pBody->getSubItemU4(0, dataId)) goto MYREPLY;
+ unsigned short dataId;
+ unsigned int ceid, rptid;
+ bool bChanged = false;
+ if (!pBody->getSubItemU2(0, dataId)) goto MYREPLY;
ceidListItem = pBody->getSubItem(1);
if (ceidListItem == nullptr) goto MYREPLY;
for (int i = 0; i < ceidListItem->getSubItemSize(); i++) {
@@ -1202,21 +2826,31 @@
int prtCount = rptListItem->getSubItemSize();
if (prtCount == 0) {
unlinkEventReport(ceid);
+ bChanged = true;
}
else {
for (int k = 0; k < prtCount; k++) {
if (rptListItem->getSubItemU4(k, rptid)) {
linkEventReport(ceid, rptid);
+ bChanged = true;
}
}
}
}
}
+ // 鎸佷箙鍖栧埌 CollectionEventList.txt锛堜究浜庝笅娆″惎鍔ㄤ粛淇濇寔 Link/Unlink 缁撴灉锛�
+ if (bChanged && !m_strCollectionEventFilepath.empty()) {
+ writeCollectionEventsToFile(m_strCollectionEventFilepath);
+ }
// 妫�楠岀粨鏋滄槸鍚︽纭�
for (auto item : m_collectionEvents) {
- LOGE("=== ceid:%d, prtid:%d", item->getEventId(), item->getFirstPortID());
+ unsigned int reportId = item->getFirstReportID();
+ if(reportId != 0)
+ LOGI("=== ceid:%d, prtid:%d", item->getEventId(), reportId);
+ else
+ LOGI("=== ceid:%d, prtid:--", item->getEventId());
}
MYREPLY:
@@ -1324,9 +2958,12 @@
// 娓呯┖鎵�鏈�
if (pBody->getSubItemSize() == 0) {
- m_spoolingConfig.clear();
+ LOGI("<CHsmsPassive>turns off all streams and fns");
+ m_spoolBlacklistByStream.clear();
+ m_spoolingEnabled = false;
goto MYREPLY;
}
+ m_spoolingEnabled = true;
// 渚濇閰嶇疆Stream
for (int i = 0; i < pBody->getSubItemSize(); i++) {
@@ -1335,26 +2972,34 @@
unsigned char STRID, FCNID;
pStreamItem->getSubItemU1(0, STRID);
ISECS2Item* pFcnItemList = pStreamItem->getSubItem(1);
- if (pFcnItemList->getSubItemSize() == 0) {
- m_spoolingConfig[STRID].clear();
+ if (pFcnItemList == nullptr || pFcnItemList->getSubItemSize() == 0) {
+ // No functions listed => blacklist the whole stream
+ m_spoolBlacklistByStream[STRID].clear();
}
else {
+ // Update blacklist for this stream
+ m_spoolBlacklistByStream[STRID].clear();
for (int j = 0; j < pFcnItemList->getSubItemSize(); j++) {
pFcnItemList->getSubItemU1(j, FCNID);
- m_spoolingConfig[STRID].insert(FCNID);
+ m_spoolBlacklistByStream[STRID].insert(FCNID);
}
}
}
// 鎵撳嵃楠岃瘉缁撴灉
- for (auto s : m_spoolingConfig) {
- LOGI("====> stream:%d", s.first);
- for (auto f : s.second) {
- LOGI("function:%d", f);
+ for (auto s : m_spoolBlacklistByStream) {
+ LOGI("====> spool blacklist stream:%d", s.first);
+ if (s.second.empty()) {
+ LOGI("blacklist all functions");
+ }
+ else {
+ for (auto f : s.second) {
+ LOGI("blacklist function:%d", f);
+ }
}
}
MYREPLY:
- replyAck(2, 42, pRecv->getHeader()->systemBytes, BYTE(0), "ERACK");
+ replyAck(2, 44, pRecv->getHeader()->systemBytes, BYTE(0), "ERACK");
return 0;
}
@@ -1375,14 +3020,21 @@
goto MYREPLY;
}
+ if (!isHostCommandAllowed()) {
+ CAACK = CAACK_5;
+ ERRCODE = CAACK_5;
+ strError = "rejected - ControlState not OnlineRemote";
+ goto MYREPLY;
+ }
+
ISECS2Item* pBody = pRecv->getBody();
if (pBody == nullptr || pBody->getType() != SITYPE::L) ER_PARAM_ERROR;
- unsigned int DATAID;
+ unsigned short DATAID;
unsigned char PTN;
const char* pszCarrierAction, *pszCarrierId;
- pBody->getSubItemU4(0, DATAID);
+ pBody->getSubItemU2(0, DATAID);
pBody->getSubItemString(1, pszCarrierAction);
pBody->getSubItemString(2, pszCarrierId);
pBody->getSubItemU1(3, PTN);
@@ -1403,7 +3055,9 @@
pErrItem->addU4Item(ERRCODE, "ERRCODE");
pErrItem->addItem(strError.c_str(), "ERRTEXT");
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]S3F18 (SysByte=%u)", pMessage->getHeader()->systemBytes);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
return 0;
@@ -1463,6 +3117,7 @@
// 涓㈠純
if (RSDC == 1) {
+ LOGI("<CHsmsPassive>Purge Spooled Data.");
Lock();
for (auto item : m_listActionSpooling) {
delete item;
@@ -1471,6 +3126,7 @@
Unlock();
}
else {
+ LOGI("<CHsmsPassive>Request Spooled Data.");
Lock();
for (auto item : m_listActionSpooling) {
m_listAction.push_back(item);
@@ -1480,6 +3136,86 @@
SetEvent(m_hCimWorkEvent);
}
+ return 0;
+}
+
+// S7F17 Delete Process Program (PPID list) / S7F18
+int CHsmsPassive::replyDeletePPID(IMessage* pRecv)
+{
+ if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
+ return ER_NOTSELECT;
+ }
+
+ bool allOk = true;
+ const bool deleteAll = (pRecv->getBody() == nullptr || pRecv->getBody()->getSubItemSize() == 0);
+ std::vector<std::string> ppids;
+ ISECS2Item* pBody = pRecv->getBody();
+ const int nCount = pBody ? pBody->getSubItemSize() : 0;
+ for (int i = 0; i < nCount; ++i) {
+ const char* pszPPID = nullptr;
+ if (pBody->getSubItemString(i, pszPPID) && pszPPID != nullptr) {
+ ppids.emplace_back(pszPPID);
+ }
+ else {
+ allOk = false;
+ }
+ }
+
+ if (deleteAll || !ppids.empty()) {
+ if (m_listener.onDeletePPID != nullptr) {
+ allOk = m_listener.onDeletePPID(this, ppids);
+ }
+ else {
+ // no handler provided; treat as failure
+ allOk = false;
+ LOGW("<HSMS>DeletePPID request ignored: no onDeletePPID listener");
+ }
+ }
+
+
+ replyAck(7, 18, pRecv->getHeader()->systemBytes, allOk ? BYTE(0) : BYTE(1), "ACKC7");
+ return 0;
+}
+
+// S7F5 Process Program Request -> reply S7F6 with PPID + PPBODY
+int CHsmsPassive::replyProcessProgramRequest(IMessage* pRecv)
+{
+ if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
+ return ER_NOTSELECT;
+ }
+ ISECS2Item* pBody = pRecv->getBody();
+ const char* pszPPID = nullptr;
+ if (pBody == nullptr || !pBody->getString(pszPPID) || pszPPID == nullptr) {
+ return ER_PARAM_ERROR;
+ }
+ std::string ppid(pszPPID);
+ std::string ppbody;
+ // 绠�鍗曡仛鍚堬細浠� RecipeManager 鍙栬澶囬厤鏂癸紝鎷兼垚鏂囨湰
+ auto recipeInfo = RecipeManager::getInstance().getRecipeByPPID(ppid);
+ if (!recipeInfo.strPPID.empty()) {
+ for (const auto& dev : recipeInfo.vecDeviceList) {
+ if (!ppbody.empty()) ppbody.append("\n");
+ ppbody.append(dev.strDeviceName);
+ ppbody.append(",");
+ ppbody.append(std::to_string(dev.nRecipeID));
+ ppbody.append(",");
+ ppbody.append(dev.strRecipeName);
+ // 闄勫姞鍙傛暟澶у皬淇℃伅锛堢洰鍓嶇己灏戝叿浣撳弬鏁板垪琛級
+ ppbody.append(",paramsSize=");
+ ppbody.append(std::to_string(dev.paramsRawData.size()));
+ }
+ }
+
+ IMessage* pMessage = nullptr;
+ if (HSMS_Create1Message(pMessage, m_nSessionId, 7, 6, pRecv->getHeader()->systemBytes) != 0 || pMessage == nullptr) {
+ return ER_CREATED_MESSAGE;
+ }
+ ISECS2Item* pRspBody = pMessage->getBody(); // top-level L:2
+ pRspBody->addItem(ppid.c_str(), "PPID");
+ pRspBody->addItem(ppbody.c_str(), "PPBODY");
+ m_pPassive->sendMessage(pMessage);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
+ HSMS_Destroy1Message(pMessage);
return 0;
}
@@ -1504,7 +3240,9 @@
}
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]S7F20 (SysByte=%u)", pMessage->getHeader()->systemBytes);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
return 0;
@@ -1571,6 +3309,13 @@
ISECS2Item* pReplyItemAcks = pReply->getBody()->addItem();
ISECS2Item* pReplyItemAck = pReplyItemAcks->addU1Item(0, "OBJACK");
ISECS2Item* pReplyItemErrs = pReplyItemAcks->addItem();
+
+ if (!isHostCommandAllowed()) {
+ ISECS2Item* pItemError = pReplyItemErrs->addItem();
+ pItemError->addU4Item(2001, "ERRCODE");
+ pItemError->addItem("rejected - ControlState not OnlineRemote", "ERRTEXT");
+ goto MYREPLY;
+ }
// 褰撳墠鍙鐞嗙被鍚勪负ControlJob
if (_strcmpi(pszObjType, "ControlJob") == 0) {
@@ -1668,7 +3413,9 @@
MYREPLY:
pReplyItemAck->setU1(bCreateOk ? 0 : 1, "OBJACK");
m_pPassive->sendMessage(pReply);
- LOGI("<HSMS>[SECS Msg SEND]S14F10 (SysByte=%u)", pReply->getHeader()->systemBytes);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pReply->getHeader()->sessionId, pReply->getHeader()->sType, pReply->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pReply);
HSMS_Destroy1Message(pReply);
@@ -1684,10 +3431,49 @@
ISECS2Item* pBody = pRecv->getBody();
if (pBody == nullptr || pBody->getType() != SITYPE::L) ER_PARAM_ERROR;
+ if (!isHostCommandAllowed()) {
+ IMessage* pMessage = NULL;
+ HSMS_Create1Message(pMessage, m_nSessionId, 16, 16, ++m_nSystemByte);
+ ASSERT(pMessage);
+ ISECS2Item* pItemPrjobIds = pMessage->getBody()->addItem();
+ ISECS2Item* pItemErrors = pMessage->getBody()->addItem();
+ pItemErrors->addBoolItem(false, "ACKA");
+ ISECS2Item* pItemErrors2 = pItemErrors->addItem();
+ auto err = pItemErrors2->addItem();
+ err->addU4Item(2001, "ERRCODE");
+ err->addItem("rejected - ControlState not OnlineRemote", "ERRTEXT");
+ m_pPassive->sendMessage(pMessage);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+ }
+
// 瑙i噴鏁版嵁锛屽緱鍒癈ProcessJob
+ // 瀹归噺鍓嶇疆妫�鏌ワ細褰撳墠瀹炵幇浠呮敮鎸佸崟鎵� PJ 闆嗗悎锛屽鏋滃凡鏈� PJ/CJ锛岀洿鎺ヨ繑鍥� ACKA=false
+ if (m_pModel != nullptr && !m_pModel->getMaster().isProcessJobsEmpty()) {
+ IMessage* pMessage = NULL;
+ HSMS_Create1Message(pMessage, m_nSessionId, 16, 16, ++m_nSystemByte);
+ ASSERT(pMessage);
+ pMessage->getBody()->addItem(); // PRJOBID list 涓虹┖
+ ISECS2Item* pItemErrors = pMessage->getBody()->addItem();
+ pItemErrors->addBoolItem(false, "ACKA");
+ ISECS2Item* pItemErrors2 = pItemErrors->addItem();
+ auto err = pItemErrors2->addItem();
+ err->addU4Item(1000, "ERRCODE");
+ err->addItem("PJobSpace=0 (existing ProcessJob/ControlJob)", "ERRTEXT");
+ m_pPassive->sendMessage(pMessage);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
+ HSMS_Destroy1Message(pMessage);
+ return 0;
+ }
+
ISECS2Item* pItemPjs, * pItemPj,* pItemCarriers, * pItemCarrier, *pItemSlots, *pItemRecipes;
- unsigned int DATAID;
+ unsigned short DATAID;
const char* pszPrjobid, *pszMF, *pszCarrierId, *pszRecipeName;
std::string strCarrierId;
unsigned int len;
@@ -1695,7 +3481,7 @@
std::vector<unsigned char> slots;
std::vector<SERVO::CProcessJob*> pjs;
- if (!pBody->getSubItemU4(0, DATAID)) return ER_PARAM_ERROR;
+ if (!pBody->getSubItemU2(0, DATAID)) return ER_PARAM_ERROR;
pItemPjs = pBody->getSubItem(1);
if (pItemPjs == nullptr) return ER_PARAM_ERROR;
for (int i = 0; i < pItemPjs->getSubItemSize(); i++) {
@@ -1736,11 +3522,12 @@
pjs.push_back(pj);
}
+
ASSERT(m_listener.onPRJobMultiCreate != nullptr);
int nRet = m_listener.onPRJobMultiCreate(this, pjs);
- // 鍥炲鎶ユ枃
+ // 鍥炲鎶ユ枃锛堝湪鏍¢獙/钀藉簱鍚庡啀鍥炲锛屼互渚垮甫涓婄湡瀹炵殑 issues锛�
IMessage* pMessage = NULL;
HSMS_Create1Message(pMessage, m_nSessionId, 16, 16, ++m_nSystemByte);
ASSERT(pMessage);
@@ -1766,8 +3553,14 @@
}
}
}
+ else {
+ pItemErrors->addBoolItem(true, "ACKA");
+ pItemErrors->addItem(); // 绌哄垪琛�
+ }
m_pPassive->sendMessage(pMessage);
- LOGI("<HSMS>[SECS Msg SEND]S16F16 (SysByte=%u)", pMessage->getHeader()->systemBytes);
+ LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
+ pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
+ LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
HSMS_Destroy1Message(pMessage);
@@ -1790,15 +3583,25 @@
CHsmsAction* pAction = new CHsmsAction(ACTION_ALARM_REPORT, TRUE, m_nActionTimeout);
IMessage* pMessage = NULL;
- HSMS_Create1Message(pMessage, m_nSessionId, 5 | REPLY, 1, ++m_nSystemByte);
- ASSERT(pMessage);
+ if (HSMS_Create1Message(pMessage, m_nSessionId, 5 | REPLY, 1, ++m_nSystemByte) != 0 || pMessage == NULL) {
+ LOGE("<HSMS>S5F1 create message failed");
+ delete pAction;
+ Unlock();
+ return ER_CREATED_MESSAGE;
+ }
ISECS2Item* pItem = pMessage->getBody();
pItem->addBinaryItem(szALCD, 1, "ALCD");
pItem->addU4Item(ALID, "ALID");
pItem->addItem(ALTX, "ALTX");
pAction->setSendMessage(pMessage);
if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
- m_listActionSpooling.push_back(pAction);
+ if (shouldSpool(5, 1)) {
+ m_listActionSpooling.push_back(pAction);
+ }
+ else {
+ LOGI("<HSMS>spooling disabled for S5F1, drop alarm report");
+ delete pAction;
+ }
}
else {
m_listAction.push_back(pAction);
@@ -1810,40 +3613,62 @@
}
// S6F11
-static unsigned int DATAID = 1;
+static unsigned short DATAID = 0;
int CHsmsPassive::requestEventReportSend(unsigned int CEID)
{
SERVO::CCollectionEvent* pEvent = getEvent(CEID);
if (pEvent == nullptr) {
return ER_NO_EVENT;
}
+ // 瑙﹀彂 PauseEvent 妫�娴嬫々锛堢敱 Master 璐熻矗瀹為檯绛栫暐锛�
+ if (m_pModel != nullptr) {
+ m_pModel->getMaster().handleCollectionEvent(CEID);
+ }
SERVO::CReport* pReport = pEvent->getFirstReport();
- if (pReport == nullptr) {
- return ER_UNLINK_EVENT_REPORT;
- }
Lock();
CHsmsAction* pAction = new CHsmsAction(ACTION_EVENT_REPORT, TRUE, m_nActionTimeout);
IMessage* pMessage = NULL;
- HSMS_Create1Message(pMessage, m_nSessionId, 6 | REPLY, 11, ++m_nSystemByte);
- ASSERT(pMessage);
+ if (HSMS_Create1Message(pMessage, m_nSessionId, 6 | REPLY, 11, ++m_nSystemByte) != 0 || pMessage == NULL) {
+ LOGE("<HSMS>S6F11 create message failed");
+ delete pAction;
+ Unlock();
+ return ER_CREATED_MESSAGE;
+ }
ISECS2Item* pItem = pMessage->getBody();
- pItem->addU4Item(++DATAID, "DATAID");
- pItem->addU4Item(CEID, "CEID");
- ISECS2Item* pItemList1 = pItem->addItem();
- ISECS2Item* pItemList2 = pItemList1->addItem();
- pItemList2->addU4Item(pReport->getReportId(), "RPTID");
- ISECS2Item* pItemList3 = pItemList2->addItem();
+ // pItem->addU2Item(++DATAID, "DATAID"); // 鏍规嵁鍒殑鏃ュ織鏄剧ずDATAID鎭掍负0锛屾墍浠ユ垜浠厛鐓т娇鐢�0
+ pItem->addU2Item(0, "DATAID");
+ std::string ceidNote("CEID");
+ if (pEvent != nullptr) {
+ auto& name = pEvent->getName();
+ if (!name.empty()) {
+ ceidNote += " -> ";
+ ceidNote += name;
+ }
+ }
+ pItem->addU4Item(CEID, ceidNote.c_str());
+ ISECS2Item* pItemList1 = pItem->addItem(); // L[n] reports
+ if (pReport != nullptr) {
+ ISECS2Item* pItemList2 = pItemList1->addItem();
+ pItemList2->addU4Item(pReport->getReportId(), "RPTID");
+ ISECS2Item* pItemList3 = pItemList2->addItem();
- auto vars = pReport->getVariables();
- for (auto var : vars) {
- addVariableValueToItem(pItemList3, var);
+ auto vars = pReport->getVariables();
+ for (auto var : vars) {
+ addVariableValueToItem(pItemList3, var);
+ }
}
pAction->setSendMessage(pMessage);
if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
- m_listActionSpooling.push_back(pAction);
+ if (shouldSpool(6, 11)) {
+ m_listActionSpooling.push_back(pAction);
+ }
+ else {
+ LOGI("<HSMS>spooling disabled for S6F11, drop event report (CEID=%u)", CEID);
+ delete pAction;
+ }
}
else {
m_listAction.push_back(pAction);
@@ -1874,6 +3699,21 @@
return requestEventReportSend("CarrierID_Readed");
}
+int CHsmsPassive::requestEventReportSend_CheckSlotMap()
+{
+ return requestEventReportSend("CheckSlotMap");
+}
+
+int CHsmsPassive::requestEventReportSend_SlotMapVerificationOK()
+{
+ return requestEventReportSend("SlotMapVerificationOK");
+}
+
+int CHsmsPassive::requestEventReportSend_SlotMapVerificationNG()
+{
+ return requestEventReportSend("SlotMapVerificationNG");
+}
+
int CHsmsPassive::requestEventReportSend_Port_Unload_Ready()
{
return requestEventReportSend("Port_Unload_Ready");
@@ -1882,6 +3722,11 @@
int CHsmsPassive::requestEventReportSend_Port_Load_Ready()
{
return requestEventReportSend("Port_Load_Ready");
+}
+
+int CHsmsPassive::requestEventReportSend_Port_Ready_To_Release()
+{
+ return requestEventReportSend("Port_Ready_To_Release");
}
int CHsmsPassive::requestEventReportSend_Port_Blocked()
@@ -1924,5 +3769,54 @@
return requestEventReportSend("Panel_End");
}
+int CHsmsPassive::requestEventReportSend_OCR_PanelID_Read_OK()
+{
+ return requestEventReportSend_OCR_PanelID_Read(1);
+}
+
+int CHsmsPassive::requestEventReportSend_OCR_PanelID_Read(short vcrResult)
+{
+ const char* eventName = "OCR_PanelID_Read_OK";
+ switch (vcrResult) {
+ case 1: // OK & Match
+ eventName = "OCR_PanelID_Read_OK";
+ break;
+ case 2: // OK & Mismatch
+ eventName = "OCR_PanelID_Read_Mismatch";
+ break;
+ case 3: // Fail & KeyIn Match
+ eventName = "OCR_PanelID_Read_NG";
+ break;
+ case 4: // Fail & KeyIn Mismatch
+ eventName = "OCR_PanelID_Read_NG_Mismatch";
+ break;
+ default:
+ LOGE("<CHsmsPassive>Unknown VCR result=%d, fallback to OCR_PanelID_Read_OK", vcrResult);
+ eventName = "OCR_PanelID_Read_OK";
+ break;
+ }
+
+ return requestEventReportSend(eventName);
+}
+
+int CHsmsPassive::requestEventReportSend_LoadPortNotAssoc()
+{
+ return requestEventReportSend("LoadPortNotAssoc");
+}
+
+int CHsmsPassive::requestEventReportSend_ProcessDataReport()
+{
+ return requestEventReportSend("ProcessDataReport");
+}
+
+int CHsmsPassive::requestEventReportSend_SubEqpStart()
+{
+ return requestEventReportSend("SubEqpStart");
+}
+
+int CHsmsPassive::requestEventReportSend_SubEqpEnd()
+{
+ return requestEventReportSend("SubEqpEnd");
+}
diff --git a/SourceCode/Bond/Servo/HsmsPassive.h b/SourceCode/Bond/Servo/HsmsPassive.h
index 1863abd..81c5188 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.h
+++ b/SourceCode/Bond/Servo/HsmsPassive.h
@@ -1,4 +1,4 @@
-#pragma once
+锘�#pragma once
#include <string>
#include <list>
#include "HsmsAction.h"
@@ -9,6 +9,7 @@
#include "CCollectionEvent.h"
#include "ProcessJob.h"
#include "CControlJob.h"
+#include "CDataVariable.h"
#define EQCONSTANT_VALUE_MAX 64
@@ -26,6 +27,8 @@
#define ER_UNLINK_EVENT_REPORT -5
#define ER_NO_PPID_LIST -6
#define ER_NOT_SUPPORTED -7
+#define ER_CREATED_MESSAGE -8
+
/* CAACK */
@@ -39,7 +42,7 @@
#define CAACK_6 6 /* command performed with errors */
/*
- * 常量数据结构
+ * 甯搁噺鏁版嵁缁撴瀯
*/
typedef struct _EQConstant
{
@@ -48,7 +51,7 @@
} EQConstant;
/*
- * Command 数据结构
+ * Command 鏁版嵁缁撴瀯
*/
typedef struct _CommandParameter
{
@@ -57,7 +60,7 @@
} CommandParameter;
/*
- * Report 数据结构
+ * Report 鏁版嵁缁撴瀯
*/
typedef struct _REPORT
{
@@ -66,7 +69,7 @@
} REPORT;
/*
- * Value 数据结构
+ * Value 鏁版嵁缁撴瀯
*/
typedef struct _VALUE
{
@@ -76,7 +79,6 @@
typedef std::function<void(void* pFrom)> SECSEQOFFLINE;
-typedef std::function<void(void* pFrom, std::vector<EQConstant>&)> SECSEQCONSTANTREQUEST;
typedef std::function<void(void* pFrom, const char*, std::vector<CommandParameter>&)> SECSCommand;
typedef std::function<void(void* pFrom, SYSTEMTIME& time)> DATETIMESYNC;
typedef std::function<void(void* pFrom, bool bEnable, std::vector<unsigned int>& ids)> EDEVENTREPORT;
@@ -90,17 +92,17 @@
std::string& strErrorTxt)> CARRIERACTION;
typedef std::function<int(void* pFrom, std::vector<SERVO::CProcessJob*>& pjs)> PRJOBMULTICREATE;
typedef std::function<int(void* pFrom, SERVO::CControlJob& controlJob)> CONTROLJOBCREATE;
+typedef std::function<bool(void* pFrom, const std::vector<std::string>& ppids)> DELETEPPID;
typedef struct _SECSListener
{
SECSEQOFFLINE onEQOffLine;
SECSEQOFFLINE onEQOnLine;
- SECSEQCONSTANTREQUEST onEQConstantRequest;
- SECSEQCONSTANTREQUEST onEQConstantSend;
SECSCommand onCommand;
DATETIMESYNC onDatetimeSync;
EDEVENTREPORT onEnableDisableEventReport;
EDALARMREPORT onEnableDisableAlarmReport;
QUERYPPIDLIST onQueryPPIDList;
+ DELETEPPID onDeletePPID;
CARRIERACTION onCarrierAction;
PRJOBMULTICREATE onPRJobMultiCreate;
CONTROLJOBCREATE onControlJobCreate;
@@ -115,62 +117,89 @@
~CHsmsPassive();
public:
- /* 设置机器型号 最大长度 20 bytes */
+ /* 璁剧疆鏈哄櫒鍨嬪彿 鏈�澶ч暱搴� 20 bytes */
void setEquipmentModelType(const char* pszMode);
- /* 设置软件版本号 最大长度 20 bytes */
+ /* 璁剧疆杞欢鐗堟湰鍙� 鏈�澶ч暱搴� 20 bytes */
void setSoftRev(const char* pszRev);
- /* 添加变量值到ISECS2Item */
+ /* 娣诲姞鍙橀噺鍊煎埌ISECS2Item */
void addVariableValueToItem(ISECS2Item* pParent, SERVO::CVariable* pVariable);
- // 连接Report
+ // 杩炴帴Report
void linkEventReport(unsigned int CEID, unsigned int RPTID);
- // 取消连接report
+ // 鍙栨秷杩炴帴report
void unlinkEventReport(unsigned int CEID);
// define Report
SERVO::CReport* defineReport(unsigned int RPTID, std::vector<unsigned int>& vids);
- // 取消 define report
+ // 鍙栨秷 define report
bool removeReport(int rptid);
- void clearAllReport();
+ int deleteReport(int rptid);
+ int addReport(int rptid, const std::vector<unsigned int>& vids);
+ int updateReport(int rptid, const std::vector<unsigned int>& vids);
+ void clearAllReport(BOOL bSave = FALSE);
- // 从文件中加载CVariable列表
+ // 浠庢枃浠朵腑鍔犺浇CVariable鍒楄〃
int loadVarialbles(const char* pszFilepath);
+ // 浠庢枃浠朵腑鍔犺浇CDataVariable鍒楄〃
+ int loadDataVarialbles(const char* pszFilepath);
+ // 浠庢枃浠朵腑鍔犺浇Equipment Constant鍒楄〃
+ int loadEquipmentConstants(const char* pszFilepath);
- // 取得CVariable列表
+ // 鍙栧緱CVariable鍒楄〃
std::vector<SERVO::CVariable*>& getVariables();
+ unsigned int getMaxVariableId() const;
+ std::vector<SERVO::CDataVariable*>& getDataVariables();
+ unsigned int getMaxDataVariableId() const;
- // 取得指定Variable
+ // 鍙栧緱鎸囧畾Variable
SERVO::CVariable* getVariable(int variableId);
SERVO::CVariable* getVariable(const char* pszName);
+ SERVO::CDataVariable* getDataVariable(int dvid);
+ SERVO::CDataVariable* getDataVariable(const char* pszName);
+ int getCurrentControlState();
+ bool isHostCommandAllowed();
+ int deleteVariable(int variableId);
+ int addVariable(const char* pszName, const char* pszFormat, const char* pszRemark, int& outId);
+ int updateVariable(int variableId, const char* pszName, const char* pszFormat, const char* pszRemark);
+ int deleteDataVariable(int dvid);
+ int addDataVariable(const char* pszName, const char* pszFormat, const char* pszRemark, int& outId);
+ int updateDataVariable(int dvid, const char* pszName, const char* pszFormat, const char* pszRemark);
- // 设置变量值
+ // 璁剧疆鍙橀噺鍊�
void setVariableValue(const char* pszName, __int64 value);
void setVariableValue(const char* pszName, const char* value);
void setVariableValue(const char* pszName, std::vector<SERVO::CVariable>& vars);
+ // 鎵ц涓�娈垫寔閿佺殑浠g爜鍧楋紝鐢ㄤ簬淇濊瘉 set+send 鐨勫師瀛愭��
+ void withVariableLock(const std::function<void()>& fn);
- // 从文件中加载CReport列表
+ // 浠庢枃浠朵腑鍔犺浇CReport鍒楄〃
int loadReports(const char* pszFilepath);
- // 取得Report列表
+ // 鍙栧緱Report鍒楄〃
std::vector<SERVO::CReport*>& getReports();
+ unsigned int getMaxReportId() const;
- // 从文件中加载CCollectionEvent列表
+ // 浠庢枃浠朵腑鍔犺浇CCollectionEvent鍒楄〃
int loadCollectionEvents(const char* pszFilepath);
- // 取得CCollectionEvent列表
+ // 鍙栧緱CCollectionEvent鍒楄〃
std::vector<SERVO::CCollectionEvent*>& getCollectionEvents();
+ unsigned int getMaxCollectionEventId() const;
- // 取消/删除所有CollectionEvent
+ // 鍙栨秷/鍒犻櫎鎵�鏈塁ollectionEvent
void clearAllCollectionEvent();
+ int deleteCollectionEvent(unsigned short CEID);
+ int addCollectionEvent(unsigned int CEID, const char* name, const char* desc, const std::vector<unsigned int>& rptids);
+ int updateCollectionEvent(unsigned int CEID, const char* name, const char* desc, const std::vector<unsigned int>& rptids);
- // 取得CCollectionEvent
+ // 鍙栧緱CCollectionEvent
SERVO::CCollectionEvent* getEvent(unsigned short CEID);
- // 取得Report
+ // 鍙栧緱Report
SERVO::CReport* getReport(int rptid);
void setListener(SECSListener listener);
@@ -185,14 +214,18 @@
int unserialize(const char* pszBuffer, int nBufferSize);
public:
- /* request开头的函数为主动发送数据的函数 */
+ /* request寮�澶寸殑鍑芥暟涓轰富鍔ㄥ彂閫佹暟鎹殑鍑芥暟 */
int requestAreYouThere();
int requestAlarmReport(int ALCD, int ALID, const char* ALTX);
int requestEventReportSend(unsigned int CEID);
int requestEventReportSend(const char* pszEventName);
int requestEventReportSend_CarrierID_Readed();
+ int requestEventReportSend_CheckSlotMap();
+ int requestEventReportSend_SlotMapVerificationOK();
+ int requestEventReportSend_SlotMapVerificationNG();
int requestEventReportSend_Port_Unload_Ready();
int requestEventReportSend_Port_Load_Ready();
+ int requestEventReportSend_Port_Ready_To_Release();
int requestEventReportSend_Port_Blocked();
int requestEventReportSend_PJ_Queued();
int requestEventReportSend_PJ_Start();
@@ -201,14 +234,23 @@
int requestEventReportSend_CJ_End();
int requestEventReportSend_Panel_Start();
int requestEventReportSend_Panel_End();
+ int requestEventReportSend_OCR_PanelID_Read_OK();
+ int requestEventReportSend_OCR_PanelID_Read(short vcrResult);
+ int requestEventReportSend_LoadPortNotAssoc();
+ int requestEventReportSend_ProcessDataReport();
+ int requestEventReportSend_SubEqpStart();
+ int requestEventReportSend_SubEqpEnd();
private:
void replyAck(int s, int f, unsigned int systemBytes, BYTE ack, const char* pszAckName);
- /* reply开头的函数为回复函数 */
+ /* reply寮�澶寸殑鍑芥暟涓哄洖澶嶅嚱鏁� */
int replyAreYouThere(IMessage* pRecv);
int replyEstablishCommunications(IMessage* pRecv);
int replySelectedEquipmentStatusData(IMessage* pRecv);
+ int replyStatusVariableNamelistRequest(IMessage* pRecv); // S1F11/S1F12
+ int replyDataVariableNamelistRequest(IMessage* pRecv); // S1F21/S1F22
+ int replyCollectionEventNamelistRequest(IMessage* pRecv); // S1F23/S1F24
int replyOnLine(IMessage* pRecv);
int replyOffLine(IMessage* pRecv);
int replyEquipmentConstantRequest(IMessage* pRecv);
@@ -223,6 +265,8 @@
int replyEanbleDisableAlarmReport(IMessage* pRecv);
int replyPurgeSpooledData(IMessage* pRecv);
int replyQueryPPIDList(IMessage* pRecv);
+ int replyDeletePPID(IMessage* pRecv); // S7F17/S7F18
+ int replyProcessProgramRequest(IMessage* pRecv); // S7F5/S7F6
int replyTerminalDisplay(IMessage* pRecv);
int replyCreateObj(IMessage* pRecv);
int replyPRJobMultiCreate(IMessage* pRecv);
@@ -232,7 +276,13 @@
inline void Unlock() { LeaveCriticalSection(&m_criticalSection); }
int onRecvMsg(IMessage* pMessage);
void clearAllVariabel();
+ void clearAllDataVariabel();
std::vector<unsigned int> parseVidList(CString& strNums);
+ int writeVariablesToFile(const std::string& filepath);
+ int writeDataVariablesToFile(const std::string& filepath);
+ int writeEquipmentConstantsToFile(const std::string& filepath);
+ int writeReportsToFile(const std::string& filepath);
+ int writeCollectionEventsToFile(const std::string& filepath);
private:
CModel* m_pModel;
@@ -250,6 +300,21 @@
private:
SECSListener m_listener;
+ std::string m_strVariableFilepath;
+ bool m_bVariableUtf8{ false };
+ bool m_bVariableUtf8Bom{ false };
+ std::string m_strDataVariableFilepath;
+ bool m_bDataVariableUtf8{ false };
+ bool m_bDataVariableUtf8Bom{ false };
+ std::string m_strReportFilepath;
+ bool m_bReportUtf8{ false };
+ bool m_bReportUtf8Bom{ false };
+ std::string m_strCollectionEventFilepath;
+ bool m_bCollectionUtf8{ false };
+ bool m_bCollectionUtf8Bom{ false };
+ std::string m_strEquipmentConstantFilepath;
+ bool m_bEquipmentConstantUtf8{ false };
+ bool m_bEquipmentConstantUtf8Bom{ false };
BOOL m_bCimWorking;
HANDLE m_hCimWorkEvent;
HANDLE m_hCimWorkThreadHandle;
@@ -260,6 +325,17 @@
private:
// CVariable vector
std::vector<SERVO::CVariable*> m_variabels;
+ // CDataVariable vector
+ std::vector<SERVO::CDataVariable*> m_dataVariabels;
+ // Equipment constants
+ struct EquipmentConstantEntry {
+ unsigned int id{ 0 };
+ std::string name;
+ std::string format;
+ std::string remark;
+ std::string value;
+ };
+ std::vector<EquipmentConstantEntry> m_equipmentConstants;
// CReport vector
std::vector<SERVO::CReport*> m_reports;
@@ -267,7 +343,13 @@
// CollectionEvent vector
std::vector<SERVO::CCollectionEvent*> m_collectionEvents;
- // Spooling Config
- std::map<uint16_t, std::set<uint16_t>> m_spoolingConfig;
-};
+ // Spooling blacklist: StreamId -> {FunctionId...}
+ // In this map means DO NOT spool/cache.
+ // Special case: stream 1 is not spooled regardless of config.
+ // If a stream key exists with empty set => blacklist ALL functions in that stream.
+ std::map<uint16_t, std::set<uint16_t>> m_spoolBlacklistByStream;
+ bool m_spoolingEnabled{ true };
+private:
+ bool shouldSpool(uint8_t streamId, uint8_t functionId) const;
+};
diff --git a/SourceCode/Bond/Servo/LoginDlg2.cpp b/SourceCode/Bond/Servo/LoginDlg2.cpp
new file mode 100644
index 0000000..3021f8e
--- /dev/null
+++ b/SourceCode/Bond/Servo/LoginDlg2.cpp
@@ -0,0 +1,103 @@
+锘�// LoginDlg.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "afxdialogex.h"
+#include "LoginDlg2.h"
+
+
+// CLoginDlg 瀵硅瘽妗�
+
+IMPLEMENT_DYNAMIC(CLoginDlg2, CDialogEx)
+
+CLoginDlg2::CLoginDlg2(CWnd* pParent /*=nullptr*/)
+ : CDialogEx(IDD_DIALOG_LOGIN, pParent)
+{
+}
+
+CLoginDlg2::~CLoginDlg2()
+{
+}
+
+void CLoginDlg2::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+}
+
+
+BEGIN_MESSAGE_MAP(CLoginDlg2, CDialogEx)
+ ON_BN_CLICKED(IDC_BUTTON_LOGIN, &CLoginDlg2::OnBnClickedLogin)
+ ON_STN_CLICKED(IDC_STATIC_CHANGE_PASSWORD, &CLoginDlg2::OnBnClickedChangePassword)
+END_MESSAGE_MAP()
+
+
+// CLoginDlg 娑堟伅澶勭悊绋嬪簭
+
+
+BOOL CLoginDlg2::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ // 璁剧疆绐楀彛鏍囬鍜屽垵濮嬪��
+ SetWindowText(_T("鐧诲綍"));
+
+
+ CStatic* pStaticImage = (CStatic*)GetDlgItem(IDC_STATIC_IMAGE);
+ ASSERT(pStaticImage);
+
+ CString strIconPath;
+ strIconPath.Format(_T("%s\\Res\\Operator_High_32.ico"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+ HICON hIcon = (HICON)::LoadImage(
+ nullptr,
+ strIconPath,
+ IMAGE_ICON,
+ 32, // 鍥炬爣瀹藉害
+ 32, // 鍥炬爣楂樺害
+ LR_LOADFROMFILE);
+ if (hIcon) {
+ // 璁剧疆 CStatic 鎺т欢涓哄浘鏍囨牱寮�
+ pStaticImage->ModifyStyle(0xF, SS_ICON);
+ pStaticImage->SetIcon(hIcon);
+ }
+
+ // 娣诲姞SS_NOTIFY鏍峰紡
+ CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_CHANGE_PASSWORD);
+ if (pStatic != nullptr) {
+ pStatic->ModifyStyle(0, SS_NOTIFY);
+ }
+
+ GetDlgItem(IDC_CHECK_REMEMBER_PASSWORD)->ShowWindow(SW_HIDE);
+
+
+ // test
+ SetDlgItemText(IDC_EDIT_USERNAME, _T("admin"));
+ SetDlgItemText(IDC_EDIT_PASSWORD, _T("admin123"));
+
+ return TRUE;
+}
+
+void CLoginDlg2::OnBnClickedLogin()
+{
+ GetDlgItemText(IDC_EDIT_USERNAME, m_strUsername);
+ GetDlgItemText(IDC_EDIT_PASSWORD, m_strPassword);
+
+ if (m_strUsername.IsEmpty()) {
+ AfxMessageBox(_T("璇疯緭鍏ョ敤鎴峰悕"));
+ GetDlgItem(IDC_EDIT_USERNAME)->SetFocus();
+ return;
+ }
+ if (m_strPassword.IsEmpty()) {
+ AfxMessageBox(_T("璇疯緭鍏ュ瘑鐮�"));
+ GetDlgItem(IDC_EDIT_PASSWORD)->SetFocus();
+ return;
+}
+
+
+ EndDialog(IDOK);
+}
+
+void CLoginDlg2::OnBnClickedChangePassword()
+{
+
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/LoginDlg2.h b/SourceCode/Bond/Servo/LoginDlg2.h
new file mode 100644
index 0000000..335037d
--- /dev/null
+++ b/SourceCode/Bond/Servo/LoginDlg2.h
@@ -0,0 +1,30 @@
+锘�#pragma once
+#include "afxdialogex.h"
+
+
+// CLoginDlg 瀵硅瘽妗�
+
+class CLoginDlg2 : public CDialogEx
+{
+ DECLARE_DYNAMIC(CLoginDlg2)
+
+public:
+ CLoginDlg2(CWnd* pParent = nullptr); // 鏍囧噯鏋勯�犲嚱鏁�
+ virtual ~CLoginDlg2();
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_DIALOG_LOGIN };
+#endif
+
+public:
+ CString m_strUsername;
+ CString m_strPassword;
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 鏀寔
+ virtual BOOL OnInitDialog();
+ afx_msg void OnBnClickedLogin();
+ afx_msg void OnBnClickedChangePassword();
+ DECLARE_MESSAGE_MAP()
+};
diff --git a/SourceCode/Bond/Servo/Model.cpp b/SourceCode/Bond/Servo/Model.cpp
index 8530738..e5afc98 100644
--- a/SourceCode/Bond/Servo/Model.cpp
+++ b/SourceCode/Bond/Servo/Model.cpp
@@ -1,4 +1,4 @@
-#include "stdafx.h"
+锘�#include "stdafx.h"
#include "Model.h"
#include "Log.h"
#include "Common.h"
@@ -9,6 +9,13 @@
#include "TransferManager.h"
#include "RecipeManager.h"
#include "GlassLogDb.h"
+#include "CParam.h"
+#include "CJobDataS.h"
+#include <algorithm>
+#include <iomanip>
+#include <sstream>
+#include <array>
+#include <map>
CModel::CModel()
@@ -21,11 +28,111 @@
{
}
+void CModel::refreshDerivedSVs()
+{
+ // CJobSpace: how many ControlJobs can be created (current implementation supports 0/1).
+ m_hsmsPassive.setVariableValue("CJobSpace", (__int64)(m_master.canCreateControlJob() ? 1 : 0));
+
+ // PJobSpace: how many ProcessJobs can be created (current implementation supports 0/1).
+ m_hsmsPassive.setVariableValue("PJobSpace", (__int64)(m_master.isProcessJobsEmpty() ? 1 : 0));
+}
+
+void CModel::notifyControlJobChanged()
+{
+ // 1) 鍒锋柊娲剧敓 SV
+ refreshDerivedSVs();
+ // 2) 閫氱煡涓婂眰 UI锛圧X_CODE_CONTROLJOB_CHANGED锛�
+ notify(RX_CODE_CONTROLJOB_CHANGED);
+}
+
+bool CModel::raiseSoftAlarm(int alarmId,
+ const std::string& desc,
+ int level /*= -1*/,
+ int deviceId /*= 0*/,
+ int unitId /*= 0*/,
+ const char* deviceName /*= "Software"*/,
+ const char* unitName /*= "App"*/)
+{
+ AlarmManager& alarmManager = AlarmManager::getInstance();
+ const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId);
+
+ int severity = level;
+ if (severity < 0 && info != nullptr) severity = info->nAlarmLevel;
+ if (severity < 0) severity = 0;
+
+ std::string descText = desc;
+ if (descText.empty() && info != nullptr) {
+ descText = !info->strDescription.empty() ? info->strDescription : info->strAlarmText;
+ }
+ if (descText.empty()) {
+ descText = CToolUnits::formatString("Alarm %d", alarmId);
+ }
+
+ AlarmData alarmData;
+ alarmData.nId = alarmId;
+ alarmData.nSeverityLevel = severity;
+ alarmData.nDeviceId = deviceId;
+ alarmData.nUnitId = unitId;
+ alarmData.strDeviceName = deviceName;
+ alarmData.strUnitName = unitName;
+ // 鑻ユ湭鏄惧紡鎻愪緵璁惧/鍗曞厓鍚嶇О锛屽皾璇曢�氳繃 deviceId/unitId 瑙f瀽锛坰oft alarm 榛樿鍧囦负 0锛�
+ if (alarmData.strDeviceName.empty()) {
+ alarmData.strDeviceName = alarmManager.getDeviceNameById(deviceId);
+ }
+ if (alarmData.strUnitName.empty()) {
+ alarmData.strUnitName = alarmManager.getUnitNameById(deviceId, unitId);
+ }
+ alarmData.strStartTime = CToolUnits::timeToString2(CToolUnits::getTimestamp());
+ alarmData.strEndTime = "";
+ alarmData.strDescription = descText;
+
+ int nAlarmEventId = 0;
+ bool result = alarmManager.addAlarm(alarmData, nAlarmEventId);
+ if (result) {
+ notify(RX_CODE_ALARM_SET);
+ if (m_master.isAlarmReportEnable()) {
+ m_hsmsPassive.requestAlarmReport(1, alarmId, descText.c_str());
+ }
+ }
+ return result;
+}
+
+void CModel::clearSoftAlarm(int alarmId, int deviceId, int unitId)
+{
+ AlarmManager& alarmManager = AlarmManager::getInstance();
+ alarmManager.clearAlarmByAttributes(alarmId, deviceId, unitId, CToolUnits::getCurrentTimeString());
+ notify(RX_CODE_ALARM_CLEAR);
+ if (m_master.isAlarmReportEnable()) {
+ const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId);
+ std::string descText;
+ if (info != nullptr) descText = info->strAlarmText;
+ m_hsmsPassive.requestAlarmReport(0, alarmId, descText.c_str());
+ }
+}
+
+void CModel::setControlState(ControlState newState)
+{
+ const auto prev = m_currentControlState;
+ if (newState != m_currentControlState) {
+ m_currentControlState = newState;
+ // S6F11 (CEID=600): ControlStateChanged
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("PreviousControlState", (__int64)static_cast<uint8_t>(prev));
+ m_hsmsPassive.setVariableValue("CurrentControlState", (__int64)static_cast<uint8_t>(m_currentControlState));
+ m_hsmsPassive.requestEventReportSend("ControlStateChanged");
+ });
+ notifyInt(RX_CODE_CONTROL_STATE_CHANGED, static_cast<int>(m_currentControlState));
+ } else {
+ // Keep SV in sync even if unchanged/load-time refresh.
+ m_hsmsPassive.setVariableValue("CurrentControlState", (__int64)static_cast<uint8_t>(m_currentControlState));
+ }
+}
+
IObservable* CModel::getObservable()
{
if (m_pObservable == nullptr) {
m_pObservable = RX_AllocaObservable([&](IObservableEmitter* e) -> void {
- m_pObservableEmitter = e; // 保存发射器
+ m_pObservableEmitter = e; // 淇濆瓨鍙戝皠鍣�
});
}
@@ -77,19 +184,23 @@
int CModel::init()
{
+ const ULONGLONG boot_model_begin = GetTickCount64();
CString strIniFile;
CString strUnitId;
strIniFile.Format(_T("%s\\ServoConfiguration.ini"), (LPTSTR)(LPCTSTR)m_strWorkDir);
m_configuration.setFilepath((LPTSTR)(LPCTSTR)strIniFile);
m_configuration.getUnitId(strUnitId);
- // 机器型号和软件版本号应从配置中读取,当前先固定值
+ // 鏈哄櫒鍨嬪彿鍜岃蒋浠剁増鏈彿搴斾粠閰嶇疆涓鍙栵紝褰撳墠鍏堝浐瀹氬��
CString strModeType = _T("Master");
CString strSoftRev = _T("1.0.2");
// CGlassPool
m_glassPool.initPool();
+
+ // 灏� Model 涓婁笅鏂囦紶閫掔粰 Master锛屼究浜� Master 瑙﹀彂杞欢绾ф姤璀︾瓑璺ㄥ眰鎿嶄綔
+ m_master.setModelCtx(this);
// Log
@@ -104,31 +215,35 @@
CLog::GetLog()->SetLogsDir(strLogDir);
CLog::GetLog()->SetEquipmentId((LPTSTR)(LPCTSTR)strUnitId);
LOGI("\r\n\r\n~~~ Prog Start! ~~~");
+ LOGI("[BOOT][MODEL] init begin");
SECSListener listener;
- listener.onEQOffLine = [&](void* pFrom) -> void {
- LOGI("远程请求OffLine");
+ listener.onEQOffLine = [this](void* pFrom) -> void {
+ LOGI("杩滅▼璇锋眰OffLine");
+ (void)pFrom;
+ setControlState(ControlState::OfflineHost);
};
- listener.onEQOnLine = [&](void* pFrom) -> void {
- LOGI("远程请求OnLine");
+ listener.onEQOnLine = [this](void* pFrom) -> void {
+ LOGI("杩滅▼璇锋眰OnLine");
+ (void)pFrom;
+ // Customer flow: S1F17 RequestOnline defaults to OnlineRemote.
+ setControlState(ControlState::OnlineRemote);
};
- listener.onCommand = [&](void* pFrom, const char* pszName, std::vector<CommandParameter>& params) -> void {
+ listener.onCommand = [this](void* pFrom, const char* pszName, std::vector<CommandParameter>& params) -> void {
LOGI("onCommand:%s", pszName);
+ (void)pFrom;
for (auto& item : params) {
LOGI("param:%s,%s", item.szName, item.szValue);
}
- };
- listener.onEQConstantRequest = [&](void* pFrom, std::vector<EQConstant>& eqcs) -> void {
- // 在此填充常量值,目前仅是加1后返回
- for (auto& item : eqcs) {
- sprintf_s(item.szValue, 256, "Test%d", item.id + 1);
+
+ if (pszName == nullptr) return;
+ // S2F41 GoLocal / GoRemote (RCMD)
+ if (_strcmpi(pszName, "GoLocal") == 0 || _strcmpi(pszName, "LOCAL") == 0 || _strcmpi(pszName, "GoLOCAL") == 0) {
+ setControlState(ControlState::OnlineLocal);
}
- };
- listener.onEQConstantSend = [&](void* pFrom, std::vector<EQConstant>& eqcs) -> void {
- // 在此保存和设置机器常量值
- for (auto& item : eqcs) {
- LOGI("onEQConstantRequest: %d, %s", item.id, item.szValue);
+ else if (_strcmpi(pszName, "GoRemote") == 0 || _strcmpi(pszName, "REMOTE") == 0 || _strcmpi(pszName, "GoREMOTE") == 0) {
+ setControlState(ControlState::OnlineRemote);
}
};
listener.onDatetimeSync = [&](void* pFrom, SYSTEMTIME& time) -> void {
@@ -141,6 +256,21 @@
if (ids.empty()) {
m_master.enableEventReport(bEnable);
}
+ };
+ listener.onDeletePPID = [&](void* pFrom, const std::vector<std::string>& ppids) -> bool {
+ (void)pFrom;
+ bool allOk = true;
+ std::vector<std::string> targets = ppids;
+ if (targets.empty()) {
+ // L:0 => delete all PPIDs
+ targets = RecipeManager::getInstance().getAllPPID();
+ }
+ for (auto& ppid : targets) {
+ bool ok = RecipeManager::getInstance().deleteRecipeByPPID(ppid);
+ allOk = allOk && ok;
+ LOGI("<CModel>DeletePPID: %s, result=%s", ppid.c_str(), ok ? "OK" : "FAIL");
+ }
+ return allOk;
};
listener.onEnableDisableAlarmReport = [&](void* pFrom, bool bEnable, unsigned int id) -> void {
LOGI("onEnableDisableAlarmReport bEnable:%s, id:%d", bEnable ? _T("YES") : _T("NO"), id);
@@ -172,23 +302,90 @@
return CAACK_3;
}
+ const unsigned int portIndex = PTN - 1;
+ SERVO::CLoadPort* pLoadPort = (SERVO::CLoadPort*)m_master.getEquipment(EQ_ID_LOADPORT1 + portIndex);
+ LOGI("<Model>onCarrierAction %d, %s, %d, %d", DATAID, pszCarrierAction, pszCarrierId, PTN);
+
if (_strcmpi(pszCarrierAction, "ProceedWithCarrier") == 0) {
- m_master.proceedWithCarrier(PTN);
+ // 鏂囨。娴佺▼锛歅roceedWithCarrier 涔嬪悗璁惧杩涘叆 Check SlotMap锛圵FH锛夛紝
+ // 鐪熸鐨勨�滃紑濮嬧�濈敱 ProceedWithSlotMap 鍐崇瓥瑙﹀彂銆�
+ // 浠呭綋鏈紑鍚� CompareMapsBeforeProceeding 鏃讹紝鎵嶆部鐢ㄦ棫閫昏緫鐩存帴 Start銆�
+ LOGI("<CModel>ProceedWithCarrier");
+ if (m_master.getControlJob() == nullptr || m_master.isProcessJobsEmpty()) {
+ strErrorTxt = "rejected - ControlJob/ProcessJob not ready";
+ LOGW("<CModel>ProceedWithCarrier rejected: no CJ/PJ, port=%d", portIndex + 1);
+ return CAACK_5;
+ }
+ if (pLoadPort == nullptr || !pLoadPort->isCompareMapsBeforeProceeding()) {
+ m_master.proceedWithCarrier(portIndex);
+ }
+ return CAACK_0;
+ }
+ else if (_strcmpi(pszCarrierAction, "ProceedWithSlotMap") == 0) {
+ // TODO(Host鍗忓晢):
+ // 鏂囨。涓� ProceedWithSlotMap 鍙兘浼氭惡甯� LotID / PanelIDList / SlotMap 绛夋暟鎹紙鏈�澶�13鐗囷級鐢ㄤ簬鏍煎紡鏍¢獙涓庣粦瀹氥��
+ // 褰撳墠 S3F17 瑙f瀽缁撴瀯浠呮敮鎸� {DATAID, CarrierAction, CarrierID, PTN}锛屽皻鏈疄鐜颁笂杩版墿灞曞瓧娈电殑瑙f瀽/鏍¢獙銆�
+ // 鏈潵鑻ュ鎴风‘璁� SECS-II 缁撴瀯锛岄渶瑕佸湪 CHsmsPassive::replyCarrierAction() 鎵╁睍瑙f瀽骞跺湪姝ゅ钀藉簱/鏍¢獙銆�
+ // 浠呭湪 CompareMapsBeforeProceeding 鍚敤锛圚ost 妯″紡锛変笅鍏佽姝ゅ姩浣�
+ LOGI("<CModel>ProceedWithSlotMap");
+ if (pLoadPort == nullptr || !pLoadPort->isCompareMapsBeforeProceeding()) {
+ strErrorTxt = "rejected - SlotMap check disabled";
+ return CAACK_5;
+ }
+
+ const short scanMap = pLoadPort->getScanCassetteMap();
+ const short downloadMap = pLoadPort->getDownloadCassetteMap();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("SlotMapScan", scanMap);
+ m_hsmsPassive.setVariableValue("SlotMapDownload", downloadMap);
+ if (scanMap != downloadMap) {
+ m_hsmsPassive.requestEventReportSend_SlotMapVerificationNG();
+ m_hsmsPassive.requestEventReportSend("SlotMapMismatch");
+ }
+ else {
+ m_hsmsPassive.requestEventReportSend_SlotMapVerificationOK();
+ }
+ });
+
+ if (scanMap != downloadMap) {
+ strErrorTxt = "rejected - SlotMap mismatch";
+ return CAACK_5;
+ }
+
+ // Host 纭 SlotMap 鍚庡啀寮�濮嬪姞宸�/娴佺▼
+ m_master.proceedWithCarrier(portIndex);
return CAACK_0;
}
else if (_strcmpi(pszCarrierAction, "CarrierRelease") == 0) {
- m_master.carrierRelease(PTN);
+ LOGI("<CModel>CarrierRelease");
+ m_master.carrierRelease(portIndex);
return CAACK_0;
}
strErrorTxt = "rejected - invalid state";
return CAACK_5;
- LOGI("<Model>onCarrierAction %d, %s, %d, %d", DATAID, pszCarrierAction, pszCarrierId, PTN);
};
listener.onPRJobMultiCreate = [&](void* pFrom, std::vector<SERVO::CProcessJob*>& pjs) -> int {
for (auto p : pjs) {
LOGI("<Model>onPRJobMultiCreate %s %s", p->id().c_str(), p->recipeSpec().c_str());
}
+
+ auto rejectAll = [&](uint32_t code, const std::string& msg) -> int {
+ LOGW("<Model>onPRJobMultiCreate rejected: %s", msg.c_str());
+ for (auto p : pjs) {
+ if (p != nullptr) p->addIssue(code, msg);
+ }
+ return -1;
+ };
+
+ // 鍗� PJ 妯″紡锛氬彧鎺ュ彈 1 鏉′笖褰撳墠鏃犲湪鍒� PJ
+ if (pjs.size() != 1) {
+ return rejectAll(1200, "Only 1 ProcessJob supported (single-PJ mode)");
+ }
+ if (!m_master.isProcessJobsEmpty()) {
+ return rejectAll(1201, "ProcessJob exists, cannot create new in single-PJ mode");
+ }
+
int nRet = m_master.setProcessJobs(pjs);
auto processJobs = m_master.getProcessJobs();
std::vector<SERVO::CVariable> vars;
@@ -198,8 +395,10 @@
vars.push_back(var);
}
- m_hsmsPassive.setVariableValue("PJQueued", vars);
- m_hsmsPassive.requestEventReportSend_PJ_Queued();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("PJQueued", vars);
+ m_hsmsPassive.requestEventReportSend_PJ_Queued();
+ });
return nRet;
};
listener.onControlJobCreate = [&](void* pFrom, SERVO::CControlJob& controlJob) -> int {
@@ -213,20 +412,47 @@
CString strVarialbleFile;
strVarialbleFile.Format(_T("%s\\VariableList.txt"), (LPTSTR)(LPCTSTR)m_strWorkDir);
m_hsmsPassive.loadVarialbles((LPTSTR)(LPCTSTR)strVarialbleFile);
+ strVarialbleFile.Format(_T("%s\\DataVariableList.txt"), (LPTSTR)(LPCTSTR)m_strWorkDir);
+ m_hsmsPassive.loadDataVarialbles((LPTSTR)(LPCTSTR)strVarialbleFile);
+ strVarialbleFile.Format(_T("%s\\EquipmentConstantList.txt"), (LPTSTR)(LPCTSTR)m_strWorkDir);
+ m_hsmsPassive.loadEquipmentConstants((LPTSTR)(LPCTSTR)strVarialbleFile);
+ setControlState(m_currentControlState);
+ refreshDerivedSVs();
m_hsmsPassive.init(this, "APP", 7000);
strVarialbleFile.Format(_T("%s\\ReportList.txt"), (LPTSTR)(LPCTSTR)m_strWorkDir);
m_hsmsPassive.loadReports((LPTSTR)(LPCTSTR)strVarialbleFile);
strVarialbleFile.Format(_T("%s\\CollectionEventList.txt"), (LPTSTR)(LPCTSTR)m_strWorkDir);
m_hsmsPassive.loadCollectionEvents((LPTSTR)(LPCTSTR)strVarialbleFile);
+ {
+ auto events = m_hsmsPassive.getCollectionEvents();
+ std::vector<unsigned int> ceids;
+ ceids.reserve(events.size());
+ for (auto e : events) {
+ if (e != nullptr) ceids.push_back(e->getEventId());
+ }
+ m_master.setAllowedCeids(ceids);
+ }
strVarialbleFile.Format(_T("%s\\HsmsPassive.cache"), (LPTSTR)(LPCTSTR)m_strWorkDir);
m_hsmsPassive.loadCacheFromFile(strVarialbleFile);
+ LOGI("[BOOT][MODEL] HSMS config loaded, cost=%llu ms",
+ (unsigned long long)(GetTickCount64() - boot_model_begin));
SERVO::MasterListener masterListener;
+ auto formatParamValue = [](const CParam& p) {
+ std::ostringstream oss;
+ oss.setf(std::ios::fixed);
+ oss << std::setprecision(4) << p.getDoubleValue();
+ return oss.str();
+ };
masterListener.onMasterStateChanged = [&](void* pMaster, SERVO::MASTERSTATE state) -> void {
LOGI("<CModel>Master state changed(%d)", (int)state);
notify(RX_CODE_MASTER_STATE_CHANGED);
};
+ masterListener.onControlJobChanged = [this](void* pMaster) {
+ (void)pMaster;
+ this->notifyControlJobChanged();
+ };
masterListener.onEqAlive = [&](void* pMaster, SERVO::CEquipment* pEquipment, BOOL bAlive) -> void {
LOGI("<CModel>Equipment onAlive:%s(%s).", pEquipment->getName().c_str(),
bAlive ? _T("ON") : _T("OFF"));
@@ -296,6 +522,15 @@
};
masterListener.onEqVcrEventReport = [&](void* pMaster, SERVO::CEquipment* pEquipment, SERVO::CVcrEventReport* pReport) {
LOGE("<CModel>onEqVcrEventReport.");
+ if (pReport != nullptr) {
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("VCRPanelID", pReport->getGlassId().c_str());
+ int nRet = m_hsmsPassive.requestEventReportSend_OCR_PanelID_Read(pReport->getVcrResult());
+ if (nRet != ER_NOERROR) {
+ LOGE("<CModel>requestEventReportSend_OCR_PanelID_Read failed, ret=%d", nRet);
+ }
+ });
+ }
};
masterListener.onEqDataChanged = [&](void* pMaster, SERVO::CEquipment* pEquipment, int code) {
LOGE("<CModel>onEqDataChanged.");
@@ -303,11 +538,11 @@
};
masterListener.onRobotTaskEvent = [&](void* pMaster, SERVO::CRobotTask* pTask, int code) {
if (pTask == nullptr) {
- LOGE("<CModel>onRobotTaskEvent: 空任务指针,忽略事件 code=%d", code);
+ LOGE("<CModel>onRobotTaskEvent: 绌轰换鍔℃寚閽堬紝蹇界暐浜嬩欢 code=%d", code);
return;
}
- // 任务描述与 ID 用于日志
+ // 浠诲姟鎻忚堪涓� ID 鐢ㄤ簬鏃ュ織
SERVO::CGlass* pGlass = (SERVO::CGlass*)pTask->getContext();
const std::string& strDesc = pTask->getDescription();
std::string strClassID;
@@ -319,48 +554,48 @@
}
}
- // 日志输出与状态处理
+ // 鏃ュ織杈撳嚭涓庣姸鎬佸鐞�
switch (code) {
case ROBOT_EVENT_CREATE:
- LOGI("<CModel>onRobotTaskEvent: 新任务创建(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
+ LOGI("<CModel>onRobotTaskEvent: 鏂颁换鍔″垱寤�(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
break;
case ROBOT_EVENT_FINISH:
- LOGI("<CModel>onRobotTaskEvent: 任务完成(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
+ LOGI("<CModel>onRobotTaskEvent: 浠诲姟瀹屾垚(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
break;
case ROBOT_EVENT_ERROR:
- LOGE("<CModel>onRobotTaskEvent: 任务错误(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
+ LOGE("<CModel>onRobotTaskEvent: 浠诲姟閿欒(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
break;
case ROBOT_EVENT_ABORT:
- LOGE("<CModel>onRobotTaskEvent: 任务停止(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
+ LOGE("<CModel>onRobotTaskEvent: 浠诲姟鍋滄(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
break;
case ROBOT_EVENT_RESTORE:
- LOGE("<CModel>onRobotTaskEvent: 任务回撤(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
+ LOGE("<CModel>onRobotTaskEvent: 浠诲姟鍥炴挙(%s, ClassID=%s).", strDesc.c_str(), strClassID.c_str());
break;
default:
- LOGE("<CModel>onRobotTaskEvent: 未知事件 code=%d, 任务=%s", code, strDesc.c_str());
+ LOGE("<CModel>onRobotTaskEvent: 鏈煡浜嬩欢 code=%d, 浠诲姟=%s", code, strDesc.c_str());
break;
}
- // 安全格式化时间
+ // 瀹夊叏鏍煎紡鍖栨椂闂�
auto format_time = [](time_t t) -> std::string {
if (t <= 0 || t == _I64_MIN || t == _I64_MAX) {
return "";
}
- // 使用 localtime_s 确保线程安全
+ // 浣跨敤 localtime_s 纭繚绾跨▼瀹夊叏
tm tmBuf{};
errno_t err = localtime_s(&tmBuf, &t);
if (err != 0 || tmBuf.tm_mon < 0 || tmBuf.tm_mon > 11) {
return "";
}
- // 格式化时间字符串
+ // 鏍煎紡鍖栨椂闂村瓧绗︿覆
char buf[64] = {};
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tmBuf);
return std::string(buf);
};
- // 构造 TransferData 数据结构
+ // 鏋勯�� TransferData 鏁版嵁缁撴瀯
TransferData data;
data.strClassID = strClassID;
data.strCreateTime = format_time(pTask->getCreateTime());
@@ -369,7 +604,7 @@
data.strEndTime = format_time(pTask->getFinishTime());
data.strDescription = pTask->getSimpleDescription();
- // 状态映射
+ // 鐘舵�佹槧灏�
static const char* STATUS_STR[] = {
"Ready", "Running", "Picking", "Picked", "Placing", "Restoring", "Error", "Abort", "Restored", "Completed"
};
@@ -382,77 +617,360 @@
data.strStatus = "Unknown";
}
- // 写入数据库
+ // 鍐欏叆鏁版嵁搴�
if (code == ROBOT_EVENT_FINISH || code == ROBOT_EVENT_ERROR
|| code == ROBOT_EVENT_ABORT || code == ROBOT_EVENT_RESTORE) {
int nRecordId = 0;
TransferManager::getInstance().addTransferRecord(data, nRecordId);
- LOGI("<CModel>onRobotTaskEvent: 任务记录已保存,RecordID=%d", nRecordId);
+ LOGI("<CModel>onRobotTaskEvent: 浠诲姟璁板綍宸蹭繚瀛橈紝RecordID=%d", nRecordId);
}
notifyPtrAndInt(RX_CODE_EQ_ROBOT_TASK, pTask, nullptr, code);
};
+ masterListener.onJobReceived = [&](void* pMaster, SERVO::CEquipment* pEquipment, int port, SERVO::CJobDataS* pJobDataS) {
+ (void)pMaster;
+ (void)port;
+ if (pEquipment == nullptr || pJobDataS == nullptr) return;
+ {
+ const std::string& g1 = pJobDataS->getGlass1Id();
+ const std::string& g2 = pJobDataS->getGlass2Id();
+ std::string glassId;
+ if (!g1.empty() && !g2.empty()) {
+ glassId = g1 + "+" + g2;
+ }
+ else if (!g1.empty()) {
+ glassId = g1;
+ }
+ else {
+ glassId = g2;
+ }
+ const int slotNo = pJobDataS->getTargetSlotNo();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("SubEqpName", pEquipment->getName().c_str());
+ m_hsmsPassive.setVariableValue("SubEqpSlot", slotNo);
+ m_hsmsPassive.setVariableValue("MaterialId", glassId.c_str());
+ m_hsmsPassive.requestEventReportSend("GlassReceivedJob");
+ });
+ }
+ const int eqId = pEquipment->getID();
+ const int recipeId = pJobDataS->getMasterRecipe();
+ std::string recipe = RecipeManager::getInstance().getPPIDById(recipeId);
+ if (recipe.empty()) {
+ recipe = std::to_string(recipeId);
+ }
+ const std::string prev = pEquipment->getCurrentRecipe();
+ if (recipe.empty() || recipe == prev) {
+ pEquipment->setCurrentRecipe(recipe);
+ return;
+ }
+ pEquipment->setCurrentRecipe(recipe);
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("Clock", CToolUnits::getCurrentTimeString().c_str());
+ m_hsmsPassive.setVariableValue("EQPPExecName", recipe.c_str());
+ m_hsmsPassive.setVariableValue("SubEqpName", pEquipment->getName().c_str());
+ const char* recipeVid = nullptr;
+ switch (eqId) {
+ case EQ_ID_Bonder1: recipeVid = "Bonder1CurrentRecipe"; break;
+ case EQ_ID_Bonder2: recipeVid = "Bonder2CurrentRecipe"; break;
+ case EQ_ID_VACUUMBAKE: recipeVid = "VacuumBakeCurrentRecipe"; break;
+ case EQ_ID_BAKE_COOLING: recipeVid = "BakeCoolingCurrentRecipe"; break;
+ case EQ_ID_MEASUREMENT: recipeVid = "MeasurementCurrentRecipe"; break;
+ case EQ_ID_EFEM: recipeVid = "EFEMCurrentRecipe"; break;
+ default: break;
+ }
+ if (recipeVid != nullptr) {
+ m_hsmsPassive.setVariableValue(recipeVid, recipe.c_str());
+ }
+ m_hsmsPassive.requestEventReportSend("RecipeChanged");
+ });
+ };
+ masterListener.onJobSentOut = [&](void* pMaster, SERVO::CEquipment* pEquipment, int port, SERVO::CJobDataS* pJobDataS) {
+ (void)pMaster;
+ (void)port;
+ if (pEquipment == nullptr || pJobDataS == nullptr) return;
+ const std::string& g1 = pJobDataS->getGlass1Id();
+ const std::string& g2 = pJobDataS->getGlass2Id();
+ std::string glassId;
+ if (!g1.empty() && !g2.empty()) {
+ glassId = g1 + "+" + g2;
+ }
+ else if (!g1.empty()) {
+ glassId = g1;
+ }
+ else {
+ glassId = g2;
+ }
+ const int slotNo = pJobDataS->getSourceSlotNo();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("SubEqpName", pEquipment->getName().c_str());
+ m_hsmsPassive.setVariableValue("SubEqpSlot", slotNo);
+ m_hsmsPassive.setVariableValue("MaterialId", glassId.c_str());
+ m_hsmsPassive.requestEventReportSend("GlassSentOutJob");
+ });
+ };
masterListener.onLoadPortStatusChanged = [&] (void* pMaster, SERVO::CEquipment* pEquipment, short status, __int64 data) {
LOGE("<CModel>onLoadPortStatusChanged. status = %d", status);
+ static std::map<int, short> s_prevPortStatus;
+ const int eqId = (pEquipment != nullptr) ? pEquipment->getID() : 0;
+ const short prevStatus = s_prevPortStatus[eqId];
+ s_prevPortStatus[eqId] = status;
+ SERVO::CLoadPort* pLoadPort = dynamic_cast<SERVO::CLoadPort*>(pEquipment);
+
+ // Unified PortStateChange event + SV maintenance
+ if (pLoadPort != nullptr) {
+ const unsigned int portIndex = pLoadPort->getIndex() + 1;
+ char stateVid[64] = {0};
+ char modeVid[64] = {0};
+ sprintf_s(stateVid, "PortTransferState_P%u", portIndex);
+ sprintf_s(modeVid, "AccessMode_P%u", portIndex);
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue(stateVid, (__int64)status);
+ m_hsmsPassive.setVariableValue(modeVid, (__int64)pLoadPort->getPortMode());
+ m_hsmsPassive.setVariableValue("PortId", pLoadPort->getID());
+ m_hsmsPassive.setVariableValue("PortState", (__int64)status);
+ m_hsmsPassive.requestEventReportSend("PortStateChange");
+ });
+ }
+
if (status == PORT_INUSE) {
- SERVO::CLoadPort* pLoadPort = dynamic_cast<SERVO::CLoadPort*>(pEquipment);
- if (pLoadPort != nullptr) {
- m_hsmsPassive.setVariableValue("CarrierID", pLoadPort->getCassetteId().c_str());
- }
- m_hsmsPassive.requestEventReportSend_CarrierID_Readed();
+ m_hsmsPassive.withVariableLock([&] {
+ if (pLoadPort != nullptr) {
+ const unsigned int portIndex = pLoadPort->getIndex() + 1;
+ char carrierVid[64] = {0};
+ sprintf_s(carrierVid, "CarrierID_P%u", portIndex);
+ m_hsmsPassive.setVariableValue(carrierVid, pLoadPort->getCassetteId().c_str());
+ if (prevStatus != PORT_INUSE && pLoadPort->isCompareMapsBeforeProceeding()) {
+ // TODO(Host鍗忓晢):
+ // 鏂囨。涓爣鏄庯細1-Empty锛�3-Exist锛屽洜姝ゆ垜浠彲鑳介渶瑕佸皢uint鐨刴ap杞崲涓簂ist涓婁紶
+ m_hsmsPassive.setVariableValue("SlotMap", pLoadPort->getScanCassetteMap());
+ m_hsmsPassive.requestEventReportSend_CheckSlotMap();
+ }
+ }
+ m_hsmsPassive.requestEventReportSend_CarrierID_Readed();
+ });
}
else if (status == PORT_BLOCKED) {
SERVO::CLoadPort* pLoadPort = dynamic_cast<SERVO::CLoadPort*>(pEquipment);
- if (pLoadPort != nullptr) {
- m_hsmsPassive.setVariableValue("BlockedPortId", pLoadPort->getID());
- }
- m_hsmsPassive.requestEventReportSend_Port_Blocked();
+ m_hsmsPassive.withVariableLock([&] {
+ if (pLoadPort != nullptr) {
+ m_hsmsPassive.setVariableValue("PortId", pLoadPort->getID());
+ }
+ m_hsmsPassive.requestEventReportSend_Port_Blocked();
+ });
}
else if (status == PORT_LOAD_READY) {
SERVO::CLoadPort* pLoadPort = dynamic_cast<SERVO::CLoadPort*>(pEquipment);
- if (pLoadPort != nullptr) {
- m_hsmsPassive.setVariableValue("LoadReadyPortId", pLoadPort->getID());
- }
- m_hsmsPassive.requestEventReportSend_Port_Load_Ready();
+ m_hsmsPassive.withVariableLock([&] {
+ if (pLoadPort != nullptr) {
+ m_hsmsPassive.setVariableValue("PortId", pLoadPort->getID());
+ }
+ m_hsmsPassive.requestEventReportSend_Port_Load_Ready();
+ });
}
else if (status == PORT_UNLOAD_READY) {
SERVO::CLoadPort* pLoadPort = dynamic_cast<SERVO::CLoadPort*>(pEquipment);
- if (pLoadPort != nullptr) {
- m_hsmsPassive.setVariableValue("UnloadReadyPortId", pLoadPort->getID());
- }
- m_hsmsPassive.requestEventReportSend_Port_Unload_Ready();
+ m_hsmsPassive.withVariableLock([&] {
+ if (pLoadPort != nullptr) {
+ m_hsmsPassive.setVariableValue("PortId", pLoadPort->getID());
+ if (prevStatus == PORT_INUSE) {
+ m_hsmsPassive.setVariableValue("PortId", pLoadPort->getID());
+ m_hsmsPassive.requestEventReportSend_Port_Ready_To_Release();
+ }
+ }
+ m_hsmsPassive.requestEventReportSend_Port_Unload_Ready();
+ });
+ }
+ else if (status == PORT_EMPTY) {
+ SERVO::CLoadPort* pLoadPort = dynamic_cast<SERVO::CLoadPort*>(pEquipment);
+ m_hsmsPassive.withVariableLock([&] {
+ if (pLoadPort != nullptr) {
+ m_hsmsPassive.setVariableValue("PortId", pLoadPort->getID());
+ }
+ m_hsmsPassive.requestEventReportSend_LoadPortNotAssoc();
+ });
}
notifyPtr(RX_CODE_LOADPORT_STATUS_CHANGED, pEquipment);
+ };
+ masterListener.onProcessStateChanged = [&](void* pMaster, SERVO::CEquipment* pEquipment, int slotNo, SERVO::PROCESS_STATE prevState, SERVO::PROCESS_STATE state) {
+ (void)pMaster;
+ const int eqId = pEquipment ? pEquipment->getID() : 0;
+
+ // 淇濇寔鍚屼竴閿佽寖鍥村唴锛氭洿鏂版墍闇� SV 骞朵緷娆′笂鎶ワ紝淇濊瘉 set+send 鍘熷瓙鎬�
+ m_hsmsPassive.withVariableLock([&] {
+ // Timestamp VID (Clock, VID=500) for all related reports.
+ m_hsmsPassive.setVariableValue("Clock", CToolUnits::getCurrentTimeString().c_str());
+
+ // Common payload VIDs for SubEqp/Unit
+ if (pEquipment != nullptr) {
+ m_hsmsPassive.setVariableValue("SubEqpName", pEquipment->getName().c_str());
+ }
+ m_hsmsPassive.setVariableValue("SubEqpSlot", slotNo);
+
+ // ProcessStateChanged (equipment-level): update SVs 700/701, then report CEID=700
+ m_hsmsPassive.setVariableValue("PreviousProcessState", (__int64)prevState);
+ m_hsmsPassive.setVariableValue("CurrentProcessState", (__int64)state);
+ m_hsmsPassive.requestEventReportSend("ProcessStateChanged");
+
+ // SubEqp events (per equipment, ignore slot distinction except payload)
+ static std::map<int, SERVO::PROCESS_STATE> s_prevSubEqpState;
+ const auto prevEqState = s_prevSubEqpState[eqId];
+ if (prevEqState != state) {
+ // state change
+ m_hsmsPassive.requestEventReportSend("SubEqpStateChange");
+ }
+ if (state == SERVO::PROCESS_STATE::Processing) {
+ m_hsmsPassive.requestEventReportSend_SubEqpStart();
+ }
+ else if (state == SERVO::PROCESS_STATE::Complete) {
+ m_hsmsPassive.requestEventReportSend_SubEqpEnd();
+ }
+ s_prevSubEqpState[eqId] = state;
+
+ // Unit events (per equipment slot)
+ static std::map<int, std::map<int, SERVO::PROCESS_STATE>> s_prevUnitState;
+ const auto prevUnitState = s_prevUnitState[eqId][slotNo];
+ if (prevUnitState != state) {
+ m_hsmsPassive.requestEventReportSend("UnitStateChange");
+ if (state == SERVO::PROCESS_STATE::Processing) {
+ m_hsmsPassive.requestEventReportSend("UnitStart");
+ }
+ else if (state == SERVO::PROCESS_STATE::Complete) {
+ m_hsmsPassive.requestEventReportSend("UnitEnd");
+ }
+ s_prevUnitState[eqId][slotNo] = state;
+ }
+ });
+ };
+ masterListener.onSVDataReport = [&](void* pMaster, SERVO::CEquipment* pEquipment, const std::vector<CParam>& params) {
+ (void)pMaster;
+ const int eqId = pEquipment ? pEquipment->getID() : 0;
+
+ auto sendSv = [&](const auto& vidMap, const char* evName) {
+ const size_t count = (std::min)(params.size(), vidMap.size());
+ m_hsmsPassive.withVariableLock([&] {
+ if (pEquipment != nullptr) {
+ m_hsmsPassive.setVariableValue("SubEqpName", pEquipment->getName().c_str());
+ }
+ m_hsmsPassive.setVariableValue("SubEqpSlot", 0);
+ m_hsmsPassive.setVariableValue("Clock", CToolUnits::getCurrentTimeString().c_str());
+ for (size_t idx = 0; idx < count; ++idx) {
+ const std::string val = formatParamValue(params[idx]);
+ m_hsmsPassive.setVariableValue(std::to_string(vidMap[idx]).c_str(), val.c_str());
+ }
+ m_hsmsPassive.requestEventReportSend(evName);
+ });
+ };
+
+ if (eqId == EQ_ID_Bonder1 || eqId == EQ_ID_Bonder2) {
+ static constexpr std::array<int, 19> vids = {
+ 6000,6001,6002,6003,6004,6005,6006,6007,6008,6009,
+ 6010,6011,6012,6013,6014,6015,6016,6017,6018
+ };
+ sendSv(vids, "BonderSVData");
+ }
+ else if (eqId == EQ_ID_VACUUMBAKE) {
+ static constexpr std::array<int, 18> vids = {
+ 6200,6201,6202,6203,6204,6205,6206,6207,6208,
+ 6209,6210,6211,6212,6213,6214,6215,6216,6217
+ };
+ sendSv(vids, "VacuumBakeSVData");
+ }
+ else if (eqId == EQ_ID_BAKE_COOLING) {
+ static constexpr std::array<int, 20> vids = {
+ 6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,
+ 6410,6411,6412,6413,6414,6415,6416,6417,6418,6419
+ };
+ sendSv(vids, "BakeCoolingSVData");
+ }
+ else if (eqId == EQ_ID_MEASUREMENT) {
+ static constexpr std::array<int, 2> vids = { 6600, 6601 };
+ sendSv(vids, "MeasurementSVData");
+ }
+ };
+ masterListener.onProcessDataReport = [&](void* pMaster, SERVO::CEquipment* pEquipment, const std::vector<CParam>& params) {
+ (void)pMaster;
+ const int eqId = pEquipment ? pEquipment->getID() : 0;
+
+ auto sendProcess = [&](const auto& vidMap, const char* evName) {
+ const size_t count = (std::min)(params.size(), vidMap.size());
+ m_hsmsPassive.withVariableLock([&] {
+ if (pEquipment != nullptr) {
+ m_hsmsPassive.setVariableValue("SubEqpName", pEquipment->getName().c_str());
+ }
+ m_hsmsPassive.setVariableValue("SubEqpSlot", 0);
+ m_hsmsPassive.setVariableValue("Clock", CToolUnits::getCurrentTimeString().c_str());
+ for (size_t idx = 0; idx < count; ++idx) {
+ const std::string val = formatParamValue(params[idx]);
+ m_hsmsPassive.setVariableValue(std::to_string(vidMap[idx]).c_str(), val.c_str());
+ }
+ m_hsmsPassive.requestEventReportSend(evName);
+ });
+ };
+
+ if (eqId == EQ_ID_Bonder1 || eqId == EQ_ID_Bonder2) {
+ static constexpr std::array<int, 22> vids = {
+ 6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,
+ 6111,6112,6113,6114,6115,6116,6117,6118,6119,6120,6121
+ };
+ sendProcess(vids, "BonderProcessData");
+ }
+ else if (eqId == EQ_ID_VACUUMBAKE) {
+ static constexpr std::array<int, 5> vids = { 6300,6301,6302,6303,6304 };
+ sendProcess(vids, "VacuumBakeProcessData");
+ }
+ else if (eqId == EQ_ID_BAKE_COOLING) {
+ static constexpr std::array<int, 4> vids = { 6500,6501,6502,6503 };
+ sendProcess(vids, "BakeCoolingProcessData");
+ }
+ else if (eqId == EQ_ID_MEASUREMENT) {
+ static constexpr std::array<int, 4> vids = { 6700,6701,6702,6703 };
+ sendProcess(vids, "MeasurementProcessData");
+ }
};
masterListener.onCTRoundEnd = [&](void* pMaster, int round) {
m_configuration.setContinuousTransferCount(round);
};
masterListener.onCjStart = [&](void* pMaster, void* pj) {
- m_hsmsPassive.setVariableValue("CJStartID", ((SERVO::CControlJob*)pj)->id().c_str());
- m_hsmsPassive.requestEventReportSend_CJ_Start();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("CJStartID", ((SERVO::CControlJob*)pj)->id().c_str());
+ m_hsmsPassive.requestEventReportSend_CJ_Start();
+ });
};
masterListener.onCjEnd = [&](void* pMaster, void* pj) {
- m_hsmsPassive.setVariableValue("CJEndID", ((SERVO::CControlJob*)pj)->id().c_str());
- m_hsmsPassive.requestEventReportSend_CJ_End();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("CJEndID", ((SERVO::CControlJob*)pj)->id().c_str());
+ m_hsmsPassive.requestEventReportSend_CJ_End();
+ });
- // 结批,保存ControlJob
+ // 缁撴壒锛屼繚瀛楥ontrolJob
//
};
masterListener.onPjStart = [&](void* pMaster, void* pj) {
- m_hsmsPassive.setVariableValue("PJStartID", ((SERVO::CProcessJob*)pj)->id().c_str());
- m_hsmsPassive.requestEventReportSend_PJ_Start();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("PJStartID", ((SERVO::CProcessJob*)pj)->id().c_str());
+ m_hsmsPassive.requestEventReportSend_PJ_Start();
+ });
};
masterListener.onPjEnd = [&](void* pMaster, void* pj) {
- m_hsmsPassive.setVariableValue("PJEndID", ((SERVO::CProcessJob*)pj)->id().c_str());
- m_hsmsPassive.requestEventReportSend_PJ_End();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("PJEndID", ((SERVO::CProcessJob*)pj)->id().c_str());
+ m_hsmsPassive.requestEventReportSend_PJ_End();
+ });
};
masterListener.onPanelStart = [&](void* pMaster, void* pPanel) {
- m_hsmsPassive.setVariableValue("PanelStartID", ((SERVO::CGlass*)pPanel)->getID().c_str());
- m_hsmsPassive.requestEventReportSend_Panel_Start();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("PanelStartID", ((SERVO::CGlass*)pPanel)->getID().c_str());
+ m_hsmsPassive.requestEventReportSend_Panel_Start();
+ });
};
masterListener.onPanelEnd = [&](void* pMaster, void* pPanel) {
- m_hsmsPassive.setVariableValue("PanelEndID", ((SERVO::CGlass*)pPanel)->getID().c_str());
- m_hsmsPassive.requestEventReportSend_Panel_End();
+ m_hsmsPassive.withVariableLock([&] {
+ m_hsmsPassive.setVariableValue("PanelEndID", ((SERVO::CGlass*)pPanel)->getID().c_str());
+ m_hsmsPassive.requestEventReportSend_Panel_End();
+ // Placeholder payload to match log shape: EV_PROCESS_DATA_REPORT can carry a single A-string (may be empty).
+ m_hsmsPassive.setVariableValue("ProcessDataReportText", "");
+ m_hsmsPassive.requestEventReportSend_ProcessDataReport();
+ });
auto& db = GlassLogDb::Instance();
db.insertFromCGlass((*(SERVO::CGlass*)pPanel));
SERVO::CGlass* pBuddy = ((SERVO::CGlass*)pPanel)->getBuddy();
@@ -464,33 +982,37 @@
m_master.setContinuousTransferCount(m_configuration.getContinuousTransferCount());
- // master 设置缓存文件
+ // master 璁剧疆缂撳瓨鏂囦欢
CString strMasterDataFile;
strMasterDataFile.Format(_T("%s\\Master.dat"), (LPTSTR)(LPCTSTR)m_strWorkDir);
m_master.setCacheFilepath((LPTSTR)(LPCTSTR)strMasterDataFile);
m_master.setCompareMapsBeforeProceeding(m_configuration.isCompareMapsBeforeProceeding());
m_master.setJobMode(m_configuration.isJobMode());
- // 加截Job
+ // 鍔犳埅Job
strMasterDataFile.Format(_T("%s\\MasterState.dat"), (LPTSTR)(LPCTSTR)m_strWorkDir);
std::string strPath = std::string((LPTSTR)(LPCTSTR)strMasterDataFile);
m_master.setStateFile(strPath);
- // 加载警告信息
+ // 鍔犺浇璀﹀憡淇℃伅
AlarmManager& alarmManager = AlarmManager::getInstance();
char szBuffer[MAX_PATH];
sprintf_s(szBuffer, MAX_PATH, "%s\\AlarmList.csv", (LPTSTR)(LPCTSTR)m_strWorkDir);
alarmManager.readAlarmFile(szBuffer);
+ LOGI("[BOOT][MODEL] Alarm list loaded, cost=%llu ms",
+ (unsigned long long)(GetTickCount64() - boot_model_begin));
- // Glass数据库
+ // Glass鏁版嵁搴�
strLogDir.Format(_T("%s\\db\\process.db"), (LPTSTR)(LPCTSTR)m_strWorkDir);
std::string path((LPTSTR)(LPCTSTR)strLogDir);
GlassLogDb::Init(path);
+ LOGI("[BOOT][MODEL] init finished, total cost=%llu ms",
+ (unsigned long long)(GetTickCount64() - boot_model_begin));
return 0;
}
diff --git a/SourceCode/Bond/Servo/Model.h b/SourceCode/Bond/Servo/Model.h
index e1b1563..51a5427 100644
--- a/SourceCode/Bond/Servo/Model.h
+++ b/SourceCode/Bond/Servo/Model.h
@@ -3,6 +3,17 @@
#include "HsmsPassive.h"
#include "CMaster.h"
#include "CGlassPool.h"
+#include <cstdint>
+#include <string>
+
+enum class ControlState : uint8_t {
+ OfflineEquipment = 0,
+ OfflineAttempt = 1,
+ Online = 2,
+ OfflineHost = 3,
+ OnlineLocal = 4,
+ OnlineRemote = 5,
+};
class CModel
{
@@ -20,6 +31,21 @@
void setPortEnable(unsigned int index, BOOL bEnable);
int init();
int term();
+
+ ControlState getControlState() const noexcept { return m_currentControlState; }
+ void setControlState(ControlState newState);
+ bool raiseSoftAlarm(int alarmId,
+ const std::string& desc = "",
+ int level = -1,
+ int deviceId = 0,
+ int unitId = 0,
+ const char* deviceName = "Software",
+ const char* unitName = "App");
+ void clearSoftAlarm(int alarmId, int deviceId = 0, int unitId = 0);
+
+private:
+ void refreshDerivedSVs();
+ void notifyControlJobChanged();
public:
int notify(int code);
@@ -46,5 +72,7 @@
IObservableEmitter* m_pObservableEmitter;
CString m_strWorkDir;
CString m_strDataDir;
-};
+private:
+ ControlState m_currentControlState{ ControlState::OfflineEquipment };
+};
diff --git a/SourceCode/Bond/Servo/PageRecipe.cpp b/SourceCode/Bond/Servo/PageRecipe.cpp
index cf387ce..841a96f 100644
--- a/SourceCode/Bond/Servo/PageRecipe.cpp
+++ b/SourceCode/Bond/Servo/PageRecipe.cpp
@@ -138,9 +138,12 @@
// 閬嶅巻鏁版嵁骞舵彃鍏ュ埌CListCtrl涓�
for (int i = 0; i < static_cast<int>(vecRecipe.size()); ++i) {
const RecipeInfo& recipe = vecRecipe[i];
+ // 鍘熺▼搴忚姹侾PID鏈夊瓙閰嶆柟锛屽厛娉ㄩ噴
+ /*
if (recipe.vecDeviceList.empty() || recipe.vecDeviceList.size() > 6){
continue;
}
+ */
m_listPPID.InsertItem(i, _T("")); // 绗�0鍒楃┖鐧�
@@ -386,13 +389,12 @@
void CPageRecipe::OnBnClickedButtonNew()
{
- // TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
- //CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT);
- //int nSel = pComboBox->GetCurSel();
- //SERVO::CEquipment* pEq = (SERVO::CEquipment*)pComboBox->GetItemDataPtr(nSel);
- //if (pEq == nullptr) {
- // return;
- //}
+ int rc = UX_CanExecute(L"recipe");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ UX_RecordAction(L"recipe");
CRecipeDeviceBindDlg dlg(this);
if (dlg.DoModal() == IDOK) {
@@ -452,6 +454,13 @@
void CPageRecipe::OnBnClickedButtonModify()
{
+ int rc = UX_CanExecute(L"recipe");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ UX_RecordAction(L"recipe");
+
// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT);
if (pComboBox == nullptr || !::IsWindow(pComboBox->m_hWnd)) {
@@ -492,6 +501,13 @@
void CPageRecipe::OnBnClickedButtonDelete()
{
+ int rc = UX_CanExecute(L"recipe");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ UX_RecordAction(L"recipe");
+
// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
POSITION pos = m_listPPID.GetFirstSelectedItemPosition();
if (!pos) {
@@ -518,6 +534,13 @@
void CPageRecipe::OnBnClickedButtonDeleteAll()
{
+ int rc = UX_CanExecute(L"recipe");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return;
+ }
+ UX_RecordAction(L"recipe");
+
// TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
if (IDYES != AfxMessageBox(_T("纭畾瑕佸垹闄ゅ叏閮ㄩ厤鏂硅褰曞悧锛�"), MB_YESNO | MB_ICONWARNING)) {
return;
diff --git a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
index 62ad3a0..27bc7a8 100644
--- a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
+++ b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
@@ -438,14 +438,68 @@
{
int selPort = (0 <= m_nCurSelPort && m_nCurSelPort <= 3) ? m_nCurSelPort
: m_comboPort.GetCurSel();
- if (selPort < 0 || selPort >= 4) return;
- m_pPort[selPort]->sendCassetteCtrlCmd(CCC_PROCESS_START, nullptr, 0, 0, 0, nullptr, nullptr);
+ if (selPort < 0 || selPort >= 4) {
+ LOGE("ProcessStart invalid port index: %d", selPort);
+ return;
+ }
+
+ SERVO::CLoadPort* pPort = m_pPort[selPort];
+ if (pPort == nullptr) {
+ LOGE("ProcessStart port pointer is null, index: %d", selPort);
+ return;
+ }
+
+ constexpr short cmd = CCC_PROCESS_START;
+ LOGI("ProcessStart request: port=%d, cmd=%d", selPort + 1, cmd);
+ int ret = pPort->sendCassetteCtrlCmd(cmd, nullptr, 0, 0, 0, nullptr,
+ [selPort](int code) -> int {
+ if (code == WOK) {
+ LOGI("ProcessStart write complete: port=%d, code=WOK", selPort + 1);
+ }
+ else {
+ LOGE("ProcessStart write failed: port=%d, code=%d", selPort + 1, code);
+ }
+ return 0;
+ });
+ if (ret != 0) {
+ LOGE("ProcessStart sendCassetteCtrlCmd immediate failure: port=%d, ret=%d", selPort + 1, ret);
+ }
+ else {
+ LOGI("ProcessStart sendCassetteCtrlCmd dispatched: port=%d", selPort + 1);
+ }
}
void CPortConfigurationDlg::OnBnClickedButtonProcessCancel()
{
int selPort = (0 <= m_nCurSelPort && m_nCurSelPort <= 3) ? m_nCurSelPort
: m_comboPort.GetCurSel();
- if (selPort < 0 || selPort >= 4) return;
- m_pPort[selPort]->sendCassetteCtrlCmd(CCC_PROCESS_CANCEL, nullptr, 0, 0, 0, nullptr, nullptr);
+ if (selPort < 0 || selPort >= 4) {
+ LOGE("ProcessCancel invalid port index: %d", selPort);
+ return;
+ }
+
+ SERVO::CLoadPort* pPort = m_pPort[selPort];
+ if (pPort == nullptr) {
+ LOGE("ProcessCancel port pointer is null, index: %d", selPort);
+ return;
+ }
+
+ constexpr short cmd = CCC_PROCESS_CANCEL;
+ LOGI("ProcessCancel request: port=%d, cmd=%d", selPort + 1, cmd);
+ int ret = pPort->sendCassetteCtrlCmd(cmd, nullptr, 0, 0, 0, nullptr,
+ [selPort](int code) -> int {
+ if (code == WOK) {
+ LOGI("ProcessCancel write complete: port=%d, code=WOK", selPort + 1);
+ }
+ else {
+ LOGE("ProcessCancel write failed: port=%d, code=%d", selPort + 1, code);
+ }
+ return 0;
+ });
+ if (ret != 0) {
+ LOGE("ProcessCancel sendCassetteCtrlCmd immediate failure: port=%d, ret=%d", selPort + 1, ret);
+ }
+ else {
+ LOGI("ProcessCancel sendCassetteCtrlCmd dispatched: port=%d", selPort + 1);
+ }
}
diff --git a/SourceCode/Bond/Servo/ProcessJob.cpp b/SourceCode/Bond/Servo/ProcessJob.cpp
index 184b298..eb92df5 100644
--- a/SourceCode/Bond/Servo/ProcessJob.cpp
+++ b/SourceCode/Bond/Servo/ProcessJob.cpp
@@ -75,6 +75,11 @@
return m_issues;
}
+ void CProcessJob::addIssue(uint32_t code, const std::string& msg)
+ {
+ m_issues.push_back({ code, msg });
+ }
+
bool CProcessJob::validate(const IResourceView& rv)
{
m_issues.clear();
@@ -83,10 +88,6 @@
auto add = [&](uint32_t code, std::string msg) {
m_issues.push_back({ code, std::move(msg) });
};
-
- if (!rv.isProcessJobsEmpty()) {
- add(1000, "ProcessJobs Conflict!");
- }
// —— 基本 / 标识 ——
if (m_pjId.empty()) add(1001, "PJID empty");
diff --git a/SourceCode/Bond/Servo/ProcessJob.h b/SourceCode/Bond/Servo/ProcessJob.h
index bbc8926..576b30a 100644
--- a/SourceCode/Bond/Servo/ProcessJob.h
+++ b/SourceCode/Bond/Servo/ProcessJob.h
@@ -133,6 +133,7 @@
// 返回问题清单(空=通过)
bool validate(const IResourceView& rv);
const std::vector<ValidationIssue>& issues() const;
+ void addIssue(uint32_t code, const std::string& msg);
// —— 状态机(带守卫)——
bool queue(); // NoState -> Queued
diff --git a/SourceCode/Bond/Servo/ProductionStats.cpp b/SourceCode/Bond/Servo/ProductionStats.cpp
new file mode 100644
index 0000000..edf6272
--- /dev/null
+++ b/SourceCode/Bond/Servo/ProductionStats.cpp
@@ -0,0 +1,438 @@
+#include "stdafx.h"
+#include "ProductionStats.h"
+
+#include <algorithm>
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+#include <unordered_map>
+
+#include "Configuration.h"
+#include "Log.h"
+#include "sqlite3.h"
+
+#ifdef min
+#undef min
+#endif
+#ifdef max
+#undef max
+#endif
+
+static std::string FormatLocal(const std::chrono::system_clock::time_point& tp)
+{
+ const std::time_t tt = std::chrono::system_clock::to_time_t(tp);
+ std::tm tm{};
+ localtime_s(&tm, &tt);
+ std::ostringstream oss;
+ oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
+ return oss.str();
+}
+
+static std::string FormatUtcIso(const std::chrono::system_clock::time_point& tp)
+{
+ const std::time_t tt = std::chrono::system_clock::to_time_t(tp);
+ std::tm tm{};
+ gmtime_s(&tm, &tt);
+ std::ostringstream oss;
+ oss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
+ return oss.str();
+}
+
+static bool TryParseLocalTime(const std::string& text, std::chrono::system_clock::time_point& outTp)
+{
+ if (text.empty()) return false;
+ std::tm tm{};
+ std::istringstream iss(text);
+ iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
+ if (iss.fail()) return false;
+ tm.tm_isdst = -1;
+ const std::time_t tt = mktime(&tm);
+ if (tt == (time_t)-1) return false;
+ outTp = std::chrono::system_clock::from_time_t(tt);
+ return true;
+}
+
+static std::string GetExeDir()
+{
+ char path[MAX_PATH] = {};
+ GetModuleFileNameA(nullptr, path, MAX_PATH);
+ std::string exePath(path);
+ const size_t pos = exePath.find_last_of("\\/");
+ return (pos == std::string::npos) ? std::string() : exePath.substr(0, pos);
+}
+
+static bool FileExistsA(const std::string& path)
+{
+ const DWORD attr = GetFileAttributesA(path.c_str());
+ return (attr != INVALID_FILE_ATTRIBUTES) && ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0);
+}
+
+static std::string PickDbPath(const std::string& rel1, const std::string& rel2)
+{
+ const std::string base = GetExeDir();
+ const std::string p1 = base + "\\" + rel1;
+ if (FileExistsA(p1)) return p1;
+ return base + "\\" + rel2;
+}
+
+static void ComputeOutputFromProcessDb(
+ const ProductionShiftWindow& win,
+ ProductionOutputSummary& out)
+{
+ const std::string dbPath = PickDbPath("db\\process.db", "DB\\process.db");
+
+ sqlite3* db = nullptr;
+ if (sqlite3_open_v2(dbPath.c_str(), &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) {
+ if (db) sqlite3_close(db);
+ return;
+ }
+
+ const char* sql =
+ "SELECT class_id, buddy_id, aoi_result, "
+ "IFNULL(strftime('%Y-%m-%d %H:%M:%S', t_start, 'localtime'), ''),"
+ "IFNULL(strftime('%Y-%m-%d %H:%M:%S', t_end, 'localtime'), '') "
+ "FROM glass_log "
+ "WHERE t_end IS NOT NULL AND t_end >= ? AND t_end < ?;";
+
+ sqlite3_stmt* stmt = nullptr;
+ if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
+ sqlite3_close(db);
+ return;
+ }
+
+ sqlite3_bind_text(stmt, 1, win.startUtcIso.c_str(), -1, SQLITE_TRANSIENT);
+ sqlite3_bind_text(stmt, 2, win.endUtcIso.c_str(), -1, SQLITE_TRANSIENT);
+
+ struct PairAgg {
+ bool hasPass = false;
+ bool hasFail = false;
+ bool hasNo = false;
+ long long maxTaktSeconds = -1;
+ };
+ std::unordered_map<std::string, PairAgg> pairs;
+
+ for (;;) {
+ const int rc = sqlite3_step(stmt);
+ if (rc == SQLITE_ROW) {
+ const char* classId = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
+ const char* buddyId = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
+ const int aoi = sqlite3_column_int(stmt, 2);
+ const char* sStart = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
+ const char* sEnd = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 4));
+
+ const std::string a = classId ? classId : "";
+ const std::string b = buddyId ? buddyId : "";
+ std::string key;
+ if (!b.empty()) {
+ if (a <= b) key = a + "|" + b;
+ else key = b + "|" + a;
+ }
+ else {
+ key = a;
+ }
+
+ auto& agg = pairs[key];
+ if (aoi == 1) agg.hasPass = true;
+ else if (aoi == 2) agg.hasFail = true;
+ else agg.hasNo = true;
+
+ std::chrono::system_clock::time_point tpStart{}, tpEnd{};
+ if (TryParseLocalTime(sStart ? sStart : "", tpStart) && TryParseLocalTime(sEnd ? sEnd : "", tpEnd) && tpEnd > tpStart) {
+ const auto secs = std::chrono::duration_cast<std::chrono::seconds>(tpEnd - tpStart).count();
+ if (secs > agg.maxTaktSeconds) agg.maxTaktSeconds = secs;
+ }
+ }
+ else if (rc == SQLITE_DONE) {
+ break;
+ }
+ else {
+ break;
+ }
+ }
+
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+
+ out.pairsTotal = static_cast<long long>(pairs.size());
+ long long sumTakt = 0;
+ long long cntTakt = 0;
+ for (const auto& kv : pairs) {
+ const auto& agg = kv.second;
+ if (agg.hasFail) out.pairsFail++;
+ else if (agg.hasPass) out.pairsPass++;
+ else out.pairsNoResult++;
+
+ if (agg.maxTaktSeconds >= 0) {
+ sumTakt += agg.maxTaktSeconds;
+ cntTakt += 1;
+ }
+ }
+ const long long denom = out.pairsPass + out.pairsFail;
+ out.yield = (denom > 0) ? (static_cast<double>(out.pairsPass) / static_cast<double>(denom)) : 0.0;
+ out.taktSamplePairs = cntTakt;
+ out.avgTaktSeconds = (cntTakt > 0) ? (static_cast<double>(sumTakt) / static_cast<double>(cntTakt)) : 0.0;
+}
+
+static void ComputeAlarmSummaryFromDb(
+ const ProductionShiftWindow& win,
+ ProductionAlarmSummary& out)
+{
+ const std::string dbPath = PickDbPath("DB\\AlarmManager.db", "DB\\AlarmManager.db");
+ sqlite3* db = nullptr;
+ if (sqlite3_open_v2(dbPath.c_str(), &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) {
+ if (db) sqlite3_close(db);
+ return;
+ }
+
+ // 1) triggered within shift
+ {
+ const char* sql =
+ "SELECT COUNT(1) FROM alarms "
+ "WHERE start_time >= ? AND start_time < ?;";
+ sqlite3_stmt* stmt = nullptr;
+ if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) {
+ sqlite3_bind_text(stmt, 1, win.startLocal.c_str(), -1, SQLITE_TRANSIENT);
+ sqlite3_bind_text(stmt, 2, win.endLocal.c_str(), -1, SQLITE_TRANSIENT);
+ if (sqlite3_step(stmt) == SQLITE_ROW) out.alarmsTriggered = sqlite3_column_int(stmt, 0);
+ sqlite3_finalize(stmt);
+ }
+ }
+
+ // 2) overlapping (including active)
+ {
+ const char* sql =
+ "SELECT severity_level, start_time, end_time "
+ "FROM alarms "
+ "WHERE start_time < ? AND (end_time IS NULL OR end_time >= ?);";
+ sqlite3_stmt* stmt = nullptr;
+ if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) {
+ sqlite3_bind_text(stmt, 1, win.endLocal.c_str(), -1, SQLITE_TRANSIENT);
+ sqlite3_bind_text(stmt, 2, win.startLocal.c_str(), -1, SQLITE_TRANSIENT);
+
+ const auto now = std::chrono::system_clock::now();
+ for (;;) {
+ const int rc = sqlite3_step(stmt);
+ if (rc == SQLITE_ROW) {
+ const int severity = sqlite3_column_int(stmt, 0);
+ const char* sStart = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
+ const char* sEnd = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
+ std::chrono::system_clock::time_point aStart{};
+ if (!TryParseLocalTime(sStart ? sStart : "", aStart)) continue;
+ std::chrono::system_clock::time_point aEnd{};
+ bool hasEnd = TryParseLocalTime(sEnd ? sEnd : "", aEnd);
+ if (!hasEnd) aEnd = std::min(now, win.end);
+
+ const auto clipStart = std::max(aStart, win.start);
+ const auto clipEnd = std::min(aEnd, win.end);
+ if (clipEnd > clipStart) {
+ const auto secs = std::chrono::duration_cast<std::chrono::seconds>(clipEnd - clipStart).count();
+ out.downtimeMinutes += static_cast<double>(secs) / 60.0;
+ }
+
+ out.bySeverity[severity] += 1;
+ out.alarmsOverlapping += 1;
+ }
+ else if (rc == SQLITE_DONE) {
+ break;
+ }
+ else {
+ break;
+ }
+ }
+
+ sqlite3_finalize(stmt);
+ }
+ }
+
+ sqlite3_close(db);
+}
+
+static void ComputeTransferSummaryFromDb(
+ const ProductionShiftWindow& win,
+ ProductionTransferSummary& out)
+{
+ const std::string dbPath = PickDbPath("DB\\TransferManager.db", "DB\\TransferManager.db");
+ sqlite3* db = nullptr;
+ if (sqlite3_open_v2(dbPath.c_str(), &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) {
+ if (db) sqlite3_close(db);
+ return;
+ }
+
+ const char* sql =
+ "SELECT status, create_time, end_time "
+ "FROM transfers "
+ "WHERE end_time >= ? AND end_time < ? AND end_time != '';";
+ sqlite3_stmt* stmt = nullptr;
+ if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
+ sqlite3_close(db);
+ return;
+ }
+
+ sqlite3_bind_text(stmt, 1, win.startLocal.c_str(), -1, SQLITE_TRANSIENT);
+ sqlite3_bind_text(stmt, 2, win.endLocal.c_str(), -1, SQLITE_TRANSIENT);
+
+ long long totalSecs = 0;
+ long long cntSecs = 0;
+
+ for (;;) {
+ const int rc = sqlite3_step(stmt);
+ if (rc == SQLITE_ROW) {
+ const char* sStatus = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
+ const char* sCreate = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
+ const char* sEnd = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
+
+ const std::string status = sStatus ? sStatus : "";
+ out.byStatus[status] += 1;
+ out.transfersFinished += 1;
+
+ std::chrono::system_clock::time_point tpCreate{}, tpEnd{};
+ if (TryParseLocalTime(sCreate ? sCreate : "", tpCreate) && TryParseLocalTime(sEnd ? sEnd : "", tpEnd) && tpEnd > tpCreate) {
+ totalSecs += std::chrono::duration_cast<std::chrono::seconds>(tpEnd - tpCreate).count();
+ cntSecs += 1;
+ }
+ }
+ else if (rc == SQLITE_DONE) {
+ break;
+ }
+ else {
+ break;
+ }
+ }
+
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+
+ out.avgCreateToEndSeconds = (cntSecs > 0) ? (static_cast<double>(totalSecs) / static_cast<double>(cntSecs)) : 0.0;
+}
+
+bool ProductionStats::GetCurrentShiftWindow(CConfiguration& config, ProductionShiftWindow& outWindow)
+{
+ int dayMin = 8 * 60;
+ int nightMin = 20 * 60;
+ config.getProductionShiftStartMinutes(dayMin, nightMin);
+
+ const auto now = std::chrono::system_clock::now();
+ const std::time_t ttNow = std::chrono::system_clock::to_time_t(now);
+ std::tm tmNow{};
+ localtime_s(&tmNow, &ttNow);
+ std::tm tmMid = tmNow;
+ tmMid.tm_hour = 0;
+ tmMid.tm_min = 0;
+ tmMid.tm_sec = 0;
+ tmMid.tm_isdst = -1;
+ const std::time_t ttMid = mktime(&tmMid);
+ if (ttMid == (time_t)-1) return false;
+
+ const auto midnight = std::chrono::system_clock::from_time_t(ttMid);
+ const auto startDayToday = midnight + std::chrono::minutes(dayMin);
+ const auto startNightToday = midnight + std::chrono::minutes(nightMin);
+
+ const auto startDay = (now >= startDayToday) ? startDayToday : (startDayToday - std::chrono::hours(24));
+ const auto startNight = (now >= startNightToday) ? startNightToday : (startNightToday - std::chrono::hours(24));
+
+ ProductionShiftType type = ProductionShiftType::Day;
+ auto start = startDay;
+ if (startNight > startDay) {
+ type = ProductionShiftType::Night;
+ start = startNight;
+ }
+
+ const int durationMin =
+ (type == ProductionShiftType::Day)
+ ? ((nightMin - dayMin + 24 * 60) % (24 * 60))
+ : ((dayMin - nightMin + 24 * 60) % (24 * 60));
+ if (durationMin <= 0) return false;
+
+ outWindow.type = type;
+ outWindow.start = start;
+ outWindow.end = start + std::chrono::minutes(durationMin);
+ outWindow.startLocal = FormatLocal(outWindow.start);
+ outWindow.endLocal = FormatLocal(outWindow.end);
+ outWindow.startUtcIso = FormatUtcIso(outWindow.start);
+ outWindow.endUtcIso = FormatUtcIso(outWindow.end);
+ return true;
+}
+
+bool ProductionStats::ComputeCurrentShiftSummary(CConfiguration& config, ProductionShiftSummary& outSummary)
+{
+ ProductionShiftWindow win;
+ if (!GetCurrentShiftWindow(config, win)) return false;
+
+ outSummary = ProductionShiftSummary{};
+ outSummary.window = win;
+
+ ComputeOutputFromProcessDb(win, outSummary.output);
+ ComputeAlarmSummaryFromDb(win, outSummary.alarms);
+ ComputeTransferSummaryFromDb(win, outSummary.transfers);
+ return true;
+}
+
+void ProductionStats::LogCurrentShiftSummary(CConfiguration& config)
+{
+ ProductionShiftSummary s;
+ if (!ComputeCurrentShiftSummary(config, s)) {
+ LOGE("<ProductionStats>Failed to compute shift summary.");
+ return;
+ }
+
+ const char* shiftName = (s.window.type == ProductionShiftType::Day) ? "Day" : "Night";
+ LOGI("<ProductionStats>Shift=%s, [%s ~ %s]", shiftName, s.window.startLocal.c_str(), s.window.endLocal.c_str());
+ LOGI("<ProductionStats>Output(pairs): total=%lld, pass=%lld, fail=%lld, no_result=%lld, yield=%.2f%%",
+ s.output.pairsTotal, s.output.pairsPass, s.output.pairsFail, s.output.pairsNoResult, s.output.yield * 100.0);
+ LOGI("<ProductionStats>Takt: avg=%.1fs, samples=%lld", s.output.avgTaktSeconds, s.output.taktSamplePairs);
+ LOGI("<ProductionStats>Alarms: triggered=%d, overlapping=%d, downtime=%.1f min",
+ s.alarms.alarmsTriggered, s.alarms.alarmsOverlapping, s.alarms.downtimeMinutes);
+ if (!s.alarms.bySeverity.empty()) {
+ std::ostringstream oss;
+ oss << "<ProductionStats>AlarmsBySeverity:";
+ for (const auto& kv : s.alarms.bySeverity) oss << " L" << kv.first << "=" << kv.second;
+ LOGI("%s", oss.str().c_str());
+ }
+ LOGI("<ProductionStats>Transfers: finished=%d, avg(create->end)=%.1fs", s.transfers.transfersFinished, s.transfers.avgCreateToEndSeconds);
+ if (!s.transfers.byStatus.empty()) {
+ std::ostringstream oss;
+ oss << "<ProductionStats>TransfersByStatus:";
+ for (const auto& kv : s.transfers.byStatus) oss << " " << kv.first << "=" << kv.second;
+ LOGI("%s", oss.str().c_str());
+ }
+}
+
+bool ProductionStats::ComputeDayNightSummaries(CConfiguration& config, ProductionShiftSummary& outDay, ProductionShiftSummary& outNight)
+{
+ ProductionShiftSummary cur;
+ if (!ComputeCurrentShiftSummary(config, cur)) return false;
+
+ // Determine previous adjacent window for the other shift.
+ ProductionShiftSummary other = cur;
+ if (cur.window.type == ProductionShiftType::Day) {
+ other.window.type = ProductionShiftType::Night;
+ other.window.end = cur.window.start;
+ other.window.start = cur.window.start - (cur.window.end - cur.window.start);
+ }
+ else {
+ other.window.type = ProductionShiftType::Day;
+ other.window.end = cur.window.start;
+ other.window.start = cur.window.start - (cur.window.end - cur.window.start);
+ }
+ other.window.startLocal = FormatLocal(other.window.start);
+ other.window.endLocal = FormatLocal(other.window.end);
+ other.window.startUtcIso = FormatUtcIso(other.window.start);
+ other.window.endUtcIso = FormatUtcIso(other.window.end);
+
+ other.output = ProductionOutputSummary{};
+ other.alarms = ProductionAlarmSummary{};
+ other.transfers = ProductionTransferSummary{};
+ ComputeOutputFromProcessDb(other.window, other.output);
+ ComputeAlarmSummaryFromDb(other.window, other.alarms);
+ ComputeTransferSummaryFromDb(other.window, other.transfers);
+
+ if (cur.window.type == ProductionShiftType::Day) {
+ outDay = std::move(cur);
+ outNight = std::move(other);
+ }
+ else {
+ outNight = std::move(cur);
+ outDay = std::move(other);
+ }
+ return true;
+}
diff --git a/SourceCode/Bond/Servo/ProductionStats.h b/SourceCode/Bond/Servo/ProductionStats.h
new file mode 100644
index 0000000..c884228
--- /dev/null
+++ b/SourceCode/Bond/Servo/ProductionStats.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <chrono>
+#include <map>
+#include <string>
+
+class CConfiguration;
+
+enum class ProductionShiftType : int {
+ Day = 1,
+ Night = 2
+};
+
+struct ProductionShiftWindow {
+ ProductionShiftType type{ ProductionShiftType::Day };
+ std::chrono::system_clock::time_point start{};
+ std::chrono::system_clock::time_point end{};
+ std::string startLocal; // "YYYY-MM-DD HH:MM:SS"
+ std::string endLocal; // "YYYY-MM-DD HH:MM:SS"
+ std::string startUtcIso; // "YYYY-MM-DDTHH:MM:SSZ"
+ std::string endUtcIso; // "YYYY-MM-DDTHH:MM:SSZ"
+};
+
+struct ProductionOutputSummary {
+ long long pairsTotal = 0; // "瀵规暟"
+ long long pairsPass = 0;
+ long long pairsFail = 0;
+ long long pairsNoResult = 0;
+ double yield = 0.0; // pairsPass / (pairsPass + pairsFail), 0 if denom==0
+
+ // Average takt time derived from glass_log.t_start/t_end (per pair, seconds)
+ double avgTaktSeconds = 0.0;
+ long long taktSamplePairs = 0;
+};
+
+struct ProductionAlarmSummary {
+ int alarmsTriggered = 0; // start_time within shift window
+ int alarmsOverlapping = 0; // overlaps shift window (including active)
+ double downtimeMinutes = 0.0; // overlap minutes (best-effort)
+ std::map<int, int> bySeverity; // severity_level -> count (overlapping)
+};
+
+struct ProductionTransferSummary {
+ int transfersFinished = 0; // end_time within shift window
+ std::map<std::string, int> byStatus;
+ double avgCreateToEndSeconds = 0.0;
+};
+
+struct ProductionShiftSummary {
+ ProductionShiftWindow window;
+ ProductionOutputSummary output;
+ ProductionAlarmSummary alarms;
+ ProductionTransferSummary transfers;
+};
+
+class ProductionStats {
+public:
+ static bool GetCurrentShiftWindow(CConfiguration& config, ProductionShiftWindow& outWindow);
+ static bool ComputeCurrentShiftSummary(CConfiguration& config, ProductionShiftSummary& outSummary);
+ static void LogCurrentShiftSummary(CConfiguration& config);
+
+ // Computes "current shift" and its adjacent other shift, so UI can always show Day+Night numbers.
+ // - If current is Day: day=current day shift, night=previous night shift.
+ // - If current is Night: night=current night shift, day=previous day shift.
+ static bool ComputeDayNightSummaries(CConfiguration& config, ProductionShiftSummary& outDay, ProductionShiftSummary& outNight);
+};
diff --git a/SourceCode/Bond/Servo/Servo.cpp b/SourceCode/Bond/Servo/Servo.cpp
index 33f8657..c46246e 100644
--- a/SourceCode/Bond/Servo/Servo.cpp
+++ b/SourceCode/Bond/Servo/Servo.cpp
@@ -1,5 +1,5 @@
-
-// Servo.cpp : 定义应用程序的类行为。
+锘�
+// Servo.cpp : 瀹氫箟搴旂敤绋嬪簭鐨勭被琛屼负銆�
//
#include "stdafx.h"
@@ -17,9 +17,12 @@
#include "MapPosWnd.h"
#include "HmTab.h"
#include "CControlJobManagerDlg.h"
+#include "ToolUnits.h"
+#include "CUserManager2.h"
+#include "AccordionWnd.h"
-// 声明全局变量,用于管理 GDI+ 初始化
+// 澹版槑鍏ㄥ眬鍙橀噺锛岀敤浜庣鐞� GDI+ 鍒濆鍖�
ULONG_PTR g_diplusToken;
GdiplusStartupInput g_diplusStartupInput;
@@ -35,34 +38,36 @@
END_MESSAGE_MAP()
-// CServoApp 构造
+// CServoApp 鏋勯��
CServoApp::CServoApp()
{
- // 支持重新启动管理器
+ // 鏀寔閲嶆柊鍚姩绠$悊鍣�
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
- // TODO: 在此处添加构造代码,
- // 将所有重要的初始化放置在 InitInstance 中
+ // TODO: 鍦ㄦ澶勬坊鍔犳瀯閫犱唬鐮侊紝
+ // 灏嗘墍鏈夐噸瑕佺殑鍒濆鍖栨斁缃湪 InitInstance 涓�
+ m_nVersionNumber = 8;
+ m_strVersionName = _T("1.0.08");
}
-// 唯一的一个 CServoApp 对象
+// 鍞竴鐨勪竴涓� CServoApp 瀵硅薄
CServoApp theApp;
-// CServoApp 初始化
+// CServoApp 鍒濆鍖�
BOOL CServoApp::InitInstance()
{
- // TODO: 调用 AfxInitRichEdit2() 以初始化 richedit2 库。\n" // 如果一个运行在 Windows XP 上的应用程序清单指定要
- // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
- //则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
+ // TODO: 璋冪敤 AfxInitRichEdit2() 浠ュ垵濮嬪寲 richedit2 搴撱�俓n" // 濡傛灉涓�涓繍琛屽湪 Windows XP 涓婄殑搴旂敤绋嬪簭娓呭崟鎸囧畾瑕�
+ // 浣跨敤 ComCtl32.dll 鐗堟湰 6 鎴栨洿楂樼増鏈潵鍚敤鍙鍖栨柟寮忥紝
+ //鍒欓渶瑕� InitCommonControlsEx()銆� 鍚﹀垯锛屽皢鏃犳硶鍒涘缓绐楀彛銆�
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
- // 将它设置为包括所有要在应用程序中使用的
- // 公共控件类。
+ // 灏嗗畠璁剧疆涓哄寘鎷墍鏈夎鍦ㄥ簲鐢ㄧ▼搴忎腑浣跨敤鐨�
+ // 鍏叡鎺т欢绫汇��
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
@@ -71,24 +76,24 @@
AfxEnableControlContainer();
- // 创建 shell 管理器,以防对话框包含
- // 任何 shell 树视图控件或 shell 列表视图控件。
+ // 鍒涘缓 shell 绠$悊鍣紝浠ラ槻瀵硅瘽妗嗗寘鍚�
+ // 浠讳綍 shell 鏍戣鍥炬帶浠舵垨 shell 鍒楄〃瑙嗗浘鎺т欢銆�
CShellManager *pShellManager = new CShellManager;
- // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
+ // 婵�娲烩�淲indows Native鈥濊瑙夌鐞嗗櫒锛屼互渚垮湪 MFC 鎺т欢涓惎鐢ㄤ富棰�
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
- // 标准初始化
- // 如果未使用这些功能并希望减小
- // 最终可执行文件的大小,则应移除下列
- // 不需要的特定初始化例程
- // 更改用于存储设置的注册表项
- // TODO: 应适当修改该字符串,
- // 例如修改为公司或组织名
- SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
+ // 鏍囧噯鍒濆鍖�
+ // 濡傛灉鏈娇鐢ㄨ繖浜涘姛鑳藉苟甯屾湜鍑忓皬
+ // 鏈�缁堝彲鎵ц鏂囦欢鐨勫ぇ灏忥紝鍒欏簲绉婚櫎涓嬪垪
+ // 涓嶉渶瑕佺殑鐗瑰畾鍒濆鍖栦緥绋�
+ // 鏇存敼鐢ㄤ簬瀛樺偍璁剧疆鐨勬敞鍐岃〃椤�
+ // TODO: 搴旈�傚綋淇敼璇ュ瓧绗︿覆锛�
+ // 渚嬪淇敼涓哄叕鍙告垨缁勭粐鍚�
+ SetRegistryKey(_T("搴旂敤绋嬪簭鍚戝鐢熸垚鐨勬湰鍦板簲鐢ㄧ▼搴�"));
- // 本程序文件目录
+ // 鏈▼搴忔枃浠剁洰褰�
TCHAR sDrive[_MAX_DRIVE];
TCHAR sDir[_MAX_DIR];
TCHAR sFilename[_MAX_FNAME], sAppFilename[_MAX_FNAME];
@@ -100,71 +105,77 @@
m_model.setWorkDir((LPTSTR)(LPCTSTR)m_strAppDir);
- // 注册控件
+ // 鐢ㄦ埛鏁版嵁搴撶鐞�
+ CString strDir = m_strAppDir + _T("\\DB");
+ CUserManager2::getInstance().init((LPTSTR)(LPCTSTR)strDir);
+
+
+ // 娉ㄥ唽鎺т欢
CServoGraph::RegisterWndClass();
CVerticalLine::RegisterWndClass();
CHorizontalLine::RegisterWndClass();
CEqsGraphWnd::RegisterWndClass();
CMapPosWnd::RegisterWndClass();
CHmTab::RegisterWndClass();
+ CAccordionWnd::RegisterWndClass();
- // 初始化Rx库
+ // 鍒濆鍖朢x搴�
RX_Init();
HSMS_Initialize();
- // 初始化 GDI+
+ // 鍒濆鍖� GDI+
InitGDIPlus();
- // 初始化 MFC RichEdit 控件
+ // 鍒濆鍖� MFC RichEdit 鎺т欢
AfxInitRichEdit2();
- // 初始化报警管理器
+ // 鍒濆鍖栨姤璀︾鐞嗗櫒
try {
if (!AlarmManager::getInstance().initAlarmTable()) {
- AfxMessageBox("初始化报警管理器失败!");
+ AfxMessageBox("鍒濆鍖栨姤璀︾鐞嗗櫒澶辫触锛�");
return FALSE;
}
}
catch (const std::exception& ex) {
CString errorMsg;
- errorMsg.Format(_T("初始化报警管理器失败:%s"), CString(ex.what()));
+ errorMsg.Format(_T("鍒濆鍖栨姤璀︾鐞嗗櫒澶辫触锛�%s"), CString(ex.what()));
AfxMessageBox(errorMsg, MB_ICONERROR);
return FALSE;
}
- // 初始化搬运记录管理库
+ // 鍒濆鍖栨惉杩愯褰曠鐞嗗簱
try {
if (!TransferManager::getInstance().initTransferTable()) {
- AfxMessageBox("初始化搬运记录管理库设置失败!");
+ AfxMessageBox("鍒濆鍖栨惉杩愯褰曠鐞嗗簱璁剧疆澶辫触锛�");
return FALSE;
}
}
catch (const std::exception& ex) {
CString errorMsg;
- errorMsg.Format(_T("初始化搬运记录管理库设置失败:%s"), CString(ex.what()));
+ errorMsg.Format(_T("鍒濆鍖栨惉杩愯褰曠鐞嗗簱璁剧疆澶辫触锛�%s"), CString(ex.what()));
AfxMessageBox(errorMsg, MB_ICONERROR);
return FALSE;
}
- // 初始化运行日志管理库
+ // 鍒濆鍖栬繍琛屾棩蹇楃鐞嗗簱
try {
if (!SystemLogManager::getInstance().initSystemLogTable()) {
- AfxMessageBox("初始化运行日志管理库失败!");
+ AfxMessageBox("鍒濆鍖栬繍琛屾棩蹇楃鐞嗗簱澶辫触锛�");
return FALSE;
}
}
catch (const std::exception& ex) {
CString errorMsg;
- errorMsg.Format(_T("初始化运行日志管理库失败:%s"), CString(ex.what()));
+ errorMsg.Format(_T("鍒濆鍖栬繍琛屾棩蹇楃鐞嗗簱澶辫触锛�%s"), CString(ex.what()));
AfxMessageBox(errorMsg, MB_ICONERROR);
return FALSE;
}
- // 初始化用户管理库
+ // 鍒濆鍖栫敤鎴风鐞嗗簱
try {
UserManager& userManager = UserManager::getInstance();
#if !defined(_DEBUG)
@@ -175,21 +186,21 @@
}
catch (const std::exception& ex) {
CString errorMsg;
- errorMsg.Format(_T("初始化用户管理库失败:%s"), CString(ex.what()));
+ errorMsg.Format(_T("鍒濆鍖栫敤鎴风鐞嗗簱澶辫触锛�%s"), CString(ex.what()));
AfxMessageBox(errorMsg, MB_ICONERROR);
return FALSE;
}
- // 初始化配方管理库
+ // 鍒濆鍖栭厤鏂圭鐞嗗簱
try {
if (!RecipeManager::getInstance().initRecipeTable()) {
- AfxMessageBox("初始化配方管理库失败!");
+ AfxMessageBox("鍒濆鍖栭厤鏂圭鐞嗗簱澶辫触锛�");
return FALSE;
}
}
catch (const std::exception& ex) {
CString errorMsg;
- errorMsg.Format(_T("初始化配方管理库失败:%s"), CString(ex.what()));
+ errorMsg.Format(_T("鍒濆鍖栭厤鏂圭鐞嗗簱澶辫触锛�%s"), CString(ex.what()));
AfxMessageBox(errorMsg, MB_ICONERROR);
return FALSE;
}
@@ -200,28 +211,28 @@
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
- // TODO: 在此放置处理何时用
- // “确定”来关闭对话框的代码
+ // TODO: 鍦ㄦ鏀剧疆澶勭悊浣曟椂鐢�
+ // 鈥滅‘瀹氣�濇潵鍏抽棴瀵硅瘽妗嗙殑浠g爜
}
else if (nResponse == IDCANCEL)
{
- // TODO: 在此放置处理何时用
- // “取消”来关闭对话框的代码
+ // TODO: 鍦ㄦ鏀剧疆澶勭悊浣曟椂鐢�
+ // 鈥滃彇娑堚�濇潵鍏抽棴瀵硅瘽妗嗙殑浠g爜
}
else if (nResponse == -1)
{
- TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
- TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
+ TRACE(traceAppMsg, 0, "璀﹀憡: 瀵硅瘽妗嗗垱寤哄け璐ワ紝搴旂敤绋嬪簭灏嗘剰澶栫粓姝€�俓n");
+ TRACE(traceAppMsg, 0, "璀﹀憡: 濡傛灉鎮ㄥ湪瀵硅瘽妗嗕笂浣跨敤 MFC 鎺т欢锛屽垯鏃犳硶 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS銆俓n");
}
- // 删除上面创建的 shell 管理器。
+ // 鍒犻櫎涓婇潰鍒涘缓鐨� shell 绠$悊鍣ㄣ��
if (pShellManager != NULL)
{
delete pShellManager;
}
- // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
- // 而不是启动应用程序的消息泵。
+ // 鐢变簬瀵硅瘽妗嗗凡鍏抽棴锛屾墍浠ュ皢杩斿洖 FALSE 浠ヤ究閫�鍑哄簲鐢ㄧ▼搴忥紝
+ // 鑰屼笉鏄惎鍔ㄥ簲鐢ㄧ▼搴忕殑娑堟伅娉点��
return FALSE;
}
@@ -231,42 +242,43 @@
m_model.term();
HSMS_Term();
RX_Term();
+ UX_Shutdown();
- // 清理 GDI+
+ // 娓呯悊 GDI+
TermGDIPlus();
- // 销毁报警表
+ // 閿�姣佹姤璀﹁〃
AlarmManager::getInstance().termAlarmTable();
- // 销毁搬运记录管理库
+ // 閿�姣佹惉杩愯褰曠鐞嗗簱
TransferManager::getInstance().termTransferTable();
- // 销毁运行日志
+ // 閿�姣佽繍琛屾棩蹇�
SystemLogManager::getInstance().termSystemLogTable();
- // 销毁用户表
+ // 閿�姣佺敤鎴疯〃
#if !defined(_DEBUG)
-// 清除 UserManager 的无操作检测
+// 娓呴櫎 UserManager 鐨勬棤鎿嶄綔妫�娴�
UserManager::getInstance().terminateIdleDetection();
KillTimer(1);
#endif
- // 销毁配方表
+ // 閿�姣侀厤鏂硅〃
RecipeManager::getInstance().termRecipeTable();
return CWinApp::ExitInstance();
}
-// 初始化 GDI+
+// 鍒濆鍖� GDI+
void CServoApp::InitGDIPlus()
{
- // 初始化 GDI+ 图形库
+ // 鍒濆鍖� GDI+ 鍥惧舰搴�
GdiplusStartup(&g_diplusToken, &g_diplusStartupInput, NULL);
}
-// 清理 GDI+
+// 娓呯悊 GDI+
void CServoApp::TermGDIPlus()
{
- // 清理 GDI+ 图形库
+ // 娓呯悊 GDI+ 鍥惧舰搴�
GdiplusShutdown(g_diplusToken);
}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index f3f35d6..d24a894 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index 2e09825..f1eff4a 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -222,6 +222,8 @@
<ClInclude Include="..\jsoncpp\include\json\value.h" />
<ClInclude Include="..\jsoncpp\include\json\writer.h" />
<ClInclude Include="..\jsoncpp\lib_json\json_batchallocator.h" />
+ <ClInclude Include="AccordionWnd.h" />
+ <ClInclude Include="AlarmPopupDlg.h" />
<ClInclude Include="CBaseDlg.h" />
<ClInclude Include="CCarrierSlotGrid.h" />
<ClInclude Include="CCarrierSlotSelector.h" />
@@ -233,6 +235,7 @@
<ClInclude Include="CControlJobManagerDlg.h" />
<ClInclude Include="CCustomCheckBox.h" />
<ClInclude Include="CCollectionEvent.h" />
+ <ClInclude Include="CEventEditDlg.h" />
<ClInclude Include="CEquipmentPage3.h" />
<ClInclude Include="CExpandableListCtrl.h" />
<ClInclude Include="CGlassPool.h" />
@@ -240,14 +243,21 @@
<ClInclude Include="ClientListDlg.h" />
<ClInclude Include="CMyStatusbar.h" />
<ClInclude Include="CPageCollectionEvent.h" />
+ <ClInclude Include="CPageCtrlState.h" />
<ClInclude Include="CPageGlassList.h" />
<ClInclude Include="CPageLinkSignal.h" />
+ <ClInclude Include="CPageProdOverview.h" />
<ClInclude Include="CPageReport.h" />
<ClInclude Include="CPageVarialbles.h" />
+ <ClInclude Include="CPageDataVarialbles.h" />
+ <ClInclude Include="CPanelProduction.h" />
+ <ClInclude Include="HmLabel.h" />
+ <ClInclude Include="ProductionStats.h" />
<ClInclude Include="CParam.h" />
<ClInclude Include="CCjPage1.h" />
<ClInclude Include="CProcessDataListDlg.h" />
<ClInclude Include="CReport.h" />
+ <ClInclude Include="CReportEditDlg.h" />
<ClInclude Include="CRobotCmdContainerDlg.h" />
<ClInclude Include="CRobotCmdTestDlg.h" />
<ClInclude Include="CPagePortStatus.h" />
@@ -255,7 +265,12 @@
<ClInclude Include="CRobotTaskDlg.h" />
<ClInclude Include="CSVData.h" />
<ClInclude Include="CServoUtilsTool.h" />
+ <ClInclude Include="CUserManager2.h" />
+ <ClInclude Include="CUserManager2Dlg.h" />
+ <ClInclude Include="CUserEdit2Dlg.h" />
+ <ClInclude Include="CUserXLogDlg.h" />
<ClInclude Include="CVariable.h" />
+ <ClInclude Include="CVariableEditDlg2.h" />
<ClInclude Include="DeviceRecipeParamDlg.h" />
<ClInclude Include="GlassJson.h" />
<ClInclude Include="GlassLogDb.h" />
@@ -280,6 +295,7 @@
<ClInclude Include="InputDialog.h" />
<ClInclude Include="JobSlotGrid.h" />
<ClInclude Include="LoginDlg.h" />
+ <ClInclude Include="LoginDlg2.h" />
<ClInclude Include="MsgDlg.h" />
<ClInclude Include="PageRecipe.h" />
<ClInclude Include="CDoubleGlass.h" />
@@ -435,6 +451,8 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="AccordionWnd.cpp" />
+ <ClCompile Include="AlarmPopupDlg.cpp" />
<ClCompile Include="CBaseDlg.cpp" />
<ClCompile Include="CCarrierSlotGrid.cpp" />
<ClCompile Include="CCarrierSlotSelector.cpp" />
@@ -446,6 +464,7 @@
<ClCompile Include="CControlJobManagerDlg.cpp" />
<ClCompile Include="CCustomCheckBox.cpp" />
<ClCompile Include="CCollectionEvent.cpp" />
+ <ClCompile Include="CEventEditDlg.cpp" />
<ClCompile Include="CEquipmentPage3.cpp" />
<ClCompile Include="CExpandableListCtrl.cpp" />
<ClCompile Include="CGlassPool.cpp" />
@@ -453,14 +472,22 @@
<ClCompile Include="ClientListDlg.cpp" />
<ClCompile Include="CMyStatusbar.cpp" />
<ClCompile Include="CPageCollectionEvent.cpp" />
+ <ClCompile Include="CPageCtrlState.cpp" />
<ClCompile Include="CPageGlassList.cpp" />
<ClCompile Include="CPageLinkSignal.cpp" />
+ <ClCompile Include="CPageProdOverview.cpp" />
<ClCompile Include="CPageReport.cpp" />
<ClCompile Include="CPageVarialbles.cpp" />
+ <ClCompile Include="CPageDataVarialbles.cpp" />
+ <ClCompile Include="ConfigurationProduction.cpp" />
+ <ClCompile Include="CPanelProduction.cpp" />
+ <ClCompile Include="HmLabel.cpp" />
+ <ClCompile Include="ProductionStats.cpp" />
<ClCompile Include="CParam.cpp" />
<ClCompile Include="CCjPage1.cpp" />
<ClCompile Include="CProcessDataListDlg.cpp" />
<ClCompile Include="CReport.cpp" />
+ <ClCompile Include="CReportEditDlg.cpp" />
<ClCompile Include="CRobotCmdContainerDlg.cpp" />
<ClCompile Include="CRobotCmdTestDlg.cpp" />
<ClCompile Include="CPagePortStatus.cpp" />
@@ -468,7 +495,12 @@
<ClCompile Include="CRobotTaskDlg.cpp" />
<ClCompile Include="CSVData.cpp" />
<ClCompile Include="CServoUtilsTool.cpp" />
+ <ClCompile Include="CUserManager2.cpp" />
+ <ClCompile Include="CUserManager2Dlg.cpp" />
+ <ClCompile Include="CUserEdit2Dlg.cpp" />
+ <ClCompile Include="CUserXLogDlg.cpp" />
<ClCompile Include="CVariable.cpp" />
+ <ClCompile Include="CVariableEditDlg2.cpp" />
<ClCompile Include="DeviceRecipeParamDlg.cpp" />
<ClCompile Include="GlassJson.cpp" />
<ClCompile Include="GlassLogDb.cpp" />
@@ -491,6 +523,7 @@
<ClCompile Include="InputDialog.cpp" />
<ClCompile Include="JobSlotGrid.cpp" />
<ClCompile Include="LoginDlg.cpp" />
+ <ClCompile Include="LoginDlg2.cpp" />
<ClCompile Include="MsgDlg.cpp" />
<ClCompile Include="PageRecipe.cpp" />
<ClCompile Include="CDoubleGlass.cpp" />
@@ -641,4 +674,4 @@
<Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index 8c439f7..09bb6e1 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -174,6 +174,7 @@
<ClCompile Include="CReport.cpp" />
<ClCompile Include="CVariable.cpp" />
<ClCompile Include="CPageVarialbles.cpp" />
+ <ClCompile Include="CPageDataVarialbles.cpp" />
<ClCompile Include="CPageReport.cpp" />
<ClCompile Include="CPageCollectionEvent.cpp" />
<ClCompile Include="ProcessJob.cpp" />
@@ -227,6 +228,23 @@
<ClCompile Include="..\DAQBridge\proto\ProtocolCodec.cpp">
<Filter>DAQBridge</Filter>
</ClCompile>
+ <ClCompile Include="ClientListDlg.cpp" />
+ <ClCompile Include="LoginDlg2.cpp" />
+ <ClCompile Include="CUserManager2.cpp" />
+ <ClCompile Include="CUserManager2Dlg.cpp" />
+ <ClCompile Include="CUserEdit2Dlg.cpp" />
+ <ClCompile Include="CUserXLogDlg.cpp" />
+ <ClCompile Include="CVariableEditDlg2.cpp" />
+ <ClCompile Include="CEventEditDlg.cpp" />
+ <ClCompile Include="CReportEditDlg.cpp" />
+ <ClCompile Include="ConfigurationProduction.cpp" />
+ <ClCompile Include="CPanelProduction.cpp" />
+ <ClCompile Include="ProductionStats.cpp" />
+ <ClCompile Include="AccordionWnd.cpp" />
+ <ClCompile Include="CPageProdOverview.cpp" />
+ <ClCompile Include="HmLabel.cpp" />
+ <ClCompile Include="CPageCtrlState.cpp" />
+ <ClCompile Include="AlarmPopupDlg.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AlarmManager.h" />
@@ -248,6 +266,7 @@
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="TerminalDisplayDlg.h" />
+ <ClInclude Include="ProductionStats.h" />
<ClInclude Include="SECSRuntimeManager.h" />
<ClInclude Include="CCLinkPerformance\CCLinkIEControl.h">
<Filter>CCLinkPerformance</Filter>
@@ -406,6 +425,7 @@
<ClInclude Include="CReport.h" />
<ClInclude Include="CVariable.h" />
<ClInclude Include="CPageVarialbles.h" />
+ <ClInclude Include="CPageDataVarialbles.h" />
<ClInclude Include="CPageReport.h" />
<ClInclude Include="CPageCollectionEvent.h" />
<ClInclude Include="ProcessJob.h" />
@@ -497,6 +517,21 @@
<ClInclude Include="..\DAQBridge\DAQConfig.h">
<Filter>DAQBridge</Filter>
</ClInclude>
+ <ClInclude Include="ClientListDlg.h" />
+ <ClInclude Include="LoginDlg2.h" />
+ <ClInclude Include="CUserManager2.h" />
+ <ClInclude Include="CUserManager2Dlg.h" />
+ <ClInclude Include="CUserEdit2Dlg.h" />
+ <ClInclude Include="CUserXLogDlg.h" />
+ <ClInclude Include="CVariableEditDlg2.h" />
+ <ClInclude Include="CEventEditDlg.h" />
+ <ClInclude Include="CReportEditDlg.h" />
+ <ClInclude Include="CPanelProduction.h" />
+ <ClInclude Include="AccordionWnd.h" />
+ <ClInclude Include="CPageProdOverview.h" />
+ <ClInclude Include="HmLabel.h" />
+ <ClInclude Include="CPageCtrlState.h" />
+ <ClInclude Include="AlarmPopupDlg.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Servo.rc" />
@@ -540,4 +575,4 @@
<UniqueIdentifier>{885738f6-3122-4bb9-8308-46b7f692fb13}</UniqueIdentifier>
</Filter>
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.user b/SourceCode/Bond/Servo/Servo.vcxproj.user
index 82c7903..0c03257 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.user
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.user
@@ -7,6 +7,6 @@
<RemoteDebuggerCommand>\\DESKTOP-IODBVIQ\Servo\Debug\Servo.exe</RemoteDebuggerCommand>
<RemoteDebuggerWorkingDirectory>\\DESKTOP-IODBVIQ\Servo\Debug\</RemoteDebuggerWorkingDirectory>
<RemoteDebuggerServerName>DESKTOP-IODBVIQ</RemoteDebuggerServerName>
- <DebuggerFlavor>WindowsRemoteDebugger</DebuggerFlavor>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index 5dac166..8bd8ca8 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/SourceCode/Bond/Servo/ServoDlg.cpp
@@ -16,6 +16,7 @@
#include "CRobotCmdContainerDlg.h"
#include "CRobotCmdTestDlg.h"
#include "LoginDlg.h"
+#include "LoginDlg2.h"
#include "ChangePasswordDlg.h"
#include "UserManagerDlg.h"
#include "SystemLogManagerDlg.h"
@@ -30,6 +31,10 @@
#include "InputDialog.h"
#include "ClientListDlg.h"
#include "CControlJobManagerDlg.h"
+#include "AlarmManager.h"
+#include "CUserManager2.h"
+#include "CUserManager2Dlg.h"
+#include "CUserXLogDlg.h"
#ifdef _DEBUG
@@ -44,7 +49,7 @@
#define TIMER_ID_UPDATE_RUMTIME 2
/* Test */
-#define TIMER_ID_TEST 3
+#define TIMER_ID_LOGIN 3
// 鐢ㄤ簬搴旂敤绋嬪簭鈥滃叧浜庘�濊彍鍗曢」鐨� CAboutDlg 瀵硅瘽妗�
@@ -89,9 +94,11 @@
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_crBkgnd = APPDLG_BACKGROUND_COLOR;
m_hbrBkgnd = nullptr;
+ m_nLeftPanelType = 2;
m_pTerminalDisplayDlg = nullptr;
m_pObserver = nullptr;
m_pPanelMaster = nullptr;
+ m_pPanelProduction = nullptr;
m_pPanelEquipment = nullptr;
m_pPanelAttributes = nullptr;
m_pPageGraph1 = nullptr;
@@ -104,6 +111,8 @@
m_pTopToolbar = nullptr;
m_pMyStatusbar = nullptr;
m_pRobotTaskDlg = nullptr;
+ m_pTab = nullptr;
+ m_pAlarmPopupDlg = nullptr;
}
void CServoDlg::DoDataExchange(CDataExchange* pDX)
@@ -131,12 +140,24 @@
ON_UPDATE_COMMAND_UI(ID_MENU_FILE_SECSTEST, &CServoDlg::OnUpdateMenuFileSecsTest)
ON_COMMAND(ID_MENU_PROJECT_VARIABLE_LIST, &CServoDlg::OnMenuProjectVarialbleList)
ON_UPDATE_COMMAND_UI(ID_MENU_PROJECT_VARIABLE_LIST, &CServoDlg::OnUpdateMenuProjectVarialbleList)
+ ON_COMMAND(ID_MENU_TEST_ALARM_ON, &CServoDlg::OnMenuTestAlarmOn)
+ ON_UPDATE_COMMAND_UI(ID_MENU_TEST_ALARM_ON, &CServoDlg::OnUpdateMenuTestAlarmOn)
+ ON_COMMAND(ID_MENU_TEST_ALARM_OFF, &CServoDlg::OnMenuTestAlarmOff)
+ ON_UPDATE_COMMAND_UI(ID_MENU_TEST_ALARM_OFF, &CServoDlg::OnUpdateMenuTestAlarmOff)
ON_COMMAND(ID_MENU_TEST_MESSAGE_SET, &CServoDlg::OnMenuTestMessageSet)
ON_UPDATE_COMMAND_UI(ID_MENU_TEST_MESSAGE_SET, &CServoDlg::OnUpdateMenuTestMessageSet)
ON_COMMAND(ID_MENU_TEST_MESSAGE_CLEAR, &CServoDlg::OnMenuTestMessageClear)
ON_UPDATE_COMMAND_UI(ID_MENU_TEST_MESSAGE_CLEAR, &CServoDlg::OnUpdateMenuTestMessageClear)
ON_COMMAND(ID_MENU_TOOLS_CLIENT_LIST, &CServoDlg::OnMenuToolsClientList)
ON_UPDATE_COMMAND_UI(ID_MENU_TOOLS_CLIENT_LIST, &CServoDlg::OnUpdateMenuToolsClientList)
+ ON_COMMAND(ID_MENU_TOOLS_CURVE_EMPTY, &CServoDlg::OnMenuToolsCurveEmptyMode)
+ ON_UPDATE_COMMAND_UI(ID_MENU_TOOLS_CURVE_EMPTY, &CServoDlg::OnUpdateMenuToolsCurveEmptyMode)
+ ON_COMMAND(ID_MENU_TOOLS_CURVE_PRODUCTION, &CServoDlg::OnMenuToolsCurveProductionMode)
+ ON_UPDATE_COMMAND_UI(ID_MENU_TOOLS_CURVE_PRODUCTION, &CServoDlg::OnUpdateMenuToolsCurveProductionMode)
+ ON_COMMAND(ID_MENU_WND_TEST_PANEL, &CServoDlg::OnMenuWndTestPanel)
+ ON_UPDATE_COMMAND_UI(ID_MENU_WND_TEST_PANEL, &CServoDlg::OnUpdateMenuWndTestPanel)
+ ON_COMMAND(ID_MENU_WND_PRO_PANEL, &CServoDlg::OnMenuWndProPanel)
+ ON_UPDATE_COMMAND_UI(ID_MENU_WND_PRO_PANEL, &CServoDlg::OnUpdateMenuWndProPanel)
ON_COMMAND(ID_MENU_HELP_ABOUT, &CServoDlg::OnMenuHelpAbout)
ON_WM_INITMENUPOPUP()
ON_WM_TIMER()
@@ -174,7 +195,7 @@
ASSERT(m_pPanelAttributes);
m_pPanelEquipment->loadDataFromEquipment(pEquipment);
m_pPanelAttributes->ShowWindow(SW_HIDE);
- if (!m_pPanelEquipment->IsWindowVisible()) {
+ if (!m_pPanelEquipment->IsWindowVisible() && m_nLeftPanelType == 1) {
m_pPanelEquipment->ShowWindow(SW_SHOW);
Resize();
}
@@ -197,7 +218,7 @@
else if (RX_CODE_MASTER_STATE_CHANGED == code) {
SERVO::MASTERSTATE state = theApp.m_model.getMaster().getState();
if (state == SERVO::MASTERSTATE::READY) {
- m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(TRUE);
+ m_pTopToolbar ->GetBtn(IDC_BUTTON_RUN)->EnableWindow(TRUE);
m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(TRUE);
m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(TRUE);
m_pTopToolbar->GetBtn(IDC_BUTTON_STOP)->EnableWindow(FALSE);
@@ -249,6 +270,27 @@
SetTimer(TIMER_ID_UPDATE_RUMTIME, 500, nullptr);
}
}
+ else if (RX_CODE_CONTROLJOB_CHANGED == code) {
+ auto* cj = theApp.m_model.getMaster().getControlJob();
+ CString text;
+ if (cj != nullptr) {
+ std::string st = cj->getStateText();
+ text.Format(_T("ControlJob: %s (%s)"), cj->id().c_str(), st.c_str());
+ if (cj->state() == SERVO::CJState::Paused) {
+ text += _T(" [Paused]");
+ }
+ }
+ else {
+ text = _T("ControlJob: None");
+ }
+ if (m_pMyStatusbar != nullptr) {
+ m_pMyStatusbar->setJobText((LPTSTR)(LPCTSTR)text);
+ if (cj != nullptr && cj->state() == SERVO::CJState::Paused) {
+ m_pMyStatusbar->setBackgroundColor(STATUSBAR_BK_ALARM);
+ m_pMyStatusbar->setForegroundColor(RGB(0, 0, 0));
+ }
+ }
+ }
else if (RX_CODE_EQ_ROBOT_TASK == code) {
int exCode;
if (pAny->getIntValue("exCode", exCode)) {
@@ -295,6 +337,9 @@
//dlg.DoModal();
}
}
+ else if (RX_CODE_ALARM_SET == code || RX_CODE_ALARM_CLEAR == code) {
+ RefreshAlarmBadge();
+ }
if (RX_CODE_PASSIVE_STATUS_CHANGED == code) {
int state = 0;
@@ -302,18 +347,18 @@
if (STATE::NOT_CONNECTED == state) {
m_pMyStatusbar->setCimBtnText("Disconnected");
- //m_labelPassiveState.setBackground(DISCONNECTED_BACKGROUND);
- //m_labelPassiveState.setForeground(DISCONNECTED_FOREGROUND, TRUE);
+ m_pMyStatusbar->setCimBtnColors(
+ CIM_STATUS_BK_DISCONNECTED, CIM_STATUS_BK_DISCONNECTED, RGB(0, 0, 0));
}
else if (STATE::NOT_SELECTED == state) {
m_pMyStatusbar->setCimBtnText("Not Selected");
- //m_labelPassiveState.setBackground(NOT_SELECTED_BACKGROUND);
- //m_labelPassiveState.setForeground(NOT_SELECTED_FOREGROUND, TRUE);
+ m_pMyStatusbar->setCimBtnColors(
+ CIM_STATUS_BK_DISCONNECTED, CIM_STATUS_BK_DISCONNECTED, RGB(0, 0, 0));
}
else if (STATE::SELECTED == state) {
m_pMyStatusbar->setCimBtnText("Selected");
- //m_labelPassiveState.setBackground(SELECTED_BACKGROUND);
- //m_labelPassiveState.setForeground(SELECTED_FOREGROUND, TRUE);
+ m_pMyStatusbar->setCimBtnColors(
+ CIM_STATUS_BK_SELECTED, CIM_STATUS_BK_SELECTED, RGB(0, 0, 0));
}
}
pAny->release();
@@ -329,9 +374,88 @@
}
}
+void CServoDlg::RefreshAlarmBadge()
+{
+ if (m_pTopToolbar == nullptr) return;
+ auto activeAlarms = AlarmManager::getInstance().getActiveAlarms();
+
+ // 缁存姢鏈鍒楄〃锛氬綋鍓嶆椿璺冧笖鏈湪宸茶闆嗗悎涓殑鎶ヨ
+ std::unordered_set<int> activeIds;
+ m_unreadAlarms.clear();
+ for (const auto& alarm : activeAlarms) {
+ activeIds.insert(alarm.nId);
+ if (m_ackAlarms.find(alarm.nId) == m_ackAlarms.end()) {
+ m_unreadAlarms.push_back(alarm);
+ }
+ }
+ // 绉婚櫎宸茶闆嗗悎涓凡涓嶅啀娲昏穬鐨勫憡璀�
+ for (auto it = m_ackAlarms.begin(); it != m_ackAlarms.end(); ) {
+ if (activeIds.find(*it) == activeIds.end()) {
+ it = m_ackAlarms.erase(it);
+ }
+ else {
+ ++it;
+ }
+ }
+
+ int count = static_cast<int>(m_unreadAlarms.size());
+
+ auto* pBtn = dynamic_cast<CBlButton*>(m_pTopToolbar->GetBtn(IDC_BUTTON_ALARM));
+ if (pBtn != nullptr) {
+ if (count <= 0) {
+ pBtn->SetBadgeNumber(0);
+ pBtn->ShowDotBadge(FALSE, RGB(255, 0, 0));
+ pBtn->StopFlash();
+ }
+ else if (count <= 9) {
+ pBtn->ShowDotBadge(FALSE, RGB(255, 0, 0));
+ pBtn->SetBadgeNumber(count);
+ if (!pBtn->IsFlash()) pBtn->Flash(600);
+ }
+ else {
+ pBtn->SetBadgeNumber(0);
+ pBtn->ShowDotBadge(TRUE, RGB(255, 0, 0));
+ if (!pBtn->IsFlash()) pBtn->Flash(600);
+ }
+ pBtn->EnableWindow(TRUE);
+ }
+}
+
+void CServoDlg::AckAlarm(int alarmId)
+{
+ m_ackAlarms.insert(alarmId);
+ RefreshAlarmBadge();
+}
+
+void CServoDlg::RaiseTestAlarm()
+{
+ theApp.m_model.raiseSoftAlarm(ALID_SOFTWARE_TEST_ALARM, "Test Alarm (Ctrl+Alt+T)");
+}
+
+void CServoDlg::ClearTestAlarm()
+{
+ theApp.m_model.clearSoftAlarm(ALID_SOFTWARE_TEST_ALARM);
+}
+
+void CServoDlg::MarkAlarmsRead()
+{
+ auto* pBtn = dynamic_cast<CBlButton*>(m_pTopToolbar ? m_pTopToolbar->GetBtn(IDC_BUTTON_ALARM) : nullptr);
+ for (const auto& alarm : m_unreadAlarms) {
+ m_ackAlarms.insert(alarm.nId);
+ }
+ m_unreadAlarms.clear();
+ if (pBtn != nullptr) {
+ pBtn->SetBadgeNumber(0);
+ pBtn->ShowDotBadge(FALSE, RGB(255, 0, 0));
+ pBtn->StopFlash();
+ }
+}
+
BOOL CServoDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
+ const ULONGLONG boot_ui_begin = GetTickCount64();
+ CWaitCursor wait; // 鏁翠釜鍒濆鍖栨湡鏄剧ず绛夊緟鍏夋爣锛岄伩鍏嶇敤鎴疯浠ヤ负鍗℃
// 灏嗏�滃叧浜�...鈥濊彍鍗曢」娣诲姞鍒扮郴缁熻彍鍗曚腑銆�
@@ -359,14 +483,27 @@
SetIcon(m_hIcon, FALSE); // 璁剧疆灏忓浘鏍�
+ // 鏈�澶у寲/绐楀彛鏍囬/鐗堟湰鍙�
+ ShowWindow(SW_MAXIMIZE);
+ CString strTitle;
+ strTitle.Format(_T("Bond Master -- V%s(%d)"), theApp.m_strVersionName, theApp.m_nVersionNumber);
+ SetWindowText(strTitle);
+
+
// model init
+ const ULONGLONG boot_model_begin = GetTickCount64();
theApp.m_model.init();
- SetTimer(TIMER_ID_TEST, 1000, nullptr);
+ LOGI("[BOOT][UI] m_model.init finished, cost=%llu ms (since OnInit start %llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_model_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
+ SetTimer(TIMER_ID_LOGIN, 1500, nullptr);
+ LOGI("[BOOT][UI] after model.init, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
// 鑿滃崟
CMenu menu;
menu.LoadMenu(IDR_MENU_APP);
SetMenu(&menu);
+ LOGI("[BOOT][UI] menu loaded, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
// toolbar
@@ -379,25 +516,48 @@
ASSERT(hMenu);
::EnableMenuItem(hMenu, ID_OPEATOR_SWITCH, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
m_pTopToolbar->GetBtn(IDC_BUTTON_JOBS)->EnableWindow(TRUE);
+ LOGI("[BOOT][UI] toolbar created, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
// Tab
+ const ULONGLONG boot_pages_begin = GetTickCount64();
m_pPageGraph1 = new CPageGraph1();
m_pPageGraph1->Create(IDD_PAGE_GRAPH1, this);
+ LOGI("[BOOT][UI] page Graph1 created, cost=%llu ms (elapsed=%llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_pages_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
m_pPageGraph2 = new CPageGraph2();
m_pPageGraph2->Create(IDD_PAGE_GRAPH2, this);
+ LOGI("[BOOT][UI] page Graph2 created, cost=%llu ms (elapsed=%llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_pages_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
m_pPageGlassList = new CPageGlassList();
m_pPageGlassList->Create(IDD_PAGE_GLASS_LIST, this);
+ LOGI("[BOOT][UI] page GlassList created, cost=%llu ms (elapsed=%llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_pages_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
m_pPageRecipe = new CPageRecipe();
m_pPageRecipe->Create(IDD_PAGE_RECIPE, this);
+ LOGI("[BOOT][UI] page Recipe created, cost=%llu ms (elapsed=%llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_pages_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
m_pPageAlarm = new CPageAlarm();
m_pPageAlarm->Create(IDD_DIALOG_ALARM, this);
+ LOGI("[BOOT][UI] page Alarm created, cost=%llu ms (elapsed=%llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_pages_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
m_pPageLog = new CPageLog();
m_pPageLog->Create(IDD_DIALOG_LOG, this);
+ LOGI("[BOOT][UI] page Log created, cost=%llu ms (elapsed=%llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_pages_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
m_pPageTransferLog = new CPageTransferLog();
m_pPageTransferLog->Create(IDD_PAGE_TRANSFER_LOG, this);
+ LOGI("[BOOT][UI] page TransferLog created, cost=%llu ms (elapsed=%llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_pages_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
- CHmTab* m_pTab = CHmTab::Hook(GetDlgItem(IDC_TAB1)->m_hWnd);
+ m_pTab = CHmTab::Hook(GetDlgItem(IDC_TAB1)->m_hWnd);
m_pTab->SetPaddingLeft(20);
m_pTab->SetItemMarginLeft(18);
m_pTab->AddItem("鐘舵�佸浘", FALSE);
@@ -410,21 +570,34 @@
m_pTab->SetCurSel(0);
m_pTab->SetBkgndColor(RGB(222, 222, 222));
ShowChildPage(0);
+ LOGI("[BOOT][UI] pages/tabs created, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
+ // 璇诲彇闈㈡澘瀹�
+ CString strIniFile;
+ strIniFile.Format(_T("%s\\%s.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir, (LPTSTR)(LPCTSTR)theApp.m_strAppFile);
+ int nPanelWidth = GetPrivateProfileInt(_T("App"), _T("MasterPanelWidth"),
+ int((double)GetSystemMetrics(SM_CXSCREEN) * 0.25), (LPTSTR)(LPCTSTR)strIniFile);
m_pPanelMaster = new CPanelMaster();
+ m_pPanelMaster->setPanelWidth(nPanelWidth);
m_pPanelMaster->Create(IDD_PANEL_MASTER, this);
- m_pPanelMaster->ShowWindow(SW_SHOW);
+ m_pPanelProduction = new CPanelProduction();
+ m_pPanelProduction->setPanelWidth(nPanelWidth);
+ m_pPanelProduction->Create(IDD_PANEL_PRODUCTION, this);
+ SetLeftPanelType(m_nLeftPanelType, false);
m_pPanelEquipment = new CPanelEquipment();
m_pPanelEquipment->Create(IDD_PANEL_EQUIPMENT, this);
m_pPanelAttributes = new CPanelAttributes();
m_pPanelAttributes->Create(IDD_PANEL_ATTRIBUTES, this);
+ LOGI("[BOOT][UI] panels created, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
// statusbar
m_pMyStatusbar = new CMyStatusbar();
m_pMyStatusbar->Create(IDD_STATUSBAR, this);
m_pMyStatusbar->ShowWindow(SW_SHOW);
+ m_pMyStatusbar->setJobText("ControlJob: None");
+ LOGI("[BOOT][UI] statusbar created, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
@@ -441,10 +614,30 @@
InitRxWindows();
Resize();
+ // 鍔犺浇鍘嗗彶缂撳瓨鎻愮ず
+ {
+ CWaitCursor wait;
+ if (m_pMyStatusbar != nullptr) {
+ m_pMyStatusbar->setRunTimeText(_T("姝e湪鍔犺浇鍘嗗彶缂撳瓨..."));
+ m_pMyStatusbar->UpdateWindow();
+ }
+ LOGI("[BOOT][UI] before master.init, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
- // 鐩稿綋浜庡欢鏃惰皟鐢╩aster鐨勫垵濮嬪寲
- theApp.m_model.m_master.init();
- theApp.m_model.loadPortParams();
+ // 鐩稿綋浜庡欢鏃惰皟鐢╩aster鐨勫垵濮嬪寲
+ const ULONGLONG boot_master_begin = GetTickCount64();
+ theApp.m_model.m_master.init();
+ LOGI("[BOOT][UI] m_master.init finished, cost=%llu ms (since OnInit start %llu ms)",
+ (unsigned long long)(GetTickCount64() - boot_master_begin),
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
+ theApp.m_model.loadPortParams();
+ }
+
+ // 杩樺師鐘舵�佹爮杩愯鏃堕棿鏄剧ず锛堥伩鍏嶄竴鐩村仠鐣欏湪鈥滄鍦ㄥ姞杞藉巻鍙茬紦瀛�...鈥濓級
+ if (m_pMyStatusbar != nullptr) {
+ CString strText;
+ GetRuntimeFormatText(strText, "");
+ m_pMyStatusbar->setRunTimeText((LPTSTR)(LPCTSTR)strText);
+ }
// 鍒濆鍖杕aster浠ュ悗闇�瑕佹帶浠剁粦瀹氭暟鎹�
@@ -453,9 +646,14 @@
// 鏇存柊鐧诲綍鐘舵��
UpdateLoginStatus();
+ // 鍒濆鍖栨姤璀﹁鏍�
+ RefreshAlarmBadge();
//SystemLogManager::getInstance.log(SystemLogManager::LogType::Info, _T("BondEq鍚姩..."));
//SystemLogManager::getInstance.
+
+ LOGI("[BOOT][UI] OnInitDialog finished, total cost=%llu ms",
+ (unsigned long long)(GetTickCount64() - boot_ui_begin));
return TRUE; // 闄ら潪灏嗙劍鐐硅缃埌鎺т欢锛屽惁鍒欒繑鍥� TRUE
}
@@ -627,10 +825,34 @@
pPage3->Create(IDD_PAGE_VARIABLE);
dlg.addPage(pPage3, "Variable");
+ CPageDataVarialbles* pPage4 = new CPageDataVarialbles();
+ pPage4->Create(IDD_PAGE_VARIABLE);
+ dlg.addPage(pPage4, "DataVariable");
+
dlg.DoModal();
}
void CServoDlg::OnUpdateMenuProjectVarialbleList(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(TRUE);
+}
+
+void CServoDlg::OnMenuTestAlarmOn()
+{
+ RaiseTestAlarm();
+}
+
+void CServoDlg::OnUpdateMenuTestAlarmOn(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(TRUE);
+}
+
+void CServoDlg::OnMenuTestAlarmOff()
+{
+ ClearTestAlarm();
+}
+
+void CServoDlg::OnUpdateMenuTestAlarmOff(CCmdUI* pCmdUI)
{
pCmdUI->Enable(TRUE);
}
@@ -672,6 +894,50 @@
void CServoDlg::OnUpdateMenuToolsClientList(CCmdUI* pCmdUI)
{
pCmdUI->Enable(TRUE);
+}
+
+void CServoDlg::OnMenuToolsCurveEmptyMode()
+{
+ theApp.m_model.getMaster().setCurveMode(SERVO::CurveMode::EmptyChamber);
+}
+
+void CServoDlg::OnUpdateMenuToolsCurveEmptyMode(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(TRUE);
+ pCmdUI->SetCheck(theApp.m_model.getMaster().getCurveMode() == SERVO::CurveMode::EmptyChamber);
+}
+
+void CServoDlg::OnMenuToolsCurveProductionMode()
+{
+ theApp.m_model.getMaster().setCurveMode(SERVO::CurveMode::Production);
+}
+
+void CServoDlg::OnUpdateMenuToolsCurveProductionMode(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(TRUE);
+ pCmdUI->SetCheck(theApp.m_model.getMaster().getCurveMode() == SERVO::CurveMode::Production);
+}
+
+void CServoDlg::OnMenuWndTestPanel()
+{
+ SetLeftPanelType(1);
+}
+
+void CServoDlg::OnUpdateMenuWndTestPanel(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(TRUE);
+ pCmdUI->SetCheck(m_nLeftPanelType == 1);
+}
+
+void CServoDlg::OnMenuWndProPanel()
+{
+ SetLeftPanelType(2);
+}
+
+void CServoDlg::OnUpdateMenuWndProPanel(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(TRUE);
+ pCmdUI->SetCheck(m_nLeftPanelType == 2);
}
void CServoDlg::OnMenuHelpAbout()
@@ -727,6 +993,12 @@
m_pMyStatusbar = nullptr;
}
+ if (m_pAlarmPopupDlg != nullptr) {
+ m_pAlarmPopupDlg->DestroyWindow();
+ delete m_pAlarmPopupDlg;
+ m_pAlarmPopupDlg = nullptr;
+ }
+
if (m_pRobotTaskDlg != nullptr) {
m_pRobotTaskDlg->DestroyWindow();
delete m_pRobotTaskDlg;
@@ -743,6 +1015,12 @@
m_pPanelMaster->DestroyWindow();
delete m_pPanelMaster;
m_pPanelMaster = nullptr;
+ }
+
+ if (m_pPanelProduction != nullptr) {
+ m_pPanelProduction->DestroyWindow();
+ delete m_pPanelProduction;
+ m_pPanelProduction = nullptr;
}
if (m_pPanelEquipment != nullptr) {
@@ -845,9 +1123,16 @@
int nPanelWidth = 0;
- if (m_pPanelMaster != nullptr) {
+ if (m_pPanelMaster != nullptr && ::IsWindow(m_pPanelMaster->GetSafeHwnd())
+ && m_pPanelMaster->IsWindowVisible()) {
nPanelWidth = m_pPanelMaster->getPanelWidth();
m_pPanelMaster->MoveWindow(x, y, nPanelWidth, y2 - y);
+ x += nPanelWidth;
+ }
+
+ if (m_pPanelProduction != nullptr && m_pPanelProduction->IsWindowVisible()) {
+ nPanelWidth = m_pPanelProduction->getPanelWidth();
+ m_pPanelProduction->MoveWindow(x, y, nPanelWidth, y2 - y);
x += nPanelWidth;
}
@@ -880,6 +1165,32 @@
m_pMyStatusbar->MoveWindow(0, y2, rcClient.Width(), STATUSBAR_HEIGHT);
+}
+
+void CServoDlg::SetLeftPanelType(int type, bool resize)
+{
+ if (type != 1 && type != 2) {
+ type = 1;
+ }
+
+ m_nLeftPanelType = type;
+ if (m_pPanelMaster != nullptr) {
+ m_pPanelMaster->ShowWindow(SW_HIDE);
+ }
+ if (m_pPanelProduction != nullptr) {
+ m_pPanelProduction->ShowWindow(SW_HIDE);
+ }
+ if (type == 1 && m_pPanelMaster != nullptr) {
+ m_pPanelMaster->ShowWindow(SW_SHOW);
+ }
+ else if (type == 2 && m_pPanelProduction != nullptr) {
+ m_pPanelProduction->ShowWindow(SW_SHOW);
+ }
+
+ if (resize && ::IsWindow(m_hWnd)) {
+ Resize();
+ DrawMenuBar();
+ }
}
void CServoDlg::OnClose()
@@ -928,11 +1239,23 @@
m_pMyStatusbar->setRunTimeText((LPTSTR)(LPCTSTR)strText);
}
- else if(TIMER_ID_TEST == nIDEvent){
- static __int64 tttt = 0;
- tttt++;
- theApp.m_model.m_hsmsPassive.setVariableValue("CJobSpace", tttt % 10);
- theApp.m_model.m_hsmsPassive.setVariableValue("PJobSpace", tttt % 5);
+ else if(TIMER_ID_LOGIN == nIDEvent){
+ KillTimer(TIMER_ID_LOGIN);
+ if (!CUserManager2::getInstance().isLoggedIn()) {
+ CLoginDlg2 dlg;
+ if (dlg.DoModal() != IDOK) {
+ PostMessage(WM_CLOSE);
+ }
+ else {
+ bool bRet = CUserManager2::getInstance().login((LPTSTR)(LPCTSTR)dlg.m_strUsername,
+ (LPTSTR)(LPCTSTR)dlg.m_strPassword);
+ if (!bRet) {
+ AfxMessageBox("鐧诲綍澶辫触锛岃妫�鏌ョ敤鎴峰悕鎴栧瘑鐮佹槸鍚︽纭紒");
+ PostMessage(WM_CLOSE);
+ }
+ UpdateLoginStatus();
+ }
+ }
}
@@ -942,7 +1265,20 @@
LRESULT CServoDlg::OnPanelResize(WPARAM wParam, LPARAM lParam)
{
int width = (int)wParam;
- // m_pPanel->SetPanelWidth(width);
+
+ if (m_pPanelMaster != nullptr) {
+ m_pPanelMaster->setPanelWidth(width);
+ }
+ if (m_pPanelProduction != nullptr) {
+ m_pPanelProduction->setPanelWidth(width);
+ }
+
+ CString strIniFile, strValue;
+ strIniFile.Format(_T("%s\\%s.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir, (LPTSTR)(LPCTSTR)theApp.m_strAppFile);
+ strValue.Format(_T("%d"), width);
+ WritePrivateProfileString(_T("App"), _T("MasterPanelWidth"),
+ (LPTSTR)(LPCTSTR)strValue, (LPTSTR)(LPCTSTR)strIniFile);
+
Resize();
return 0;
@@ -970,32 +1306,24 @@
void CServoDlg::UpdateLoginStatus()
{
HMENU hMenu = m_pTopToolbar->GetOperatorMenu();
- UserManager& userManager = UserManager::getInstance();
- if (userManager.isLoggedIn())
- {
- ::EnableMenuItem(hMenu, ID_OPEATOR_LOGIN, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
- ::EnableMenuItem(hMenu, ID_OPERATOR_CHANGE_PASSWORD, MF_BYCOMMAND | MF_ENABLED);
+ CUserManager2& userManager = CUserManager2::getInstance();
+ if (userManager.isLoggedIn()) {
::EnableMenuItem(hMenu, ID_OPERATOR_SYSTEM_LOG, MF_BYCOMMAND | MF_ENABLED);
::EnableMenuItem(hMenu, ID_OPEATOR_SWITCH, MF_BYCOMMAND | MF_ENABLED);
- ::EnableMenuItem(hMenu, ID_OPERATOR_LOGOUT, MF_BYCOMMAND | MF_ENABLED);
- if (userManager.getCurrentUserRole() == UserRole::SuperAdmin) {
+ if (userManager.IsAdminCurrent()) {
::EnableMenuItem(hMenu, ID_OPEATOR_USER_MANAGER, MF_BYCOMMAND | MF_ENABLED);
}
else {
::EnableMenuItem(hMenu, ID_OPEATOR_USER_MANAGER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
}
- m_pTopToolbar->SetOperatorBtnText(userManager.getCurrentUser().c_str());
+ m_pTopToolbar->SetOperatorBtnText(userManager.getCurrentUserName().c_str());
}
else {
- ::EnableMenuItem(hMenu, ID_OPEATOR_LOGIN, MF_BYCOMMAND | MF_ENABLED);
- ::EnableMenuItem(hMenu, ID_OPERATOR_CHANGE_PASSWORD, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
::EnableMenuItem(hMenu, ID_OPEATOR_USER_MANAGER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
::EnableMenuItem(hMenu, ID_OPERATOR_SYSTEM_LOG, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
::EnableMenuItem(hMenu, ID_OPEATOR_SWITCH, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
- ::EnableMenuItem(hMenu, ID_OPERATOR_LOGOUT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
-
m_pTopToolbar->SetOperatorBtnText(_T("鏈櫥褰�"));
}
}
@@ -1003,15 +1331,14 @@
LRESULT CServoDlg::OnToolbarBtnClicked(WPARAM wParam, LPARAM lParam)
{
int id = (int)lParam;
- if (id == IDC_BUTTON_RUN || id == IDC_BUTTON_STOP) {
- UserRole emRole = UserManager::getInstance().getCurrentUserRole();
- if (emRole != UserRole::SuperAdmin) {
- AfxMessageBox(_T("褰撳墠鐢ㄦ埛骞堕潪绠$悊鍛橈紒锛侊紒"));
- return 1;
- }
- }
-
if (id == IDC_BUTTON_RUN) {
+ int rc = UX_CanExecute(L"start");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return 0;
+ }
+ UX_RecordAction(L"start");
+
if (theApp.m_model.getMaster().getState() == SERVO::MASTERSTATE::MSERROR) {
AfxMessageBox("褰撳墠鏈夋満鍙板彂鐢熼敊璇紝涓嶈兘鍚姩锛岃纭瑙e喅闂鍚庡啀灏濊瘯閲嶆柊鍚姩锛�");
}
@@ -1048,6 +1375,13 @@
}
}
else if (id == IDC_BUTTON_STOP) {
+ int rc = UX_CanExecute(L"stop");
+ if (rc != 1) {
+ AfxMessageBox("鎿嶄綔鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊浜哄憳锛�");
+ return 0;
+ }
+ UX_RecordAction(L"stop");
+
if (theApp.m_model.getMaster().stop() == 0) {
m_pTopToolbar->GetBtn(IDC_BUTTON_STOP)->EnableWindow(FALSE);
}
@@ -1071,6 +1405,16 @@
dlg.SetEFEM(pEFEM);
dlg.DoModal();
}
+ else if (id == IDC_BUTTON_ALARM) {
+ if (m_pAlarmPopupDlg == nullptr) {
+ m_pAlarmPopupDlg = new CAlarmPopupDlg();
+ m_pAlarmPopupDlg->Create(IDD_DIALOG_POPUP_ALARM, this);
+ m_pAlarmPopupDlg->CenterWindow();
+ }
+ m_pAlarmPopupDlg->RefreshContent();
+ m_pAlarmPopupDlg->ShowWindow(SW_SHOW);
+ MarkAlarmsRead();
+ }
else if (id == IDC_BUTTON_SETTINGS) {
SERVO::CEquipment* pEq = theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
((SERVO::CEFEM*)pEq)->printDebugRobotState();
@@ -1083,6 +1427,27 @@
}
else if (id == IDC_BUTTON_OPERATOR) {
int menuId = (int)wParam;
+ if (menuId == 0) {
+ CUserManager2Dlg dlg;
+ dlg.DoModal();
+ }
+ else if (menuId == 1) {
+ CUserXLogDlg dlg;
+ dlg.DoModal();
+ }
+ else if (menuId == 2) {
+ CLoginDlg2 dlg;
+ if (dlg.DoModal() == IDOK) {
+ bool bRet = CUserManager2::getInstance().login((LPTSTR)(LPCTSTR)dlg.m_strUsername,
+ (LPTSTR)(LPCTSTR)dlg.m_strPassword);
+ if (!bRet) {
+ AfxMessageBox("鐧诲綍澶辫触锛岃妫�鏌ョ敤鎴峰悕鎴栧瘑鐮佹槸鍚︽纭紒");
+ }
+ UpdateLoginStatus();
+ }
+ }
+
+ /*
SystemLogManager& logManager = SystemLogManager::getInstance();
UserManager& userManager = UserManager::getInstance();
if (menuId == 0) {
@@ -1131,6 +1496,7 @@
}
UpdateLoginStatus();
+ */
}
return 0;
@@ -1160,6 +1526,11 @@
return 0;
}
+BOOL CServoDlg::PreTranslateMessage(MSG* pMsg)
+{
+ return CDialogEx::PreTranslateMessage(pMsg);
+}
+
CString& CServoDlg::GetRuntimeFormatText(CString& strText, const char* pszSuffix)
{
ULONGLONG ullRunTime = (ULONGLONG)(theApp.m_model.getMaster().getRunTime() * 0.001);
@@ -1183,4 +1554,4 @@
}
return strText;
-}
\ No newline at end of file
+}
diff --git a/SourceCode/Bond/Servo/ServoDlg.h b/SourceCode/Bond/Servo/ServoDlg.h
index bdc47a4..e05c9e5 100644
--- a/SourceCode/Bond/Servo/ServoDlg.h
+++ b/SourceCode/Bond/Servo/ServoDlg.h
@@ -3,6 +3,8 @@
//
#pragma once
+#include <vector>
+#include <unordered_set>
#include "BlButton.h"
#include "PageLog.h"
#include "PageAlarm.h"
@@ -12,12 +14,18 @@
#include "CPanelMaster.h"
#include "CPanelEquipment.h"
#include "CPanelAttributes.h"
+#include "CPanelProduction.h"
#include "CPageGraph1.h"
#include "CPageGraph2.h"
+#include "HmTab.h"
#include "TopToolbar.h"
#include "CMyStatusbar.h"
#include "CRobotTaskDlg.h"
#include "CPageGlassList.h"
+#include "CPageVarialbles.h"
+#include "CPageDataVarialbles.h"
+#include "AlarmPopupDlg.h"
+#include "AlarmManager.h"
// CServoDlg 瀵硅瘽妗�
@@ -30,10 +38,16 @@
public:
void ShowTerminalText(const char* pszText, unsigned int duration = -1);
+ void AckAlarm(int alarmId);
private:
void InitRxWindows();
+ void RefreshAlarmBadge();
+ void MarkAlarmsRead();
+ void RaiseTestAlarm();
+ void ClearTestAlarm();
void Resize();
+ void SetLeftPanelType(int type, bool resize = true);
void ShowChildPage(int index);
void UpdateLoginStatus();
CString& GetRuntimeFormatText(CString& strText, const char* pszSuffix);
@@ -49,6 +63,10 @@
CPageAlarm* m_pPageAlarm;
CPageLog* m_pPageLog;
CPageTransferLog* m_pPageTransferLog;
+ CAlarmPopupDlg* m_pAlarmPopupDlg;
+ CHmTab* m_pTab;
+ std::vector<AlarmData> m_unreadAlarms;
+ std::unordered_set<int> m_ackAlarms;
// 瀵硅瘽妗嗘暟鎹�
#ifdef AFX_DESIGN_TIME
@@ -64,8 +82,10 @@
HICON m_hIcon;
COLORREF m_crBkgnd;
HBRUSH m_hbrBkgnd;
+ int m_nLeftPanelType; // 1: CPanelMaster, 2: CPanelProduction
CTopToolbar* m_pTopToolbar;
CPanelMaster* m_pPanelMaster;
+ CPanelProduction* m_pPanelProduction;
CPanelEquipment* m_pPanelEquipment;
CPanelAttributes* m_pPanelAttributes;
CMyStatusbar* m_pMyStatusbar;
@@ -94,16 +114,29 @@
afx_msg void OnUpdateMenuFileExit(CCmdUI* pCmdUI);
afx_msg void OnMenuProjectVarialbleList();
afx_msg void OnUpdateMenuProjectVarialbleList(CCmdUI* pCmdUI);
+ afx_msg void OnMenuTestAlarmOn();
+ afx_msg void OnUpdateMenuTestAlarmOn(CCmdUI* pCmdUI);
+ afx_msg void OnMenuTestAlarmOff();
+ afx_msg void OnUpdateMenuTestAlarmOff(CCmdUI* pCmdUI);
afx_msg void OnMenuTestMessageSet();
afx_msg void OnUpdateMenuTestMessageSet(CCmdUI* pCmdUI);
afx_msg void OnMenuTestMessageClear();
afx_msg void OnUpdateMenuTestMessageClear(CCmdUI* pCmdUI);
afx_msg void OnMenuToolsClientList();
afx_msg void OnUpdateMenuToolsClientList(CCmdUI* pCmdUI);
+ afx_msg void OnMenuToolsCurveEmptyMode();
+ afx_msg void OnUpdateMenuToolsCurveEmptyMode(CCmdUI* pCmdUI);
+ afx_msg void OnMenuToolsCurveProductionMode();
+ afx_msg void OnUpdateMenuToolsCurveProductionMode(CCmdUI* pCmdUI);
+ afx_msg void OnMenuWndTestPanel();
+ afx_msg void OnUpdateMenuWndTestPanel(CCmdUI* pCmdUI);
+ afx_msg void OnMenuWndProPanel();
+ afx_msg void OnUpdateMenuWndProPanel(CCmdUI* pCmdUI);
afx_msg void OnMenuHelpAbout();
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg LRESULT OnPanelResize(WPARAM wParam, LPARAM lParam);
afx_msg void OnTabSelChanged(NMHDR* nmhdr, LRESULT* result);
LRESULT OnToolbarBtnClicked(WPARAM wParam, LPARAM lParam);
LRESULT OnStatusbarBtnClicked(WPARAM wParam, LPARAM lParam);
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
};
diff --git a/SourceCode/Bond/Servo/ToolUnits.cpp b/SourceCode/Bond/Servo/ToolUnits.cpp
index 6d5df0d..e3e1dca 100644
--- a/SourceCode/Bond/Servo/ToolUnits.cpp
+++ b/SourceCode/Bond/Servo/ToolUnits.cpp
@@ -7,6 +7,8 @@
#include <ctime>
#include <iomanip>
#include <sstream>
+#include <vector>
+#include <cstdarg>
CToolUnits::CToolUnits()
{
@@ -574,4 +576,65 @@
std::ostringstream oss;
oss << std::put_time(&tm, "%Y%m%d%H%M%S"); // 例:2025-09-15 08:23:07
return oss.str();
-}
\ No newline at end of file
+}
+
+std::wstring CToolUnits::AnsiToWString(const std::string& str)
+{
+ if (str.empty()) return std::wstring();
+
+ int len = ::MultiByteToWideChar(CP_ACP, 0,
+ str.c_str(), -1,
+ nullptr, 0);
+ if (len <= 0) return std::wstring();
+
+ std::wstring ws;
+ ws.resize(len - 1);
+
+ ::MultiByteToWideChar(CP_ACP, 0,
+ str.c_str(), -1,
+ &ws[0], len);
+
+ return ws;
+}
+
+std::string CToolUnits::WStringToAnsi(const std::wstring& wstr)
+{
+ if (wstr.empty()) return std::string();
+
+ int len = ::WideCharToMultiByte(CP_ACP, 0,
+ wstr.c_str(), -1,
+ nullptr, 0,
+ nullptr, nullptr);
+ if (len <= 0) return std::string();
+
+ std::string str;
+ str.resize(len - 1);
+
+ ::WideCharToMultiByte(CP_ACP, 0,
+ wstr.c_str(), -1,
+ &str[0], len,
+ nullptr, nullptr);
+
+ return str;
+}
+std::string CToolUnits::formatString(const char* fmt, ...)
+{
+ if (fmt == nullptr) return std::string();
+
+ char buf[512] = {0};
+ va_list args;
+ va_start(args, fmt);
+ int n = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, fmt, args);
+ va_end(args);
+
+ if (n >= 0 && n < (int)sizeof(buf)) {
+ return std::string(buf);
+ }
+
+ // ?????????????????
+ std::vector<char> tmp((n > 0 ? n + 2 : 1024), 0);
+ va_start(args, fmt);
+ _vsnprintf_s(tmp.data(), tmp.size(), _TRUNCATE, fmt, args);
+ va_end(args);
+ return std::string(tmp.data());
+}
diff --git a/SourceCode/Bond/Servo/ToolUnits.h b/SourceCode/Bond/Servo/ToolUnits.h
index f88788d..0d171c7 100644
--- a/SourceCode/Bond/Servo/ToolUnits.h
+++ b/SourceCode/Bond/Servo/ToolUnits.h
@@ -3,6 +3,8 @@
#include <chrono>
#include <optional>
#include <utility>
+#include <vector>
+#include <cstdarg>
enum class QuickRange { Today, Last7Days, ThisMonth, ThisYear };
using TP = std::chrono::system_clock::time_point;
@@ -60,5 +62,7 @@
const char* fmt = "%Y-%m-%d %H:%M:%S");
static std::string TimePointToLocalStringMs(const std::optional<TP>& tp);
static std::string NowStrSec();
+ static std::wstring AnsiToWString(const std::string& str);
+ static std::string WStringToAnsi(const std::wstring& wstr);
+ static std::string formatString(const char* fmt, ...);
};
-
diff --git a/SourceCode/Bond/Servo/TopToolbar.cpp b/SourceCode/Bond/Servo/TopToolbar.cpp
index 47d5ee4..f2f37dd 100644
--- a/SourceCode/Bond/Servo/TopToolbar.cpp
+++ b/SourceCode/Bond/Servo/TopToolbar.cpp
@@ -8,9 +8,6 @@
#include "Common.h"
-#define SHOW_BATCH 1
-#define SHOW_CT 1
-
// CTopToolbar 对话框
IMPLEMENT_DYNAMIC(CTopToolbar, CDialogEx)
@@ -73,6 +70,11 @@
m_btnOperator.SetMenu(hMenu);
+ CString strIniFile;
+ strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+ bool bEnableRunCT = GetPrivateProfileInt("APP", _T("EnableRunCT"), 0, strIniFile) != 0;
+ m_btnRunCt.ShowWindow(bEnableRunCT ? SW_SHOW : SW_HIDE);
+
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
@@ -127,19 +129,19 @@
x += BTN_WIDTH;
x += 2;
-#ifdef SHOW_BATCH
pItem = GetDlgItem(IDC_BUTTON_RUN_BATCH);
- pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
- x += BTN_WIDTH;
- x += 2;
-#endif
+ if (::IsWindowVisible(pItem->GetSafeHwnd())) {
+ pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
+ x += BTN_WIDTH;
+ x += 2;
+ }
-#ifdef SHOW_CT
pItem = GetDlgItem(IDC_BUTTON_RUN_CT);
- pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
- x += BTN_WIDTH;
- x += 2;
-#endif
+ if (::IsWindowVisible(pItem->GetSafeHwnd())) {
+ pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
+ x += BTN_WIDTH;
+ x += 2;
+ }
pItem = GetDlgItem(IDC_BUTTON_STOP);
pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index 6c9865f..ef26aa5 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ
diff --git a/SourceCode/Bond/Servo/stdafx.h b/SourceCode/Bond/Servo/stdafx.h
index 8e4739a..36ba555 100644
--- a/SourceCode/Bond/Servo/stdafx.h
+++ b/SourceCode/Bond/Servo/stdafx.h
@@ -75,7 +75,7 @@
#include "..\RxWindows1.0\include\RxWindowsLib.h"
#include "..\HSMSSDK\Include\HSMSSDK.h"
-
+#include "..\UserXLibrary\UserXAPI.h"
#ifdef _UNICODE
diff --git a/SourceCode/Bond/USERXLibrary/UserXAPI.h b/SourceCode/Bond/USERXLibrary/UserXAPI.h
new file mode 100644
index 0000000..94e141a
--- /dev/null
+++ b/SourceCode/Bond/USERXLibrary/UserXAPI.h
@@ -0,0 +1,136 @@
+// UserX 用户管理库(C 接口)/ UserX user management library (C API)
+// 覆盖功能 / Features: users, roles, actions, auth, logs(固定 SQLite3 持久化 / SQLite3 persistence)
+
+#pragma once
+
+#if defined(_WIN32)
+# ifdef USERXLIBRARY_EXPORTS
+# define UX_API extern "C" __declspec(dllexport)
+# else
+# define UX_API extern "C" __declspec(dllimport)
+# endif
+#else
+# define UX_API extern "C"
+#endif
+
+#include <wchar.h>
+
+
+#ifdef _COMPILE_AS_LIB
+#warning "compiling as lib!"
+#else
+#ifdef _DEBUG
+#ifndef _WIN64
+#pragma comment(lib, "../USERXLibrary/lib/Win32/Debug/UserXLibrary.lib")
+#else
+#pragma comment(lib, "../USERXLibrary/lib/x64/Debug/UserXLibrary.lib")
+#endif
+#else
+#ifndef _WIN64
+#pragma comment(lib, "../USERXLibrary/lib/Win32/Release/UserXLibrary.lib")
+#else
+#pragma comment(lib, "../USERXLibrary/lib/x64/Release/UserXLibrary.lib")
+#endif
+#endif
+#endif // !BUILD_AS_LIB
+
+
+// 额外错误码 / Extra error code
+#ifndef UX_ERR_BAD_PASSWORD
+#define UX_ERR_BAD_PASSWORD -10 // 密码错误 / bad password
+#endif
+
+// 错误码宏定义 / Error code macros
+#define UX_OK 0 // 成功 / success
+#define UX_ERR_INVALID_ARGS -1 // 参数错误 / invalid arguments
+#define UX_ERR_NOT_FOUND -2 // 未找到 / not found
+#define UX_ERR_EXISTS -3 // 已存在 / already exists
+#define UX_ERR_PERMISSION -4 // 权限不足 / permission denied
+#define UX_ERR_NOT_LOGGED_IN -5 // 未登录 / not logged in
+#define UX_ERR_BUFFER_TOO_SMALL -6 // 缓冲区不足 / buffer too small
+#define UX_ERR_DB -7 // I/O 或数据库错误 / I/O or database error
+#define UX_ERR_NOT_DEFINED -8 // 未定义(如动作不存在)/ not defined
+#define UX_ERR_DB_NOT_EMPTY -9 // 数据库非空(已初始化)/ database not empty
+
+// 返回码 / Return codes(详见上方宏)
+// UX_OK, UX_ERR_INVALID_ARGS, UX_ERR_NOT_FOUND, UX_ERR_EXISTS,
+// UX_ERR_PERMISSION, UX_ERR_NOT_LOGGED_IN, UX_ERR_BUFFER_TOO_SMALL,
+// UX_ERR_DB, UX_ERR_NOT_DEFINED, UX_ERR_DB_NOT_EMPTY
+
+// 初始化:指定数据目录,内部将打开/创建 SQLite 数据库文件 UserX.db
+// Init: provide data directory; opens/creates SQLite DB file UserX.db
+UX_API int UX_Init(const wchar_t* storage_dir);
+
+// 可选:覆盖数据库路径(仅使用第一个参数作为 .db 文件路径,其他忽略)
+// Optional: override DB path (use first arg as .db path; others ignored)
+UX_API int UX_SetStorage(const wchar_t* users_db_path,
+ const wchar_t* /*unused_logs*/,
+ const wchar_t* /*unused_roles*/,
+ const wchar_t* /*unused_actions*/);
+
+// 配置角色(name:level 按行)/ Configure roles from lines "name:level"
+// 仅允许在“空数据库”(roles/users/actions/logs 四表均无数据)时调用;否则返回 -9。
+// Only allowed when DB is empty (roles/users/actions/logs all zero rows); otherwise returns -9.
+// e.g. L"Admin:100\nEngineer:50\nOperator:10\n"
+UX_API int UX_SetRoleDefinitions(const wchar_t* roles_text);
+
+// 获取角色列表(name:level 按行);buffer 为空或不足时返回所需大小
+// Get roles list as lines; returns required size if buffer is null/too small
+UX_API int UX_GetRoles(wchar_t* buffer, int buffer_chars);
+
+// 用户管理 / User management
+UX_API int UX_AddUser(const wchar_t* username,
+ const wchar_t* display_name,
+ const wchar_t* password,
+ const wchar_t* role_name);
+
+UX_API int UX_DeleteUser(const wchar_t* username);
+
+// 更新任意字段(传 nullptr 表示保持不变)/ Update subset; nullptr keeps field
+UX_API int UX_UpdateUser(const wchar_t* username,
+ const wchar_t* new_display_name,
+ const wchar_t* new_password,
+ const wchar_t* new_role_name,
+ int enabled /* -1 keep, 0 disable, 1 enable */);
+
+UX_API int UX_EnableUser(const wchar_t* username, int enabled /*0/1*/);
+UX_API int UX_ResetPassword(const wchar_t* username, const wchar_t* new_password);
+UX_API int UX_RenameUser(const wchar_t* username, const wchar_t* new_display_name);
+
+// 管理员权限:删除所有用户 / Admin-only
+UX_API int UX_DeleteAllUsers();
+
+// 获取用户列表(每行:username,display,level,enabled)/ Query users
+UX_API int UX_GetUsers(wchar_t* buffer, int buffer_chars);
+
+// 认证 / Authentication
+UX_API int UX_Login(const wchar_t* username, const wchar_t* password);
+UX_API void UX_Logout();
+UX_API int UX_IsLoggedIn();
+UX_API void UX_Shutdown(); // 释放资源 / shutdown and free resources
+UX_API int UX_GetCurrentUser(wchar_t* buffer, int buffer_chars);
+
+// 动作与权限 / Actions & permissions
+// 定义/覆盖动作(名称、描述、最低角色)/ Define/overwrite action
+UX_API int UX_DefineAction(const wchar_t* action_name,
+ const wchar_t* description,
+ const wchar_t* min_role_name);
+
+// 可执行返回1,不可执行返回0;负数为错误 / 1 allowed, 0 denied
+UX_API int UX_CanExecute(const wchar_t* action_name);
+
+// 记录动作到日志(时间、用户、动作、描述)/ Record action to logs
+UX_API int UX_RecordAction(const wchar_t* action_name);
+
+// 获取动作列表(每行:name,desc,minlevel)/ Get actions list
+UX_API int UX_GetActions(wchar_t* buffer, int buffer_chars);
+
+// 记录尝试(允许/拒绝)/ record an attempt with allowed flag (1 allowed, 0 denied)
+UX_API int UX_RecordAttempt(const wchar_t* action_name, int allowed /*1/0*/);
+
+// 查询最近 N 条日志(每行:ISO8601时间,用户名,动作,描述)/ Query logs
+UX_API int UX_QueryLogs(int last_n, wchar_t* buffer, int buffer_chars);
+
+// 根据错误码返回文字描述(中文/English)
+// Return a human-readable message for an error code
+UX_API const wchar_t* UX_ErrorMessage(int code);
diff --git a/SourceCode/Bond/x64/Debug/AlarmList.txt b/SourceCode/Bond/x64/Debug/AlarmList.txt
index 69473c8..59dab8f 100644
--- a/SourceCode/Bond/x64/Debug/AlarmList.txt
+++ b/SourceCode/Bond/x64/Debug/AlarmList.txt
@@ -225,3 +225,4 @@
+9000,0,PauseEvent触发
diff --git a/SourceCode/Bond/x64/Debug/CollectionEventList.txt b/SourceCode/Bond/x64/Debug/CollectionEventList.txt
index f955838..b42a9e6 100644
--- a/SourceCode/Bond/x64/Debug/CollectionEventList.txt
+++ b/SourceCode/Bond/x64/Debug/CollectionEventList.txt
@@ -1,18 +1,25 @@
+CEID,CE Name,Descriptions,Attached RPTID
300,AccessMode_To_Manual,,(300)
301,AccessMode_To_Auto,,(301)
600,ControlStateChanged,,(600)
700,ProcessStateChanged,,(700)
+10018,ProcessDataReport,,(33)
+10015,SubEqpStart,,(10015)
+10016,SubEqpEnd,,(10016)
+10017,SubEqpStateChange,,(10017)
10000,RecipeChanged,,(10000)
10030,CarrierArrived,,(10300)
10031,CarrierRemoved,,(10300)
+10032,LoadPortNotAssoc,,(50014)
10040,ReadyToLoad,,(10300)
10041,ReadyToUnLoad,,(10300)
10051,CarrierIDWaitingForHost,,(10051)
10052,CarrierIDVerificationOK,,(10052)
10053,CarrierIDVerificationNG,,(10052)
-10061,SlotMapWaitingForHost,,(10061)
+10061,CheckSlotMap,,(10061)
10062,SlotMapVerificationOK,,(10062)
10063,SlotMapVerificationNG,,(10062)
+10064,SlotMapMismatch,Slot map mismatch between scan and download,(10062)
10071,GlassIDReadWaitingForHost,,(10071)
10072,GlassIDReadVerificationOK,,(10072)
10073,GlassIDReadVerificationNG,,(10072)
@@ -48,4 +55,22 @@
50008,Port_Unload_Ready,,(50008)
50009,Port_Load_Ready,,(50009)
50010,Port_Blocked,,(50010)
-
+50011,OCR_PanelID_Read_OK,OCR read OK and match,(50012)
+50015,OCR_PanelID_Read_NG,OCR read fail (key-in match),(50012)
+50016,OCR_PanelID_Read_Mismatch,OCR read OK but mismatch,(50012)
+50017,OCR_PanelID_Read_NG_Mismatch,OCR read fail and key-in mismatch,(50012)
+50012,Port_Ready_To_Release,,(50013)
+50020,PortStateChange,,(50020)
+50021,GlassReceivedJob,Glass received into equipment slot,(50021)
+50022,GlassSentOutJob,Glass sent out from equipment slot,(50022)
+60000,BonderSVData,,(60000)
+61000,BonderProcessData,,(61000)
+62000,VacuumBakeSVData,,(62000)
+63000,VacuumBakeProcessData,,(63000)
+64000,BakeCoolingSVData,,(64000)
+65000,BakeCoolingProcessData,,(65000)
+66000,MeasurementSVData,,(66000)
+67000,MeasurementProcessData,,(67000)
+12000,UnitStart,,(12000)
+12001,UnitStateChange,,(12001)
+12002,UnitEnd,,(12002)
diff --git a/SourceCode/Bond/x64/Debug/DataVariableList.txt b/SourceCode/Bond/x64/Debug/DataVariableList.txt
new file mode 100644
index 0000000..db4f8a2
--- /dev/null
+++ b/SourceCode/Bond/x64/Debug/DataVariableList.txt
@@ -0,0 +1,98 @@
+DVID,DV Name,DV Format,DV Remark
+6000,Bonder_SV_ProcessStep,A20,Bonder SV: 宸ヨ壓杩愯姝ラ
+6001,Bonder_SV_BladderPressure,A20,Bonder SV: 姘斿泭鍘嬪姏褰撳墠
+6002,Bonder_SV_UpperChamberPressure,A20,Bonder SV: 涓婅厰鍘嬪姏鍚堣
+6003,Bonder_SV_PipeVacuumGauge,A20,Bonder SV: 绠¢亾鐪熺┖琛�
+6004,Bonder_SV_ChamberVacuumGauge,A20,Bonder SV: 鑵斾綋鐪熺┖琛�
+6005,Bonder_SV_UpperTemp1,A20,Bonder SV: 涓婅厰娓╁害1
+6006,Bonder_SV_UpperTemp2,A20,Bonder SV: 涓婅厰娓╁害2
+6007,Bonder_SV_UpperTemp3,A20,Bonder SV: 涓婅厰娓╁害3
+6008,Bonder_SV_UpperTemp4,A20,Bonder SV: 涓婅厰娓╁害4
+6009,Bonder_SV_UpperTemp5,A20,Bonder SV: 涓婅厰娓╁害5
+6010,Bonder_SV_UpperTemp6,A20,Bonder SV: 涓婅厰娓╁害6
+6011,Bonder_SV_LowerTemp1,A20,Bonder SV: 涓嬭厰娓╁害1
+6012,Bonder_SV_LowerTemp2,A20,Bonder SV: 涓嬭厰娓╁害2
+6013,Bonder_SV_LowerTemp3,A20,Bonder SV: 涓嬭厰娓╁害3
+6014,Bonder_SV_LowerTemp4,A20,Bonder SV: 涓嬭厰娓╁害4
+6015,Bonder_SV_LowerTemp5,A20,Bonder SV: 涓嬭厰娓╁害5
+6016,Bonder_SV_LowerTemp6,A20,Bonder SV: 涓嬭厰娓╁害6
+6017,Bonder_SV_HeatingRemaining,A20,Bonder SV: 鍔犵儹鍓╀綑鏃堕棿
+6018,Bonder_SV_PressingRemaining,A20,Bonder SV: 鍘嬪悎鍓╀綑鏃堕棿
+6100,Bonder_PD_AlignDelay,A20,Bonder PD: 瀵逛綅寤舵椂
+6101,Bonder_PD_DwellTime,A20,Bonder PD: 淇濆帇鏃堕棿
+6102,Bonder_PD_BreakVacuumDelay,A20,Bonder PD: 鐮寸湡绌哄欢鏃�
+6103,Bonder_PD_TurboPumpStartDelay,A20,Bonder PD: 鍒嗗瓙娉靛惎鍔ㄥ欢鏃�
+6104,Bonder_PD_AttachVacuumDelay,A20,Bonder PD: 鎶界湡绌哄欢鏃�
+6105,Bonder_PD_HeatingWaitDelay,A20,Bonder PD: 绛夊緟鍔犵儹寤舵椂
+6106,Bonder_PD_BladderPressureSet,A20,Bonder PD: 姘斿泭鍘嬪姏璁惧畾
+6107,Bonder_PD_BladderPressurizeRate,A20,Bonder PD: 姘斿泭鍔犲帇閫熺巼
+6108,Bonder_PD_BladderDepressurizeRate,A20,Bonder PD: 姘斿泭娉勫帇閫熺巼
+6109,Bonder_PD_AttachPressureLimit,A20,Bonder PD: 璐村悎鍘嬪姏涓婇檺
+6110,Bonder_PD_UpperZTorqueSpeed,A20,Bonder PD: 涓婅厰Z棰勮创鍚堥�熷害
+6111,Bonder_PD_UpperTempSet,A20,Bonder PD: 涓婅厰娓╁害璁惧畾
+6112,Bonder_PD_LowerTempSet,A20,Bonder PD: 涓嬭厰娓╁害璁惧畾
+6113,Bonder_PD_PreAttachSpeed,A20,Bonder PD: 棰勮创鍚堥�熷害
+6114,Bonder_PD_AttachSpeed,A20,Bonder PD: 璐村悎閫熷害
+6115,Bonder_PD_UpperHeatDistance,A20,Bonder PD: 涓婅厰鍔犵儹璺濈
+6116,Bonder_PD_AttachPressIn,A20,Bonder PD: 璐村悎鍘嬪叆鍔�
+6117,Bonder_PD_UpperBreakVacuumDist,A20,Bonder PD: 涓婅厰鐮寸湡绌鸿窛绂�
+6118,Bonder_PD_LowerPinBreakVacuumDist,A20,Bonder PD: 涓嬮《閽堢牬鐪熺┖璺濈
+6119,Bonder_PD_LowerPinHeatDistance,A20,Bonder PD: 涓嬮《閽堝姞鐑窛绂�
+6120,Bonder_PD_PumpGaugeSet,A20,Bonder PD: 鐪熺┖娉电湡绌鸿瀹�
+6121,Bonder_PD_TurboReachSet,A20,Bonder PD: 鍒嗗瓙娉靛埌杈捐瀹�
+6200,VacuumBake_SV_A_ProcessStep,A20,VacuumBake SV A: 宸ヨ壓杩愯姝ラ
+6201,VacuumBake_SV_A_ChamberVacuum,A20,VacuumBake SV A: 鑵斾綋鐪熺┖
+6202,VacuumBake_SV_A_Temp1,A20,VacuumBake SV A: 娓╁害1
+6203,VacuumBake_SV_A_Temp2,A20,VacuumBake SV A: 娓╁害2
+6204,VacuumBake_SV_A_Temp4,A20,VacuumBake SV A: 娓╁害4
+6205,VacuumBake_SV_A_Temp5,A20,VacuumBake SV A: 娓╁害5
+6206,VacuumBake_SV_A_Temp6,A20,VacuumBake SV A: 娓╁害6
+6207,VacuumBake_SV_A_Temp7,A20,VacuumBake SV A: 娓╁害7
+6208,VacuumBake_SV_A_BakeRemaining,A20,VacuumBake SV A: 鐑樼儰鍓╀綑鏃堕棿
+6209,VacuumBake_SV_B_ProcessStep,A20,VacuumBake SV B: 宸ヨ壓杩愯姝ラ
+6210,VacuumBake_SV_B_ChamberVacuum,A20,VacuumBake SV B: 鑵斾綋鐪熺┖
+6211,VacuumBake_SV_B_Temp1,A20,VacuumBake SV B: 娓╁害1
+6212,VacuumBake_SV_B_Temp2,A20,VacuumBake SV B: 娓╁害2
+6213,VacuumBake_SV_B_Temp4,A20,VacuumBake SV B: 娓╁害4
+6214,VacuumBake_SV_B_Temp5,A20,VacuumBake SV B: 娓╁害5
+6215,VacuumBake_SV_B_Temp6,A20,VacuumBake SV B: 娓╁害6
+6216,VacuumBake_SV_B_Temp7,A20,VacuumBake SV B: 娓╁害7
+6217,VacuumBake_SV_B_BakeRemaining,A20,VacuumBake SV B: 鐑樼儰鍓╀綑鏃堕棿
+6300,VacuumBake_PD_ParamIndex,A20,VacuumBake PD: 鍙傛暟搴忓彿
+6301,VacuumBake_PD_HeatTime,A20,VacuumBake PD: 鍔犵儹鏃堕棿
+6302,VacuumBake_PD_BreakVacuumTime,A20,VacuumBake PD: 鐮寸湡绌烘椂闂�
+6303,VacuumBake_PD_VacuumReach,A20,VacuumBake PD: 鐪熺┖杈惧埌鍊�
+6304,VacuumBake_PD_TempSet,A20,VacuumBake PD: 涓绘帶娓╁害璁惧畾
+6400,BakeCooling_SV_A_BakeStep,A20,BakeCooling SV A: 鐑樼儰姝ラ
+6401,BakeCooling_SV_A_Temp1,A20,BakeCooling SV A: 娓╁害1
+6402,BakeCooling_SV_A_Temp2,A20,BakeCooling SV A: 娓╁害2
+6403,BakeCooling_SV_A_Temp4,A20,BakeCooling SV A: 娓╁害4
+6404,BakeCooling_SV_A_Temp5,A20,BakeCooling SV A: 娓╁害5
+6405,BakeCooling_SV_A_Temp6,A20,BakeCooling SV A: 娓╁害6
+6406,BakeCooling_SV_A_Temp7,A20,BakeCooling SV A: 娓╁害7
+6407,BakeCooling_SV_A_BakeRemaining,A20,BakeCooling SV A: 鐑樼儰鍓╀綑鏃堕棿
+6408,BakeCooling_SV_A_CoolStep,A20,BakeCooling SV A: 鍐峰嵈姝ラ
+6409,BakeCooling_SV_A_CoolRemaining,A20,BakeCooling SV A: 鍐峰嵈鍓╀綑鏃堕棿
+6410,BakeCooling_SV_B_BakeStep,A20,BakeCooling SV B: 鐑樼儰姝ラ
+6411,BakeCooling_SV_B_Temp1,A20,BakeCooling SV B: 娓╁害1
+6412,BakeCooling_SV_B_Temp2,A20,BakeCooling SV B: 娓╁害2
+6413,BakeCooling_SV_B_Temp4,A20,BakeCooling SV B: 娓╁害4
+6414,BakeCooling_SV_B_Temp5,A20,BakeCooling SV B: 娓╁害5
+6415,BakeCooling_SV_B_Temp6,A20,BakeCooling SV B: 娓╁害6
+6416,BakeCooling_SV_B_Temp7,A20,BakeCooling SV B: 娓╁害7
+6417,BakeCooling_SV_B_BakeRemaining,A20,BakeCooling SV B: 鐑樼儰鍓╀綑鏃堕棿
+6418,BakeCooling_SV_B_CoolStep,A20,BakeCooling SV B: 鍐峰嵈姝ラ
+6419,BakeCooling_SV_B_CoolRemaining,A20,BakeCooling SV B: 鍐峰嵈鍓╀綑鏃堕棿
+6500,BakeCooling_PD_ParamIndex,A20,BakeCooling PD: 鍙傛暟搴忓彿
+6501,BakeCooling_PD_TimeOrTemp1,A20,BakeCooling PD: 鏃堕棿/娓╁害1
+6502,BakeCooling_PD_TimeOrTemp2,A20,BakeCooling PD: 鏃堕棿/娓╁害2
+6503,BakeCooling_PD_Reserved,A20,BakeCooling PD: 棰勭暀
+6600,Measurement_SV_ProcessStep,A20,Measurement SV: 宸ヨ壓杩愯姝ラ
+6601,Measurement_SV_AOIScore,A20,Measurement SV: AOI鍒嗘暟
+6700,Measurement_PD_ParamIndex,A20,Measurement PD: 鍙傛暟搴忓彿
+6701,Measurement_PD_Time,A20,Measurement PD: 鏃堕棿
+6702,Measurement_PD_Value1,A20,Measurement PD: 娴嬮噺鍊�1
+6703,Measurement_PD_Value2,A20,Measurement PD: 娴嬮噺鍊�2
+10200,SlotMap,U1,SlotMap(Scan)
+10201,SlotMapScan,U1,SlotMap(Scan)
+10202,SlotMapDownload,U1,SlotMap(Download)
diff --git a/SourceCode/Bond/x64/Debug/ReportList.txt b/SourceCode/Bond/x64/Debug/ReportList.txt
index ceadc9c..b4d31bc 100644
--- a/SourceCode/Bond/x64/Debug/ReportList.txt
+++ b/SourceCode/Bond/x64/Debug/ReportList.txt
@@ -1,17 +1,23 @@
-RPTID,(VID1,VID2,...)
+锘縍PTID,(VID1,VID2,...)
+# Notes:
+# - RPTIDs 60000-67000, 700, 10017, 20000 include SubEqpName(5018) and SubEqpSlot(5019).
+# - RPTIDs 50008/50009/50010/50013/50014/50020 use PortId(5022).
+# - OCR events (CEID 50011/50015/50016/50017) use RPTID 50012 with VCRPanelID(5014).
+
+33,(5017)
300,(1,300)
301,(1,300)
600,(500,600,601)
-700,(500,700,701)
+700,(500,5018,5019,700,701)
10000,(200,201)
-10300,(1,10000)
-10051,(1,10000,10100,10101)
-10061,(1,10000,10100,10200)
-10062,(1,10000,10100,10201,10202)
-10071,(1,10000,10100,10203,20000)
-10072,(1,10000,10100,10203,20000,20001)
-10080,(1,10000,10100)
-20000,(1,10000,10203)
+10300,(1,10000,10001,10002,10003)
+10051,(1,10000,10001,10002,10003,10100,10101)
+10061,(1,10000,10001,10002,10003,10100,10200)
+10062,(1,10000,10001,10002,10003,10100,10201,10202)
+10071,(1,10000,10001,10002,10003,10100,10203,20000)
+10072,(1,10000,10001,10002,10003,10100,10203,20000,20001)
+10080,(1,10000,10001,10002,10003,10100)
+20000,(1,5018,5019,10000,10001,10002,10003,10203)
30000,(1,30000,30001)
31000,(1,31000,31001)
40000,(1,10203,20000)
@@ -23,8 +29,28 @@
50005,(5007)
50006,(5008)
50007,(5009)
-50008,(5010)
-50009,(5011)
-50010,(5012)
-
+50008,(5022)
+50009,(5022)
+50010,(5022)
+50011,(5013)
+50012,(5014)
+50013,(5022)
+50014,(5022)
+50020,(500,5022,5021)
+50021,(5018,5019,5023)
+50022,(5018,5019,5023)
+60000,(500,5018,5019,6000,6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016,6017,6018)
+61000,(500,5018,5019,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116,6117,6118,6119,6120,6121)
+62000,(500,5018,5019,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210,6211,6212,6213,6214,6215,6216,6217)
+63000,(500,5018,5019,6300,6301,6302,6303,6304)
+64000,(500,5018,5019,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411,6412,6413,6414,6415,6416,6417,6418,6419)
+65000,(500,5018,5019,6500,6501,6502,6503)
+66000,(500,5018,5019,6600,6601)
+67000,(500,5018,5019,6700,6701,6702,6703)
+10015,(5018,5019)
+10016,(5018,5019)
+10017,(500,5018,5019)
+12000,(500,5018,5019)
+12001,(500,5018,5019)
+12002,(500,5018,5019)
diff --git a/SourceCode/Bond/x64/Debug/VariableList.txt b/SourceCode/Bond/x64/Debug/VariableList.txt
index 659b40e..06499c8 100644
--- a/SourceCode/Bond/x64/Debug/VariableList.txt
+++ b/SourceCode/Bond/x64/Debug/VariableList.txt
@@ -1,49 +1,42 @@
SVID,SV Name,SV Format,SV Remark
-100,PortTransferState,U1,0=OutOfService\r\n1=TransferBlocked\r\n2=ReadyToLoad\r\n3=ReadyToUnload\r\n4=InService\r\n5=TransferReady
-300,AccessMode,U1,1=Manual\r\n2=Auto
-500,Clock,A50,
-600,CurrentControlState,U1,0:Offline:equipment\r\n1:Offline-Attempt\r\n2:Online\r\n3:Offline:host\r\n4:Online:Local\r\n5:Online:Remote
-601,PreviousControlState,U1,
-700,CurrentProcessState,U1,0:DOWN\r\n1:IDLE\r\n2.SETUP\r\n3.EXCUTING\r\n4.MAINTAIN\r\n5.ALARM
-701,PreviousProcessState,U1,
-800,EFEMPPExecName,A20,
-801,EQPPExecName,A20,
-2000,RbRAxisTorque,I2,鏈哄櫒浜篟杞存壄鐭�
-2001,RbLAxisTorque,l2,鏈哄櫒浜篖杞存壄鐭�
-2002,RbZAxisTorque,l2,鏈哄櫒浜篫杞存壄鐭�
-2003,RbTHAxisTorque,l2,鏈哄櫒浜篢H杞存壄鐭�
-2004,RbXAxisTorque,l2,鏈哄櫒浜篨杞存壄鐭�
-2005,AxisX111,l2,X111鐩告満鍓嶇Щ鏍界數鏈烘壄鐭�
-2006,AxisX112,l2,X112鐩告満鍚庣Щ鏍界數鏈烘壄鐭�
-2007,AxisU113,l2,U113浜у搧鏃嬭浆鐢垫満鎵煩
-2008,AxisX114,l2,X114浜у搧宸︽暣鍒楃數鏈烘壄鐭�
-2009,AxisY121,l2,Y121浜у搧鍙虫暣鍒楃數鏈烘壄鐭�
-2010,AxisY122,l2,Y122浜у搧鍓嶆暣鍒楃數鏈烘壄鐭�
-2011,AxisY123,l2,Y123浜у搧鍚庨樀鍒楃數鏈烘壄鐭�
-2012,MainAir,U2,鎬昏繘姘斿帇鍔涘��
-2013,MainVacuum,l2,鎬荤湡绌哄帇鍔涘��
-2014,RbMainVacuum,l2,鏈哄櫒浜虹湡绌哄��
-2015,LPMainVacuum,l2,LP鐪熺┖鍊�#D265
-2016,LPMainAir,U2,LP鍘嬬┖鍊�
-2017,ALVacuum,l2,Aligner鐪熺┖鍊�
-2018,FFU1RPM,U2,FFU1杞��
-2019,FFU2RPM,U2,FFU2杞��
-2020,FFU3RPM,U2,FFU3杞��
-2021,FFU4RPM,U2,FFU4杞��
-2022,ESDValue,I2,闈欑數妫�娴嬪��
-2023,OCREnable,U2,"OCR浣胯兘锛歄:寮�鍚� 1锛氬睆钄�"
-2024,CCDEnable,U2,"CCD浣胯兘锛歄:寮�鍚� 1锛氬睆钄�"
-2025,FFUParameter,U2,FFU璁惧畾鍊�
-5000,CarrierID,A20,鍗″專ID
+10000,CarrierID_P1,A50,Carrier ID for Port 1
+10001,CarrierID_P2,A50,Carrier ID for Port 2
+10002,CarrierID_P3,A50,Carrier ID for Port 3
+10003,CarrierID_P4,A50,Carrier ID for Port 4
+100,PortTransferState_P1,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+101,PortTransferState_P2,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+102,PortTransferState_P3,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+103,PortTransferState_P4,U1,1=LoadReady;2=Loaded;3=InUse;4=UnloadReady;5=Empty;6=Blocked
+300,AccessMode_P1,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
+301,AccessMode_P2,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
+302,AccessMode_P3,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
+303,AccessMode_P4,U1,0=OutOfService;1=TransferBlocked;2=ReadyToLoad;3=ReadyToUnload;4=InService;5=TransferReady
+500,Clock,A50,Current timestamp string
+600,CurrentControlState,U1,0=OfflineEquipment;1=OfflineAttempt;2=Online;3=OfflineHost;4=OnlineLocal;5=OnlineRemote
+601,PreviousControlState,U1,Previous control state (same code set as CurrentControlState)
+700,CurrentProcessState,U1,0=Ready;1=Processing;2=Complete;3=Error
+701,PreviousProcessState,U1,Previous process state (0=Ready;1=Processing;2=Complete;3=Error)
+800,EFEMPPExecName,A20,Current PPExec name from EFEM
+801,EQPPExecName,A20,Current PPExec name from equipment
+8100,Bonder1CurrentRecipe,A50,Current recipe for Bonder1
+8101,Bonder2CurrentRecipe,A50,Current recipe for Bonder2
+8102,VacuumBakeCurrentRecipe,A50,Current recipe for VacuumBake
+8103,BakeCoolingCurrentRecipe,A50,Current recipe for BakeCooling
+8104,MeasurementCurrentRecipe,A50,Current recipe for Measurement
+8105,EFEMCurrentRecipe,A50,Current recipe for EFEM
5001,CJobSpace,U1,CJ Space
5002,PJobSpace,U1,PJ Space
-5003,PJQueued,L,PJ Queued
-5004,PJStartID,A20,PJStartID
-5005,PJEndID,A20,PJEndID
-5006,PanelStartID,A20,PanelStartID
-5007,PanelEndID,A20,PanelEndID
-5008,CJStartID,A20,CJStartID
-5009,CJEndID,A20,CJEndID
-5010,UnloadReadyPortId,U2,"Port ID"
-5011,LoadReadyPortId,U2,"Port ID"
-5012,BlockedPortId,U2,"Port ID"
\ No newline at end of file
+5003,PJQueued,L,PJ queued list (IDs)
+5004,PJStartID,A20,PJ start ID
+5005,PJEndID,A20,PJ end ID
+5006,PanelStartID,A20,Panel start ID
+5007,PanelEndID,A20,Panel end ID
+5008,CJStartID,A20,CJ start ID
+5009,CJEndID,A20,CJ end ID
+5014,VCRPanelID,A20,Panel ID from reader
+5017,ProcessDataReportText,A50,EV_PROCESS_DATA_REPORT payload (placeholder)
+5018,SubEqpName,A20,Sub equipment name (SubEqp/Unit/Process/SV/ProcessData events)
+5019,SubEqpSlot,U1,Slot number for SubEqp/Unit; 0 when not applicable
+5021,PortState,U1,Port transfer/state code for PortStateChange
+5022,PortId,U1,Unified port ID for all Port events
+5023,MaterialId,A50,Material/Glass ID for Received/SentOut events
diff --git a/SourceCode/Bond/x64/Debug/test.ini b/SourceCode/Bond/x64/Debug/test.ini
new file mode 100644
index 0000000..bf28634
--- /dev/null
+++ b/SourceCode/Bond/x64/Debug/test.ini
@@ -0,0 +1,10 @@
+[SimEap]
+; Enable/disable simulator (1=on, 0=off)
+Enabled=1
+
+; Step interval in milliseconds
+IntervalMs=2000
+
+; Run a single step once. Change Step value to trigger again.
+; 0 means do nothing.
+Step=1
diff --git a/SourceCode/Bond/x64/Release/AlarmList.txt b/SourceCode/Bond/x64/Release/AlarmList.txt
index 34ad3a9..40bca15 100644
--- a/SourceCode/Bond/x64/Release/AlarmList.txt
+++ b/SourceCode/Bond/x64/Release/AlarmList.txt
@@ -225,3 +225,4 @@
+9000,0,PauseEvent触发
--
Gitblit v1.9.3