c3d188e6c595dcf1f11e105757521e405dcb648b..4a9f803ce6896554706b97fe36e0002d18e1f346
2025-07-16 mrDarker
1. [TaskControl] 添加设备启用状态判断(IsEnabled) 2. 在多个工艺任务(如 bake_to_cooling、bond_to...
4a9f80 对比 | 目录
2025-07-03 mrDarker
Merge branch 'clh' into liuyang
d3118e 对比 | 目录
2025-07-03 mrDarker
1. 重构机器人动画逻辑,采用分帧动画机制,避免在 OnTimer 中阻塞主线程; 2. 动画过程中允许动态中断,确保设备状态变化时可即时响应; 3....
62a4a8 对比 | 目录
2025-07-03 LAPTOP-SNT8I5JK\Boounion
1.人工搬出后实时刷新界面,去掉小绿点;
c55e35 对比 | 目录
2025-07-03 LAPTOP-SNT8I5JK\Boounion
1.增加下载的map和efem扫描的map的比较,如果不一致,则Cassette Process Cancel 并抛出到应用层。一致则Cassette...
62a56d 对比 | 目录
2025-07-02 LAPTOP-SNT8I5JK\Boounion
1.文档整理;
4346aa 对比 | 目录
2025-07-02 LAPTOP-SNT8I5JK\Boounion
1.将OnPanelDataReport相关功能由EFEM移到Aligner; 2.Glass增加最初来源Port...
10f486 对比 | 目录
2025-07-01 LAPTOP-SNT8I5JK\Boounion
1.VCR结果上报,测试OK; 2.Aligner和Measurement的Panel data上报。
276074 对比 | 目录
2025-07-01 LAPTOP-SNT8I5JK\Boounion
1.Port Status 测试; 2.Cassette ctrl cmd测试和修改地址;
e731e0 对比 | 目录
2025-07-01 LAPTOP-SNT8I5JK\Boounion
1.修改Glass的JobDataS的更新逻辑,在收到OnReceiveJob时,机器内未保存和管理Glass, 此时Glass在Arm上,应通过Ar...
aabaff 对比 | 目录
2025-06-30 LAPTOP-SNT8I5JK\Boounion
Merge branch 'liuyang' into clh
e979a3 对比 | 目录
2025-06-30 LAPTOP-SNT8I5JK\Boounion
1.调试后更新优化;
dba7d2 对比 | 目录
2025-06-28 LAPTOP-SNT8I5JK\Boounion
1.在stroedJob和Fetchout Job中增加Port的校验; 2.在物料调度中增加检测物料是否已经处理完成的校验;
2ee440 对比 | 目录
2025-06-28 LAPTOP-SNT8I5JK\Boounion
1.自绘按钮,修改为支持文字在按钮下,或在按钮右。 2.日志页,修改为“包含”和“排除”关键字,以及正则表达式的支持,便于在调试过程中快速观察日志
6747bc 对比 | 目录
2025-06-28 LAPTOP-SNT8I5JK\Boounion
1.BakeCoolingr Slot位置修改; 2.Bonder的Slot绑定信号Path修正; 3.搬送任务,转换到EFEM的Slot和Pos修正...
91ec30 对比 | 目录
2025-06-27 mrDarker
Merge branch 'clh' into liuyang
3b9f1a 对比 | 目录
2025-06-27 mrDarker
1. 防止定时器重复刷新手臂的状态 2. 手臂显示的位置通过配置文件读取
74401e 对比 | 目录
2025-06-27 LAPTOP-SNT8I5JK\Boounion
1.Graph刷新问题; 2.AttributeVector造成的闪退问题;
86074f 对比 | 目录
2025-06-26 LAPTOP-SNT8I5JK\Boounion
Merge branch 'liuyang' into clh
838262 对比 | 目录
2025-06-26 LAPTOP-SNT8I5JK\Boounion
1.Bonder 增加job process start/end report
135d1d 对比 | 目录
2025-06-26 mrDarker
1. 添加定时器获取当前EFEM位置,并且更新(包括手臂是否有片)
637cc6 对比 | 目录
2025-06-26 mrDarker
Merge branch 'clh' into liuyang
adf46e 对比 | 目录
2025-06-26 mrDarker
1. 添加设备位置映射表,并且新增控件移动
790469 对比 | 目录
2025-06-26 LAPTOP-SNT8I5JK\Boounion
6.定时轮询比特位,修改为只轮询EFEM, Bonder1, Bonder2, BakeCooling, VacuuumBake, Measurent...
b54cb6 对比 | 目录
2025-06-26 mrDarker
Merge branch 'clh' into liuyang
da96e6 对比 | 目录
2025-06-25 LAPTOP-SNT8I5JK\Boounion
1.Reveive信息获取错误,已修正;
4a17d9 对比 | 目录
2025-06-25 LAPTOP-SNT8I5JK\Boounion
1.日志窗口的自动流动模式和固定模式自动切换;
a4f4d9 对比 | 目录
2025-06-24 LAPTOP-SNT8I5JK\Boounion
1.与EFEM对接调试Port Command(Type Change、Mode Change、Transfer Mode Change、Enable...
d1aa8f 对比 | 目录
2025-06-23 mrDarker
1. 添加CC-Link长整型读写,并修复需要曾整型读取的地方
3afa7d 对比 | 目录
2025-06-23 mrDarker
Merge branch 'clh' into liuyang
417d38 对比 | 目录
2025-06-23 LAPTOP-SNT8I5JK\Boounion
1.配方列表,搜索框,UI调整,未添加搜索功能;
56d92f 对比 | 目录
2025-06-23 mrDarker
1. 在配置文件中添加设备名称 2. 配方绑定界面显示需要的设备ID和设备名称
81d64c 对比 | 目录
2025-06-23 mrDarker
1. 配方管理类添加关键字查询(PPID和描述)
bfc9e3 对比 | 目录
已添加6个文件
已修改71个文件
3052 ■■■■ 文件已修改
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Document/ESWIN_EAS_Bonder_Inline_Mapping_Address_v1.1.7(5).xlsx 补丁 | 查看 | 原始文档 | blame | 历史
Document/SECS/GlassID_C12M0001A_Trace.csv 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Document/SECS/Port_MaterialFlow_Trace.csv 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Document/SECS/SECS_CEID_Format_Table.csv 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Document/SECS/Standard_Material_Flow_23Steps.csv 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Document/SECS通讯整理.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/BlButton.cpp 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/BlButton.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CAligner.cpp 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CAligner.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CArm.cpp 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CArm.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CBakeCooling.cpp 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CBakeCooling.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CBonder.cpp 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.cpp 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.h 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEFEM.cpp 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEFEM.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEqAlarmStep.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEqModeStep.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEqVcrEventStep.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.cpp 460 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.h 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipmentPage1.cpp 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CGlass.cpp 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CGlass.h 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CLoadPort.cpp 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CLoadPort.h 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMaster.cpp 175 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMaster.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMeasurement.cpp 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageCassetteCtrlCmd.cpp 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph1.cpp 388 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph1.h 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph2.cpp 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageLinkSignal.cpp 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageLinkSignal.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPagePortProperty.cpp 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPath.cpp 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPath.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPortStatusReport.cpp 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPortStatusReport.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CRecipeList.cpp 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CRecipesManager.cpp 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CRecipesManager.h 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CRobotTask.cpp 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CStep.cpp 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CStep.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CVacuumBake.cpp 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CVcrEventReport.cpp 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Common.h 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Configuration.cpp 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Configuration.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/LogEdit.cpp 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/LogEdit.h 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Model.cpp 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/MsgDlg.cpp 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/MsgDlg.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PageLog.cpp 62 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PageLog.h 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PageRecipe.cpp 281 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PageRecipe.h 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PageTransferLog.cpp 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeDeviceBindDlg.h 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeManager.cpp 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeManager.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.rc 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoCommo.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.cpp 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoGraph.cpp 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoGraph.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/resource.h 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/x64/Debug/Res/logcat_include.ico 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -54,3 +54,4 @@
Document/共享文件夹.rar
SourceCode/Bond/x64/Debug/Master.dat
SourceCode/Bond/x64/Debug/Config/signals.csv
SourceCode/Bond/x64/Debug/Config/robot_offset.ini
Document/ESWIN_EAS_Bonder_Inline_Mapping_Address_v1.1.7(5).xlsx
Binary files differ
Document/SECS/GlassID_C12M0001A_Trace.csv
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
时间,CEID,事件名称,Port,Glass ID,附加信息
14:12:24.903,3000,Lot ä¸ŠæŠ¥,3,C12M0001A,首次进片
14:12:47.008,3030,Slot Map,3,C12M0001A,Slot çŠ¶æ€ U1x8
14:12:47.849,S3F17,ProceedWithCarrier,3,C12M0001A,准备载具信息
14:12:47.865,4022,VID错误上报,-,C12M0001A.1,ERR[UndefinedVID]
14:13:06.204,S16F15,ProcessProgram下载,-,C12M0001A,下载 PJ_C12M0001A_012F
14:13:07.782,S14F9,ControlJob定义,-,C12M0001A,CJ_C12M0001A_012E
14:13:08.204,3040,Job Start,3,C12M0001A,-
14:13:08.282,4011,异常上报,-,C12M0001A.1,ERR[UndefinedVID]
14:13:08.454,10005,报警,-,A1F8A00412.1,EFEM + UndefinedVID
14:54:14.072,3041,Job Finish,3,C12M0001A,加工完成阶段
14:54:14.072,4012,报警/错误,-,C12M0001A.4,ERR[UndefinedVID]
14:54:14.072,2004,Program完成,-,PJ_C12M0001A_012F,Step=4
Document/SECS/Port_MaterialFlow_Trace.csv
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
时间,CEID,事件,Port,Lot/Glass ID,备注
13:50:52.461,3000,Lot ä¸ŠæŠ¥,3,C12M0001A,初始进片
14:12:24.903,3000,Lot ä¸ŠæŠ¥,3,C12M0001A,再次上报
14:12:46.992,3140,Port Mapping,3,LP3,Port3 æ˜ å°„ LP3
14:12:46.992,3102,Load Request,3,-,加载请求
14:12:46.992,3150,Unload Request,3,-,卸载请求
14:13:08.266,3040,Job Start,3,C12M0001A,工艺开始
14:54:14.072,3041,Job Finish,3,C12M0001A,工艺完成
14:54:14.072,4012,报警上报,-,C12M0001A,报警
14:54:14.072,2004,程序完成,-,PJ_C12M0001A_012F,Step=4
Document/SECS/SECS_CEID_Format_Table.csv
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
CEID,事件名称,数据结构说明
3000,Lot ä¸ŠæŠ¥,"1:时间戳, 4:Port号, 5:Lot ID"
3401,Job å¼€å§‹,"1:时间戳, 4:Port号, 15:Step"
3402,Job å®Œæˆé˜¶æ®µ1,"1:时间戳, 4:Port号, 15:Step"
3403,Job å®Œæˆé˜¶æ®µ2,"1:时间戳, 4:Port号, 15:Step"
3404,Job å®Œæˆé˜¶æ®µ3,"1:时间戳, 4:Port号, 15:Step, 5:Lot ID"
3030,Slot Map,6:<U1数组> è¡¨ç¤ºå ä½çŠ¶æ€
3140,Port Mapping,"50005:<A""LPx"">"
3102,Load è¯·æ±‚,无内容
3150,Unload è¯·æ±‚,无内容
3151,Unload å®Œæˆ,无内容
3517,Carrier ç§»åЍ䏭(1),50004:<U1>
3518,Carrier ç§»åЍ䏭(2),50004:<U1>
3519,Carrier ç§»åŠ¨å®Œæˆ,无内容
3522,Process Done?,无内容
4022/4023,报警/异常,"11:模块, 12:错误代码"
10004,报警上报,"11:错误ID, 18:模块或代码"
Document/SECS/Standard_Material_Flow_23Steps.csv
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
步骤编号,事件名称,协议消息,补充说明,是否设备侧(Y/N)
1,Material Arrived (TransferBlock),E87_06,物料到达 Port,Y
2,CarrierID Readed,E87_03,CarrierID è¯»å–,格式与长度限制,Y
3,Query CJ Space,S1F3/S1F4,Host æŸ¥è¯¢æ˜¯å¦æœ‰ç©ºé—² CJ,N
4,Query PJ Space,S16F21/S16F22,Host æŸ¥è¯¢æ˜¯å¦æœ‰ç©ºé—² PJ,N
5,Query PPID List,S7F19/S7F20,检查是否有 PPID è®¾ç½®ã€è·¯å¾„、格式限制,N
6,ProceedWithCarrier,S3F17,允许搬入 Carrier,N
7,Check SlotMap,E87_14,检查 Slot å ä½çŠ¶æ€,Y
8,ProceedWithSlotMap,S3F17,传入 SlotMap、LotID、PanelID,N
9,SlotMap Verify OK,E87_15,SlotMap éªŒè¯é€šè¿‡,Y
10,Create PJ,S16F15,创建 Process Job,可能含自定义 PanelID,N
11,PJ Queued,E40_01,PJ å·²æŽ’队,Y
12,Create CJ,S14F9/S14F10,创建 Control Job,N
13,CJ Start,E94_07,CJ å¯åЍ,Y
14,PJ Start,E40_04,PJ å¼€å§‹,Y
15,OCR,OCR_PanelID_Read_OK,读取物料 ID,Y
16,Panel Start,E90_11,开始加工 Panel,Y
17,Panel End,E90_12,加工完成,异常需报 WaferEnd,Y
18,PJ End,E40_06,PJ å¤„理结束,Y
19,CJ End,E94_10,CJ å®Œç»“,Y
20,Ready to Release,E87_19,可释放 Carrier,Y
21,Carrier Release,S3F17,Host ä¸‹æ–™åŠ¨ä½œ,N
22,Ready to Unload,E87_09,可卸载,Y
23,Material Removed,E87_08,物料已取走,Y
Document/SECSͨѶÕûÀí.xlsx
Binary files differ
SourceCode/Bond/Servo/BlButton.cpp
@@ -36,6 +36,7 @@
    m_hIcon[1] = nullptr;
    m_nIconWidth = 0;
    m_nFlashState = 0;
    m_bTextRight = FALSE;
}
@@ -159,6 +160,11 @@
    return m_nFlashState != 0;
}
void CBlButton::SetTextRight()
{
    m_bTextRight = TRUE;
}
void CBlButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    HDC hDC = lpDrawItemStruct->hDC;
@@ -200,15 +206,24 @@
    HICON hIcon = this->IsWindowEnabled() ? m_hIcon[0] : m_hIcon[01];
    if (hIcon != nullptr) {
        int xIcon = (rcClient.right - rcClient.top - m_nIconWidth) / 2;
        if (m_bTextRight) {
            xIcon = 15;
        }
        if (m_hMenu != nullptr) xIcon -= 10;
        int yIcon = (rcClient.bottom - rcClient.top - m_nIconWidth) / 2;
        if (nTextLen != 0) {
        if (nTextLen != 0 && !m_bTextRight) {
            yIcon -= 8;
        }
        DrawIconEx(hDC, xIcon, yIcon,
            hIcon, m_nIconWidth, m_nIconWidth, 0, 0, DI_NORMAL);
        rcText.top = yIcon + m_nIconWidth + 2;
        if (m_bTextRight) {
            rcText.left = xIcon + m_nIconWidth + 2;
        }
        else {
            rcText.top = yIcon + m_nIconWidth + 2;
        }
    }
@@ -223,18 +238,19 @@
    ::SetBkMode(hDC, TRANSPARENT);
    ::SetTextColor(hDC, m_crText[state]);
    UINT format1 = m_bTextRight ? DT_LEFT : DT_CENTER;
    if ((BS_MULTILINE & GetStyle()) == BS_MULTILINE) {
        CRect rcBound;
        int height = DrawTextA(hDC, szText, (int)strlen(szText), &rcBound,  DT_CENTER | DT_CALCRECT | DT_EDITCONTROL);
        rcText.top = rcBound.top + (rcClient.bottom - rcClient.top - height) / 2;
        rcText.bottom = rcText.top + height;
        DrawTextA(hDC, szText, (int)strlen(szText), &rcText,  DT_CENTER | DT_EDITCONTROL);
        DrawTextA(hDC, szText, (int)strlen(szText), &rcText, format1 | DT_EDITCONTROL);
    }
    else {
        if (m_hMenu != nullptr) {
            rcText.right -= (10);
        }
        DrawTextA(hDC, szText, (int)strlen(szText), &rcText, DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
        DrawTextA(hDC, szText, (int)strlen(szText), &rcText, format1 | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
    }
    ::SelectObject(hDC, hOldFont);
SourceCode/Bond/Servo/BlButton.h
@@ -53,6 +53,7 @@
    void Flash(int ms);
    void StopFlash();
    BOOL IsFlash();
    void SetTextRight();
private:
    BOOL CustomBitBlt(HDC hDC, LPRECT lprc, CString& strBkgndBmp, int nFrame, int nAllFrame,
@@ -78,6 +79,7 @@
    HICON m_hIcon[2];
    int m_nIconWidth;
    int m_nFlashState;        // é—ªçƒçŠ¶æ€ï¼Œ0:不闪;1和2为闪烁切换中
    BOOL m_bTextRight;
public:
    virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);
SourceCode/Bond/Servo/CAligner.cpp
@@ -48,6 +48,34 @@
        m_slot[0].setName("Slot 1");
    }
    void CAligner::initSteps()
    {
        CEquipment::initSteps();
        {
            // Panel Data Report
            CEqReadStep* pStep = new CEqReadStep(0x617f, 386 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodePanelDataReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_PANEL_DATA_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0x15e);
            if (addStep(STEP_ID_PANEL_DATA_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
    }
    void CAligner::onReceiveLBData(const char* pszData, size_t size)
    {
        __super::onReceiveLBData(pszData, size);
        CHECK_READ_STEP_SIGNAL(STEP_ID_PANEL_DATA_REPORT, pszData, size);
    }
    void CAligner::onTimer(UINT nTimerid)
    {
        CEquipment::onTimer(nTimerid);
SourceCode/Bond/Servo/CAligner.h
@@ -16,6 +16,8 @@
        virtual void term();
        virtual void initPins();
        virtual void initSlots();
        virtual void initSteps();
        virtual void onReceiveLBData(const char* pszData, size_t size);
        virtual void onTimer(UINT nTimerid);
        virtual void serialize(CArchive& ar);
        virtual void getAttributeVector(CAttributeVector& attrubutes);
SourceCode/Bond/Servo/CArm.cpp
@@ -102,4 +102,33 @@
        return 0;
    }
    int CArm::glassUpdateJobDataS(CJobDataS* pJobDataS)
    {
        ASSERT(pJobDataS);
        Lock();
        CGlass* pGlass = (CGlass*)m_slot[0].getContext();
        if (pGlass == nullptr) {
            Unlock();
            LOGE("<CArm>glassUpdateJobDataS失败,找不到对应的Glass");
            return -1;
        }
        CJobDataS* pSrcJs = pGlass->getJobDataS();
        if (pSrcJs->getCassetteSequenceNo() != pJobDataS->getCassetteSequenceNo()
            || pSrcJs->getJobSequenceNo() != pJobDataS->getJobSequenceNo()) {
            Unlock();
            LOGE("<CArm>glassUpdateJobDataS失败,CassetteNo不匹配([%d,%d] != [%d,%d])",
                pSrcJs->getCassetteSequenceNo(),
                pJobDataS->getCassetteSequenceNo(),
                pSrcJs->getJobSequenceNo(),
                pJobDataS->getJobSequenceNo());
            return -2;
        }
        pGlass->updateJobDataS(pJobDataS);
        Unlock();
        return 0;
    }
}
SourceCode/Bond/Servo/CArm.h
@@ -26,6 +26,10 @@
        // è°ƒç”¨tempFetchOut后,pGlass必须release一次
        int tempFetchOut(OUT CGlass*& pGlass);
        // æ›´æ–°JobDataS
        // Equipment在onReceivedJob事件时调用
        int glassUpdateJobDataS(CJobDataS* pJobDataS);
    };
}
SourceCode/Bond/Servo/CBakeCooling.cpp
@@ -239,7 +239,7 @@
        {
            // Received Job Report Upstream #1~9
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 4; i++) {
                CEqReadStep* pStep = new CEqReadStep(0x10c90 + 320 * i, 320 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
@@ -263,13 +263,13 @@
        {
            // Sent Out Job Report Downstream #1~9
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 4; i++) {
                CEqReadStep* pStep = new CEqReadStep(0x10000 + 320 * i, 320 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
                            int port = (int)(__int64)((CEqReadStep*)pFrom)->getProp("Port");
                            if (port > 0) {
                                decodeReceivedJobReport((CStep*)pFrom, port, pszData, size);
                                decodeSentOutJobReport((CStep*)pFrom, port, pszData, size);
                            }
                        }
                        return -1;
@@ -287,7 +287,7 @@
        {
            // Fetched Out Job Report #1~15
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 4; i++) {
                CEqReadStep* pStep = new CEqReadStep(0x11c31 + 18 * i, 18 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
@@ -311,7 +311,7 @@
        {
            // Stored Job Report #1~15
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 4; i++) {
                CEqReadStep* pStep = new CEqReadStep(0x11b23 + 18 * i, 18 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
@@ -331,6 +331,38 @@
                }
            }
        }
        // process start/end report
        {
            CEqReadStep* pStep = new CEqReadStep(0x11D3F, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessStartReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_START_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0x933);
            if (addStep(STEP_ID_JOB_PROCESS_START_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
        {
            CEqReadStep* pStep = new CEqReadStep(0x11D4C, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessEndReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_END_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0x934);
            if (addStep(STEP_ID_JOB_PROCESS_END_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
    }
    // å¿…须要实现的虚函数,在此初始化Slot信息
@@ -344,18 +376,18 @@
        m_slot[1].enable();
        m_slot[1].setPosition(m_nID);
        m_slot[1].setNo(2);
        m_slot[1].setName("Bake 2");
        m_slot[1].setLinkSignalPath(0);
        m_slot[1].setName("Cooling 1");
        m_slot[1].setLinkSignalPath(1);
        m_slot[2].enable();
        m_slot[2].setPosition(m_nID);
        m_slot[2].setNo(3);
        m_slot[2].setName("Cooling 1");
        m_slot[2].setLinkSignalPath(1);
        m_slot[2].setName("Bake 2");
        m_slot[2].setLinkSignalPath(2);
        m_slot[3].enable();
        m_slot[3].setPosition(m_nID);
        m_slot[3].setNo(4);
        m_slot[3].setName("Cooling 2");
        m_slot[3].setLinkSignalPath(1);
        m_slot[3].setLinkSignalPath(3);
    }
    void CBakeCooling::onTimer(UINT nTimerid)
@@ -382,4 +414,11 @@
    {
        return 25000;
    }
    bool CBakeCooling::isSlotProcessed(int slot)
    {
        CGlass* pGlass = getGlassFromSlot(slot);
        if (pGlass == nullptr) return false;
        return pGlass->isProcessed(m_nID, getSlotUnit(slot));
    }
}
SourceCode/Bond/Servo/CBakeCooling.h
@@ -22,7 +22,8 @@
        virtual void getAttributeVector(CAttributeVector& attrubutes);
        virtual int recvIntent(CPin* pPin, CIntent* pIntent);
        virtual int getIndexerOperationModeBaseValue();
        virtual short getSlotUnit(short slot) { return slot <= 2 ? 0 : 1; };
        virtual short getSlotUnit(short slotNo) { return slotNo % 2 == 1 ? 0 : 1; };
        virtual bool isSlotProcessed(int slot);
    };
}
SourceCode/Bond/Servo/CBonder.cpp
@@ -244,7 +244,7 @@
        {
            // Received Job Report Upstream #1~9
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 2; i++) {
                CEqReadStep* pStep = new CEqReadStep((m_nIndex == 0 ? 0x8c90 : 0xcc90) + 320 * i, 320 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
@@ -267,13 +267,13 @@
        {
            // Sent Out Job Report Downstream #1~9
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 2; i++) {
                CEqReadStep* pStep = new CEqReadStep((m_nIndex == 0 ? 0x8000 : 0xc000) + 320 * i, 320 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
                            int port = (int)(__int64)((CEqReadStep*)pFrom)->getProp("Port");
                            if (port > 0) {
                                decodeReceivedJobReport((CStep*)pFrom, port, pszData, size);
                                decodeSentOutJobReport((CStep*)pFrom, port, pszData, size);
                            }
                        }
                        return -1;
@@ -291,7 +291,7 @@
        {
            // Fetched Out Job Report #1~15
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 2; i++) {
                CEqReadStep* pStep = new CEqReadStep((m_nIndex == 0 ? 0x9c31 : 0xdc31) + 18 * i, 18 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
@@ -315,7 +315,7 @@
        {
            // Stored Job Report #1~15
            char szBuffer[256];
            for (int i = 0; i < 1; i++) {
            for (int i = 0; i < 2; i++) {
                CEqReadStep* pStep = new CEqReadStep((m_nIndex == 0 ? 0x9b23 : 0xdb23) + 18 * i, 18 * 2,
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
@@ -349,23 +349,6 @@
        }
        {
            // Panel Data Report
            CEqReadStep* pStep = new CEqReadStep(0xA17f, 386 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodePanelDataReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_PANEL_DATA_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0x45e);
            if (addStep(STEP_ID_PANEL_DATA_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
        {
            // FAC Data Report
            CEqReadStep* pStep = new CEqReadStep(0xA60E, 108 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
@@ -381,6 +364,38 @@
                delete pStep;
            }
        }
        // process start/end report
        {
            CEqReadStep* pStep = new CEqReadStep(m_nIndex == 0 ? 0x9D3F : 0xDD3F, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessStartReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_START_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(m_nIndex == 0 ? 0x333 : 0x633);
            if (addStep(STEP_ID_JOB_PROCESS_START_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
        {
            CEqReadStep* pStep = new CEqReadStep(m_nIndex == 0 ? 0x9D4C : 0xDD4C, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessEndReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_END_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(m_nIndex == 0 ? 0x334 : 0x634);
            if (addStep(STEP_ID_JOB_PROCESS_END_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
    }
    // å¿…须要实现的虚函数,在此初始化Slot信息
@@ -390,17 +405,30 @@
        m_slot[0].setPosition(m_nID);
        m_slot[0].setNo(1);
        m_slot[0].setName("Slot 1(G2)");
        m_slot[0].setLinkSignalPath(0);
        m_slot[0].setType(MaterialsType::G2);
        m_slot[1].enable();
        m_slot[1].setPosition(m_nID);
        m_slot[1].setNo(2);
        m_slot[1].setName("Slot 2(G1)");
        m_slot[1].setLinkSignalPath(1);
        m_slot[1].setType(MaterialsType::G1);
    }
    void CBonder::onTimer(UINT nTimerid)
    {
        CEquipment::onTimer(nTimerid);
        // test
        /*
        static int i[2] = { 0, 0 };
        i[m_nIndex]++;
        if (i[m_nIndex] == 15) {
            char szBuffer[26];
            decodeJobProcessStartReport(getStep(STEP_ID_JOB_PROCESS_START_REPORT), szBuffer, 26);
        }
        */
    }
    void CBonder::serialize(CArchive& ar)
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.cpp
@@ -9,7 +9,7 @@
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
static char* THIS_FILE = __FILE__;
#define new DEBUG_NEW
#endif
@@ -695,6 +695,100 @@
    return 0;
}
// æ‰©å±•读取位数据
long CPerformanceMelsec::ReadBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nBitCount, BitContainer& vecData) {
    long nRet = ValidateStationAndSize(station, nBitCount);
    if (nRet != 0) {
        UpdateLastError(nRet);
        return nRet;
    }
    if (nDevNo % 8 != 0) {
        UpdateLastError(ERROR_CODE_INVALID_PARAM);
        return ERROR_CODE_INVALID_PARAM;
    }
    const short nDevType = CalculateDeviceType(station, enDevType);
    const long nWordCount = (nBitCount + 15) / 16;
    const long nByteSize = nWordCount * sizeof(short);
    std::vector<char> vecRaw;
    nRet = ReadDataEx(station, nDevType, nDevNo, nByteSize, vecRaw);
    if (nRet != 0) {
        return nRet;
    }
    vecData.clear();
    for (long i = 0; i < nWordCount; ++i) {
        short word = static_cast<unsigned char>(vecRaw[i * 2]) |
            (static_cast<unsigned char>(vecRaw[i * 2 + 1]) << 8);
        for (int j = 0; j < 16; ++j) {
            vecData.push_back((word & (1 << j)) != 0);
            if (vecData.size() >= static_cast<size_t>(nBitCount)) {
                return 0;
            }
        }
    }
    return 0;
}
// æ‰©å±•读取字数据
long CPerformanceMelsec::ReadWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nWordCount, WordContainer& vecData) {
    long nRet = ValidateStationAndSize(station, nWordCount);
    if (nRet != 0) {
        UpdateLastError(nRet);
        return nRet;
    }
    const short nDevType = CalculateDeviceType(station, enDevType);
    const long nByteSize = nWordCount * sizeof(short);
    std::vector<char> vecRaw;
    nRet = ReadDataEx(station, nDevType, nDevNo, nByteSize, vecRaw);
    if (nRet != 0) {
        return nRet;
    }
    vecData.clear();
    for (long i = 0; i < nWordCount; ++i) {
        short value = static_cast<unsigned char>(vecRaw[i * 2]) |
            (static_cast<unsigned char>(vecRaw[i * 2 + 1]) << 8);
        vecData.push_back(value);
    }
    return 0;
}
// æ‰©å±•读取双字数据
long CPerformanceMelsec::ReadDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nDWordCount, DWordContainer& vecData) {
    long nRet = ValidateStationAndSize(station, nDWordCount);
    if (nRet != 0) {
        UpdateLastError(nRet);
        return nRet;
    }
    const short nDevType = CalculateDeviceType(station, enDevType);
    const long nByteSize = nDWordCount * sizeof(uint32_t);
    std::vector<char> vecRaw;
    nRet = ReadDataEx(station, nDevType, nDevNo, nByteSize, vecRaw);
    if (nRet != 0) {
        return nRet;
    }
    vecData.clear();
    for (long i = 0; i < nDWordCount; ++i) {
        uint32_t val = static_cast<unsigned char>(vecRaw[i * 4 + 0]) |
            (static_cast<unsigned char>(vecRaw[i * 4 + 1]) << 8) |
            (static_cast<unsigned char>(vecRaw[i * 4 + 2]) << 16) |
            (static_cast<unsigned char>(vecRaw[i * 4 + 3]) << 24);
        vecData.push_back(val);
    }
    return 0;
}
// æ‰©å±•写数据
long CPerformanceMelsec::WriteDataEx(const StationIdentifier& station, long nDevType, long nDevNo, const std::vector<char>& vecData) {
    // éªŒè¯ç«™ç‚¹å‚数和数据有效性
@@ -724,6 +818,82 @@
    return nRet;
}
// æ‰©å±•写位数据
long CPerformanceMelsec::WriteBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const BitContainer& vecData) {
    long nRet = ValidateStationAndData(station, vecData);
    if (nRet != 0) {
        UpdateLastError(nRet);
        return nRet;
    }
    if (nDevNo % 8 != 0) {
        UpdateLastError(ERROR_CODE_INVALID_PARAM);
        return ERROR_CODE_INVALID_PARAM;
    }
    const short nDevType = CalculateDeviceType(station, enDevType);
    const size_t nWordCount = (vecData.size() + 15) / 16;
    std::vector<short> vecWordBuffer(nWordCount, 0);
    for (size_t i = 0; i < vecData.size(); ++i) {
        if (vecData[i]) {
            vecWordBuffer[i / 16] |= (1 << (i % 16));
        }
    }
    // è½¬æ¢ short -> char
    std::vector<char> vecByteBuffer;
    vecByteBuffer.resize(nWordCount * sizeof(short));
    for (size_t i = 0; i < nWordCount; ++i) {
        vecByteBuffer[i * 2] = static_cast<char>(vecWordBuffer[i] & 0xFF);
        vecByteBuffer[i * 2 + 1] = static_cast<char>((vecWordBuffer[i] >> 8) & 0xFF);
    }
    return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer);
}
// æ‰©å±•写字数据
long CPerformanceMelsec::WriteWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const WordContainer& vecData) {
    long nRet = ValidateStationAndData(station, vecData);
    if (nRet != 0) {
        UpdateLastError(nRet);
        return nRet;
    }
    const short nDevType = CalculateDeviceType(station, enDevType);
    std::vector<char> vecByteBuffer;
    vecByteBuffer.resize(vecData.size() * sizeof(short));
    for (size_t i = 0; i < vecData.size(); ++i) {
        vecByteBuffer[i * 2] = static_cast<char>(vecData[i] & 0xFF);
        vecByteBuffer[i * 2 + 1] = static_cast<char>((vecData[i] >> 8) & 0xFF);
    }
    return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer);
}
// æ‰©å±•写双字数据
long CPerformanceMelsec::WriteDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const DWordContainer& vecData) {
    long nRet = ValidateStationAndData(station, vecData);
    if (nRet != 0) {
        UpdateLastError(nRet);
        return nRet;
    }
    const short nDevType = CalculateDeviceType(station, enDevType);
    std::vector<char> vecByteBuffer;
    vecByteBuffer.resize(vecData.size() * sizeof(uint32_t));
    for (size_t i = 0; i < vecData.size(); ++i) {
        vecByteBuffer[i * 4] = static_cast<char>(vecData[i] & 0xFF);
        vecByteBuffer[i * 4 + 1] = static_cast<char>((vecData[i] >> 8) & 0xFF);
        vecByteBuffer[i * 4 + 2] = static_cast<char>((vecData[i] >> 16) & 0xFF);
        vecByteBuffer[i * 4 + 3] = static_cast<char>((vecData[i] >> 24) & 0xFF);
    }
    return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer);
}
// æ‰©å±•软元件随机读取
long CPerformanceMelsec::ReadRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, std::vector<char>& vecData) {
    if (vecSoftElements.empty()) {
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.h
@@ -388,7 +388,13 @@
    // æ‰©å±•读写数据
    long ReadDataEx(const StationIdentifier& station, long nDevType, long nDevNo, long nSize, std::vector<char>& vecData);
    long ReadBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nBitCount, BitContainer& vecData);
    long ReadWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nWordCount, WordContainer& vecData);
    long ReadDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nDWordCount, DWordContainer& vecData);
    long WriteDataEx(const StationIdentifier& station, long nDevType, long nDevNo, const std::vector<char>& vecData);
    long WriteBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const BitContainer& vecData);
    long WriteWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const WordContainer& vecData);
    long WriteDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const DWordContainer& vecData);
    // æ‰©å±•软元件随机读写(支持多个软元件)
    long ReadRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, std::vector<char>& vecData);
SourceCode/Bond/Servo/CEFEM.cpp
@@ -473,6 +473,7 @@
            pStep->setName(STEP_EQ_VCR1_EVENT_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0x4a);
            pStep->setReturnDev(0x91e);
            if (addStep(STEP_ID_VCR1_EVENT_REPORT, pStep) != 0) {
                delete pStep;
            }
@@ -676,23 +677,6 @@
        }
        {
            // Panel Data Report
            CEqReadStep* pStep = new CEqReadStep(0x617f, 386 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodePanelDataReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_PANEL_DATA_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0x15e);
            if (addStep(STEP_ID_PANEL_DATA_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
        {
            // FAC Data Report
            CEqReadStep* pStep = new CEqReadStep(0x6301, 108 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
@@ -803,16 +787,27 @@
                m_pPort[i]->onReceiveLBData(pszData, size);
            }
        }
        m_pAligner->onReceiveLBData(pszData, size);
        // æ›´æ–°ä¿¡å·åˆ°LoadPort, Robot, Aligner, Fliper
        m_pPort[0]->setLinkSignalBlock(0, &m_bLinkSignal[0][0]);
        m_pPort[1]->setLinkSignalBlock(0, &m_bLinkSignal[1][0]);
        m_pPort[2]->setLinkSignalBlock(0, &m_bLinkSignal[2][0]);
        m_pPort[3]->setLinkSignalBlock(0, &m_bLinkSignal[3][0]);
        m_pArmTray[0]->setLinkSignalBlock(0, &m_bLinkSignal[4][0]);
        m_pArmTray[1]->setLinkSignalBlock(0, &m_bLinkSignal[5][0]);
        m_pAligner->setLinkSignalBlock(0, &m_bLinkSignal[6][0]);
        m_pFliper->setLinkSignalBlock(0, &m_bLinkSignal[7][0]);
        m_pPort[0]->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[0][0]);
        m_pPort[1]->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[1][0]);
        m_pPort[2]->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[2][0]);
        m_pPort[3]->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[3][0]);
        m_pArmTray[0]->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[4][0]);
        m_pArmTray[1]->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[5][0]);
        m_pAligner->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[6][0]);
        m_pFliper->setLinkSignalUpstreamBlock(0, &m_bLinkSignalToUpstream[7][0]);
        m_pPort[0]->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[0][0]);
        m_pPort[1]->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[1][0]);
        m_pPort[2]->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[2][0]);
        m_pPort[3]->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[3][0]);
        m_pArmTray[0]->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[4][0]);
        m_pArmTray[1]->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[5][0]);
        m_pAligner->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[6][0]);
        m_pFliper->setLinkSignalDownstreamBlock(0, &m_bLinkSignalToDownstream[7][0]);
    }
    int CEFEM::onReceivedJob(int port, CJobDataS* pJobDataS)
@@ -872,16 +867,11 @@
        return 10000;
    }
    void CEFEM::printDebugString001()
    void CEFEM::printDebugRobotState()
    {
        for (int i = 0; i < 8; i++) {
            LOGI("<CEquipment-%s>%d, Signal:%s, %s, %s, %s",
                m_strName.c_str(), i,
                m_bLinkSignal[i][SIGNAL_UPSTREAM_INLINE] ? "ON" : "OFF",
                m_bLinkSignal[i][SIGNAL_UPSTREAM_TROUBLE] ? "ON" : "OFF",
                m_bLinkSignal[i][SIGNAL_INTERLOCK] ? "ON" : "OFF",
                m_bLinkSignal[i][SIGNAL_SEND_ABLE] ? "ON" : "OFF"
            );
        }
        LOGI("<CEFEM>Robot status:%d, ARM1:%s, ARM2:%s",
            m_robotData.status,
            m_robotData.armState[1] ? _T("ON") : _T("OFF"),
            m_robotData.armState[2] ? _T("ON") : _T("OFF"));
    }
}
SourceCode/Bond/Servo/CEFEM.h
@@ -42,7 +42,6 @@
        int robotCmd(ROBOT_CMD_PARAM& robotCmdParam, ONWRITED onWritedBlock = nullptr);
        int robotCmds(ROBOT_CMD_PARAM* robotCmdParam, unsigned int count, ONWRITED onWritedBlock = nullptr);
        RMDATA& getRobotMonitoringData();
        void printDebugString001();
        // å¿«æ·å°è£…
        int robotSendHome(int seq, ONWRITED onWritedBlock = nullptr);
@@ -60,6 +59,7 @@
        int robotSendTransferAndHome(int seq, int armNo, int fromPos, int toPos, int fromSlot, int toSlot, ONWRITED onWritedBlock = nullptr);
        int robotSendGetAndPut(int seq, int armNo, int getPos, int getSlot, int putPos, int putSlot, ONWRITED onWritedBlock = nullptr);
        int robotSendPutAndHome(int seq, int armNo, int putPos, int putSlot, ONWRITED onWritedBlock = nullptr);
        void printDebugRobotState();
    private:
        CLoadPort* m_pPort[4];
SourceCode/Bond/Servo/CEqAlarmStep.cpp
@@ -58,7 +58,7 @@
        m_nAlarmCode = (unsigned int)CToolUnits::toInt16(&szBuffer[6]);
        m_nAlarmLevel = (unsigned int)CToolUnits::toInt16(&szBuffer[8]);
        LOGI("<CEqAlarmStep> Equipment Alarm state Changed<State:%d, Unit:%d, Level:%d, Code:%d, ID:%d>\n",
        LOGI("<CEqAlarmStep> Equipment Alarm state Changed<State:%d, Unit:%d, Level:%d, Code:%d, ID:%d>",
            m_nAlarmState, m_nUnitId, m_nAlarmLevel, m_nAlarmCode, m_nAlarmId,
            m_strText.c_str(), m_strDescription.c_str());
SourceCode/Bond/Servo/CEqModeStep.cpp
@@ -32,7 +32,7 @@
        CReadStep::onReadData();
        DWordContainer dc;
        if (0 != m_pCclink->ReadDWordData(m_station, DeviceType::W, m_nModeDev, 1, dc)) {
        if (0 != m_pCclink->ReadDWordDataEx(m_station, DeviceType::W, m_nModeDev, 1, dc)) {
            return -2;
        }
        if (dc.size() < 1) {
SourceCode/Bond/Servo/CEqVcrEventStep.cpp
@@ -54,7 +54,7 @@
        }
        m_vcrEventReport.unserialize(szBuffer, 60);
        LOGI("<CEqVcrEventStep-%s>Read VCR Event Report\n", m_strName.c_str());
        LOGI("<CEqVcrEventStep-%s>Read VCR Event Report", m_strName.c_str());
        return 0;
    }
SourceCode/Bond/Servo/CEquipment.cpp
@@ -7,26 +7,11 @@
#include "Servo.h"
#define CHECK_READ_STEP_SIGNAL(addr, data, size) {                            \
    BOOL bFlag = isBitOn(data, size, addr);                                    \
    SERVO::CStep* pStep = getStep(addr);                                    \
    if (pStep != nullptr) {                                                    \
        ((CReadStep*)pStep)->onReadSignal(bFlag ? addr : 0);                \
    }                                                                        \
}
#define CHECK_WRITE_STEP_SIGNAL(addr, data, size) {                            \
    BOOL bFlag = isBitOn(data, size, addr);                                    \
    SERVO::CStep* pStep = getStep(addr);                                    \
    if (pStep != nullptr) {                                                    \
        ((CWriteStep*)pStep)->onRecvSignal(bFlag ? addr : 0);                \
    }                                                                        \
}
namespace SERVO {
    CEquipment::CEquipment() : m_nID(0), m_strName(""), m_strDescription(""), m_station(0, 255)
    {
        m_bEnable = TRUE;
        m_listener = { };
        m_alive = { FALSE, 0, FALSE };
        m_bCimState = FALSE;
@@ -35,11 +20,13 @@
        m_bLocalAlarm = FALSE;
        m_bAutoRecipeChange = FALSE;
        m_bVCREnable[0] = FALSE;
        memset(m_bLinkSignal, 0, sizeof(m_bLinkSignal));
        memset(m_bLinkSignalToUpstream, 0, sizeof(m_bLinkSignalToUpstream));
        memset(m_bLinkSignalToDownstream, 0, sizeof(m_bLinkSignalToDownstream));
        m_pCclink = nullptr;
        m_nBaseAlarmId = 0;
        m_pArm = nullptr;
        m_processState = PROCESS_STATE::Ready;
        m_blockReadBit = { 0 };
        InitializeCriticalSection(&m_criticalSection);
    }
@@ -68,6 +55,16 @@
        m_outputPins.clear();
        DeleteCriticalSection(&m_criticalSection);
    }
    void CEquipment::SetEnable(BOOL bEnable)
    {
        m_bEnable = bEnable;
    }
    BOOL CEquipment::IsEnabled() const
    {
        return m_bEnable;
    }
    void CEquipment::setListener(EquipmentListener listener)
@@ -399,15 +396,31 @@
        BOOL bFlag;
        int index = 0;
        for (int i = 0; i < 8; i++) {
            m_bLinkSignal[i][SIGNAL_UPSTREAM_INLINE] = isBitOn(pszData, size, index + 0);
            m_bLinkSignal[i][SIGNAL_UPSTREAM_TROUBLE] = isBitOn(pszData, size, index + 1);
            m_bLinkSignal[i][SIGNAL_INTERLOCK] = isBitOn(pszData, size, index + 2);
            m_bLinkSignal[i][SIGNAL_SEND_ABLE] = isBitOn(pszData, size, index + 3);
            m_bLinkSignalToUpstream[i][SIGNAL_UPSTREAM_INLINE] = isBitOn(pszData, size, index + 0);
            m_bLinkSignalToUpstream[i][SIGNAL_UPSTREAM_TROUBLE] = isBitOn(pszData, size, index + 1);
            m_bLinkSignalToUpstream[i][SIGNAL_INTERLOCK] = isBitOn(pszData, size, index + 2);
            m_bLinkSignalToUpstream[i][SIGNAL_SEND_ABLE] = isBitOn(pszData, size, index + 3);
            index += 0x40;
            if (m_bLinkSignalToUpstream[i][SIGNAL_SEND_ABLE]) {
                onSendAble(i+1);
            }
        }         
        if(m_bLinkSignal[0][SIGNAL_SEND_ABLE]) {
            onSendAble();
        index += 0x40 * 2;
        for (int i = 0; i < 8; i++) {
            m_bLinkSignalToDownstream[i][SIGNAL_UPSTREAM_INLINE] = isBitOn(pszData, size, index + 0);
            m_bLinkSignalToDownstream[i][SIGNAL_UPSTREAM_TROUBLE] = isBitOn(pszData, size, index + 1);
            m_bLinkSignalToDownstream[i][SIGNAL_INTERLOCK] = isBitOn(pszData, size, index + 2);
            m_bLinkSignalToDownstream[i][SIGNAL_RECEIVE_ABLE] = isBitOn(pszData, size, index + 3);
            index += 0x40;
            if (m_bLinkSignalToDownstream[0][SIGNAL_RECEIVE_ABLE]) {
                onReceiveAble(i + 1);
            }
        }
        // å…¶å®ƒä¿¡å·åŠå“åº”
        index = 0x540;
@@ -583,13 +596,22 @@
        CHECK_WRITE_STEP_SIGNAL(STEP_ID_IN_OP_CMD_REPLY, pszData, size);
        // Panel Data Report
        CHECK_WRITE_STEP_SIGNAL(STEP_ID_PANEL_DATA_REPORT, pszData, size);
        CHECK_READ_STEP_SIGNAL(STEP_ID_PANEL_DATA_REPORT, pszData, size);
        // Panel Data Request
        CHECK_WRITE_STEP_SIGNAL(STEP_ID_PANEL_DATA_REQUEST, pszData, size);    
        // Job Data Request
        CHECK_READ_STEP_SIGNAL(STEP_ID_JOB_DATA_REQUEST, pszData, size);
        // job process start/end report
        CHECK_READ_STEP_SIGNAL(STEP_ID_JOB_PROCESS_START_REPORT, pszData, size);
        CHECK_READ_STEP_SIGNAL(STEP_ID_JOB_PROCESS_END_REPORT, pszData, size);
        CHECK_WRITE_STEP_SIGNAL(STEP_ID_PROT1_CASSETTE_CTR_CMD_REPLY, pszData, size);
        CHECK_WRITE_STEP_SIGNAL(STEP_ID_PROT2_CASSETTE_CTR_CMD_REPLY, pszData, size);
        CHECK_WRITE_STEP_SIGNAL(STEP_ID_PROT3_CASSETTE_CTR_CMD_REPLY, pszData, size);
        CHECK_WRITE_STEP_SIGNAL(STEP_ID_PROT4_CASSETTE_CTR_CMD_REPLY, pszData, size);
    }
    BOOL CEquipment::isBitOn(const char* pszData, size_t size, int index)
@@ -642,25 +664,47 @@
        return m_bVCREnable[index];
    }
    BOOL CEquipment::isLinkSignalOn(unsigned int path, unsigned int signal)
    BOOL CEquipment::isLinkSignalUpstreamOn(unsigned int path, unsigned int signal)
    {
        if (path >= PATH_MAX) return FALSE;
        if (signal >= SIGNAL_MAX) return FALSE;
        return m_bLinkSignal[path][signal];
        return m_bLinkSignalToUpstream[path][signal];
    }
    void CEquipment::setLinkSignal(unsigned int path, unsigned int signal, BOOL bOn)
    BOOL CEquipment::isLinkSignalDownstreamOn(unsigned int path, unsigned int signal)
    {
        if (path >= PATH_MAX) return FALSE;
        if (signal >= SIGNAL_MAX) return FALSE;
        return m_bLinkSignalToDownstream[path][signal];
    }
    void CEquipment::setLinkSignalUpstream(unsigned int path, unsigned int signal, BOOL bOn)
    {
        if (path >= PATH_MAX) return;
        if (signal >= SIGNAL_MAX) return;
        m_bLinkSignal[path][signal] = bOn;
        m_bLinkSignalToUpstream[path][signal] = bOn;
    }
    void CEquipment::setLinkSignalBlock(unsigned int path, BOOL* pSignal)
    void CEquipment::setLinkSignalUpstreamBlock(unsigned int path, BOOL* pSignal)
    {
        if (path >= PATH_MAX) return;
        for (int i = 0; i < SIGNAL_MAX; i++) {
            m_bLinkSignal[path][i] = pSignal[i];
            m_bLinkSignalToUpstream[path][i] = pSignal[i];
        }
    }
    void CEquipment::setLinkSignalDownstream(unsigned int path, unsigned int signal, BOOL bOn)
    {
        if (path >= PATH_MAX) return;
        if (signal >= SIGNAL_MAX) return;
        m_bLinkSignalToDownstream[path][signal] = bOn;
    }
    void CEquipment::setLinkSignalDownstreamBlock(unsigned int path, BOOL* pSignal)
    {
        if (path >= PATH_MAX) return;
        for (int i = 0; i < SIGNAL_MAX; i++) {
            m_bLinkSignalToDownstream[path][i] = pSignal[i];
        }
    }
@@ -759,6 +803,11 @@
        return m_outputPins;
    }
    CRecipeList* CEquipment::getRecipeList(int unitNo)
    {
        return m_recipesManager.getRecipeList(unitNo);
    }
    int CEquipment::recvIntent(CPin* pPin, CIntent* pIntent)
    {
        ASSERT(pPin);
@@ -791,7 +840,7 @@
        return 0;
    }
    int CEquipment::fetchedOutJob(CJobDataB* pJobDataB)
    int CEquipment::fetchedOutJob(int port, CJobDataB* pJobDataB)
    {
        if (m_pArm == nullptr) {
            return -1;
@@ -814,7 +863,6 @@
            return -3;
        }
        ((CArm*)m_pArm)->tempStore(pContext);
        pContext->release();
        Unlock();
@@ -831,7 +879,7 @@
        return 0;
    }
    int CEquipment::storedJob(CJobDataB* pJobDataB, short putSlot)
    int CEquipment::storedJob(int port, CJobDataB* pJobDataB, short putSlot)
    {
        if (m_pArm == nullptr) {
            return -1;
@@ -857,7 +905,7 @@
        if (m_listener.onDataChanged != nullptr) {
            m_listener.onDataChanged(this, EDCC_STORED_JOB);
        }
        return 0;
    }
@@ -905,7 +953,6 @@
    CGlass* CEquipment::getGlassWithCassette(int cassetteSequenceNo, int jobSequenceNo)
    {
        CSlot* pSlot = nullptr;
        for (int i = 0; i < SLOT_MAX; i++) {
            if (!m_slot[i].isEnable()) continue;
            CGlass* pGlass = (CGlass*)m_slot[i].getContext();
@@ -1111,7 +1158,7 @@
        return 0;
    }
    int CEquipment::masterRecipeListRequest(short unitNo)
    int CEquipment::masterRecipeListRequest(short unitNo, ONSYNCINGSTATECHANGED block)
    {
        SERVO::CEqWriteStep* pStep = (SERVO::CEqWriteStep*)getStepWithName(STEP_EQ_MASTER_RECIPE_LIST_REQ);
        if (pStep == nullptr) {
@@ -1119,6 +1166,7 @@
        }
        LOGI("<CEquipment-%s>正在请求单元<%d>主配方列表", m_strName.c_str(), unitNo);
        m_recipesManager.setOnSyncingStateChanged(block);
        if (m_recipesManager.syncing() != 0) {
            return -2;
        }
@@ -1180,6 +1228,28 @@
            if (!m_slot[i].isEnable()) continue;
            if (m_slot[i].isLock()) continue;
            if (!m_slot[i].isEmpty()) continue;
            int lsPath = m_slot[i].getLinkSignalPath();
            if (!m_bLinkSignalToDownstream[lsPath][SIGNAL_UPSTREAM_INLINE]
                || m_bLinkSignalToDownstream[lsPath][SIGNAL_UPSTREAM_TROUBLE]
                || !m_bLinkSignalToDownstream[lsPath][SIGNAL_INTERLOCK]
                || !m_bLinkSignalToDownstream[lsPath][SIGNAL_RECEIVE_ABLE]) continue;
            MaterialsType slotType = m_slot[i].getType();
            if (type == MaterialsType::G1 && slotType == MaterialsType::G2) continue;
            if (type == MaterialsType::G2 && slotType == MaterialsType::G1) continue;
            return &m_slot[i];
        }
        return nullptr;
    }
    CSlot* CEquipment::getAvailableSlotForGlassExcludeSignal(MaterialsType type)
    {
        for (int i = 0; i < SLOT_MAX; i++) {
            if (!m_slot[i].isEnable()) continue;
            if (m_slot[i].isLock()) continue;
            if (!m_slot[i].isEmpty()) continue;
            MaterialsType slotType = m_slot[i].getType();
            if (type == MaterialsType::G1 && slotType == MaterialsType::G2) continue;
@@ -1236,12 +1306,14 @@
            if (!m_slot[i].isEnable()) continue;
            if (m_slot[i].isLock()) continue;
            CGlass* pGlass = (CGlass*)m_slot[i].getContext();
            if (!isSlotProcessed(i)) continue;
            if (pGlass == nullptr) continue;
            if(pGlass->getInspResult(m_nID, 0) == InspResult::Fail) continue;
            int lsPath = m_slot[i].getLinkSignalPath();
            if(!m_bLinkSignal[lsPath][SIGNAL_UPSTREAM_INLINE]
                || m_bLinkSignal[lsPath][SIGNAL_UPSTREAM_TROUBLE]
                || !m_bLinkSignal[lsPath][SIGNAL_INTERLOCK]
                || !m_bLinkSignal[lsPath][SIGNAL_SEND_ABLE] ) continue;
            if(!m_bLinkSignalToUpstream[lsPath][SIGNAL_UPSTREAM_INLINE]
                || m_bLinkSignalToUpstream[lsPath][SIGNAL_UPSTREAM_TROUBLE]
                || !m_bLinkSignalToUpstream[lsPath][SIGNAL_INTERLOCK]
                || !m_bLinkSignalToUpstream[lsPath][SIGNAL_SEND_ABLE] ) continue;
            MaterialsType glassType = pGlass->getType();
            if (glassType == MaterialsType::G1 && putSlotType == MaterialsType::G2) continue;
@@ -1262,11 +1334,12 @@
                    if (m_slot[i].isLock()) continue;
                    CGlass* pGlass = (CGlass*)m_slot[i].getContext();
                    if (pGlass == nullptr) continue;
                    if (!isSlotProcessed(i+1)) continue;
                    int lsPath = m_slot[i].getLinkSignalPath();
                    if (!m_bLinkSignal[lsPath][SIGNAL_UPSTREAM_INLINE]
                        || m_bLinkSignal[lsPath][SIGNAL_UPSTREAM_TROUBLE]
                        || !m_bLinkSignal[lsPath][SIGNAL_INTERLOCK]
                        || !m_bLinkSignal[lsPath][SIGNAL_SEND_ABLE]) continue;
                    if (!m_bLinkSignalToUpstream[lsPath][SIGNAL_UPSTREAM_INLINE]
                        || m_bLinkSignalToUpstream[lsPath][SIGNAL_UPSTREAM_TROUBLE]
                        || !m_bLinkSignalToUpstream[lsPath][SIGNAL_INTERLOCK]
                        || !m_bLinkSignalToUpstream[lsPath][SIGNAL_SEND_ABLE]) continue;
                    MaterialsType glassType = pGlass->getType();
                    if (glassType == MaterialsType::G1 && putSlotType == MaterialsType::G2) continue;
@@ -1275,6 +1348,26 @@
                    return &m_slot[i];
                }
            }
        }
        return nullptr;
    }
    CSlot* CEquipment::getInspFailSlot()
    {
        for (int i = 0; i < SLOT_MAX; i++) {
            if (!m_slot[i].isEnable()) continue;
            if (m_slot[i].isLock()) continue;
            CGlass* pGlass = (CGlass*)m_slot[i].getContext();
            if (pGlass == nullptr) continue;
            if (pGlass->getInspResult(m_nID, 0) != InspResult::Fail) continue;
            int lsPath = m_slot[i].getLinkSignalPath();
            if (!m_bLinkSignalToUpstream[lsPath][SIGNAL_UPSTREAM_INLINE]
                || m_bLinkSignalToUpstream[lsPath][SIGNAL_UPSTREAM_TROUBLE]
                || !m_bLinkSignalToUpstream[lsPath][SIGNAL_INTERLOCK]
                || !m_bLinkSignalToUpstream[lsPath][SIGNAL_SEND_ABLE]) continue;
            return &m_slot[i];
        }
        return nullptr;
@@ -1320,6 +1413,10 @@
        if (pSlot == nullptr) return -1;
        pSlot->setContext(nullptr);
        if (m_listener.onDataChanged != nullptr) {
            m_listener.onDataChanged(this, EDCC_FETCHOUT_JOB);
        }
        return 0;
    }
@@ -1341,11 +1438,8 @@
        // ç¼“å­˜Attribute,用于调试时显示信息
        unsigned int weight = 201;
        CAttributeVector attrubutes;
        CAttributeVector& attrubutes = pStep->attributeVector();
        processData.getAttributeVector(attrubutes, weight);
        pStep->addAttributeVector(attrubutes);
        onProcessData(&processData);
        return nRet;
@@ -1359,11 +1453,8 @@
        // ç¼“å­˜Attribute,用于调试时显示信息
        unsigned int weight = 201;
        CAttributeVector attrubutes;
        CAttributeVector& attrubutes = pStep->attributeVector();
        jobDataS.getAttributeVector(attrubutes, weight);
        pStep->addAttributeVector(attrubutes);
        onReceivedJob(port, &jobDataS);
        return nRet;
@@ -1372,7 +1463,16 @@
    int CEquipment::onReceivedJob(int port, CJobDataS* pJobDataS)
    {
        LOGI("<CEquipment-%s>onReceivedJob.", m_strName.c_str());
        return 0;
        // å¯ä»¥åœ¨æ­¤æ›´æ–°JobDataS数据了
        int nRet = ((CArm*)m_pArm)->glassUpdateJobDataS(pJobDataS);
        if (nRet < 0) {
            LOGE("<CEquipment-%s>onReceivedJob,更新JobDataS失败,glassUpdateJobDataS返回%d",
                m_strName.c_str(), nRet);
        }
        return nRet;
    }
    int CEquipment::decodeSentOutJobReport(CStep* pStep, int port, const char* pszData, size_t size)
@@ -1383,11 +1483,8 @@
        // ç¼“å­˜Attribute,用于调试时显示信息
        unsigned int weight = 201;
        CAttributeVector attrubutes;
        CAttributeVector& attrubutes = pStep->attributeVector();
        jobDataS.getAttributeVector(attrubutes, weight);
        pStep->addAttributeVector(attrubutes);
        onSentOutJob(port, &jobDataS);
        return nRet;
@@ -1396,17 +1493,6 @@
    int CEquipment::onSentOutJob(int port, CJobDataS* pJobDataS)
    {
        LOGI("<CEquipment-%s>onSentOutJob.", m_strName.c_str());
        // å¯ä»¥åœ¨æ­¤æ›´æ–°JobDataS数据了
        CGlass* pGlass = getGlass(pJobDataS->getGlass1Id().c_str());
        if (pGlass == nullptr) {
            LOGE("<CEquipment-%s>onSentOutJob,没有找到对应的Glass(CassetteSequenceNo:%d, JobSequenceNo:%d, ID=%s),请检查数据,注意风险。",
                m_strName.c_str(), pJobDataS->getCassetteSequenceNo(), pJobDataS->getJobSequenceNo(),
                pJobDataS->getGlass1Id().c_str());
            return -1;
        }
        pGlass->updateJobDataS(pJobDataS);
        return 0;
    }
@@ -1458,7 +1544,7 @@
        LOGI("<CEquipment-%s>onPreFetchedOutJob:port:%d|GlassId:%s",
            m_strName.c_str(), port, pJobDataB->getGlassId().c_str());
        if (m_listener.onPreFethedOutJob != nullptr) {
            return m_listener.onPreFethedOutJob(this, pJobDataB);
            return m_listener.onPreFethedOutJob(this, port, pJobDataB);
        }
        return TRUE;
@@ -1471,7 +1557,7 @@
        BOOL bCheck = onPreFetchedOutJob(port, pJobDataB);
        if (bCheck) {
            return fetchedOutJob(pJobDataB);
            return fetchedOutJob(port, pJobDataB);
        }
        // æ•°æ®å¼‚常,处理或显示
@@ -1533,9 +1619,8 @@
        // ç¼“å­˜Attribute,用于调试时显示信息
        unsigned int weight = 201;
        CAttributeVector attrubutes;
        CAttributeVector& attrubutes = pStep->attributeVector();
        vcrEventReport.getAttributeVector(attrubutes, weight);
        pStep->addAttributeVector(attrubutes);
        // 0426, å…ˆå›ºå®šè¿”回1(OK)
        ((CReadStep*)pStep)->setReturnCode((short)VCR_Reply_Code::OK);
@@ -1554,7 +1639,7 @@
        index += sizeof(short);
        CToolUnits::convertString(&pszData[index], 128 * 2, strPanelJudgeData);
        index += 128 * 2;
        CToolUnits::convertString(&pszData[index], 256 * 2, strPanelJudgeData);
        CToolUnits::convertString(&pszData[index], 256 * 2, strPanelGradeData);
        index += 256 * 2;
@@ -1569,6 +1654,15 @@
        pStep->addAttribute(new CAttribute("PanelGradeData",
            strPanelGradeData.c_str(), "", weight++));
        // æ›´æ–°æ£€æµ‹ç»“æžœ
        CGlass* pGlass = getGlassWithCassette(cassetteNo, jobSequenceNo);
        if (pGlass == nullptr) {
            LOGE("<CEquipment-%s>更新Panel Data失败,找不到对应的Glass.cassetteNo=%d, jobSequenceNo=%d",
                getName().c_str(), cassetteNo, jobSequenceNo);
            return -1;
        }
        pGlass->setInspResult(m_nID, 0, judgeStringToInspResult(strPanelJudgeData));
        return 0;
    }
@@ -1618,6 +1712,164 @@
        return 0;
    }
    int CEquipment::decodeJobProcessStartReport(CStep* pStep, const char* pszData, size_t size)
    {
        int port = (int)(__int64)pStep->getProp("Port");
        LOGI("<CEquipment-%s>decodeJobProcessStartReport, port:%d", getName().c_str(), port);
        short cassetteNo, jobSequenceNo, unitNo, subUnitNo, slotNo;
        int year, month, day, hour, minute, second;
        int index = 0;
        memcpy(&cassetteNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&jobSequenceNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&unitNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&subUnitNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&slotNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&year, &pszData[index], sizeof(short));
        index += sizeof(short);
        month = (int)pszData[index];
        index += 1;
        day = (int)pszData[index];
        index += 1;
        hour = (int)pszData[index];
        index += 1;
        minute = (int)pszData[index];
        index += 1;
        second = (int)pszData[index];
        index += 1;
        LOGI("<CEquipment-%s>cassetteNo:%d, jobSequenceNo:%d,unitNo:%d, subUnitNo:%d, slotNo:%d %d-%d-%d %d:%d:%d",
            getName().c_str(),
            cassetteNo,
            jobSequenceNo,
            unitNo,
            subUnitNo,
            slotNo,
            year, month, day, hour, minute, second
            );
        if (m_processState != PROCESS_STATE::Processing) {
            setProcessState(PROCESS_STATE::Processing);
        }
        // ç¼“å­˜Attribute,用于调试时显示信息
        unsigned int weight = 201;
        pStep->addAttribute(new CAttribute("CassetteNo",
            std::to_string(cassetteNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("JobSequenceNo",
            std::to_string(jobSequenceNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("UnitNo",
            std::to_string(unitNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("SubUnitNo",
            std::to_string(subUnitNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("SlotNo",
            std::to_string(slotNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("ProcessStartTime",
            (std::to_string(year) + std::to_string(day) + std::to_string(day) + std::to_string(hour) + std::to_string(minute) + std::to_string(second)).c_str()
            , "", weight++));
        return 0;
    }
    int CEquipment::decodeJobProcessEndReport(CStep* pStep, const char* pszData, size_t size)
    {
        int port = (int)(__int64)pStep->getProp("Port");
        LOGI("<CEquipment-%s>decodeJobProcessEndReport, port:%d", getName().c_str(), port);
        short cassetteNo, jobSequenceNo, unitNo, subUnitNo, slotNo;
        int year, month, day, hour, minute, second;
        int index = 0;
        std::string strPanelJudgeData, strPanelGradeData;
        memcpy(&cassetteNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&jobSequenceNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&unitNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&subUnitNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&slotNo, &pszData[index], sizeof(short));
        index += sizeof(short);
        memcpy(&year, &pszData[index], sizeof(short));
        index += sizeof(short);
        month = (int)pszData[index];
        index += 1;
        day = (int)pszData[index];
        index += 1;
        hour = (int)pszData[index];
        index += 1;
        minute = (int)pszData[index];
        index += 1;
        second = (int)pszData[index];
        index += 1;
        LOGI("<CEquipment-%s>cassetteNo:%d, jobSequenceNo:%d,unitNo:%d, subUnitNo:%d, slotNo:%d %d-%d-%d %d:%d:%d",
            getName().c_str(),
            cassetteNo,
            jobSequenceNo,
            unitNo,
            subUnitNo,
            slotNo,
            year, month, day, hour, minute, second
        );
        if (m_processState != PROCESS_STATE::Complete) {
            setProcessState(PROCESS_STATE::Complete);
        }
        CGlass* pGlass = getGlassFromSlot(slotNo);
        if (pGlass == nullptr) {
            LOGE("<CEquipment-%s>decodeJobProcessEndReport, æ‰¾ä¸åˆ°å¯¹åº”glass", getName().c_str());
        }
        else {
            CJobDataS* pJs = pGlass->getJobDataS();
            if (pJs->getCassetteSequenceNo() == cassetteNo
                && pJs->getJobSequenceNo() == jobSequenceNo) {
                pGlass->processEnd(m_nID, getSlotUnit(slotNo));
                if (m_processState != PROCESS_STATE::Complete) {
                    setProcessState(PROCESS_STATE::Complete);
                }
            }
            else {
                LOGE("<CEquipment-%s>decodeJobProcessEndReport, jobSequenceNo或jobSequenceNo不匹配",
                    getName().c_str());
            }
        }
        // ç¼“å­˜Attribute,用于调试时显示信息
        unsigned int weight = 201;
        pStep->addAttribute(new CAttribute("CassetteNo",
            std::to_string(cassetteNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("JobSequenceNo",
            std::to_string(jobSequenceNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("UnitNo",
            std::to_string(unitNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("SubUnitNo",
            std::to_string(subUnitNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("SlotNo",
            std::to_string(slotNo).c_str(), "", weight++));
        pStep->addAttribute(new CAttribute("ProcessStartTime",
            (std::to_string(year) + std::to_string(day) + std::to_string(day) + std::to_string(hour) + std::to_string(minute) + std::to_string(second)).c_str()
            , "", weight++));
        return 0;
    }
    int CEquipment::onPreStoredJob(int port, CJobDataB* pJobDataB, short& putSlot)
    {
        LOGI("<CEquipment-%s>onPreStoredJob:port:%d|GlassId:%s",
@@ -1634,7 +1886,7 @@
        CJobDataS* pJobDataS = pGlass->getJobDataS();
        ASSERT(pJobDataS);
        if (!compareJobData(pJobDataB, pJobDataS)) {
            LOGE("<CFliper-%s>onPreFetchedOutJob,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;
@@ -1642,17 +1894,18 @@
        // å¦‚果没有可用位置,报错
        Lock();
        CSlot* pSlot = getAvailableSlotForGlass((MaterialsType)pJobDataS->getMaterialsType());
        if (pSlot == nullptr) {
        CSlot* pSlot = getSlot(putSlot - 1);
        ASSERT(pSlot);
        if (pSlot->getContext() != nullptr) {
            Unlock();
            LOGE("<CFliper-%s>onPreFetchedOutJob,找不到匹配的Slot,不能进料,请注意风险!", m_strName.c_str());
            LOGE("<CEquipemnt-%s>onPreStoredJob,指定slot(port:%d)有料,请注意风险!", m_strName.c_str(), port);
            return FALSE;
        }
        Unlock();
        if (m_listener.onPreStoredJob != nullptr) {
            if (!m_listener.onPreStoredJob(this, pJobDataB, putSlot)) {
            if (!m_listener.onPreStoredJob(this, port, pJobDataB, putSlot)) {
                return FALSE;
            }
@@ -1672,11 +1925,11 @@
        short putSlot = 0;
        BOOL bCheck = onPreStoredJob(port, pJobDataB, putSlot);
        if (bCheck) {
            return storedJob(pJobDataB, putSlot);
            return storedJob(port, pJobDataB, putSlot);
        }
        // æ•°æ®å¼‚常,处理或显示
        LOGI("<CEquipment-%s>onStoredJob Error.ort:%d|GlassId:%s",
        LOGI("<CEquipment-%s>onStoredJob Error.port:%d|GlassId:%s",
            m_strName.c_str(), port, pJobDataB->getGlassId().c_str());
        return -1;
    }
@@ -1692,13 +1945,16 @@
     * å½“从CC-Link检测到设备Send Able为On时调用此函数
     * å¯èƒ½ä¼šå¤šæ¬¡é‡å¤è°ƒç”¨(根据扫描频率), æ³¨æ„é˜²å‘†
     */
    int CEquipment::onSendAble()
    int CEquipment::onSendAble(int port)
    {
        LOGI("<CEquipment-%s>onSendAble.", m_strName.c_str());
        LOGI("<CEquipment-%s>onSendAble.port:%d", m_strName.c_str(), port);
        if (m_processState != PROCESS_STATE::Complete) {
            setProcessState(PROCESS_STATE::Complete);
        }
        return 0;
    }
    int CEquipment::onReceiveAble(int port)
    {
        LOGI("<CEquipment-%s>onReceiveAble.port:%d", m_strName.c_str(), port);
        return 0;
    }
@@ -1725,4 +1981,38 @@
        return TRUE;
    }
    void CEquipment::printDebugString001()
    {
        for (int i = 0; i < 8; i++) {
            LOGI("<CEquipment-%s>Link Signal to UP stream Path#%d, Signal:%s, %s, %s, %s",
                m_strName.c_str(), i,
                m_bLinkSignalToUpstream[i][SIGNAL_UPSTREAM_INLINE] ? "ON" : "OFF",
                m_bLinkSignalToUpstream[i][SIGNAL_UPSTREAM_TROUBLE] ? "ON" : "OFF",
                m_bLinkSignalToUpstream[i][SIGNAL_INTERLOCK] ? "ON" : "OFF",
                m_bLinkSignalToUpstream[i][SIGNAL_SEND_ABLE] ? "ON" : "OFF"
            );
        }
        for (int i = 0; i < 8; i++) {
            LOGI("<CEquipment-%s>Link Signal to Down stream Path#%d, Signal:%s, %s, %s, %s",
                m_strName.c_str(), i,
                m_bLinkSignalToDownstream[i][SIGNAL_UPSTREAM_INLINE] ? "ON" : "OFF",
                m_bLinkSignalToDownstream[i][SIGNAL_UPSTREAM_TROUBLE] ? "ON" : "OFF",
                m_bLinkSignalToDownstream[i][SIGNAL_INTERLOCK] ? "ON" : "OFF",
                m_bLinkSignalToDownstream[i][SIGNAL_SEND_ABLE] ? "ON" : "OFF"
            );
        }
    }
    InspResult CEquipment::judgeStringToInspResult(std::string& strJudge)
    {
        if (strJudge.compare("N") == 0) {
            return InspResult::Fail;
        }
        if (strJudge.compare("G") == 0) {
            return InspResult::Pass;
        }
        return InspResult::NotInspected;
    }
}
SourceCode/Bond/Servo/CEquipment.h
@@ -43,15 +43,18 @@
#define SIGNAL_UPSTREAM_TROUBLE    1
#define SIGNAL_INTERLOCK        2
#define SIGNAL_SEND_ABLE        3
#define SIGNAL_RECEIVE_ABLE        3
    typedef std::function<void(int writeCode, int retCode)> ONWRITEDRET;
    typedef std::function<void(void* pEiuipment, BOOL bAlive)> ONALIVE;
    typedef std::function<void(void* pEiuipment, int code)> ONDATACHANGED;
    typedef std::function<void(void* pEiuipment, int state, int alarmId, int unitId, int level)> ONALARM;
    typedef std::function<void(void* pEiuipment, void* pReport)> ONVCREVENTREPORT;
    typedef std::function<BOOL(void* pEiuipment, CJobDataB* pJobDataB)> ONPREFETCHEDOUTJOB;
    typedef std::function<BOOL(void* pEiuipment, CJobDataB* pJobDataB, short& putSlot)> ONPRESTOREDJOB;
    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, PROCESS_STATE state)> ONPROCESSSTATE;
    typedef std::function<void(void* pEiuipment, short scanMap, short downMap)> ONMAPMISMATCH;
    typedef struct _EquipmentListener
    {
        ONALIVE                onAlive;
@@ -62,6 +65,7 @@
        ONPREFETCHEDOUTJOB    onPreFethedOutJob;
        ONPRESTOREDJOB        onPreStoredJob;
        ONPROCESSSTATE        onProcessStateChanged;
        ONMAPMISMATCH        onMapMismatch;
    } EquipmentListener;
@@ -72,6 +76,8 @@
        virtual ~CEquipment();
    public:
        void SetEnable(BOOL bEnable);
        BOOL IsEnabled() const;
        virtual const char* getClassName() = 0;
        virtual void setListener(EquipmentListener listener);
        void setCcLink(CCCLinkIEControl* pCcLink);
@@ -108,13 +114,14 @@
        virtual void onReceiveLBData(const char* pszData, size_t size);
        virtual int onStepEvent(CStep* pStep, int code);
        virtual CPin* addPin(PinType type, char* pszName);
        virtual short getSlotUnit(short slot) { return 0; };
        virtual short getSlotUnit(short slotNo) { return 0; };
        CPin* getPin(char* pszName);
        std::vector<CPin*>& CEquipment::getInputPins();
        std::vector<CPin*>& CEquipment::getOutputPins();
        CRecipeList* getRecipeList(int unitNo);
        virtual int recvIntent(CPin* pPin, CIntent* pIntent);
        virtual int fetchedOutJob(CJobDataB* pJobDataB);
        virtual int storedJob(CJobDataB* pJobDataB, short putSlot);
        virtual int fetchedOutJob(int port, CJobDataB* pJobDataB);
        virtual int storedJob(int port, CJobDataB* pJobDataB, short putSlot);
        virtual int onReceivedJob(int port, CJobDataS* pJobDataS);
        virtual int onSentOutJob(int port, CJobDataS* pJobDataS);
        virtual BOOL onPreFetchedOutJob(int port, CJobDataB* pJobDataB);
@@ -122,9 +129,11 @@
        virtual BOOL onPreStoredJob(int port, CJobDataB* pJobDataB, short& putSlot);
        virtual int onStoredJob(int port, CJobDataB* pJobDataB);
        virtual int onProcessData(CProcessData* pProcessData);
        virtual int onSendAble();
        virtual int onSendAble(int port);
        virtual int onReceiveAble(int port);
        virtual int onProcessStateChanged(PROCESS_STATE state);
        virtual int getIndexerOperationModeBaseValue();
        virtual bool isSlotProcessed(int slot) { return true; };
        bool isAlarmStep(SERVO::CStep* pStep);
        bool isVcrEventStep(SERVO::CStep* pStep);
        bool isCassetteTransferStateStep(SERVO::CStep* pStep);
@@ -142,12 +151,12 @@
        int setDateTime(short year, short month, short day, short hour, short minute, short second);
        int setDispatchingMode(DISPATCHING_MODE mode, ONWRITED onWritedBlock = nullptr);
        int indexerOperationModeChange(IDNEXER_OPERATION_MODE mode, ONWRITEDRET onWritedRetBlock);
        void printDebugString001();
        // è¯·æ±‚主配方列表
        // unitNo: 0:local; Others:unit No
        int masterRecipeListRequest(short unitNo);
        int masterRecipeListRequest(short unitNo, ONSYNCINGSTATECHANGED block);
        // è¯·æ±‚配方参数
        // masterRecipeId: ä¸»é…æ–¹id
@@ -163,6 +172,7 @@
        // èŽ·å–ä¸€ä¸ªæŒ‡å®šç‰©æ–™ç±»åž‹(G1,G2,G1&G2)的空槽位
        CSlot* getAvailableSlotForGlass(MaterialsType type);
        CSlot* getAvailableSlotForGlassExcludeSignal(MaterialsType type);
        // åœ¨æŒ‡å®šçš„æ§½åˆ—表中,获取一个指定物料类型(G1,G2,G1&G2)的空槽位
        CSlot* getAvailableSlotForGlass2(MaterialsType type, const std::vector<int>& candidates);
@@ -173,6 +183,7 @@
        // èŽ·å–ä¸€ä¸ªæŒ‡å®šç‰©æ–™ç±»åž‹(G1,G2,G1&G2)的且已经加工处理的槽位
        CSlot* getProcessedSlot(MaterialsType putSlotType);
        CSlot* getProcessedSlot2(MaterialsType putSlotType, const std::vector<int>& candidates);
        CSlot* getInspFailSlot();
        // èŽ·å–çŽ»ç’ƒç‰©æ–™
        CGlass* getGlassFromSlot(int slotNo);
@@ -193,6 +204,12 @@
        // æ‰‹åŠ¨ç§»é™¤ç‰©æ–™
        int removeGlass(int slotNo);
        // å­—符串检测结果转换
        InspResult judgeStringToInspResult(std::string& strJudge);
    // ä»¥ä¸‹ä¸ºä»ŽCC-Link读取到的Bit标志位检测函数
    public:
        BOOL isAlive();
@@ -202,11 +219,14 @@
        BOOL isLocalAlarm();
        BOOL isAutoRecipeChange();
        BOOL isVCREnable(unsigned int index);
        BOOL isLinkSignalOn(unsigned int path, unsigned int signal);
        BOOL isLinkSignalUpstreamOn(unsigned int path, unsigned int signal);
        BOOL isLinkSignalDownstreamOn(unsigned int path, unsigned int signal);
        // åªåœ¨æ¨¡æ‹Ÿæµ‹è¯•时使用的函数,用于模拟信号
        void setLinkSignal(unsigned int path, unsigned int signal, BOOL bOn);
        void setLinkSignalBlock(unsigned int path, BOOL* pSignal);
        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);
        void setLinkSignalDownstreamBlock(unsigned int path, BOOL* pSignal);
    protected:
        inline void Lock() { EnterCriticalSection(&m_criticalSection); }
@@ -224,10 +244,13 @@
        int decodePanelDataReport(CStep* pStep, const char* pszData, size_t size);
        int decodeFacDataReport(CStep* pStep, const char* pszData, size_t size);
        int decodeJobDataRequest(CStep* pStep, const char* pszData, size_t size);
        int decodeJobProcessStartReport(CStep* pStep, const char* pszData, size_t size);
        int decodeJobProcessEndReport(CStep* pStep, const char* pszData, size_t size);
        BOOL compareJobData(CJobDataB* pJobDataB, CJobDataS* pJobDataS);
        void setProcessState(PROCESS_STATE state);
    protected:
        BOOL m_bEnable;
        EquipmentListener m_listener;
        int m_nID;
        std::string m_strName;
@@ -249,7 +272,8 @@
        BOOL m_bLocalAlarm;
        BOOL m_bAutoRecipeChange;
        BOOL m_bVCREnable[VCR_MAX];
        BOOL m_bLinkSignal[PATH_MAX][SIGNAL_MAX];
        BOOL m_bLinkSignalToUpstream[PATH_MAX][SIGNAL_MAX];
        BOOL m_bLinkSignalToDownstream[PATH_MAX][SIGNAL_MAX];
    protected:
        CCCLinkIEControl* m_pCclink;
SourceCode/Bond/Servo/CEquipmentPage1.cpp
@@ -180,7 +180,7 @@
    for (int nRow = 0; nRow < SIGNAL_GRID_ROWS; ++nRow) {
        for (int nCol = 0; nCol < SIGNAL_GRID_COLS; ++nCol) {
            BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol);
            BOOL bCurrentState = m_pEquipment->isLinkSignalUpstreamOn(nRow, nCol);
            UpdateSignalState(nRow, nCol, bCurrentState);
        }
    }
@@ -210,8 +210,8 @@
        int index = nRow * SIGNAL_GRID_COLS + nCol;
        if (index >= 0 && index < (int)m_vSignalList.size() && m_vSignalList[index].bClickable && m_pEquipment != nullptr) {
            // è¯»å–当前状态并切换
            const BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol);
            m_pEquipment->setLinkSignal(nRow, nCol, !bCurrentState);
            const BOOL bCurrentState = m_pEquipment->isLinkSignalUpstreamOn(nRow, nCol);
            m_pEquipment->setLinkSignalUpstream(nRow, nCol, !bCurrentState);
        }
    });
SourceCode/Bond/Servo/CGlass.cpp
@@ -8,6 +8,8 @@
        m_pPath = nullptr;
        m_type = MaterialsType::G1;
        m_pBuddy = nullptr;
        m_nOriginPort = 0;
        m_nOriginSlot = 0;
    }
    CGlass::~CGlass()
@@ -66,6 +68,18 @@
        return m_strID;
    }
    void CGlass::setOriginPort(int port, int slot)
    {
        m_nOriginPort = port;
        m_nOriginSlot = slot;
    }
    void CGlass::getOrginPort(int& port, int& slot)
    {
        port = m_nOriginPort;
        slot = m_nOriginSlot;
    }
    CPath* CGlass::getPath()
    {
        return m_pPath;
@@ -103,6 +117,8 @@
            Lock();
            ar << (int)m_type;
            WriteString(ar, m_strID);
            ar << m_nOriginPort;
            ar << m_nOriginSlot;
            ar << (ULONGLONG)m_pPath;
            if (m_pPath != nullptr) {
                m_pPath->serialize(ar);
@@ -123,6 +139,8 @@
            ar >> type;
            m_type = (MaterialsType)type;
            ReadString(ar, m_strID);
            ar >> m_nOriginPort;
            ar >> m_nOriginSlot;
            ar >> ullPath;
            if (ullPath != 0) {
                m_pPath = new CPath();
@@ -182,12 +200,13 @@
        return m_strBuddyId;
    }
    void CGlass::processEnd(unsigned int nEqId, unsigned int nUnit)
    int CGlass::processEnd(unsigned int nEqId, unsigned int nUnit)
    {
        CPath* pPath = getPathWithEq(nEqId, nUnit);
        if (pPath != nullptr) {
            pPath->processEnd();
        }
        if (pPath == nullptr) return -1;
        pPath->processEnd();
        return 0;
    }
    BOOL CGlass::isProcessed(unsigned int nEqId, unsigned int nUnit)
@@ -197,4 +216,21 @@
        return pPath->isProcessEnd();
    }
    int CGlass::setInspResult(unsigned int nEqId, unsigned int nUnit, InspResult result)
    {
        CPath* pPath = getPathWithEq(nEqId, nUnit);
        if (pPath == nullptr) return -1;
        pPath->setInspResult(result);
        return 0;
    }
    InspResult CGlass::getInspResult(unsigned int nEqId, unsigned int nUnit)
    {
        CPath* pPath = getPathWithEq(nEqId, nUnit);
        if (pPath == nullptr) return InspResult::NotInspected;
        return pPath->getInspResult();
    }
}
SourceCode/Bond/Servo/CGlass.h
@@ -24,6 +24,8 @@
        void setType(MaterialsType type);
        void setID(const char* pszID);
        std::string& getID();
        void setOriginPort(int port, int slot);
        void getOrginPort(int& port, int& slot);
        CPath* getPathWithEq(unsigned int nEqId, unsigned int nUnit);
        CPath* getPath();
        void addPath(unsigned int nEqId, unsigned int nUnit);
@@ -35,8 +37,10 @@
        BOOL forceSetBuddy(CGlass* pGlass);
        CGlass* getBuddy();
        std::string& getBuddyId();
        void processEnd(unsigned int nEqId, unsigned int nUnit);
        int processEnd(unsigned int nEqId, unsigned int nUnit);
        BOOL isProcessed(unsigned int nEqId, unsigned int nUnit);
        int setInspResult(unsigned int nEqId, unsigned int nUnit, InspResult result);
        InspResult getInspResult(unsigned int nEqId, unsigned int nUnit);
    private:
        MaterialsType m_type;
@@ -45,6 +49,8 @@
        CJobDataS m_jobDataS;
        CGlass* m_pBuddy;
        std::string m_strBuddyId;
        int m_nOriginPort;
        int m_nOriginSlot;
    };
}
SourceCode/Bond/Servo/CLoadPort.cpp
@@ -2,15 +2,8 @@
#include "CLoadPort.h"
#include "CGlassPool.h"
#include "Servo.h"
#include "ServoCommo.h"
#define CHECK_READ_STEP_SIGNAL2(addr, data, size) {                            \
    BOOL bFlag = isBitOn(data, size, addr);                                    \
    SERVO::CStep* pStep = getStep(addr);                                    \
    if (pStep != nullptr) {                                                    \
        ((CReadStep*)pStep)->onReadSignal(bFlag ? addr : 0);                \
    }                                                                        \
}
namespace SERVO {
    CLoadPort::CLoadPort() : CEquipment()
@@ -75,15 +68,15 @@
        {
            // Cassette Ctrl Cmd
            static char* pszName[] = { STEP_EQ_P1_CASSETTE_CTRL_CMD,    STEP_EQ_P2_CASSETTE_CTRL_CMD, STEP_EQ_P3_CASSETTE_CTRL_CMD,    STEP_EQ_P4_CASSETTE_CTRL_CMD };
            static int writeSignalDev[] = { 0x120, 0x121, 0x122, 0x123 };
            static char* pszName[] = { STEP_EQ_P1_CASSETTE_CTRL_CMD, STEP_EQ_P2_CASSETTE_CTRL_CMD, STEP_EQ_P3_CASSETTE_CTRL_CMD, STEP_EQ_P4_CASSETTE_CTRL_CMD };
            static int writeSignalDev[] = { 0x130, 0x131, 0x132, 0x133 };
            static int dev[] = { 0x45, 0x1a5, 0x305, 0x465 };
            static int addr[] = { 0x480, 0x481, 0x482, 0x483 };
            static int addr[] = { STEP_ID_PROT1_CASSETTE_CTR_CMD_REPLY, STEP_ID_PROT2_CASSETTE_CTR_CMD_REPLY, STEP_ID_PROT3_CASSETTE_CTR_CMD_REPLY, STEP_ID_PROT4_CASSETTE_CTR_CMD_REPLY };
            CEqCassetteCtrlCmdStep* pStep = new CEqCassetteCtrlCmdStep();
            CEqWriteStep* pStep = new CEqWriteStep();
            pStep->setName(pszName[m_nIndex]);
            pStep->setWriteSignalDev(writeSignalDev[m_nIndex]);
            pStep->setCtrlCmdDev(dev[m_nIndex]);
            pStep->setDataDev(dev[m_nIndex]);
            if (addStep(addr[m_nIndex], pStep) != 0) {
                delete pStep;
            }
@@ -316,12 +309,12 @@
            };
            static int dev[] = { 0x6050, 0x6070, 0x6090, 0x60b0 };
            static int writeSignalDev[6][4] = {
                {0xc8, 0xc9, 0xca, 0xcb},
                {0xd0, 0xd1, 0xd2, 0xd3},
                {0xd8, 0xd9, 0xda, 0xdb},
                {0xe0, 0xe1, 0xe2, 0xe3},
                {0xe8, 0xe9, 0xea, 0xeb},
                {0xf0, 0xf1, 0xf2, 0xf3},
                {0xf8, 0xf9, 0xfa, 0xfb},
                {0x100, 0x101, 0x102, 0x103}
                {0xf0, 0xf1, 0xf2, 0xf3}
            };
            static int addr[6][4] = {
                { STEP_ID_PORT1_EMPTY, STEP_ID_PORT2_EMPTY, STEP_ID_PORT3_EMPTY, STEP_ID_PORT4_EMPTY },
@@ -337,6 +330,7 @@
                    [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                        if (code == ROK && pszData != nullptr && size > 0) {
                            decodePortStatusReport((CStep*)pFrom, pszData, size);
                            return 0;
                        }
                        return -1;
                    });
@@ -414,17 +408,25 @@
        int jobExistenceSize,
        short slotProcess,
        short jopCount,
        CJobDataA* pJobDataA)
        CJobDataA* pJobDataA,
        ONWRITED onWritedBlock)
    {
        int id = getID();
        if ( !(id == EQ_ID_LOADPORT1 || id == EQ_ID_LOADPORT2) ) {
            return -1;
        SERVO::CEqWriteStep* pStep = (SERVO::CEqWriteStep*)getCassetteCtrlCmdStep();
        ASSERT(pStep);
        char szBuffer[1024] = { 0 };
        memcpy(&szBuffer[0], &cmd, sizeof(short));
        if (jobExistence != nullptr && jobExistenceSize == 12) {
            memcpy(&szBuffer[2], jobExistence, sizeof(short) * jobExistenceSize);
        }
        memcpy(&szBuffer[26], &slotProcess, sizeof(short));
        memcpy(&szBuffer[36], &jopCount, sizeof(short));
        if (pJobDataA != nullptr) {
            pJobDataA->serialize(&szBuffer[38], 1024 - 38);
        }
        SERVO::CEqCassetteCtrlCmdStep* pStep = (SERVO::CEqCassetteCtrlCmdStep*)getCassetteCtrlCmdStep();
        ASSERT(pStep);
        return pStep->sendCtrlCmd(cmd, jobExistence, jobExistenceSize, slotProcess, jopCount, pJobDataA);
        return pStep->writeDataEx(szBuffer, 352 * 2, onWritedBlock);
    }
    CStep* CLoadPort::getCassetteCtrlCmdStep()
@@ -846,12 +848,12 @@
        static int autoType[] = { STEP_ID_PORT1_TYPE_AUTO_CHANGE, STEP_ID_PORT2_TYPE_AUTO_CHANGE,
            STEP_ID_PORT3_TYPE_AUTO_CHANGE, STEP_ID_PORT4_TYPE_AUTO_CHANGE };
        CHECK_READ_STEP_SIGNAL2(type[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(mode[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(cassetteType[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(transferMode[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(enable[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(autoType[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL(type[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL(mode[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL(cassetteType[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL(transferMode[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL(enable[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL(autoType[m_nIndex], pszData, size);
        static int typeReply[] = { STEP_ID_PROT1_TYPE_CHANGE_REPLY, STEP_ID_PROT2_TYPE_CHANGE_REPLY,
@@ -867,12 +869,12 @@
        static int cassetteTypeReply[] = { STEP_ID_PROT1_CASSETTE_TYPE_CHANGE_REPLY, STEP_ID_PROT2_CASSETTE_TYPE_CHANGE_REPLY,
            STEP_ID_PROT3_CASSETTE_TYPE_CHANGE_REPLY, STEP_ID_PROT4_CASSETTE_TYPE_CHANGE_REPLY };
        CHECK_READ_STEP_SIGNAL2(typeReply[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(modeReply[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(transferModeReply[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(enableModeReply[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(typeAutoReply[m_nIndex], pszData, size);
        CHECK_READ_STEP_SIGNAL2(cassetteTypeReply[m_nIndex], pszData, size);
        CHECK_WRITE_STEP_SIGNAL(typeReply[m_nIndex], pszData, size);
        CHECK_WRITE_STEP_SIGNAL(modeReply[m_nIndex], pszData, size);
        CHECK_WRITE_STEP_SIGNAL(transferModeReply[m_nIndex], pszData, size);
        CHECK_WRITE_STEP_SIGNAL(enableModeReply[m_nIndex], pszData, size);
        CHECK_WRITE_STEP_SIGNAL(typeAutoReply[m_nIndex], pszData, size);
        CHECK_WRITE_STEP_SIGNAL(cassetteTypeReply[m_nIndex], pszData, size);
    }
    int CLoadPort::decodePortStatusReport(CStep* pStep, const char* pszData, size_t size)
@@ -883,15 +885,37 @@
        m_portStatusReport.copyEx(portStatusReport);
        // å½“port状态为InUse, æ¯”较map
        if (m_portStatusReport.getPortStatus() == PORT_INUSE) {
            short scanMap = m_portStatusReport.getJobExistenceSlot();
            short downMap = getCassetteMap();
            if (scanMap == downMap) {
                this->sendCassetteCtrlCmd(5, nullptr, 0, 0, 0, nullptr, nullptr);
            }
            else {
                this->sendCassetteCtrlCmd(10, nullptr, 0, 0, 0, nullptr, nullptr);
                // æŠ›å‡ºåˆ°åº”用层做提示
                if (m_listener.onMapMismatch != nullptr) {
                    m_listener.onMapMismatch(this, scanMap, downMap);
                }
            }
        }
        // ç¼“å­˜Attribute,用于调试时显示信息
        unsigned int weight = 201;
        CAttributeVector attrubutes;
        CAttributeVector& attrubutes = pStep->attributeVector();
        m_portStatusReport.getAttributeVector(attrubutes, weight);
        pStep->addAttributeVector(attrubutes);
        LOGI("<CCassetteTranserStateStep>decodePortStatusReport<Status:%d, CassetteSequenceNo:%d>",
            m_portStatusReport.getPortStatus(), m_portStatusReport.getCassetteSequenceNo());
        LOGI("<CCassetteTranserStateStep>decodePortStatusReport<Status:%d, CassetteSequenceNo:%d, CassetteId:%s>",
            m_portStatusReport.getPortStatus(), m_portStatusReport.getCassetteSequenceNo(),
            m_portStatusReport.getCassetteId().c_str());
        if (m_portStatusReport.getPortStatus() == 3) {
            LOGI("<CCassetteTranserStateStep>InUse<JobExistenceSlot:%d>",
                m_portStatusReport.getJobExistenceSlot());
        }
        return nRet;
@@ -1102,6 +1126,18 @@
        m_bAutoChangeEnable = bEnable;
    }
    short CLoadPort::getCassetteMap()
    {
        short map = 0;
        for (int i = 0; i < SLOT_MAX; i++) {
            if (!m_slot[i].isEnable()) continue;
            if (m_slot[i].getContext() == nullptr) continue;
            map |= (1 << i);
        }
        return map;
    }
    /*
     * ç”Ÿæˆæµ‹è¯•用的玻璃列表
     */
@@ -1117,18 +1153,18 @@
        char szBuffer[64];
        for (int i = 0; i < 1; i++) {
        for (int i = 0; i < SLOT_MAX; i++) {
            if (!m_slot[i].isEnable()) continue;
            CJobDataS js;
            js.setCassetteSequenceNo(getNextCassetteSequenceNo());
            js.setJobSequenceNo(m_slot[i].getNo());
            sprintf_s(szBuffer, 64, "%05d%05d", js.getCassetteSequenceNo(), js.getJobSequenceNo());
            //js.setGlass1Id(szBuffer);
            js.setJobType(1);
            js.setMaterialsType((int)type);
            CGlass* pGlass = theApp.m_model.m_glassPool.allocaGlass();
            pGlass->setOriginPort(m_nIndex, i);
            pGlass->addPath(m_nID, 0);
            pGlass->processEnd(m_nID, 0);
            pGlass->setID(szBuffer);
@@ -1167,6 +1203,7 @@
            js.setGlass1Id(szBuffer);
            CGlass* pGlass = theApp.m_model.m_glassPool.allocaGlass();
            pGlass->setOriginPort(m_nIndex, nSlotIndex);
            pGlass->addPath(m_nID, 0);
            pGlass->processEnd(m_nID, 0);
            pGlass->setID(szBuffer);
SourceCode/Bond/Servo/CLoadPort.h
@@ -37,6 +37,7 @@
        void localSetCessetteType(CassetteType type);
        void localSetTransferMode(TransferMode mode);
        void localAutoChangeEnable(BOOL bEnable);
        short getCassetteMap();
    public:
        short getNextCassetteSequenceNo();
@@ -76,7 +77,8 @@
            int jobExistenceSize,
            short slotProcess,
            short jopCount,
            CJobDataA* pJobDataA);
            CJobDataA* pJobDataA,
            ONWRITED onWritedBlock);
        CStep* getCassetteCtrlCmdStep();
    private:
SourceCode/Bond/Servo/CMaster.cpp
@@ -297,11 +297,12 @@
        // å„种机器
        CLoadPort* pLoadPorts[4];
        CEFEM* pEFEM = (CEFEM*)getEquipment(EQ_ID_EFEM);
        CLoadPort* pLoadPort1 = (CLoadPort*)getEquipment(EQ_ID_LOADPORT1);
        CLoadPort* pLoadPort2 = (CLoadPort*)getEquipment(EQ_ID_LOADPORT2);
        CLoadPort* pLoadPort3 = (CLoadPort*)getEquipment(EQ_ID_LOADPORT3);
        CLoadPort* pLoadPort4 = (CLoadPort*)getEquipment(EQ_ID_LOADPORT4);
        pLoadPorts[0] = (CLoadPort*)getEquipment(EQ_ID_LOADPORT1);
        pLoadPorts[1] = (CLoadPort*)getEquipment(EQ_ID_LOADPORT2);
        pLoadPorts[2] = (CLoadPort*)getEquipment(EQ_ID_LOADPORT3);
        pLoadPorts[3] = (CLoadPort*)getEquipment(EQ_ID_LOADPORT4);
        CFliper* pFliper = (CFliper*)getEquipment(EQ_ID_FLIPER);
        CVacuumBake* pVacuumBake = (CVacuumBake*)getEquipment(EQ_ID_VACUUMBAKE);
        CAligner* pAligner = (CAligner*)getEquipment(EQ_ID_ALIGNER);
@@ -311,10 +312,10 @@
        CMeasurement* pMeasurement = (CMeasurement*)getEquipment(EQ_ID_MEASUREMENT);
        ASSERT(pEFEM);
        ASSERT(pLoadPort1);
        ASSERT(pLoadPort2);
        ASSERT(pLoadPort3);
        ASSERT(pLoadPort4);
        ASSERT(pLoadPorts[0]);
        ASSERT(pLoadPorts[1]);
        ASSERT(pLoadPorts[2]);
        ASSERT(pLoadPorts[3]);
        ASSERT(pFliper);
        ASSERT(pVacuumBake);
        ASSERT(pAligner);
@@ -544,17 +545,16 @@
                    LOGI("Arm1 %s, Arm2 %s.", rmd.armState[0] ? _T("不可用") : _T("可用"),
                        rmd.armState[1] ? _T("不可用") : _T("可用"));
                }
                CLoadPort* pEqLoadPort[] = { pLoadPort1, pLoadPort2, pLoadPort3, pLoadPort4 };
                CEquipment* pEqTar[] = { pVacuumBake, pFliper };
                if (primaryType == MaterialsType::G2) {
                    pEqTar[0] = pFliper;
                    pEqTar[1] = pVacuumBake;
                }
                for (int s = 0; s < 4; s++) {
                    if (!rmd.armState[0] && pEqLoadPort[s]->isEnable()
                        && pEqLoadPort[s]->getPortType() == PortType::Unloading
                        && pEqLoadPort[s]->getPortMode() == PortMode::ReadyToUnload) {
                        m_pActiveRobotTask = createTransferTask(pMeasurement, pEqLoadPort[s], primaryType, secondaryType);
                    if (!rmd.armState[0] && pLoadPorts[s]->isEnable()
                        && pLoadPorts[s]->getPortType() == PortType::Unloading
                        && pLoadPorts[s]->getPortMode() == PortMode::ReadyToUnload) {
                        m_pActiveRobotTask = createTransferTask(pMeasurement, pLoadPorts[s], primaryType, secondaryType);
                        if (m_pActiveRobotTask != nullptr) {
                            goto PORT_PUT;
                        }
@@ -572,6 +572,25 @@
                    LOGI("创建新任务<%s>...", strDescription.c_str());
                    continue;
                }
                // Measurement NG -> LoadPort
                // NG回原位
                if (!rmd.armState[1]) {
                    m_pActiveRobotTask = createTransferTask_restore(pMeasurement, pLoadPorts);
                    if (m_pActiveRobotTask != nullptr) {
                        m_pActiveRobotTask->pick();
                        std::string strDescription = m_pActiveRobotTask->getDescription();
                        unlock();
                        if (m_listener.onRobotTaskEvent != nullptr) {
                            m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, ROBOT_EVENT_CREATE);
                        }
                        LOGI("创建Measurement回退任务<%s>...", strDescription.c_str());
                        continue;
                    }
                }
                // BakeCooling ->Measurement
@@ -727,12 +746,30 @@
                    }
                }
                // Aligner -> LoadPort
                if (!rmd.armState[1]) {
                    m_pActiveRobotTask = createTransferTask_restore(pAligner, pLoadPorts);
                    if (m_pActiveRobotTask != nullptr) {
                        m_pActiveRobotTask->pick();
                        std::string strDescription = m_pActiveRobotTask->getDescription();
                        unlock();
                        if (m_listener.onRobotTaskEvent != nullptr) {
                            m_listener.onRobotTaskEvent(this, m_pActiveRobotTask, ROBOT_EVENT_CREATE);
                        }
                        LOGI("创建Aligner回退任务<%s>...", strDescription.c_str());
                        continue;
                    }
                }
                // LoadPort -> Aligner
                for (int s = 0; s < 4; s++) {
                    if (!rmd.armState[0] && pEqLoadPort[s]->isEnable()
                        && pEqLoadPort[s]->getPortType() == PortType::Loading
                        && pEqLoadPort[s]->getPortMode() == PortMode::ReadyToLoad) {
                        m_pActiveRobotTask = createTransferTask(pEqLoadPort[s], pAligner, primaryType, secondaryType);
                    if (!rmd.armState[0] && pLoadPorts[s]->isEnable()
                        && pLoadPorts[s]->getPortType() == PortType::Loading
                        && pLoadPorts[s]->getPortMode() == PortMode::ReadyToLoad) {
                        m_pActiveRobotTask = createTransferTask(pLoadPorts[s], pAligner, primaryType, secondaryType);
                        if (m_pActiveRobotTask != nullptr) {
                            pEFEM->setContext(m_pActiveRobotTask->getContext());
                            goto PORT_GET;
@@ -779,11 +816,12 @@
            for (auto item : m_listEquipment) {
                const StationIdentifier& station = item->getStation();
                MemoryBlock& block = item->getReadBitBlock();
                int nRet = m_cclink.ReadData2(station, (DeviceType)block.type,
                    block.start, block.size, block.buffer);
                if (0 == nRet) {
                    item->onReceiveLBData(block.buffer, block.size);
                if (block.end > block.start) {
                    int nRet = m_cclink.ReadData2(station, (DeviceType)block.type,
                        block.start, block.size, block.buffer);
                    if (0 == nRet) {
                        item->onReceiveLBData(block.buffer, block.size);
                    }
                }
            }
        }
@@ -824,7 +862,7 @@
                m_listener.onEqVcrEventReport(this, p, p2);
            }
        };
        listener.onPreFethedOutJob = [&](void* pEquipment, CJobDataB* pJobDataB) -> BOOL {
        listener.onPreFethedOutJob = [&](void* pEquipment, int port, CJobDataB* pJobDataB) -> BOOL {
            CEquipment* p = (CEquipment*)pEquipment;
@@ -857,7 +895,7 @@
            return bOk;
        };
        listener.onPreStoredJob = [&](void* pEquipment, CJobDataB* pJobDataB, short& slot) -> BOOL {
        listener.onPreStoredJob = [&](void* pEquipment, int port, CJobDataB* pJobDataB, short& slot) -> BOOL {
            CEquipment* p = (CEquipment*)pEquipment;
@@ -889,7 +927,7 @@
                else if (m_pActiveRobotTask->isRestoring() &&
                    m_pActiveRobotTask->getSrcPosition() == p->getID()) {
                    CGlass* pGlass = p->getGlassFromSlot(m_pActiveRobotTask->getSrcSlot());
                    if (pGlass == nullptr) {
                    if (pGlass == nullptr && m_pActiveRobotTask->getSrcSlot() == port) {
                        bOk = TRUE;
                        slot = m_pActiveRobotTask->getSrcSlot();
                        LOGI("<CMaster>onPreFethedOutJob, å·²æ ¡éªŒæ•°æ®ä¸€è‡´æ€§.");
@@ -974,6 +1012,10 @@
        listener.onProcessStateChanged = [&](void* pEquipment, PROCESS_STATE state) -> void {
            LOGI("<Master>onProcessStateChanged<%d>", (int)state);
        };
        listener.onMapMismatch = [&](void* pEquipment, short scanMap, short downMap) {
            LOGE("<Master-%s>Port InUse, map(%d!=%d)不一致,请检查。",
                ((CEquipment*)pEquipment)->getName().c_str(), scanMap, downMap);
        };
        pEquipment->setListener(listener);
        pEquipment->setCcLink(&m_cclink);
        m_listEquipment.push_back(pEquipment);
@@ -1028,7 +1070,7 @@
        pEquipment->setBaseAlarmId(BASE_ALARM_EFEM);
        pEquipment->setName("Fliper(G2)");
        pEquipment->setDescription("Fliper(G2).");
        pEquipment->setReadBitBlock(0x4000, 0x45ff);
        pEquipment->setReadBitBlock(0x0, 0x0);
        pEquipment->setStation(0, 255);
        addToEquipmentList(pEquipment);
@@ -1063,7 +1105,7 @@
        pEquipment->setBaseAlarmId(BASE_ALARM_EFEM);
        pEquipment->setName("Aligner");
        pEquipment->setDescription("Aligner.");
        pEquipment->setReadBitBlock(0x4000, 0x45ff);
        pEquipment->setReadBitBlock(0x0, 0x0);
        pEquipment->setStation(0, 255);
        addToEquipmentList(pEquipment);
@@ -1190,27 +1232,8 @@
        }
        // æŒ‰ä¸€å®šé¢‘率扫描LB数据
        static int i = 0;
        i++;
        /*
        if (i % (4 * 1) == 0) {
            for (auto item : m_listEquipment) {
                if (item->getID() == EQ_ID_Bonder1) {
                    const StationIdentifier& station = item->getStation();
                    MemoryBlock& block = item->getReadBitBlock();
                    int nRet = m_cclink.ReadData2(station, (DeviceType)block.type,
                        block.start, block.size, block.buffer);
                    if (0 == nRet) {
                        item->onReceiveLBData(block.buffer, block.size);
                    }
                }
            }
        }
        */
        // è‡ªåŠ¨ä¿å­˜ç¼“å­˜
        if (i % (4 * 2) == 0) {
@@ -1386,6 +1409,10 @@
        MaterialsType primaryType/* = MaterialsType::G1*/, MaterialsType secondaryType/* = MaterialsType::G2*/,
        int armNo/* = 1*/)
    {
        if (!pSrcEq->IsEnabled()) {
            return nullptr;
        }
        CRobotTask* pTask = nullptr;
        CSlot* pSrcSlot, * pTarSlot;
        pTarSlot = pTarEq->getAvailableSlotForGlass(primaryType);
@@ -1410,7 +1437,11 @@
    CRobotTask* CMaster::createTransferTask_bonder_to_bakecooling(CEquipment* pSrcEq, CEquipment* pTarEq)
    {
        std::vector<int> slots = {1, 2};
        if (!pSrcEq->IsEnabled()) {
            return nullptr;
        }
        std::vector<int> slots = {1, 3};
        CRobotTask* pTask = nullptr;
        CSlot* pSrcSlot, * pTarSlot;
@@ -1431,8 +1462,12 @@
    CRobotTask* CMaster::createTransferTask_bake_to_cooling(CEquipment* pSrcEq)
    {
        std::vector<int> slotsTar = { 3, 4 };
        std::vector<int> slotsSrc = { 1, 2 };
        if (!pSrcEq->IsEnabled()) {
            return nullptr;
        }
        std::vector<int> slotsTar = { 2, 4 };
        std::vector<int> slotsSrc = { 1, 3 };
        CRobotTask* pTask = nullptr;
        CSlot* pSrcSlot, * pTarSlot;
@@ -1453,7 +1488,11 @@
    CRobotTask* CMaster::createTransferTask_bakecooling_to_measurement(CEquipment* pSrcEq, CEquipment* pTarEq)
    {
        std::vector<int> slots = { 3, 4 };
        if (!pSrcEq->IsEnabled()) {
            return nullptr;
        }
        std::vector<int> slots = { 2, 4 };
        CRobotTask* pTask = nullptr;
        CSlot* pSrcSlot, * pTarSlot;
@@ -1472,6 +1511,42 @@
        return pTask;
    }
    CRobotTask* CMaster::createTransferTask_restore(CEquipment* pEqSrc, CLoadPort** pPorts)
    {
        if (!pEqSrc->IsEnabled()) {
            return nullptr;
        }
        CRobotTask* pTask = nullptr;
        CSlot* pSrcSlot, * pTarSlot = nullptr, * pTempSlot;
        pSrcSlot = pEqSrc->getInspFailSlot();
        if (pSrcSlot != nullptr) {
            CGlass* pGlass = (CGlass*)pSrcSlot->getContext();
            ASSERT(pGlass);
            int port, slot;
            pGlass->getOrginPort(port, slot);
            pGlass->setInspResult(pPorts[port]->getID(), 0, InspResult::Fail);
            ASSERT(0 <= port && port < 4);
            ASSERT(0 <= slot && slot < 8);
            pTempSlot = pPorts[port]->getSlot(slot);
            if (pTempSlot->getContext() == nullptr) {
                pTarSlot = pTempSlot;
            }
        }
        if (pSrcSlot != nullptr && nullptr != pTarSlot) {
            pTask = new CRobotTask();
            pTask->setContext(pSrcSlot->getContext());
            pTask->setEFEM((CEFEM*)getEquipment(EQ_ID_EFEM));
            taskSeqNo = pTask->setRobotTransferParam(taskSeqNo, 1, pSrcSlot->getPosition(),
                pTarSlot->getPosition(), pSrcSlot->getNo(), pTarSlot->getNo());
        }
        return pTask;
    }
    int CMaster::abortCurrentTask()
    {
        lock();
SourceCode/Bond/Servo/CMaster.h
@@ -96,7 +96,8 @@
        CRobotTask* createTransferTask_bonder_to_bakecooling(CEquipment* pSrcEq, CEquipment* pTarEq);
        CRobotTask* createTransferTask_bake_to_cooling(CEquipment* pSrcEq);
        CRobotTask* createTransferTask_bakecooling_to_measurement(CEquipment* pSrcEq, CEquipment* pTarEq);
        CRobotTask* createTransferTask_restore(CEquipment* pEqSrc, CLoadPort** pPorts);
    private:
        CRITICAL_SECTION m_criticalSection;
        MasterListener m_listener;
SourceCode/Bond/Servo/CMeasurement.cpp
@@ -269,7 +269,7 @@
                        if (code == ROK && pszData != nullptr && size > 0) {
                            int port = (int)(__int64)((CEqReadStep*)pFrom)->getProp("Port");
                            if (port > 0) {
                                decodeReceivedJobReport((CStep*)pFrom, port, pszData, size);
                                decodeSentOutJobReport((CStep*)pFrom, port, pszData, size);
                            }
                        }
                        return -1;
@@ -331,6 +331,55 @@
                }
            }
        }
        // process start/end report
        {
            CEqReadStep* pStep = new CEqReadStep(0x19D3F, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessStartReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_START_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0xf33);
            if (addStep(STEP_ID_JOB_PROCESS_START_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
        {
            CEqReadStep* pStep = new CEqReadStep(0x19D4C, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessEndReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_END_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0xf34);
            if (addStep(STEP_ID_JOB_PROCESS_END_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
        {
            // Panel Data Report
            CEqReadStep* pStep = new CEqReadStep(0x1A17f, 386 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodePanelDataReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_PANEL_DATA_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0x105e);
            if (addStep(STEP_ID_PANEL_DATA_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
    }
    // å¿…须要实现的虚函数,在此初始化Slot信息
SourceCode/Bond/Servo/CPageCassetteCtrlCmd.cpp
@@ -90,7 +90,9 @@
{
    ASSERT(m_pEquipment != nullptr);
    ASSERT(m_pEquipment->getID() == EQ_ID_LOADPORT1
        || m_pEquipment->getID() == EQ_ID_LOADPORT2);
        || m_pEquipment->getID() == EQ_ID_LOADPORT2
        || m_pEquipment->getID() == EQ_ID_LOADPORT3
        || m_pEquipment->getID() == EQ_ID_LOADPORT4);
    SERVO::CLoadPort* pLoadPort = (SERVO::CLoadPort*)m_pEquipment;
    short cmd = 0;
@@ -112,5 +114,14 @@
    pLoadPort->sendCassetteCtrlCmd(cmd, &jobExistence[0], jobExistenceSize, slotProcess,
        jobCount, pJobDataA);
        jobCount, pJobDataA, [&](int code) -> int {
            if (code == WOK) {
                LOGI("sendCassetteCtrlCmd æˆåŠŸ.");
            }
            else {
                LOGI("sendCassetteCtrlCmd å¤±è´¥.");
            }
            return 0;
        });
}
SourceCode/Bond/Servo/CPageGraph1.cpp
@@ -7,7 +7,19 @@
#include "afxdialogex.h"
#include "Common.h"
const std::map<SERVO::ROBOT_POSITION, RobotPositionMapping> g_positionMap = {
    { SERVO::ROBOT_POSITION::Port1,     { SERVO::ROBOT_POSITION::Port1,     1.00f,   0.00f } },
    { SERVO::ROBOT_POSITION::Port2,     { SERVO::ROBOT_POSITION::Port2,     0.90f,   0.00f } },
    { SERVO::ROBOT_POSITION::Port3,     { SERVO::ROBOT_POSITION::Port3,     0.75f,   0.00f } },
    { SERVO::ROBOT_POSITION::Port4,     { SERVO::ROBOT_POSITION::Port4,     0.60f,   0.00f } },
    { SERVO::ROBOT_POSITION::Aligner,   { SERVO::ROBOT_POSITION::Aligner,   0.40f,   0.00f } },
    { SERVO::ROBOT_POSITION::Fliper,    { SERVO::ROBOT_POSITION::Fliper,    0.25f,     0.00f } },
    { SERVO::ROBOT_POSITION::Bonder1,   { SERVO::ROBOT_POSITION::Bonder1,   0.00f,   0.00f } },
    { SERVO::ROBOT_POSITION::Bonder2,   { SERVO::ROBOT_POSITION::Bonder2,   0.00f,   180.00f } },
    { SERVO::ROBOT_POSITION::Bake,      { SERVO::ROBOT_POSITION::Bake,      0.35f,   180.00f } },
    { SERVO::ROBOT_POSITION::Cooling,   { SERVO::ROBOT_POSITION::Cooling,   0.65f,   180.00f } },
    { SERVO::ROBOT_POSITION::Measurement,{SERVO::ROBOT_POSITION::Measurement,1.00f,  180.00f } },
};
// Image
#define IMAGE_ROBOT                2
@@ -26,6 +38,11 @@
#define INDICATE_BAKE_COOLING    12
#define INDICATE_MEASUREMENT    13
// å®šæ—¶å™¨
#define TIMER_ID_DEVICE_STATUS      1   // ç”¨äºŽåˆå§‹åŒ–设备状态
#define TIMER_ID_ROBOT_STATUS       2   // ç”¨äºŽå‘¨æœŸåˆ·æ–°æœºå™¨äººä½ç½®/臂状态
#define TIMER_ID_ROBOT_ANIMATION    3    //
// CPageGraph1 å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CPageGraph1, CDialogEx)
@@ -33,11 +50,32 @@
CPageGraph1::CPageGraph1(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_PAGE_GRAPH1, pParent)
{
    m_pGraph = nullptr;
    m_pObserver = nullptr;
    m_bIsRobotMoving = FALSE;
    m_crBkgnd = PAGE_GRPAH1_BACKGROUND_COLOR;
    m_hbrBkgnd = nullptr;
    // ===== å›¾å½¢ç•Œé¢ç›¸å…³æˆå‘˜å˜é‡åˆå§‹åŒ– =====
    m_pGraph = nullptr;                                 // å›¾å½¢ç»˜å›¾å¯¹è±¡
    m_pObserver = nullptr;                              // è§‚察者对象(可能是事件观察者)
    m_crBkgnd = PAGE_GRPAH1_BACKGROUND_COLOR;           // èƒŒæ™¯é¢œè‰²
    m_hbrBkgnd = nullptr;                               // èƒŒæ™¯åˆ·å¥æŸ„
    // ===== æœºå™¨äººåŠ¨ç”»çŠ¶æ€åˆå§‹åŒ– =====
    m_bIsRobotMoving = FALSE;                           // å½“前是否正在动画移动
    m_nRobotMoveStartX = 0;                             // åŠ¨ç”»èµ·å§‹ X åæ ‡
    m_nRobotMoveEndX = 0;                               // åŠ¨ç”»ç›®æ ‡ X åæ ‡
    m_nRobotMoveSteps = 30;                             // åŠ¨ç”»æ€»æ­¥æ•°ï¼ˆåŠ¨ç”»é€Ÿåº¦æŽ§åˆ¶ï¼‰
    m_nRobotMoveCurrentStep = 0;                        // å½“前动画步数
    m_nRobotMoveStartAngle = 0.0f;                      // åŠ¨ç”»èµ·å§‹è§’åº¦
    m_nRobotMoveEndAngle = 0.0f;                        // åŠ¨ç”»ç›®æ ‡è§’åº¦
    // ===== æœºå™¨äººä¸Šä¸€æ¬¡çŠ¶æ€åˆå§‹åŒ– =====
    m_lastRobotPosition = SERVO::ROBOT_POSITION::Port1; // ä¸Šæ¬¡æœºå™¨äººä½ç½®ï¼ˆé»˜è®¤ Port1)
    m_lastArmState[0] = FALSE;                          // ä¸Šæ¬¡æœºæ¢°è‡‚1 çŠ¶æ€ï¼ˆæœªå ç”¨ï¼‰
    m_lastArmState[1] = FALSE;                          // ä¸Šæ¬¡æœºæ¢°è‡‚2 çŠ¶æ€ï¼ˆæœªå ç”¨ï¼‰
    // ===== æœºæ¢°è‡‚相对偏移量初始化(从配置中加载) =====
    m_arm1Offset = LoadArmOffset("ARM1");               // åŠ è½½æœºæ¢°è‡‚1偏移
    m_arm2Offset = LoadArmOffset("ARM2");               // åŠ è½½æœºæ¢°è‡‚2偏移
    //m_arm1Offset = { -30, -45 }; // ARM1 ä»Žä¸­å¿ƒå‘å·¦47, å‘上33
    //m_arm2Offset = { 27, -45 };     // ARM2 ä»Žä¸­å¿ƒå‘右10, å‘上33
}
CPageGraph1::~CPageGraph1()
@@ -62,6 +100,15 @@
// CPageGraph1 æ¶ˆæ¯å¤„理程序
std::string CPageGraph1::GetConfigPath()
{
    char path[MAX_PATH];
    GetModuleFileNameA(NULL, path, MAX_PATH);
    std::string exePath(path);
    std::string configDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\Config";
    CreateDirectoryA(configDir.c_str(), NULL);
    return configDir + "\\robot_offset.ini";
}
void CPageGraph1::InitRxWindows()
{
@@ -133,8 +180,8 @@
{
    CDialogEx::OnInitDialog();
    InitRxWindows();
    SetTimer(1, 3000, nullptr);
    SetTimer(TIMER_ID_DEVICE_STATUS, 3000, nullptr);
    SetTimer(TIMER_ID_ROBOT_STATUS, 1000, nullptr); // æ¯ 1000ms æ›´æ–°ä¸€æ¬¡çŠ¶æ€
    // å›¾ç¤º
    m_pGraph = CServoGraph::Hook(GetDlgItem(IDC_SERVO_GRAPH1)->GetSafeHwnd());
@@ -236,7 +283,12 @@
        newFrameColor2 = EQ_BOX_FRAME2;
        break;
    case OFFLINE:
        newBackgroundColor = RGB(222, 222, 222);
        newBackgroundColor = EQ_BOX_OFFLINE;
        newFrameColor1 = EQ_BOX_FRAME1;
        newFrameColor2 = EQ_BOX_FRAME2;
        break;
    case OCCUPIED:
        newBackgroundColor = EQ_BOX_OCCUPIED;
        newFrameColor1 = EQ_BOX_FRAME1;
        newFrameColor2 = EQ_BOX_FRAME2;
        break;
@@ -285,6 +337,8 @@
{
    CDialogEx::OnDestroy();
    KillTimer(TIMER_ID_ROBOT_STATUS);
    if (m_hbrBkgnd != nullptr) {
        ::DeleteObject(m_hbrBkgnd);
    }
@@ -293,6 +347,9 @@
        m_pObserver->unsubscribe();
        m_pObserver = NULL;
    }
    SaveArmOffset("ARM1", m_arm1Offset);
    SaveArmOffset("ARM2", m_arm2Offset);
}
void CPageGraph1::OnSize(UINT nType, int cx, int cy)
@@ -307,101 +364,97 @@
void CPageGraph1::UpdateRobotPosition(float percentage)
{
    // é™åˆ¶ç™¾åˆ†æ¯”范围在 [0, 1] ä¹‹é—´
    if (percentage < 0.0f) percentage = 0.0f;
    if (percentage > 1.0f) percentage = 1.0f;
    // æ ¹æ®ç™¾åˆ†æ¯”计算目标 X åæ ‡
    int startX = m_pGraph->GetImage(IMAGE_ROBOT)->x;
    auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
    if (!pImage) return;
    // èŽ·å–å½“å‰è§’åº¦ï¼ˆå·²é€šè¿‡ RotateRobot è®¾ç½®ï¼‰
    float angleDegrees = pImage->angle;
    float radians = angleDegrees * 3.1415926f / 180.0f;
    int startX = pImage->x;
    int endX = static_cast<int>(170 + percentage * (700 - 170));
    int y = 270;
    int cy = y + pImage->bmHeight / 2;
    int arm1Offset = 20;  // ä»Žå›¾ç‰‡åˆ°ARM1的偏移
    int arm2Offset = 73;  // ä»Žå›¾ç‰‡åˆ°ARM2的偏移
    // è®¡ç®—移动所需的时间
    // åŠ¨ç”»æ—¶é—´
    int distance = abs(endX - startX);
    int duration = static_cast<int>((distance / 100.0) * 1000);
    int duration = static_cast<int>((distance / 100.0f) * 1000);
    auto startTime = std::chrono::steady_clock::now();
    auto endTime = startTime + std::chrono::milliseconds(duration);
    // å¼€å§‹ç§»åŠ¨ï¼Œè®¾ç½®æ ‡è®°
    m_bIsRobotMoving = TRUE;
    // å¼€å§‹å¹³æ»‘移动
    while (std::chrono::steady_clock::now() < endTime) {
        auto currentTime = std::chrono::steady_clock::now();
        float progress = std::chrono::duration<float, std::milli>(currentTime - startTime).count() / duration;
        progress = min(progress, 1.0f);
        // æ ¹æ®è¿›åº¦è®¡ç®—当前位置
        int currentX = static_cast<int>(startX + progress * (endX - startX));
        m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, currentX, 270);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, currentX + arm1Offset, 294);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, currentX + arm2Offset, 294);
        int cx = currentX + pImage->bmWidth / 2;
        // åˆ·æ–°ç•Œé¢
        // æ—‹è½¬åŽçš„偏移
        int rotatedX1 = static_cast<int>(cos(radians) * m_arm1Offset.x - sin(radians) * m_arm1Offset.y);
        int rotatedY1 = static_cast<int>(sin(radians) * m_arm1Offset.x + cos(radians) * m_arm1Offset.y);
        int rotatedX2 = static_cast<int>(cos(radians) * m_arm2Offset.x - sin(radians) * m_arm2Offset.y);
        int rotatedY2 = static_cast<int>(sin(radians) * m_arm2Offset.x + cos(radians) * m_arm2Offset.y);
        // åº”用所有元素的新坐标
        m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, currentX, y);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, cx + rotatedX1, cy + rotatedY1);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, cx + rotatedX2, cy + rotatedY2);
        Invalidate();
        UpdateWindow();
        // æŽ§åˆ¶å¸§çŽ‡çº¦ä¸º 60 FPS
        std::this_thread::sleep_for(std::chrono::milliseconds(16));
    }
    // ç¡®ä¿æœ€åŽä½ç½®ç²¾ç¡®åˆ°ç›®æ ‡ä½ç½®
    m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, endX, 270);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, endX + arm1Offset, 294);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, endX + arm2Offset, 294);
    // æœ€ç»ˆä½ç½®æ ¡æ­£
    int cx = endX + pImage->bmWidth / 2;
    int rotatedX1 = static_cast<int>(cos(radians) * m_arm1Offset.x - sin(radians) * m_arm1Offset.y);
    int rotatedY1 = static_cast<int>(sin(radians) * m_arm1Offset.x + cos(radians) * m_arm1Offset.y);
    int rotatedX2 = static_cast<int>(cos(radians) * m_arm2Offset.x - sin(radians) * m_arm2Offset.y);
    int rotatedY2 = static_cast<int>(sin(radians) * m_arm2Offset.x + cos(radians) * m_arm2Offset.y);
    // ç•Œé¢é‡ç»˜
    m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, endX, y);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, cx + rotatedX1, cy + rotatedY1);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, cx + rotatedX2, cy + rotatedY2);
    Invalidate();
    // åŠ¨ç”»ç»“æŸï¼Œè®¾ç½®æ ‡è®°
    m_bIsRobotMoving = FALSE;
}
void CPageGraph1::RotateRobot(float angleInDegrees)
{
    // å°†è§’度转换为弧度
    float angleInRadians = static_cast<float>(std::acos(-1)) / 180.0f * angleInDegrees;
    // èŽ·å–æœºå™¨äººå›¾ç‰‡çš„å½“å‰åæ ‡å’Œä¸­å¿ƒ
    // èŽ·å–æœºå™¨äººå›¾ç‰‡
    auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
    if (!pImage) return;
    // æ›´æ–° Rotate å›¾ç‰‡çš„角度,确保角度保持在 [0, 360) èŒƒå›´å†…
    m_pGraph->UpdateImageAngle(IMAGE_ROBOT, static_cast<float>(fmod(pImage->angle + angleInDegrees + 360, 360)));
    // ä¿®æ­£è§’度为 0~360
    float finalAngle = fmod(angleInDegrees + 360.0f, 360.0f);
    m_pGraph->UpdateImageAngle(IMAGE_ROBOT, finalAngle);
    int cx = pImage->x + pImage->bmWidth / 2;  // å›¾ç‰‡ä¸­å¿ƒ X
    int cy = pImage->y + pImage->bmHeight / 2; // å›¾ç‰‡ä¸­å¿ƒ Y
    // è®¡ç®—中心点
    int cx = pImage->x + pImage->bmWidth / 2;
    int cy = pImage->y + pImage->bmHeight / 2;
    // æ—‹è½¬æŒ‡ç¤ºæ¡†çš„坐标
    auto* pRobot1 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM1);
    auto* pRobot2 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM2);
    // è½¬æ¢è§’度为弧度
    float radians = angleInDegrees * 3.1415926f / 180.0f;
    if (pRobot1 && pRobot2) {
        int newArmX1 = pImage->x + 20;
        int newArmY1 = 294;
    // æ—‹è½¬ offset1
    int rotatedX1 = static_cast<int>(cos(radians) * m_arm1Offset.x - sin(radians) * m_arm1Offset.y);
    int rotatedY1 = static_cast<int>(sin(radians) * m_arm1Offset.x + cos(radians) * m_arm1Offset.y);
        int newArmX2 = pImage->x + 73;
        int newArmY2 = 294;
    // æ—‹è½¬ offset2
    int rotatedX2 = static_cast<int>(cos(radians) * m_arm2Offset.x - sin(radians) * m_arm2Offset.y);
    int rotatedY2 = static_cast<int>(sin(radians) * m_arm2Offset.x + cos(radians) * m_arm2Offset.y);
        if (angleInDegrees != 0.0f) {
            // è®¡ç®—指示框1的新坐标
            newArmX1 = static_cast<int>(cx + (pRobot1->x - cx) * cos(angleInRadians) - (pRobot1->y - cy) * sin(angleInRadians));
            newArmY1 = static_cast<int>(cy + (pRobot1->x - cx) * sin(angleInRadians) + (pRobot1->y - cy) * cos(angleInRadians));
    // æ›´æ–°æŒ‡ç¤ºæ¡†
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, cx + rotatedX1, cy + rotatedY1);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, cx + rotatedX2, cy + rotatedY2);
            // è®¡ç®—指示框2的新坐标
            newArmX2 = static_cast<int>(cx + (pRobot2->x - cx) * cos(angleInRadians) - (pRobot2->y - cy) * sin(angleInRadians));
            newArmY2 = static_cast<int>(cy + (pRobot2->x - cx) * sin(angleInRadians) + (pRobot2->y - cy) * cos(angleInRadians));
        }
        // æ›´æ–°æŒ‡ç¤ºæ¡†çš„位置
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, newArmX1, newArmY1);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, newArmX2, newArmY2);
    }
    // å¼ºåˆ¶é‡ç»˜ç•Œé¢
    Invalidate();
}
@@ -435,28 +488,130 @@
    }
}
void CPageGraph1::MoveRobotToPosition(SERVO::ROBOT_POSITION position)
{
    auto it = g_positionMap.find(position);
    if (it == g_positionMap.end()) {
        TRACE("Invalid robot position: %d\n", static_cast<int>(position));
        return;
    }
    const RobotPositionMapping& mapping = it->second;
    // å¹³å°ç§»åЍ
    UpdateRobotPosition(mapping.percentage);
    // æ—‹è½¬æ–¹å‘
    RotateRobot(mapping.angle);
    m_lastRobotPosition = position;
}
void CPageGraph1::StartRobotMoveToPosition(SERVO::ROBOT_POSITION position)
{
    auto it = g_positionMap.find(position);
    if (it == g_positionMap.end()) {
        TRACE("Invalid robot position: %d\n", static_cast<int>(position));
        return;
    }
    const RobotPositionMapping& mapping = it->second;
    auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
    if (!pImage) return;
    m_nRobotMoveStartX = pImage->x;
    m_nRobotMoveEndX = static_cast<int>(170 + mapping.percentage * (700 - 170));
    m_nRobotMoveStartAngle = pImage->angle;     // èµ·å§‹è§’度(当前角度)
    m_nRobotMoveEndAngle = mapping.angle;       // ç›®æ ‡è§’度
    m_nRobotMoveCurrentStep = 0;
    m_targetRobotPosition = position;
    m_bIsRobotMoving = TRUE;
    SetTimer(TIMER_ID_ROBOT_ANIMATION, 16, nullptr);
}
POINT CPageGraph1::LoadArmOffset(const std::string& armName)
{
    std::string iniPath = GetConfigPath();
    POINT pt;
    pt.x = GetPrivateProfileIntA("Offsets", (armName + "_X").c_str(), 0, iniPath.c_str());
    pt.y = GetPrivateProfileIntA("Offsets", (armName + "_Y").c_str(), 0, iniPath.c_str());
    return pt;
}
void CPageGraph1::SaveArmOffset(const std::string& armName, const POINT& pt)
{
    std::string iniPath = GetConfigPath();
    char buf[16];
    sprintf_s(buf, "%d", pt.x);
    WritePrivateProfileStringA("Offsets", (armName + "_X").c_str(), buf, iniPath.c_str());
    sprintf_s(buf, "%d", pt.y);
    WritePrivateProfileStringA("Offsets", (armName + "_Y").c_str(), buf, iniPath.c_str());
}
void CPageGraph1::OnGraphItemClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
    BYSERVOGRAPH_NMHDR* pGraphNmhdr = reinterpret_cast<BYSERVOGRAPH_NMHDR*>(pNMHDR);
    CString s; s.Format(_T("OnGraphItemClicked %d"), pGraphNmhdr->dwData);
    SERVO::CEquipment* pEquipment = (SERVO::CEquipment*)m_pGraph->GetIndicateBoxData(pGraphNmhdr->dwData);
    if (pEquipment != nullptr) {
        theApp.m_model.notifyPtr(RX_CODE_SELECT_EQUIPMENT, pEquipment);
    }
    // ç§»åŠ¨åˆ°æŒ‡å®šä½ç½® (测试使用)
    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;
}
void CPageGraph1::OnTimer(UINT_PTR nIDEvent)
{
    if (1 == nIDEvent) {
        KillTimer(1);
    if (TIMER_ID_DEVICE_STATUS == nIDEvent) {
        KillTimer(TIMER_ID_DEVICE_STATUS);
        // æ›´æ–°çŠ¶æ€
        {
            SERVO::CEquipment* pEquipment = (SERVO::CEFEM*)theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
            ASSERT(pEquipment);
            DeviceStatus status = pEquipment->isAlive() ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
            UpdateDeviceStatus(INDICATE_FLIPER, status);
            UpdateDeviceStatus(INDICATE_ALIGNER, status);
            UpdateDeviceStatus(INDICATE_LPORT1, status);
            UpdateDeviceStatus(INDICATE_LPORT2, status);
            UpdateDeviceStatus(INDICATE_LPORT3, status);
            UpdateDeviceStatus(INDICATE_LPORT4, status);
            UpdateDeviceStatus(INDICATE_ROBOT_ARM1, status);
            UpdateDeviceStatus(INDICATE_ROBOT_ARM2, status);
        }
@@ -474,6 +629,95 @@
            DeviceStatus status = pEquipment->isAlive() ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
            UpdateDeviceStatus(INDICATE_BONDER2, status);
        }
        {
            SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_VACUUMBAKE);
            ASSERT(pEquipment);
            DeviceStatus status = pEquipment->isAlive() ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
            UpdateDeviceStatus(INDICATE_VACUUM_BAKE, status);
        }
        {
            SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_BAKE_COOLING);
            ASSERT(pEquipment);
            DeviceStatus status = pEquipment->isAlive() ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
            UpdateDeviceStatus(INDICATE_BAKE_COOLING, status);
        }
        {
            SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_MEASUREMENT);
            ASSERT(pEquipment);
            DeviceStatus status = pEquipment->isAlive() ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
            UpdateDeviceStatus(INDICATE_MEASUREMENT, status);
        }
    }
    else if (nIDEvent == TIMER_ID_ROBOT_STATUS) {
        SERVO::CEFEM* pEFEM = (SERVO::CEFEM*)theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
        if (!pEFEM || !pEFEM->isAlive()) {
            return;
        }
        // å¦‚果设备在线,那么更新 ARM çŠ¶æ€
        SERVO::RMDATA& robotData = pEFEM->getRobotMonitoringData();
        if (m_lastArmState[0] != robotData.armState[0]) {
            m_lastArmState[0] = robotData.armState[0];
            DeviceStatus arm1Status;
            arm1Status = robotData.armState[0] ? DeviceStatus::OCCUPIED : DeviceStatus::ONLINE;
            UpdateDeviceStatus(INDICATE_ROBOT_ARM1, arm1Status);
        }
        if (m_lastArmState[1] != robotData.armState[1]) {
            m_lastArmState[1] = robotData.armState[1];
            DeviceStatus arm2Status;
            arm2Status = robotData.armState[1] ? DeviceStatus::OCCUPIED : DeviceStatus::ONLINE;
            UpdateDeviceStatus(INDICATE_ROBOT_ARM2, arm2Status);
        }
        // ä½ç½®ä¿¡æ¯çŠ¶æ€æ˜¾ç¤º
        if (robotData.position != m_lastRobotPosition) {
            StartRobotMoveToPosition(robotData.position);
        }
    }
    else if (nIDEvent == TIMER_ID_ROBOT_ANIMATION) {
        if (!m_bIsRobotMoving) {
            KillTimer(TIMER_ID_ROBOT_ANIMATION);
            return;
        }
        m_nRobotMoveCurrentStep++;
        float progress = static_cast<float>(m_nRobotMoveCurrentStep) / m_nRobotMoveSteps;
        if (progress >= 1.0f) {
            progress = 1.0f;
            m_bIsRobotMoving = FALSE;
            KillTimer(TIMER_ID_ROBOT_ANIMATION);
            m_lastRobotPosition = m_targetRobotPosition;
        }
        // å¹³æ»‘计算位置
        int currentX = static_cast<int>(m_nRobotMoveStartX + progress * (m_nRobotMoveEndX - m_nRobotMoveStartX));
        auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
        if (!pImage) return;
        int cx = currentX + pImage->bmWidth / 2;
        int y = 270;
        int cy = y + pImage->bmHeight / 2;
        // å¹³æ»‘计算角度
        float currentAngle = m_nRobotMoveStartAngle + progress * (m_nRobotMoveEndAngle - m_nRobotMoveStartAngle);
        float radians = currentAngle * 3.1415926f / 180.0f;
        int rotatedX1 = static_cast<int>(cos(radians) * m_arm1Offset.x - sin(radians) * m_arm1Offset.y);
        int rotatedY1 = static_cast<int>(sin(radians) * m_arm1Offset.x + cos(radians) * m_arm1Offset.y);
        int rotatedX2 = static_cast<int>(cos(radians) * m_arm2Offset.x - sin(radians) * m_arm2Offset.y);
        int rotatedY2 = static_cast<int>(sin(radians) * m_arm2Offset.x + cos(radians) * m_arm2Offset.y);
        m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, currentX, y);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, cx + rotatedX1, cy + rotatedY1);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, cx + rotatedX2, cy + rotatedY2);
        m_pGraph->UpdateImageAngle(IMAGE_ROBOT, currentAngle);
        Invalidate();
    }
    CDialogEx::OnTimer(nIDEvent);
SourceCode/Bond/Servo/CPageGraph1.h
@@ -1,12 +1,19 @@
#pragma once
#include "ServoGraph.h"
#include "ServoCommo.h"
enum DeviceStatus {
    ONLINE,       // åœ¨çº¿
    OFFLINE,      // ç¦»çº¿
    OCCUPIED      // æœ‰ç‰‡ï¼ˆå ç”¨ï¼‰
};
struct RobotPositionMapping {
    SERVO::ROBOT_POSITION position;
    float percentage;
    float angle;
    int arm; // 0 è¡¨ç¤º ARM1,1 è¡¨ç¤º ARM2
};
// CPageGraph1 å¯¹è¯æ¡†
@@ -20,11 +27,16 @@
public:
    std::string GetConfigPath();
    void InitRxWindows();
    void UpdateDeviceStatus(int id, DeviceStatus status);
    void UpdateRobotPosition(float percentage);
    void RotateRobot(float angleInDegrees);
    void BindEquipmentToGraph();
    void MoveRobotToPosition(SERVO::ROBOT_POSITION position);
    void StartRobotMoveToPosition(SERVO::ROBOT_POSITION position);
    POINT LoadArmOffset(const std::string& armName);
    void SaveArmOffset(const std::string& armName, const POINT& pt);
private:
    IObserver* m_pObserver;
@@ -33,6 +45,36 @@
    COLORREF m_crBkgnd;
    HBRUSH m_hbrBkgnd;
    // ===== æœºå™¨äººåŠ¨ç”»ç›¸å…³æˆå‘˜å˜é‡ =====
    // åŠ¨ç”»ç›®æ ‡ä½ç½®ï¼ˆæœºå™¨äººç›®æ ‡ä½ç½®æžšä¸¾ï¼‰
    SERVO::ROBOT_POSITION m_targetRobotPosition;
    // åŠ¨ç”»èµ·å§‹å’Œç›®æ ‡ X åæ ‡ï¼ˆæ°´å¹³ä½ç§»ï¼‰
    int m_nRobotMoveStartX;
    int m_nRobotMoveEndX;
    // åŠ¨ç”»æ­¥æ•°æŽ§åˆ¶ï¼ˆæ€»æ­¥æ•° & å½“前步)
    int m_nRobotMoveSteps;          // åŠ¨ç”»æ€»æ­¥æ•°ï¼ˆæŽ§åˆ¶åŠ¨ç”»é€Ÿåº¦ï¼‰
    int m_nRobotMoveCurrentStep;    // å½“前动画步数进度
    // åŠ¨ç”»èµ·å§‹å’Œç›®æ ‡è§’åº¦ï¼ˆæ—‹è½¬è§’åº¦ï¼Œå•ä½ï¼šåº¦ï¼‰
    float m_nRobotMoveStartAngle;   // èµ·å§‹è§’度
    float m_nRobotMoveEndAngle;     // ç›®æ ‡è§’度(旋转角度)
    // ===== æœºå™¨äººçŠ¶æ€ç›¸å…³æˆå‘˜å˜é‡ =====
    // ä¸Šä¸€æ¬¡å·²å®Œæˆçš„位置(用于判断是否需要移动)
    SERVO::ROBOT_POSITION m_lastRobotPosition;
    // ä¸Šä¸€æ¬¡å·²æ›´æ–°çš„æœºå™¨äºº ARM å ç”¨çŠ¶æ€ï¼ˆä¸¤ä¸ªæœºæ¢°è‡‚ï¼‰
    BOOL m_lastArmState[2];
    // ä¸¤ä¸ªæœºæ¢°è‡‚相对于机器人中心的偏移(像素坐标)
    POINT m_arm1Offset;
    POINT m_arm2Offset;
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_PAGE_GRAPH1 };
SourceCode/Bond/Servo/CPageGraph2.cpp
@@ -159,7 +159,9 @@
        }
        if (pEquipment->getID() == EQ_ID_LOADPORT1
            || pEquipment->getID() == EQ_ID_LOADPORT2) {
            || pEquipment->getID() == EQ_ID_LOADPORT2
            || pEquipment->getID() == EQ_ID_LOADPORT3
            || pEquipment->getID() == EQ_ID_LOADPORT4) {
            CPageCassetteCtrlCmd* pPage = new CPageCassetteCtrlCmd();
            pPage->setEquipment(pEquipment);
            pPage->Create(IDD_PAGE_CASSETTE_CTRL_CMD);
@@ -265,14 +267,14 @@
        else if (nCmd == ID_EQSGRAPHITEM_TEST4) {
            SERVO::CEquipment* pEquipment = (SERVO::CEquipment*)pItem->pData;
            if (pEquipment != nullptr) {
                pEquipment->setLinkSignal(0, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignal(0, SIGNAL_UPSTREAM_TROUBLE, FALSE);
                pEquipment->setLinkSignal(0, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignal(0, SIGNAL_SEND_ABLE, TRUE);
                pEquipment->setLinkSignal(1, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignal(1, SIGNAL_UPSTREAM_TROUBLE, FALSE);
                pEquipment->setLinkSignal(1, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignal(1, SIGNAL_SEND_ABLE, TRUE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_UPSTREAM_TROUBLE, FALSE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_SEND_ABLE, TRUE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_UPSTREAM_TROUBLE, FALSE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_SEND_ABLE, TRUE);
            }
            if (pEquipment != nullptr && (pEquipment->getID() == EQ_ID_Bonder1
@@ -288,14 +290,14 @@
        else if (nCmd == ID_EQSGRAPHITEM_TEST5) {
            SERVO::CEquipment* pEquipment = (SERVO::CEquipment*)pItem->pData;
            if (pEquipment != nullptr) {
                pEquipment->setLinkSignal(0, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignal(0, SIGNAL_UPSTREAM_TROUBLE, TRUE);
                pEquipment->setLinkSignal(0, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignal(0, SIGNAL_SEND_ABLE, FALSE);
                pEquipment->setLinkSignal(1, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignal(1, SIGNAL_UPSTREAM_TROUBLE, TRUE);
                pEquipment->setLinkSignal(1, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignal(1, SIGNAL_SEND_ABLE, FALSE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_UPSTREAM_TROUBLE, TRUE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignalUpstream(0, SIGNAL_SEND_ABLE, FALSE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_UPSTREAM_INLINE, TRUE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_UPSTREAM_TROUBLE, TRUE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_INTERLOCK, TRUE);
                pEquipment->setLinkSignalUpstream(1, SIGNAL_SEND_ABLE, FALSE);
            }
        }
        else if (nCmd == ID_EQSGRAPHITEM_TEST6) {
@@ -379,7 +381,7 @@
            // è¯·æ±‚主配方列表
            if (pEquipment != nullptr) {
                pEquipment->masterRecipeListRequest(0);
                pEquipment->masterRecipeListRequest(0, nullptr);
            }            
        }
SourceCode/Bond/Servo/CPageLinkSignal.cpp
@@ -19,6 +19,7 @@
CPageLinkSignal::CPageLinkSignal(CWnd* pParent /*=nullptr*/)
    : CHMPropertyPage(IDD_PAGE_LINK_SIGNAL, pParent)
    , m_bEnable(TRUE)
{
    m_pEquipment = nullptr;
}
@@ -30,6 +31,7 @@
void CPageLinkSignal::DoDataExchange(CDataExchange* pDX)
{
    CHMPropertyPage::DoDataExchange(pDX);
    DDX_Check(pDX, IDC_CHECK_ENABLE, m_bEnable);
}
@@ -38,6 +40,7 @@
    ON_WM_DESTROY()
    ON_WM_SIZE()
    ON_WM_TIMER()
    ON_BN_CLICKED(IDC_CHECK_ENABLE, &CPageLinkSignal::OnClickedCheckEnable)
END_MESSAGE_MAP()
@@ -56,6 +59,12 @@
    KillTimer(TIMER_ID_SIGNAL_UPDATE);
    SetTimer(TIMER_ID_SIGNAL_UPDATE, TIMER_INTERVAL_MS, nullptr);
    if (m_pEquipment) {
        m_bEnable = m_pEquipment->IsEnabled();
        UpdateData(FALSE);
    }
    return TRUE;  // return TRUE unless you set the focus to a control
                  // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
@@ -139,6 +148,14 @@
            BTN_Y - 36,
            rcItem.Width(), rcItem.Height());
    }
    pItem = GetDlgItem(IDC_CHECK_ENABLE);
    if (pItem) {
        int nCheckboxX = max(0, BTN_X - 50);
        int nCheckboxY = max(0, BTN_Y - rcItem.Height() - 30);
        pItem->GetWindowRect(&rcItem);
        pItem->MoveWindow(nCheckboxX, nCheckboxY, rcItem.Width(), rcItem.Height());
    }
}
void CPageLinkSignal::OnTimer(UINT_PTR nIDEvent)
@@ -158,7 +175,7 @@
    for (int nRow = 0; nRow < 8; ++nRow) {
        for (int nCol = 0; nCol < 8; ++nCol) {
            BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol);
            BOOL bCurrentState = m_pEquipment->isLinkSignalUpstreamOn(nRow, nCol) && m_pEquipment->IsEnabled();
            UpdateSignalState(nRow, nCol, bCurrentState);
        }
    }
@@ -177,4 +194,9 @@
    }
}
void CPageLinkSignal::OnClickedCheckEnable()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    UpdateData(TRUE);
    m_pEquipment->SetEnable(m_bEnable);
}
SourceCode/Bond/Servo/CPageLinkSignal.h
@@ -21,6 +21,7 @@
private:
    SERVO::CEquipment* m_pEquipment;
    CBlButton* m_pBtn[8][8];
    BOOL m_bEnable;
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
@@ -37,4 +38,5 @@
    afx_msg void OnDestroy();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    afx_msg void OnClickedCheckEnable();
};
SourceCode/Bond/Servo/CPagePortProperty.cpp
@@ -9,6 +9,9 @@
#include "MsgDlg.h"
#define DELAY_CLOSE        2000
// CPagePortProperty å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CPagePortProperty, CHMPropertyPage)
@@ -130,6 +133,7 @@
    // TODO: åœ¨æ­¤å¤„添加消息处理程序代码
}
int g_nMsgDlgShow = 0;
void CPagePortProperty::OnBnClickedCheckEnable()
{
    BOOL bCheck = ((CButton*)GetDlgItem(IDC_CHECK_ENABLE))->GetCheck() == BST_CHECKED;
@@ -137,6 +141,7 @@
    // enable port
    g_nMsgDlgShow = 0;
    CMsgDlg msgDlg("请等待", "正在操作,请等待...");
    ASSERT(m_pPort != nullptr);
@@ -146,28 +151,29 @@
        if (code == WOK) {
            LOGI("%s Port æˆåŠŸ.", bCheck ? _T("enable") : _T("disable"));
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("%s Port æˆåŠŸ."), bCheck ? _T("enable") : _T("disable"));
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_SUCCEED);
            msgDlg.SetTitle(_T("操作成功"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(0);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("%s Port æˆåŠŸ."), bCheck ? _T("enable") : _T("disable"));
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_SUCCEED);
                msgDlg.SetTitle(_T("操作成功"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(0);
            }
        }
        else {
            LOGI("%s Port å¤±è´¥ï¼Œcode:%d", bCheck ? _T("enable") : _T("disable"), code);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("%s Port å¤±è´¥ï¼Œcode:%d"), bCheck ? _T("enable") : _T("disable"), code);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_ERROR);
            msgDlg.SetTitle(_T("操作失败"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(-1);
            
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("%s Port å¤±è´¥ï¼Œcode:%d"), bCheck ? _T("enable") : _T("disable"), code);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_ERROR);
                msgDlg.SetTitle(_T("操作失败"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(-1);
            }
            // è¿˜åŽŸæŽ§ä»¶çŠ¶æ€
            EnableCtrls(!bCheck);
            ((CButton*)GetDlgItem(IDC_CHECK_ENABLE))->SetCheck(!bCheck ? BST_CHECKED : BST_UNCHECKED);
@@ -177,10 +183,12 @@
        });
    msgDlg.DoModal();
    g_nMsgDlgShow = 1;
}
void CPagePortProperty::OnCbnSelchangeComboPortType()
{
    g_nMsgDlgShow = 0;
    CMsgDlg msgDlg("请等待", "正在操作,请等待...");
    msgDlg.SetData((DWORD_PTR)this);
@@ -193,36 +201,40 @@
        if (code == WOK) {
            LOGI("设置Port Type(%d)成功.", index + 1);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Port Type(%d)成功"), index + 1);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_SUCCEED);
            msgDlg.SetTitle(_T("操作成功"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(0);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Port Type(%d)成功"), index + 1);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_SUCCEED);
                msgDlg.SetTitle(_T("操作成功"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(0);
            }
        }
        else {
            LOGI("设置Port Type(%d)失败,code:%d", index + 1, code);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Port Type(%d)失败,code:%d"), index + 1, code);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_ERROR);
            msgDlg.SetTitle(_T("操作失败"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(-1);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Port Type(%d)失败,code:%d"), index + 1, code);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_ERROR);
                msgDlg.SetTitle(_T("操作失败"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(-1);
            }
        }
        return 0;
    });
    msgDlg.DoModal();
    g_nMsgDlgShow = 1;
}
void CPagePortProperty::OnCbnSelchangeComboPortMode()
{
    g_nMsgDlgShow = 0;
    CMsgDlg msgDlg("请等待", "正在操作,请等待...");
    msgDlg.SetData((DWORD_PTR)this);
@@ -235,126 +247,137 @@
        if (code == WOK) {
            LOGI("设置Port Mode(%d)成功.", index);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Port Mode(%d)成功"), index);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_SUCCEED);
            msgDlg.SetTitle(_T("操作成功"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(0);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Port Mode(%d)成功"), index);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_SUCCEED);
                msgDlg.SetTitle(_T("操作成功"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(0);
            }
        }
        else {
            LOGI("设置Port Mode(%d)失败,code:%d", index, code);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Port Mode(%d)失败,code:%d"), index, code);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_ERROR);
            msgDlg.SetTitle(_T("操作失败"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(-1);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Port Mode(%d)失败,code:%d"), index, code);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_ERROR);
                msgDlg.SetTitle(_T("操作失败"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(-1);
            }
        }
        return 0;
        });
    msgDlg.DoModal();
    g_nMsgDlgShow = 1;
}
void CPagePortProperty::OnCbnSelchangeComboPortCassertType()
{
    g_nMsgDlgShow = 0;
    CMsgDlg msgDlg("请等待", "正在操作,请等待...");
    msgDlg.SetData((DWORD_PTR)this);
    ASSERT(m_pPort != nullptr);
    int index = ((CComboBox*)GetDlgItem(IDC_COMBO_PORT_CASSERT_TYPE))->GetCurSel();
    m_pPort->setPortMode(SERVO::PortMode(index + 1), [&](int code) -> int {
    m_pPort->setCassetteType(SERVO::CassetteType(index + 1), [&](int code) -> int {
        Sleep(100);
        CString strMsg;
        if (code == WOK) {
            LOGI("设置Cassette type(%d)成功.", index + 1);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Cassette type(%d)成功"), index + 1);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_SUCCEED);
            msgDlg.SetTitle(_T("操作成功"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(0);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Cassette type(%d)成功"), index + 1);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_SUCCEED);
                msgDlg.SetTitle(_T("操作成功"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(0);
            }
        }
        else {
            LOGI("设置Cassette type(%d)失败,code:%d", index + 1, code);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Cassette type(%d)失败,code:%d"), index + 1, code);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_ERROR);
            msgDlg.SetTitle(_T("操作失败"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(-1);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Cassette type(%d)失败,code:%d"), index + 1, code);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_ERROR);
                msgDlg.SetTitle(_T("操作失败"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(-1);
            }
        }
        return 0;
        });
    msgDlg.DoModal();
    g_nMsgDlgShow = 1;
}
void CPagePortProperty::OnCbnSelchangeComboPortTransferMode()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    g_nMsgDlgShow = 0;
    CMsgDlg msgDlg("请等待", "正在操作,请等待...");
    msgDlg.SetData((DWORD_PTR)this);
    ASSERT(m_pPort != nullptr);
    int index = ((CComboBox*)GetDlgItem(IDC_COMBO_PORT_TRANSFER_MODE))->GetCurSel();
    m_pPort->setPortMode(SERVO::PortMode(index + 1), [&](int code) -> int {
    m_pPort->setTransferMode(SERVO::TransferMode(index + 1), [&](int code) -> int {
        Sleep(100);
        CString strMsg;
        if (code == WOK) {
            LOGI("设置Transfer mode(%d)成功.", index + 1);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Transfer mode(%d)成功"), index + 1);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_SUCCEED);
            msgDlg.SetTitle(_T("操作成功"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(0);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Transfer mode(%d)成功"), index + 1);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_SUCCEED);
                msgDlg.SetTitle(_T("操作成功"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(0);
            }
        }
        else {
            LOGI("设置Transfer mode(%d)失败,code:%d", index + 1, code);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("设置Transfer mode(%d)失败,code:%d"), index + 1, code);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_ERROR);
            msgDlg.SetTitle(_T("操作失败"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(-1);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("设置Transfer mode(%d)失败,code:%d"), index + 1, code);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_ERROR);
                msgDlg.SetTitle(_T("操作失败"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(-1);
            }
        }
        return 0;
        });
    msgDlg.DoModal();
    g_nMsgDlgShow = 1;
}
void CPagePortProperty::OnBnClickedCheckAutoChange()
{
    BOOL bCheck = ((CButton*)GetDlgItem(IDC_CHECK_AUTO_CHANGE))->GetCheck() == BST_CHECKED;
    EnableCtrls(bCheck);
    // enable port
    g_nMsgDlgShow = 0;
    CMsgDlg msgDlg("请等待", "正在操作,请等待...");
    ASSERT(m_pPort != nullptr);
@@ -365,26 +388,28 @@
        if (code == WOK) {
            LOGI("%s Auto Change æˆåŠŸ.", bCheck ? _T("enable") : _T("disable"));
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("%s Auto Change æˆåŠŸ."), bCheck ? _T("enable") : _T("disable"));
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_SUCCEED);
            msgDlg.SetTitle(_T("操作成功"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(0);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("%s Auto Change æˆåŠŸ."), bCheck ? _T("enable") : _T("disable"));
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_SUCCEED);
                msgDlg.SetTitle(_T("操作成功"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(0);
            }
        }
        else {
            LOGI("%s Auto Change å¤±è´¥ï¼Œcode:%d", bCheck ? _T("enable") : _T("disable"), code);
            msgDlg.DelayClose(3000);
            strMsg.Format(_T("%s Auto Change å¤±è´¥ï¼Œcode:%d"), bCheck ? _T("enable") : _T("disable"), code);
            msgDlg.DelayClose(3000);
            msgDlg.SetIcon(MSG_BOX_ERROR);
            msgDlg.SetTitle(_T("操作失败"));
            msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
            msgDlg.SetMarquee(FALSE, 0);
            msgDlg.SetCompleteCode(-1);
            if (g_nMsgDlgShow == 0 && ::IsWindow(msgDlg.GetSafeHwnd())) {
                strMsg.Format(_T("%s Auto Change å¤±è´¥ï¼Œcode:%d"), bCheck ? _T("enable") : _T("disable"), code);
                msgDlg.DelayClose(DELAY_CLOSE);
                msgDlg.SetIcon(MSG_BOX_ERROR);
                msgDlg.SetTitle(_T("操作失败"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(-1);
            }
            // è¿˜åŽŸæŽ§ä»¶çŠ¶æ€
            ((CButton*)GetDlgItem(IDC_CHECK_AUTO_CHANGE))->SetCheck(!bCheck ? BST_CHECKED : BST_UNCHECKED);
@@ -394,6 +419,7 @@
        });
    msgDlg.DoModal();
    g_nMsgDlgShow = 1;
}
void CPagePortProperty::EnableCtrls(BOOL bEnable)
SourceCode/Bond/Servo/CPath.cpp
@@ -10,6 +10,7 @@
        m_timeOut = 0;
        m_timeIn = CToolUnits::getTimestamp();
        m_bProcessed = FALSE;
        m_inspResult = InspResult::NotInspected;
        m_pPrev = nullptr;
        m_pNext = nullptr;
    }
@@ -20,9 +21,10 @@
        m_nUnit = nUnit;
        m_timeOut = 0;
        m_timeIn = CToolUnits::getTimestamp();
        m_bProcessed = FALSE;
        m_inspResult = InspResult::NotInspected;
        m_pPrev = nullptr;
        m_pNext = nullptr;
        m_bProcessed = FALSE;
    }
    CPath::~CPath()
@@ -52,17 +54,21 @@
            ar << m_timeIn;
            ar << m_timeOut;
            ar << m_bProcessed;
            ar << (int)m_inspResult;
            ar << (ULONGLONG)m_pNext;
            if (m_pNext != nullptr) {
                m_pNext->serialize(ar);
            }
        }
        else {
            int temp;
            ar >> m_nEqID;
            ar >> m_nUnit;
            ar >> m_timeIn;
            ar >> m_timeOut;
            ar >> m_bProcessed;
            ar >> temp; m_inspResult = (InspResult)temp;
            ULONGLONG ulNext;
            ar >> ulNext;
            if ((CPath*)ulNext != nullptr) {
@@ -109,6 +115,16 @@
        return m_bProcessed;
    }
    void CPath::setInspResult(InspResult result)
    {
        m_inspResult = result;
    }
    InspResult CPath::getInspResult()
    {
        return m_inspResult;
    }
    CPath* CPath::getPrev()
    {
        return m_pPrev;
SourceCode/Bond/Servo/CPath.h
@@ -1,4 +1,5 @@
#pragma once
#include "ServoCommo.h"
namespace SERVO {
@@ -24,6 +25,8 @@
        ULONGLONG getOutTime();
        void processEnd();
        BOOL isProcessEnd();
        void setInspResult(InspResult result);
        InspResult getInspResult();
    private:    
        unsigned int m_nEqID;
@@ -31,6 +34,7 @@
        ULONGLONG m_timeIn;
        ULONGLONG m_timeOut;
        BOOL m_bProcessed;
        InspResult m_inspResult;
        CPath* m_pPrev;
        CPath* m_pNext;
    };
SourceCode/Bond/Servo/CPortStatusReport.cpp
@@ -237,6 +237,11 @@
        return false;
    }
    short CPortStatusReport::getJobExistenceSlot()
    {
        return m_nJobExistenceSlot[0];
    }
    void CPortStatusReport::WriteString(CArchive& ar, std::string& string)
    {
        CString strTemp = string.c_str();
SourceCode/Bond/Servo/CPortStatusReport.h
@@ -28,6 +28,7 @@
    public:
        bool canPickFromPort();
        bool isJobExistenceSlot();
        short getJobExistenceSlot();
    private:
        void WriteString(CArchive& ar, std::string& string);
@@ -40,7 +41,7 @@
        short m_nJobExistenceSlot[12];        // 12 * 16 = 192 ä¸ªslot是否存在glass
        short m_nLoadingCassetteType;        // 1: Actual Cassette 2 : Empty Cassette
        short m_nQTimeFlag;
        short m_nCassetteMappingState;        // 1: mapping use  2: mapping not use
        short m_nCassetteMappingState;        // 1: mapping use, 2: mapping not use
        short m_nCassetteStatus;
    };
}
SourceCode/Bond/Servo/CRecipeList.cpp
@@ -33,6 +33,9 @@
            reset();
            return MRLRC_GROUP_COUNT_NG;
        }
        if (currentGroup == 0) {
            reset();
        }
        if (m_nCurrentGroupCount + 1 > currentGroup) {
            return MRLRC_DUPLICATION_GROUP_COUNT_NG;
        }
SourceCode/Bond/Servo/CRecipesManager.cpp
@@ -20,6 +20,7 @@
        m_hWorkStop = nullptr;
        m_hWorkThreadHandle = nullptr;
        m_nTimeoutCount = 0;
        m_onSyncingStateChanged = nullptr;
        ::InitializeCriticalSection(&m_cs);
    }
@@ -62,6 +63,9 @@
        m_nTimeoutCount = 0;
        unlock();
        if (m_onSyncingStateChanged != nullptr) {
            m_onSyncingStateChanged(m_nSyncStatus);
        }
        if (m_hWorkStop == nullptr) {
            m_hWorkStop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
@@ -78,6 +82,11 @@
        m_nSyncStatus = SS_FAILED;
        m_nTimeoutCount = 0;
        unlock();
        if (m_onSyncingStateChanged != nullptr) {
            m_onSyncingStateChanged(m_nSyncStatus);
        }
    }
    short CRecipesManager::decodeRecipeListReport(const char* pszData, size_t size)
@@ -144,17 +153,19 @@
            int nRet = pRecipeList->addRecipePacket(toatlGroupCount, currentGroupCount, pszIdsData, 250 * 2);
            if (MRLRC_CURRENT_RECIPE_COMPLETE == nRet) {
                lock();
                if (m_nTotalMasterRecipeCount == m_mapRecipesTemp.size()) {
                    for (auto item : m_mapRecipes) {
                        delete item.second;
                    }
                    m_mapRecipes = m_mapRecipesTemp;
                    m_mapRecipesTemp.clear();
                    m_nSyncStatus = SS_COMPLETE;
                    unlock();
                    return MRLRC_OK;
                for (auto item : m_mapRecipes) {
                    delete item.second;
                }
                m_mapRecipes = m_mapRecipesTemp;
                m_mapRecipesTemp.clear();
                m_nSyncStatus = SS_COMPLETE;
                unlock();
                if (m_onSyncingStateChanged != nullptr) {
                    m_onSyncingStateChanged(m_nSyncStatus);
                }
                return MRLRC_OK;
            }
            else if (MRLRC_CONTINUE == nRet) {
                return MRLRC_CONTINUE;
@@ -268,6 +279,18 @@
        return iter->second;
    }
    CRecipeList* CRecipesManager::getRecipeList(int unitNo)
    {
        auto iter = m_mapRecipes.find(unitNo);
        if (iter == m_mapRecipes.end()) return nullptr;
        return iter->second;
    }
    void CRecipesManager::setOnSyncingStateChanged(ONSYNCINGSTATECHANGED block)
    {
        m_onSyncingStateChanged = block;
    }
    unsigned CRecipesManager::TimeoutCheckWorkingProc()
    {
        while (1) {
SourceCode/Bond/Servo/CRecipesManager.h
@@ -1,6 +1,7 @@
#pragma once
#include <map>
#include "CRecipeList.h"
#include <functional>
#define SS_NONE                    0
@@ -10,6 +11,8 @@
#define SS_FAILED                4
namespace SERVO {
    typedef std::function<void(int state)> ONSYNCINGSTATECHANGED;
    class CRecipesManager
    {
    public:
@@ -17,12 +20,14 @@
        virtual ~CRecipesManager();
    public:
        void setOnSyncingStateChanged(ONSYNCINGSTATECHANGED block);
        unsigned TimeoutCheckWorkingProc();
        int syncing();
        void syncFailed();
        short decodeRecipeListReport(const char* pszData, size_t size);
        short decodeRecipeParameterReport(const char* pszData, size_t size);
        CRecipeList* getRecipeListFromTemp(int unitNo);
        CRecipeList* getRecipeList(int unitNo);
    public:
        inline void lock() { ::EnterCriticalSection(&m_cs); };
@@ -39,6 +44,7 @@
        int m_nTotalParameterCount;
        std::map<int, CRecipeList*> m_mapRecipes;
        std::map<int, CRecipeList*> m_mapRecipesTemp;
        ONSYNCINGSTATECHANGED m_onSyncingStateChanged;
    };
}
SourceCode/Bond/Servo/CRobotTask.cpp
@@ -359,6 +359,9 @@
        case EQ_ID_LOADPORT2:
        case EQ_ID_LOADPORT3:
        case EQ_ID_LOADPORT4:
            tarPos = srcPos;
            tarSlot = srcSlot;
            break;
        case EQ_ID_ARM_TRAY1:
        case EQ_ID_ARM_TRAY2:
        case EQ_ID_ALIGNER:
@@ -368,25 +371,25 @@
            break;
        case EQ_ID_Bonder1:
            if (1 <= srcSlot && srcSlot <= 2) {
                tarPos = 9 + srcSlot;
                tarPos = 9 + srcSlot - 1;
                tarSlot = 1;
            }
            break;
        case EQ_ID_Bonder2:
            if (1 <= srcSlot && srcSlot <= 2) {
                tarPos = 11 + srcSlot;
                tarPos = 11 + srcSlot - 1;
                tarSlot = 1;
            }
            break;
        case EQ_ID_VACUUMBAKE:
            if (1 <= srcSlot && srcSlot <= 2) {
                tarPos = 13 + srcSlot;
                tarPos = 13 + srcSlot - 1;
                tarSlot = 1;
            }
            break;
        case EQ_ID_BAKE_COOLING:
            if (1 <= srcSlot && srcSlot <= 4) {
                tarPos = 15 + srcSlot;
                tarPos = 15 + srcSlot - 1;
                tarSlot = 1;
            }
            break;
SourceCode/Bond/Servo/CStep.cpp
@@ -59,8 +59,17 @@
            std::to_string(m_station.nNetNo).c_str(), "", weight++));
        attrubutes.addAttribute(new CAttribute("Station",
            std::to_string(m_station.nStNo).c_str(), "", weight++));
        attrubutes.addAttributeVector(m_attributeVector);
        auto as = m_attributeVector.getAttributes();
        for (auto item : as) {
            attrubutes.addAttribute(new CAttribute(item->getName().c_str(),
                item->getValue().c_str(), item->getDescription().c_str(), item->getWeight()));
        }
    }
    CAttributeVector& CStep::attributeVector()
    {
        return m_attributeVector;
    }
    void CStep::init()
SourceCode/Bond/Servo/CStep.h
@@ -24,6 +24,7 @@
        int getID();
        void setName(const char* pszName);
        std::string& getName();
        CAttributeVector& attributeVector();
        virtual void getAttributeVector(CAttributeVector& attrubutes);
        virtual void init();
        virtual void term();
SourceCode/Bond/Servo/CVacuumBake.cpp
@@ -269,7 +269,7 @@
                        if (code == ROK && pszData != nullptr && size > 0) {
                            int port = (int)(__int64)((CEqReadStep*)pFrom)->getProp("Port");
                            if (port > 0) {
                                decodeReceivedJobReport((CStep*)pFrom, port, pszData, size);
                                decodeSentOutJobReport((CStep*)pFrom, port, pszData, size);
                            }
                        }
                        return -1;
@@ -331,6 +331,38 @@
                }
            }
        }
        // process start/end report
        {
            CEqReadStep* pStep = new CEqReadStep(0x15D3F, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessStartReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_START_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0xc33);
            if (addStep(STEP_ID_JOB_PROCESS_START_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
        {
            CEqReadStep* pStep = new CEqReadStep(0x15D4C, 13 * 2,
                [&](void* pFrom, int code, const char* pszData, size_t size) -> int {
                    if (code == ROK && pszData != nullptr && size > 0) {
                        decodeJobProcessEndReport((CStep*)pFrom, pszData, size);
                    }
                    return -1;
                });
            pStep->setName(STEP_EQ_JOB_PROCESS_END_REPORT);
            pStep->setProp("Port", (void*)1);
            pStep->setWriteSignalDev(0xc34);
            if (addStep(STEP_ID_JOB_PROCESS_END_REPORT, pStep) != 0) {
                delete pStep;
            }
        }
    }
    // å¿…须要实现的虚函数,在此初始化Slot信息
SourceCode/Bond/Servo/CVcrEventReport.cpp
@@ -46,7 +46,7 @@
    int CVcrEventReport::serialize(char* pszBuffer, int nBufferSize)
    {
        if (nBufferSize < 640) return -1;
        if (nBufferSize < 30) return -1;
        int index = 0;
        int strLen = min(20, m_strGlassId.size());
@@ -73,7 +73,7 @@
    int CVcrEventReport::unserialize(const char* pszBuffer, int nBufferSize)
    {
        if (nBufferSize < 640) return -1;
        if (nBufferSize < 30) return -1;
        int index = 0;
        CToolUnits::convertString(&pszBuffer[index], 20, m_strGlassId);
SourceCode/Bond/Servo/Common.h
@@ -34,6 +34,7 @@
#define PAGE_GRPAH2_BACKGROUND_COLOR        RGB(255, 255, 255)
#define EQ_BOX_OFFLINE                        RGB(222, 222, 222)
#define EQ_BOX_ONLINE                        RGB(0, 176, 80)
#define EQ_BOX_OCCUPIED                     RGB(255, 127, 39)
#define EQ_BOX_FRAME1                        RGB(22, 22, 22)
#define EQ_BOX_FRAME2                        RGB(255, 127, 39)
#define CR_MSGBOX_BKGND                        RGB(7, 71, 166)
@@ -79,6 +80,49 @@
#define EQ_ID_ARM                101
#define EQ_ID_OPERATOR_REMOVE    102
/* Equipment Name */
#define EQ_NAME_LOADPORT1            "LoadPort1"
#define EQ_NAME_LOADPORT2            "LoadPort2"
#define EQ_NAME_LOADPORT3            "LoadPort3"
#define EQ_NAME_LOADPORT4            "LoadPort4"
#define EQ_NAME_ARM_TRAY1            "ArmTray1"
#define EQ_NAME_ARM_TRAY2            "ArmTray2"
#define EQ_NAME_ALIGNER                "Aligner"
#define EQ_NAME_FLIPER                "Fliper"
#define EQ_NAME_VACUUMBAKE            "VacuumBake"
#define EQ_NAME_BONDER1                "Bonder1"
#define EQ_NAME_BONDER2                "Bonder2"
#define EQ_NAME_BAKE_COOLING        "BakeCooling"
#define EQ_NAME_MEASUREMENT            "Measurement"
#define EQ_NAME_EFEM                "EFEM"
#define EQ_NAME_ARM                    "Arm"
#define EQ_NAME_OPERATOR_REMOVE        "OperatorRemove"
// è®¾å¤‡å…ƒä¿¡æ¯ç»“构体
struct DeviceMetaInfo {
    int nDeviceID;
    const char* strDeviceName;  // æŒ‡é’ˆï¼Œä»…指向常量字符串
};
// å…¨å±€è®¾å¤‡å…ƒä¿¡æ¯åˆ—表
static const DeviceMetaInfo g_allDeviceMetaInfos[] = {
    {EQ_ID_LOADPORT1, EQ_NAME_LOADPORT1},
    {EQ_ID_LOADPORT2, EQ_NAME_LOADPORT2},
    {EQ_ID_LOADPORT3, EQ_NAME_LOADPORT3},
    {EQ_ID_LOADPORT4, EQ_NAME_LOADPORT4},
    {EQ_ID_ARM_TRAY1, EQ_NAME_ARM_TRAY1},
    {EQ_ID_ARM_TRAY2, EQ_NAME_ARM_TRAY2},
    {EQ_ID_ALIGNER,   EQ_NAME_ALIGNER},
    {EQ_ID_FLIPER,    EQ_NAME_FLIPER},
    {EQ_ID_VACUUMBAKE, EQ_NAME_VACUUMBAKE},
    {EQ_ID_Bonder1, EQ_NAME_BONDER1},
    {EQ_ID_Bonder2, EQ_NAME_BONDER2},
    {EQ_ID_BAKE_COOLING, EQ_NAME_BAKE_COOLING},
    {EQ_ID_MEASUREMENT, EQ_NAME_MEASUREMENT},
    {EQ_ID_EFEM, EQ_NAME_EFEM},
    {EQ_ID_ARM, EQ_NAME_ARM},
    {EQ_ID_OPERATOR_REMOVE, EQ_NAME_OPERATOR_REMOVE},
};
/* step name */
#define STEP_MODE                        _T("EQMode")
@@ -247,6 +291,8 @@
#define STEP_EQ_PANEL_DATA_REPORT        _T("EQPanelDataReport")
#define STEP_EQ_FAC_DATA_REPORT            _T("EQFacDataReport")
#define STEP_EFEM_PANEL_DATA_REQUEST    _T("EFEMPanelDataRequest")
#define STEP_EQ_JOB_PROCESS_START_REPORT    _T("EQJobProcessStartReport")
#define STEP_EQ_JOB_PROCESS_END_REPORT        _T("EQJobProcessEndReport")
/* Step ID */
#define STEP_ID_CIMMODE_CHANGED_CMD_REPLY        0x550
@@ -319,6 +365,8 @@
#define STEP_ID_FETCHED_OUT_JOB_REPORT13        0x5BB
#define STEP_ID_FETCHED_OUT_JOB_REPORT14        0x5BC
#define STEP_ID_FETCHED_OUT_JOB_REPORT15        0x5BD
#define STEP_ID_JOB_PROCESS_START_REPORT        0x5BF
#define STEP_ID_JOB_PROCESS_END_REPORT            0x5C0
#define STEP_ID_JOB_DATA_REQUEST                0x5C1
#define STEP_ID_PANEL_DATA_REQUEST                0x5D0
#define STEP_ID_PANEL_DATA_REPORT                0x5D1
@@ -396,6 +444,10 @@
#define STEP_ID_PROT2_CASSETTE_TYPE_CHANGE_REPLY    0x699
#define STEP_ID_PROT3_CASSETTE_TYPE_CHANGE_REPLY    0x69A
#define STEP_ID_PROT4_CASSETTE_TYPE_CHANGE_REPLY    0x69B
#define STEP_ID_PROT1_CASSETTE_CTR_CMD_REPLY        0x6A0
#define STEP_ID_PROT2_CASSETTE_CTR_CMD_REPLY        0x6A1
#define STEP_ID_PROT3_CASSETTE_CTR_CMD_REPLY        0x6A2
#define STEP_ID_PROT4_CASSETTE_CTR_CMD_REPLY        0x6A3
#define STEP_ID_ROBOT_CMD_REPLY                        0x6b0
SourceCode/Bond/Servo/Configuration.cpp
@@ -92,6 +92,17 @@
    return (int)texts.size();    
}
void CConfiguration::setFilterMode(int mode)
{
    WritePrivateProfileString(_T("Logcat"), _T("FilterMode"),
        std::to_string(mode).c_str(), m_strFilepath);
}
int CConfiguration::getFilterMode()
{
    return GetPrivateProfileInt(_T("Logcat"), _T("FilterMode"), 0, m_strFilepath);
}
int CConfiguration::getP2RemoteEqReconnectInterval()
{
    return GetPrivateProfileInt(_T("P2"), _T("RemoteEqReconnectInterval"), 20, m_strFilepath);
SourceCode/Bond/Servo/Configuration.h
@@ -20,6 +20,8 @@
    void setLogcatIncludeRegex(BOOL bRegex);
    BOOL isLogcatIncludeRegex();
    int getCustomLogcatIncludeTexts(std::vector<std::string>& texts);
    void setFilterMode(int mode);
    int getFilterMode();
    BOOL getPortParms(unsigned int index, BOOL& bEnable, int& type, int& mode,
        int& cassetteType, int& transferMode, BOOL& bAutoChangeEnable);
SourceCode/Bond/Servo/LogEdit.cpp
@@ -8,7 +8,9 @@
CLogEdit::CLogEdit()
{
    m_nMaxLineCount = 0xffff;
    m_nMaxLines = 0xffff;
    m_nTrimLines = 100;
    m_bAutoScroll = TRUE;
}
@@ -18,11 +20,14 @@
BEGIN_MESSAGE_MAP(CLogEdit, CEdit)
    ON_WM_CONTEXTMENU()
    ON_WM_VSCROLL()
    ON_WM_MOUSEWHEEL()
END_MESSAGE_MAP()
void CLogEdit::SetMaxLineCount(int line)
{
    m_nMaxLineCount = line;
    m_nMaxLines = line;
    m_nTrimLines = min(m_nMaxLines, 4000);
}
void CLogEdit::OnContextMenu(CWnd* pWnd, CPoint point)
@@ -49,39 +54,64 @@
    }
}
void CLogEdit::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    // æ¯æ¬¡æ»šåŠ¨æ—¶æ£€æŸ¥æ˜¯å¦è¿˜åœ¨åº•éƒ¨
    m_bAutoScroll = IsScrollBarAtBottom();
    CEdit::OnVScroll(nSBCode, nPos, pScrollBar);
}
BOOL CLogEdit::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
    // æ¯æ¬¡æ»šåŠ¨æ—¶æ£€æŸ¥æ˜¯å¦è¿˜åœ¨åº•éƒ¨
    m_bAutoScroll = IsScrollBarAtBottom();
    return CEdit::OnMouseWheel(nFlags, zDelta, pt);
}
BOOL CLogEdit::IsScrollBarAtBottom()
{
    SCROLLINFO si = { sizeof(si), SIF_ALL };
    GetScrollInfo(SB_VERT, &si);
    return (si.nPos + (int)si.nPage >= si.nMax);
}
void CLogEdit::AppendText(const char* pszText)
{
    // èŽ·å–é€‰æ‹©èŒƒå›´ä»¥ä¾¿æ¢å¤
    int nStart, nEnd;
    GetSel(nStart, nEnd);
    SetRedraw(FALSE);
    // å‰ªåˆ‡è¿‡å¤šè¡Œ
    int totalLines = GetLineCount();
    if (totalLines > m_nMaxLines) {
        int startChar = LineIndex(0);
        int endChar = LineIndex(m_nTrimLines);
        if (startChar >= 0 && endChar > startChar) {
            SetSel(startChar, endChar);
            ReplaceSel(_T(""));
        }
    }
    // è¶…过指定行数则删除最前面的行
    int nLineCount = GetLineCount();
    while (nLineCount > m_nMaxLineCount) {
        int nLin1End = LineIndex(1);
        nStart -= nLin1End;
        if (nStart < 0) nStart = 0;
        nEnd -= nLin1End;
        if (nEnd < 0) nEnd = 0;
    // ä¿å­˜å½“前选择
    int start, end;
    GetSel(start, end);
    bool hasSelection = (start != end);
        SetSel(0, nLin1End);
        ReplaceSel(_T(""));
        nLineCount = GetLineCount();
    }
    int endPos = GetWindowTextLength();
    SetSel(endPos, endPos);
    ReplaceSel(pszText);
    if (m_bAutoScroll && !hasSelection) {
        LineScroll(GetLineCount());
    }
    // è¿½åŠ åˆ°æœ€åŽ
    int length = GetWindowTextLength();
    SetSel(length, length);
    ReplaceSel(pszText);
    PostMessage(WM_VSCROLL, SB_BOTTOM, 0);
    // æ¢å¤é€‰æ‹©
    if (hasSelection) {
        SetSel(start, end);
    }
    SetRedraw(TRUE);
    // æ¢å¤
    if (nStart == 0 && nEnd == 0) {
        nStart = GetWindowTextLength();
        nEnd = nStart;
    }
    SetSel(nStart, nEnd);
    if (m_bAutoScroll && !hasSelection) {
        Invalidate();
        UpdateWindow();
    }
}
SourceCode/Bond/Servo/LogEdit.h
@@ -10,12 +10,17 @@
public:
    void SetMaxLineCount(int line);
    void AppendText(const char* pszText);
    BOOL IsScrollBarAtBottom();
private:
    int m_nMaxLineCount;
    int m_nMaxLines;
    int m_nTrimLines;
    BOOL m_bAutoScroll;        // æ˜¯å¦è‡ªåŠ¨æ»šåŠ¨
    DECLARE_MESSAGE_MAP()
    afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
    afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
public:
    afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
};
SourceCode/Bond/Servo/Model.cpp
@@ -121,13 +121,13 @@
        notify(RX_CODE_MASTER_STATE_CHANGED);
    };
    masterListener.onEqAlive = [&](void* pMaster, SERVO::CEquipment* pEquipment, BOOL bAlive) -> void {
        LOGI("<CModel>Equipment onAlive:%s(%s).\n", pEquipment->getName().c_str(),
        LOGI("<CModel>Equipment onAlive:%s(%s).", pEquipment->getName().c_str(),
            bAlive ? _T("ON") : _T("OFF"));
        notifyPtr(RX_CODE_EQ_ALIVE, pEquipment);
    };
    masterListener.onEqCimStateChanged = [&](void* pMaster, SERVO::CEquipment* pEquipment, BOOL bOn) -> void {
        LOGI("<CModel>Equipment Cim State:%s(%s).\n", pEquipment->getName().c_str(),
        LOGI("<CModel>Equipment Cim State:%s(%s).", pEquipment->getName().c_str(),
            bOn ? _T("ON") : _T("OFF"));
        notifyPtr(RX_CODE_EQ_ALIVE, pEquipment);
@@ -190,8 +190,16 @@
        }
        // ä»»åŠ¡æè¿°ä¸Ž ID ç”¨äºŽæ—¥å¿—
        SERVO::CGlass* pGlass = (SERVO::CGlass*)pTask->getContext();
        const std::string& strDesc = pTask->getDescription();
        const std::string& strClassID = pTask->getId();
        std::string strClassID;
        if (pGlass != nullptr) {
            strClassID = pGlass->getID();
            if (pGlass->getBuddy() != nullptr) {
                strClassID += "/";
                strClassID += pGlass->getBuddy()->getID();
            }
        }
        // æ—¥å¿—输出与状态处理
        switch (code) {
@@ -245,23 +253,26 @@
        // çŠ¶æ€æ˜ å°„
        static const char* STATUS_STR[] = {
            "Unknown", "Ready", "Running", "Picking", "Placing", "Restoring", "Error", "Abort", "Completed"
            "Ready", "Running", "Picking", "Picked", "Placing", "Restoring", "Error", "Abort", "Restored", "Completed"
        };
        auto state = pTask->getState();
        int index = static_cast<int>(state);
        if (index > 0 && index < static_cast<int>(std::size(STATUS_STR))) {
        if (index >= 0 && index < static_cast<int>(std::size(STATUS_STR))) {
            data.strStatus = STATUS_STR[index];
        }
        else {
            data.strStatus = STATUS_STR[0];
            data.strStatus = "Unknown";
        }
        // å†™å…¥æ•°æ®åº“
        int nRecordId = 0;
        TransferManager::getInstance().addTransferRecord(data, nRecordId);
        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);
        }
        notifyPtrAndInt(RX_CODE_EQ_ROBOT_TASK, pTask, nullptr, code);
        LOGI("<CModel>onRobotTaskEvent: ä»»åŠ¡è®°å½•å·²ä¿å­˜ï¼ŒRecordID=%d", nRecordId);
    };
    m_master.setListener(masterListener);
SourceCode/Bond/Servo/MsgDlg.cpp
@@ -22,6 +22,7 @@
    m_crTitle = CR_MSGBOX_TITLE;
    m_crMessage = CR_MSGBOX_MESSAGE;
    m_nIcon = MSG_BOX_TIP;
    m_bDelayClose = FALSE;
}
CMsgDlg::CMsgDlg(CString strTitle, CString strMessage)
@@ -169,7 +170,10 @@
void CMsgDlg::DelayClose(int nDelay)
{
    SetTimer(1, nDelay, NULL);
    if (!m_bDelayClose) {
        m_bDelayClose = TRUE;
        SetTimer(1, nDelay, NULL);
    }
}
void CMsgDlg::ShowCloseButton(BOOL bVisible)
SourceCode/Bond/Servo/MsgDlg.h
@@ -52,6 +52,7 @@
    int m_nCompleteCode;
    DWORD_PTR m_dwData;
    DWORD_PTR m_dwDataEx;
    BOOL m_bDelayClose;
// å¯¹è¯æ¡†æ•°æ®
SourceCode/Bond/Servo/PageLog.cpp
@@ -20,8 +20,9 @@
    m_hbrBkgnd = nullptr;
    m_pObserver = nullptr;
    m_nLevel = 0;
    m_strIncludeText = _T("");
    m_bIncludeRegex = FALSE;
    m_strFilterText = _T("");
    m_bRegex = FALSE;
    m_filterMode = FilterMode::Include;
}
CPageLog::~CPageLog()
@@ -70,19 +71,24 @@
                    && pAny->getIntValue("exCode", level)) {
                    if (level >= m_nLevel) {
                        CString strText = pszLogMsg;
                        BOOL bInclude = TRUE;
                        if (!m_strIncludeText.IsEmpty()) {
                            if (!m_bIncludeRegex) {
                                bInclude = (strText.Find(m_strIncludeText) >= 0);
                        BOOL bMatch = TRUE;
                        if (!m_strFilterText.IsEmpty()) {
                            if (!m_bRegex) {
                                bMatch = (strText.Find(m_strFilterText) >= 0);
                            }
                            else {
                                bInclude = std::regex_search((LPTSTR)(LPCTSTR)strText,
                                    std::regex((LPTSTR)(LPCTSTR)m_strIncludeText));
                                CString strTemp = strText;
                                strTemp.TrimRight();
                                bMatch = std::regex_match((LPTSTR)(LPCTSTR)strTemp,
                                    std::regex((LPTSTR)(LPCTSTR)m_strFilterText));
                            }
                            if (m_filterMode == FilterMode::Exclude) {
                                bMatch = !bMatch;
                            }
                        }
                        if (bInclude) {
                        if (bMatch) {
                            strText.Replace("\n", "\r\n");
                            AppendLog(level, (LPTSTR)(LPCTSTR)strText);
                        }
@@ -110,9 +116,12 @@
    // ç¼“å­˜
    m_nLevel = theApp.m_model.m_configuration.getLogcatLevel();
    theApp.m_model.m_configuration.getLogcatIncludeText(m_strIncludeText);
    m_bIncludeRegex = theApp.m_model.m_configuration.isLogcatIncludeRegex();
    theApp.m_model.m_configuration.getLogcatIncludeText(m_strFilterText);
    m_bRegex = theApp.m_model.m_configuration.isLogcatIncludeRegex();
    theApp.m_model.m_configuration.getCustomLogcatIncludeTexts(m_customIncludeTexts);
    m_customIncludeTexts.clear();
    m_customIncludeTexts.push_back("包含");
    m_customIncludeTexts.push_back("排除");
    // Level
@@ -135,6 +144,9 @@
        strIcon1, IMAGE_ICON, 24, 24,
        LR_LOADFROMFILE | LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
    m_btnInclude.SetIcon(hIcon, hIcon, 24);
    m_filterMode = (FilterMode)theApp.m_model.m_configuration.getFilterMode();
    m_btnInclude.SetTextRight();
    m_btnInclude.SetWindowText(m_filterMode == FilterMode::Include ? "包含" : "排除");
    {
        HMENU hMenu = LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCEA(IDR_MENU_INCLUDE));
@@ -143,19 +155,19 @@
        int i = 0;
        for (auto& item : m_customIncludeTexts) {
            i++;
            InsertMenu(hSubMenu, 0, MF_BYPOSITION, 0x1998 + i, item.c_str());
            InsertMenu(hSubMenu, i, MF_BYPOSITION, 0x1998 + i, item.c_str());
            m_btnInclude.SetMenu(hMenu);
        }
    }
    SetDlgItemText(IDC_EDIT_INCLUDE, m_strIncludeText);
    SetDlgItemText(IDC_EDIT_INCLUDE, m_strFilterText);
    CButton* pCheckBox = (CButton*)GetDlgItem(IDC_CHECK_REGEX);
    pCheckBox->SetCheck(m_bIncludeRegex ? BST_CHECKED : BST_UNCHECKED);
    pCheckBox->SetCheck(m_bRegex ? BST_CHECKED : BST_UNCHECKED);
    // å†…容
    m_logEdit.SetMaxLineCount(500);
    m_logEdit.SetMaxLineCount(6000);
    m_logEdit.SetLimitText(-1);
    Resize();
@@ -290,28 +302,22 @@
void CPageLog::OnButtonIncludeMenuClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
    BLBUTTON_NMHDR* pblbNmhdr = reinterpret_cast<BLBUTTON_NMHDR*>(pNMHDR);
    int position = (int)pblbNmhdr->dwData;
    std::string& strInclude = m_customIncludeTexts.at(position);
    SetDlgItemText(IDC_EDIT_INCLUDE, strInclude.c_str());
    CButton* pCheckBox = (CButton*)GetDlgItem(IDC_CHECK_REGEX);
    m_bIncludeRegex = FALSE;
    pCheckBox->SetCheck(BST_UNCHECKED);
    theApp.m_model.m_configuration.setLogcatIncludeRegex(m_bIncludeRegex);
    theApp.m_model.m_configuration.setLogcatIncludeText(m_strIncludeText);
    m_filterMode = (FilterMode)pblbNmhdr->dwData;
    theApp.m_model.m_configuration.setFilterMode((int)m_filterMode);
    m_btnInclude.SetWindowText(m_filterMode == FilterMode::Include ? "包含" : "排除");
    *pResult = 0;
}
void CPageLog::OnEnChangeEditInclude()
{
    GetDlgItemText(IDC_EDIT_INCLUDE, m_strIncludeText);
    theApp.m_model.m_configuration.setLogcatIncludeText(m_strIncludeText);
    GetDlgItemText(IDC_EDIT_INCLUDE, m_strFilterText);
    theApp.m_model.m_configuration.setLogcatIncludeText(m_strFilterText);
}
void CPageLog::OnBnClickedCheckRegex()
{
    CButton* pCheckBox = (CButton*)GetDlgItem(IDC_CHECK_REGEX);
    m_bIncludeRegex = pCheckBox->GetCheck();
    theApp.m_model.m_configuration.setLogcatIncludeRegex(m_bIncludeRegex);
    m_bRegex = pCheckBox->GetCheck();
    theApp.m_model.m_configuration.setLogcatIncludeRegex(m_bRegex);
}
SourceCode/Bond/Servo/PageLog.h
@@ -7,6 +7,12 @@
#define ID_MSG_LOGDLG_HIDE        WM_USER + 1023
enum class FilterMode {
    Include,  // åªä¿ç•™åŒ¹é…è¡Œ
    Exclude   // æŽ’除匹配行
};
// CPageLog å¯¹è¯æ¡†
class CPageLog : public CDialogEx
@@ -29,14 +35,15 @@
    HBRUSH m_hbrBkgnd;
    IObserver* m_pObserver;
    int m_nLevel;
    CString m_strIncludeText;
    BOOL m_bIncludeRegex;
    CString m_strFilterText;
    BOOL m_bRegex;
    std::vector<std::string> m_customIncludeTexts;
private:
    CBlButton m_btnLevel;
    CBlButton m_btnInclude;
    CLogEdit m_logEdit;
    FilterMode m_filterMode;
// å¯¹è¯æ¡†æ•°æ®
SourceCode/Bond/Servo/PageRecipe.cpp
@@ -5,6 +5,8 @@
#include "Servo.h"
#include "afxdialogex.h"
#include "PageRecipe.h"
#include "MsgDlg.h"
// CPageRecipe å¯¹è¯æ¡†
@@ -22,7 +24,7 @@
void CPageRecipe::FillDataToListCtrl(const std::vector<RecipeInfo>& vecRecipe) {
    CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_PPID);
    if (pListCtrl == nullptr || pListCtrl->m_hWnd == nullptr) {
    if (pListCtrl == nullptr || !::IsWindow(pListCtrl->m_hWnd)) {
        return;
    }
@@ -49,12 +51,37 @@
    m_listPPID.SetColumnWidth(nColCount - 1, LVSCW_AUTOSIZE_USEHEADER);
}
void CPageRecipe::FillRecipeListToListCtrl(SERVO::CRecipeList* pList)
{
    CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_PPID);
    if (pListCtrl == nullptr || !::IsWindow(pListCtrl->m_hWnd)) {
        return;
    }
    // æ¸…空当前CListCtrl中的所有项
    pListCtrl->DeleteAllItems();
    if (pList == nullptr) {
        return;
    }
    // éåŽ†æ•°æ®å¹¶æ’å…¥åˆ°CListCtrl中
    std::map<int, short>& ids = pList->getIds();
    for (auto item : ids) {
        int index = m_listPPID.InsertItem(m_listPPID.GetItemCount(), _T(""));
        m_listPPID.SetItemText(index, 1, std::to_string(item.first).c_str());
        m_listPPID.SetItemText(index, 2, std::to_string(item.second).c_str());
    }
    // èŽ·å–åˆ—æ•°
    int nColCount = m_listPPID.GetHeaderCtrl()->GetItemCount();
    m_listPPID.SetColumnWidth(nColCount - 1, LVSCW_AUTOSIZE_USEHEADER);
}
void CPageRecipe::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST_PPID, m_listPPID);
    DDX_Control(pDX, IDC_EDIT_PPID, m_editPPID);
    DDX_Control(pDX, IDC_EDIT_DESC, m_editDesc);
}
@@ -66,6 +93,9 @@
    ON_BN_CLICKED(IDC_BUTTON_DELETE_ALL, &CPageRecipe::OnBnClickedButtonDeleteAll)
    ON_BN_CLICKED(IDC_BUTTON_REFRESH, &CPageRecipe::OnBnClickedButtonRefresh)
    ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_PPID, &CPageRecipe::OnLvnItemChangedListPPID)
    ON_WM_DESTROY()
    ON_CBN_SELCHANGE(IDC_COMBO_EQUIPMENT, &CPageRecipe::OnCbnSelchangeComboEquipment)
    ON_WM_SHOWWINDOW()
END_MESSAGE_MAP()
@@ -74,6 +104,16 @@
BOOL CPageRecipe::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // è¯»å‡ºåˆ—宽
    CString strIniFile, strItem;
    strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    int width[8] = { 0, 80, 180, 80, 80, 100, 80, 180 };
    for (int i = 0; i < 8; i++) {
        strItem.Format(_T("Col_%d_Width"), i);
        width[i] = GetPrivateProfileInt("PageRecipeListCtrl", strItem, width[i], strIniFile);
    }
    // TODO:  åœ¨æ­¤æ·»åŠ é¢å¤–çš„åˆå§‹åŒ–
    CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_PPID);
@@ -85,11 +125,12 @@
    HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
    ListView_SetImageList(pListCtrl->GetSafeHwnd(), imageList, LVSIL_SMALL);
    pListCtrl->InsertColumn(0, _T(""), LVCFMT_RIGHT, 0); // éšè—åˆ—
    pListCtrl->InsertColumn(1, _T("No."), LVCFMT_LEFT, 80);
    pListCtrl->InsertColumn(2, _T("PPID"), LVCFMT_LEFT, 120);
    pListCtrl->InsertColumn(3, _T("描述"), LVCFMT_LEFT, 180);
    pListCtrl->InsertColumn(4, _T("创建时间"), LVCFMT_LEFT, 160);
    pListCtrl->InsertColumn(1, _T("No."), LVCFMT_LEFT, width[1]);
    pListCtrl->InsertColumn(2, _T("PPID/Recipe ID"), LVCFMT_LEFT, width[2]);
    pListCtrl->InsertColumn(3, _T("描述"), LVCFMT_LEFT, width[3]);
    pListCtrl->InsertColumn(4, _T("创建时间"), LVCFMT_LEFT, width[4]);
    pListCtrl->SetColumnWidth(4, LVSCW_AUTOSIZE_USEHEADER);
    // èŽ·å–æ‰€æœ‰æ•°æ®
    auto vecData = RecipeManager::getInstance().getAllRecipes();
@@ -103,85 +144,58 @@
{
    CDialogEx::OnSize(nType, cx, cy);
    if (m_listPPID.GetSafeHwnd()) {
        // å·¦ä¾§åˆ—表宽高自适应
        int margin = 10;
        int buttonWidth = 80;
        int buttonHeight = 30;
        int buttonSpacing = 10;
    if (!m_listPPID.GetSafeHwnd())
        return;
        CRect rect;
        GetClientRect(&rect);
        int listWidth = rect.Width() - buttonWidth - 3 * margin;
        int listHeight = rect.Height() - 2 * margin;
    // å·¦ä¾§åˆ—表宽高自适应
    int margin = 12;
    int buttonWidth = 80;
    int buttonHeight = 30;
    int buttonSpacing = 10;
        m_listPPID.MoveWindow(margin, margin + 80, listWidth, listHeight - 80);
    CWnd* pItem;
    CRect rect, rcItem;
    GetClientRect(&rect);
        // ç¼–辑框调整位置:右边对齐列表,左边固定起始
        int labelWidth = 60;
        int rightEdge = rect.right - buttonWidth - 2 * margin;
    pItem = GetDlgItem(IDC_EDIT_KEYWORD);
    ASSERT(pItem);
    pItem->GetWindowRect(rcItem);
    ScreenToClient(&rcItem);
        if (m_editPPID.GetSafeHwnd()) {
            m_editPPID.MoveWindow(labelWidth, margin, rightEdge - labelWidth, 25);
    int y = rcItem.bottom;
    y += 12;
    int listWidth = rect.Width() - buttonWidth - 3 * margin;
    m_listPPID.MoveWindow(margin, y, listWidth, rect.bottom - 12 - y);
    // æŒ‰é’®ç«–直排列在右侧
    CWnd* buttons[] = {
        GetDlgItem(IDC_BUTTON_REFRESH),
        GetDlgItem(IDC_BUTTON_DELETE),
        GetDlgItem(IDC_BUTTON_DELETE_ALL),
        GetDlgItem(IDC_BUTTON_MODIFY)
    };
    for (auto pBtn : buttons) {
        if (pBtn && pBtn->GetSafeHwnd()) {
            pBtn->MoveWindow(rect.right - buttonWidth - margin, y, buttonWidth, buttonHeight);
            y += buttonHeight + buttonSpacing;
        }
        if (m_editDesc.GetSafeHwnd()) {
            m_editDesc.MoveWindow(labelWidth, margin + 35, rightEdge - labelWidth, 25);
        }
        // æŒ‰é’®ç«–直排列在右侧
        CWnd* buttons[] = {
            GetDlgItem(IDC_BUTTON_SEARCH),
            GetDlgItem(IDC_BUTTON_MODIFY),
            GetDlgItem(IDC_BUTTON_DELETE),
            GetDlgItem(IDC_BUTTON_DELETE_ALL),
            GetDlgItem(IDC_BUTTON_REFRESH)
        };
        int y = margin;
        for (auto pBtn : buttons) {
            if (pBtn && pBtn->GetSafeHwnd()) {
                pBtn->MoveWindow(rect.right - buttonWidth - margin, y, buttonWidth, buttonHeight);
                y += buttonHeight + buttonSpacing;
            }
        }
        // åˆ—宽重设
        int col0 = 50;  // No.
        int col1 = 120; // PPID
        int col3 = 160; // åˆ›å»ºæ—¶é—´è‡ªåЍ填充
        int col2 = listWidth - col0 - col1 - col3 - 2;  // æè¿°è‡ªåЍ填充
        if (col2 < 80) {
            col2 = 80;
        }
        m_listPPID.SetColumnWidth(1, col0);
        m_listPPID.SetColumnWidth(2, col1);
        m_listPPID.SetColumnWidth(3, col2);
        m_listPPID.SetColumnWidth(4, col3);
    }
}
void CPageRecipe::OnBnClickedButtonSearch()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    CString strInput;
    m_editPPID.GetWindowText(strInput);
    int nCount = m_listPPID.GetItemCount();
    for (int i = 0; i < nCount; ++i) {
        CString strItemText = m_listPPID.GetItemText(i, 2); // ç¬¬2列为PPID
        if (strItemText == strInput) {
            m_listPPID.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
            m_listPPID.EnsureVisible(i, FALSE);
            break;
        }
    }
    CString strKeyword;
    GetDlgItemText(IDC_EDIT_KEYWORD, strKeyword);
    AfxMessageBox(strKeyword);
}
void CPageRecipe::OnBnClickedButtonModify()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    /*
    POSITION pos = m_listPPID.GetFirstSelectedItemPosition();
    if (!pos) {
        AfxMessageBox(_T("请选择要修改的配方"));
@@ -238,6 +252,7 @@
            AfxMessageBox(_T("描述更新失败"));
        }
    }
    */
}
void CPageRecipe::OnBnClickedButtonDelete()
@@ -289,9 +304,42 @@
void CPageRecipe::OnBnClickedButtonRefresh()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    auto vecData = RecipeManager::getInstance().getAllRecipes();
    FillDataToListCtrl(vecData);
    CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT);
    int nSel = pComboBox->GetCurSel();
    SERVO::CEquipment* pEq = (SERVO::CEquipment*)pComboBox->GetItemDataPtr(nSel);
    if (pEq == nullptr) {
        // èŽ·å–master配方(本地)
        auto vecData = RecipeManager::getInstance().getAllRecipes();
        FillDataToListCtrl(vecData);
    }
    else {
        // enable port
        CMsgDlg msgDlg("请等待", "正在获取配方...");
        pEq->masterRecipeListRequest(0, [&](int status) -> void {
            if (status == SS_FAILED) {
                CString strMsg;
                strMsg.Format(_T("获取配方失败!"));
                msgDlg.DelayClose(3000);
                msgDlg.SetIcon(MSG_BOX_ERROR);
                msgDlg.SetTitle(_T("操作失败"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(-1);
            }
            else if (status == SS_COMPLETE) {
                CString strMsg;
                strMsg.Format(_T("获取配方完成!"));
                msgDlg.DelayClose(3000);
                msgDlg.SetIcon(MSG_BOX_SUCCEED);
                msgDlg.SetTitle(_T("操作成功"));
                msgDlg.SetMessage((LPTSTR)(LPCTSTR)strMsg);
                msgDlg.SetMarquee(FALSE, 0);
                msgDlg.SetCompleteCode(0);
            }
            });
        msgDlg.DoModal();
    }
}
void CPageRecipe::OnLvnItemChangedListPPID(NMHDR* pNMHDR, LRESULT* pResult)
@@ -299,14 +347,85 @@
    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    *pResult = 0;
    if ((pNMLV->uChanged & LVIF_STATE) &&
        (pNMLV->uNewState & LVIS_SELECTED)) {
    CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT);
    int nEqSel = pComboBox->GetCurSel();
    int selectedCount = ListView_GetSelectedCount(m_listPPID.GetSafeHwnd());
        int nItem = pNMLV->iItem;
        CString strPPID = m_listPPID.GetItemText(nItem, 2);
        m_editPPID.SetWindowText(strPPID);
    GetDlgItem(IDC_BUTTON_MODIFY)->EnableWindow(nEqSel == 0 && selectedCount > 0);
    GetDlgItem(IDC_BUTTON_DELETE)->EnableWindow(nEqSel == 0 && selectedCount > 0);
    GetDlgItem(IDC_BUTTON_DELETE_ALL)->EnableWindow(nEqSel == 0 && selectedCount > 0);
}
        CString strDesc = m_listPPID.GetItemText(nItem, 3);
        m_editDesc.SetWindowText(strDesc);
void CPageRecipe::OnCbnSelchangeComboEquipment()
{
    CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT);
    int nEqSel = pComboBox->GetCurSel();
    int selectedCount = ListView_GetSelectedCount(m_listPPID.GetSafeHwnd());
    GetDlgItem(IDC_BUTTON_MODIFY)->EnableWindow(nEqSel == 0 && selectedCount > 0);
    GetDlgItem(IDC_BUTTON_DELETE)->EnableWindow(nEqSel == 0 && selectedCount > 0);
    GetDlgItem(IDC_BUTTON_DELETE_ALL)->EnableWindow(nEqSel == 0 && selectedCount > 0);
    GetDlgItem(IDC_EDIT_KEYWORD)->EnableWindow(nEqSel == 0);
    GetDlgItem(IDC_BUTTON_SEARCH)->EnableWindow(nEqSel == 0);
    SERVO::CEquipment* pEq = (SERVO::CEquipment*)pComboBox->GetItemDataPtr(nEqSel);
    if (pEq == nullptr) {
        auto vecData = RecipeManager::getInstance().getAllRecipes();
        FillDataToListCtrl(vecData);
    }
}
    else {
        SERVO::CRecipeList* pRecipeList = pEq->getRecipeList(0);
        FillRecipeListToListCtrl(pRecipeList);
    }
}
void CPageRecipe::OnDestroy()
{
    CDialogEx::OnDestroy();
    // ä¿å­˜åˆ—宽
    CString strIniFile, strItem, strTemp;
    strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    CHeaderCtrl* pHeader = m_listPPID.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("PageRecipeListCtrl", strItem, strTemp, strIniFile);
    }
}
void CPageRecipe::OnShowWindow(BOOL bShow, UINT nStatus)
{
    CDialogEx::OnShowWindow(bShow, nStatus);
    if (bShow) {
        CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT);
        if (pComboBox->GetCount() == 0) {
            SERVO::CMaster& master = theApp.m_model.getMaster();
            SERVO::CEquipment* pEq[] = {
                nullptr,
                master.getEquipment(EQ_ID_EFEM),
                master.getEquipment(EQ_ID_Bonder1),
                master.getEquipment(EQ_ID_Bonder2),
                master.getEquipment(EQ_ID_BAKE_COOLING),
                master.getEquipment(EQ_ID_VACUUMBAKE),
                master.getEquipment(EQ_ID_MEASUREMENT),
            };
            CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT);
            for (int i = 0; i < sizeof(pEq) / sizeof(pEq[0]); i++) {
                pComboBox->InsertString(i,
                    pEq[i] == nullptr ? _T("Master") : pEq[i]->getName().c_str());
                pComboBox->SetItemDataPtr(i, pEq[i]);
            }
            pComboBox->SetCurSel(0);
        }
    }
}
SourceCode/Bond/Servo/PageRecipe.h
@@ -14,6 +14,7 @@
private:
    void FillDataToListCtrl(const std::vector<RecipeInfo>& vecRecipe);
    void FillRecipeListToListCtrl(SERVO::CRecipeList* pList);
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
@@ -34,6 +35,8 @@
private:
    CListCtrl m_listPPID;
    CEdit m_editPPID;
    CEdit m_editDesc;
public:
    afx_msg void OnDestroy();
    afx_msg void OnCbnSelchangeComboEquipment();
    afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
};
SourceCode/Bond/Servo/PageTransferLog.cpp
@@ -275,7 +275,12 @@
        return;
    }
    int nItem = m_listCtrl.InsertItem(0, _T(""));
    int nIndex = m_listCtrl.GetItemCount();
    if (nIndex < 0) {
        return;
    }
    int nItem = m_listCtrl.InsertItem(nIndex, _T(""));
    CString str;
    str.Format(_T("%d"), data.nRecordId);
    m_listCtrl.SetItemText(nItem, 1, str);
SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp
@@ -5,10 +5,22 @@
#include "Servo.h"
#include "afxdialogex.h"
#include "RecipeDeviceBindDlg.h"
#include "RecipeManager.h"
#include "Common.h"
#define IDC_EDIT_DEVICEID_BASE     3000
#define IDC_EDIT_DEVICENAME_BASE   3050
#define IDC_COMBO_RECIPEID_BASE    3100
// ç»‘定界面需要显示的设备
static const std::vector<DeviceMetaInfo> g_vecBindDevices = {
    { EQ_ID_VACUUMBAKE,      EQ_NAME_VACUUMBAKE },
    { EQ_ID_Bonder1,         EQ_NAME_BONDER1 },
    { EQ_ID_Bonder2,         EQ_NAME_BONDER2 },
    { EQ_ID_BAKE_COOLING,    EQ_NAME_BAKE_COOLING },
    { EQ_ID_MEASUREMENT,     EQ_NAME_MEASUREMENT },
    { EQ_ID_EFEM,            EQ_NAME_EFEM }
};
// CRecipeDeviceBindDlg å¯¹è¯æ¡†
@@ -31,6 +43,8 @@
BEGIN_MESSAGE_MAP(CRecipeDeviceBindDlg, CDialogEx)
    ON_WM_CLOSE()
    ON_WM_SIZE()
END_MESSAGE_MAP()
@@ -50,24 +64,64 @@
    GetClientRect(&clientRect);
    int xStart = (clientRect.Width() - totalControlWidth) / 2;
    const int nRowCount = 8;
    const int nRowHeight = 30;
    const int yStart = 30; // é¡¶éƒ¨èµ·å§‹é«˜åº¦
    for (int i = 0; i < nRowCount; ++i)
    {
    const int nRowCount = static_cast<int>(g_vecBindDevices.size());
    for (int i = 0; i < nRowCount; ++i) {
        int y = yStart + i * nRowHeight;
        const auto& meta = g_vecBindDevices[i];
        CEdit* pEditID = new CEdit();
        pEditID->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(xStart, y, xStart + 100, y + 25), this, IDC_EDIT_DEVICEID_BASE + i);
        CString strID;
        strID.Format(_T("%d"), meta.nDeviceID);
        pEditID->SetWindowText(strID);
        pEditID->SetReadOnly(TRUE);     // è®¾å¤‡ID只读
        CEdit* pEditName = new CEdit();
        pEditName->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(xStart + 110, y, xStart + 210, y + 25), this, IDC_EDIT_DEVICENAME_BASE + i);
        pEditName->SetWindowText(CA2T(meta.strDeviceName));
        pEditName->SetReadOnly(TRUE);   // è®¾å¤‡åç§°åªè¯»
        CComboBox* pCombo = new CComboBox();
        pCombo->Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, CRect(xStart + 220, y, xStart + 340, y + 300), this, IDC_COMBO_RECIPEID_BASE + i);
        // æ·»åŠ é€‰é¡¹åˆ° ComboBox
        m_vecDevices.push_back({ pEditID, pEditName, pCombo });
    }
    return TRUE;  // return TRUE unless you set the focus to a control
    // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
void CRecipeDeviceBindDlg::OnClose()
{
    // TODO: åœ¨æ­¤æ·»åŠ æ¶ˆæ¯å¤„ç†ç¨‹åºä»£ç å’Œ/或调用默认值
    CDialogEx::OnClose();
    // æ¸…理控件
    for (auto& device : m_vecDevices) {
        if (device.editDeviceID) {
            device.editDeviceID->DestroyWindow();
            delete device.editDeviceID;
        }
        if (device.editDeviceName) {
            device.editDeviceName->DestroyWindow();
            delete device.editDeviceName;
        }
        if (device.comboRecipeID) {
            device.comboRecipeID->DestroyWindow();
            delete device.comboRecipeID;
        }
    }
    m_vecDevices.clear();
}
void CRecipeDeviceBindDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    // TODO: åœ¨æ­¤å¤„添加消息处理程序代码
}
SourceCode/Bond/Servo/RecipeDeviceBindDlg.h
@@ -20,13 +20,15 @@
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    virtual BOOL OnInitDialog();
    afx_msg void OnClose();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    DECLARE_MESSAGE_MAP()
private:
    struct DeviceWidget {
        CEdit editDeviceID;
        CEdit editDeviceName;
        CComboBox comboRecipeID;
        CEdit* editDeviceID;
        CEdit* editDeviceName;
        CComboBox* comboRecipeID;
    };
    std::vector<DeviceWidget> m_vecDevices;
SourceCode/Bond/Servo/RecipeManager.cpp
@@ -238,6 +238,29 @@
    return recipes;
}
std::vector<RecipeInfo> RecipeManager::getRecipesByKeyword(const std::string& keyword) {
    std::vector<RecipeInfo> recipes;
    if (!m_pDB || keyword.empty()) {
        return recipes;
    }
    std::ostringstream query;
    query << "SELECT ppid, description, create_time FROM recipes "
        << "WHERE ppid LIKE '%" << keyword << "%' OR description LIKE '%" << keyword << "%';";
    auto rows = m_pDB->fetchResults(query.str());
    for (const auto& row : rows) {
        if (row.size() >= 3) {
            RecipeInfo info;
            info.strPPID = row[0];
            info.strDescription = row[1];
            info.strCreateTime = row[2];
            recipes.push_back(info);
        }
    }
    return recipes;
}
std::vector<std::string> RecipeManager::getAllPPID() const {
    std::vector<std::string> vecPPID;
SourceCode/Bond/Servo/RecipeManager.h
@@ -58,6 +58,9 @@
    // æŸ¥è¯¢æ‰€æœ‰é…æ–¹
    std::vector<RecipeInfo> getAllRecipes();
    // æ ¹æ® PPID æˆ–描述查询配方
    std::vector<RecipeInfo> getRecipesByKeyword(const std::string& keyword);
    // èŽ·å–æ‰€æœ‰ PPID
    std::vector<std::string> getAllPPID() const;
SourceCode/Bond/Servo/Servo.rc
Binary files differ
SourceCode/Bond/Servo/Servo.vcxproj
@@ -46,6 +46,7 @@
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>MultiByte</CharacterSet>
    <UseOfMfc>Dynamic</UseOfMfc>
    <EnableASAN>false</EnableASAN>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
@@ -134,8 +135,8 @@
    </ResourceCompile>
    <PostBuildEvent>
      <Command>if exist "\\DESKTOP-IODBVIQ\Servo\Debug\" (
    xcopy /Y /D "$(OutDir)*.exe" "\\DESKTOP-IODBVIQ\Servo\Debug\"
    xcopy /Y /D "$(OutDir)*.pdb" "\\DESKTOP-IODBVIQ\Servo\Debug\"
    xcopy /Y /D "$(OutDir)Servo.exe" "\\DESKTOP-IODBVIQ\Servo\Debug\"
    xcopy /Y /D "$(OutDir)Servo.pdb" "\\DESKTOP-IODBVIQ\Servo\Debug\"
)</Command>
    </PostBuildEvent>
  </ItemDefinitionGroup>
SourceCode/Bond/Servo/ServoCommo.h
@@ -2,6 +2,23 @@
#include <string>
#include <vector>
#define CHECK_READ_STEP_SIGNAL(addr, data, size) {                            \
    BOOL bFlag = isBitOn(data, size, addr);                                    \
    SERVO::CStep* pStep = getStep(addr);                                    \
    if (pStep != nullptr) {                                                    \
        ((CReadStep*)pStep)->onReadSignal(bFlag ? addr : 0);                \
    }                                                                        \
}
#define CHECK_WRITE_STEP_SIGNAL(addr, data, size) {                            \
    BOOL bFlag = isBitOn(data, size, addr);                                    \
    SERVO::CStep* pStep = getStep(addr);                                    \
    if (pStep != nullptr) {                                                    \
        ((CWriteStep*)pStep)->onRecvSignal(bFlag ? addr : 0);                \
    }                                                                        \
}
namespace SERVO {
#define BLOCK_BUFFER_MAX            1024
#define ALIVE_TIMEOUT                15
@@ -14,7 +31,14 @@
        OK = 1,
        NG,
    };
    typedef RET JobDataRequestAck;
    using JobDataRequestAck = RET;
    enum class InspResult
    {
        NotInspected = 0,  // åˆå§‹åŒ–状态,尚未检测
        Pass,              // æ£€æµ‹åˆæ ¼
        Fail               // æ£€æµ‹ä¸åˆæ ¼
    };
    enum class PortType {
        Loading = 1,
SourceCode/Bond/Servo/ServoDlg.cpp
@@ -285,6 +285,10 @@
    SetIcon(m_hIcon, FALSE);        // è®¾ç½®å°å›¾æ ‡
    // model init
    theApp.m_model.init();
    // èœå•
    CMenu menu;
    menu.LoadMenu(IDR_MENU_APP);
@@ -353,9 +357,6 @@
    int height = GetSystemMetrics(SM_CYSCREEN);
    MoveWindow((width - rcWnd.Width()) / 2, 0, rcWnd.Width(), rcWnd.Height(), TRUE);
    // model init
    theApp.m_model.init();
    SetTimer(TIMER_ID_CREATE_TERMINAL, 3000, nullptr);
@@ -898,8 +899,14 @@
        dlg.DoModal();
    }
    else if (id == IDC_BUTTON_SETTINGS) {
        SERVO::CEFEM* pEFEM = (SERVO::CEFEM*)theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
        pEFEM->printDebugString001();
        SERVO::CEquipment* pEq = theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
        ((SERVO::CEFEM*)pEq)->printDebugRobotState();
        pEq->printDebugString001();
        pEq = theApp.m_model.m_master.getEquipment(EQ_ID_Bonder1);
        pEq->printDebugString001();
        pEq = theApp.m_model.m_master.getEquipment(EQ_ID_Bonder2);
        pEq->printDebugString001();
    }
    else if (id == IDC_BUTTON_OPERATOR) {
        int menuId = (int)wParam;
SourceCode/Bond/Servo/ServoGraph.cpp
@@ -722,4 +722,9 @@
    }
    return nullptr;
}
}
void CServoGraph::Invalidata()
{
    ::InvalidateRect(m_hWnd, NULL, TRUE);
}
SourceCode/Bond/Servo/ServoGraph.h
@@ -148,6 +148,7 @@
    LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam);
public:
    void Invalidata();
    void AddImage(int id, char* pszPath, int x, int y);
    void AddIndicateBox(int id, int x, int y, int width);
    void AddIndicateBox(int id, int x, int y, int width,
SourceCode/Bond/Servo/resource.h
Binary files differ
SourceCode/Bond/x64/Debug/Res/logcat_include.ico