// CPageGraph1.cpp: 实现文件
|
//
|
|
#include "stdafx.h"
|
#include "Servo.h"
|
#include "CPageGraph1.h"
|
#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
|
|
#define INDICATE_BONDER1 1
|
#define INDICATE_BONDER2 2
|
#define INDICATE_FLIPER 3
|
#define INDICATE_ALIGNER 4
|
#define INDICATE_LPORT4 5
|
#define INDICATE_LPORT3 6
|
#define INDICATE_LPORT2 7
|
#define INDICATE_LPORT1 8
|
#define INDICATE_ROBOT_ARM1 9
|
#define INDICATE_ROBOT_ARM2 10
|
#define INDICATE_VACUUM_BAKE 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)
|
|
CPageGraph1::CPageGraph1(CWnd* pParent /*=nullptr*/)
|
: CDialogEx(IDD_PAGE_GRAPH1, pParent)
|
{
|
// ===== 图形界面相关成员变量初始化 =====
|
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()
|
{
|
}
|
|
void CPageGraph1::DoDataExchange(CDataExchange* pDX)
|
{
|
CDialogEx::DoDataExchange(pDX);
|
}
|
|
|
BEGIN_MESSAGE_MAP(CPageGraph1, CDialogEx)
|
ON_WM_CTLCOLOR()
|
ON_WM_DESTROY()
|
ON_WM_SIZE()
|
ON_NOTIFY(BYSERVOGRAPH_ITEM_CLICKED, IDC_SERVO_GRAPH1, &CPageGraph1::OnGraphItemClicked)
|
ON_WM_ERASEBKGND()
|
ON_WM_TIMER()
|
END_MESSAGE_MAP()
|
|
|
// 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()
|
{
|
/* code */
|
// 订阅数据
|
IRxWindows* pRxWindows = RX_GetRxWindows();
|
pRxWindows->enableLog(5);
|
if (m_pObserver == NULL) {
|
m_pObserver = pRxWindows->allocObserver([&](IAny* pAny) -> void {
|
// onNext
|
pAny->addRef();
|
int code = pAny->getCode();
|
if (RX_CODE_EQ_ALIVE == code) {
|
// 通知设备状态
|
SERVO::CEquipment* pEquipment = nullptr;
|
if (pAny->getPtrValue("ptr", (void*&)pEquipment)) {
|
if (pEquipment != nullptr) {
|
int nID = pEquipment->getID();
|
BOOL bAlive = pEquipment->isAlive();
|
if (EQ_ID_EFEM == nID) {
|
DeviceStatus status = bAlive ? 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);
|
}
|
else if(EQ_ID_Bonder1 == nID) {
|
DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
|
UpdateDeviceStatus(INDICATE_BONDER1, status);
|
}
|
else if (EQ_ID_Bonder2 == nID) {
|
DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
|
UpdateDeviceStatus(INDICATE_BONDER2, status);
|
}
|
else if (EQ_ID_VACUUMBAKE == nID) {
|
DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
|
UpdateDeviceStatus(INDICATE_VACUUM_BAKE, status);
|
}
|
else if (EQ_ID_BAKE_COOLING == nID) {
|
DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
|
UpdateDeviceStatus(INDICATE_BAKE_COOLING, status);
|
}
|
else if (EQ_ID_MEASUREMENT == nID) {
|
DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
|
UpdateDeviceStatus(INDICATE_MEASUREMENT, status);
|
}
|
}
|
}
|
}
|
|
pAny->release();
|
}, [&]() -> void {
|
// onComplete
|
}, [&](IThrowable* pThrowable) -> void {
|
// onErrorm
|
pThrowable->printf();
|
});
|
|
theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())
|
->subscribe(m_pObserver);
|
}
|
}
|
|
BOOL CPageGraph1::OnInitDialog()
|
{
|
CDialogEx::OnInitDialog();
|
InitRxWindows();
|
SetTimer(TIMER_ID_DEVICE_STATUS, 3000, nullptr);
|
SetTimer(TIMER_ID_ROBOT_STATUS, 1000, nullptr); // 每 1000ms 更新一次状态
|
|
// 图示
|
m_pGraph = CServoGraph::Hook(GetDlgItem(IDC_SERVO_GRAPH1)->GetSafeHwnd());
|
CString strPath;
|
strPath.Format(_T("%s\\res\\Servo001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
|
m_pGraph->AddImage(1, (LPTSTR)(LPCTSTR)strPath, 0, 0);
|
|
strPath.Format(_T("%s\\res\\Robot001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
|
m_pGraph->AddImage(IMAGE_ROBOT, (LPTSTR)(LPCTSTR)strPath, 170, 270);
|
|
// 添加指示器
|
// Bonder
|
m_pGraph->AddIndicateBox(INDICATE_BONDER1, 220, 172, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_BONDER1, "", "Bonder 1");
|
m_pGraph->AddIndicateBox(INDICATE_BONDER2, 220, 516, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_BONDER2, "", "Bonder 2");
|
|
|
// 翻转
|
m_pGraph->AddIndicateBox(INDICATE_FLIPER, 338, 172, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_FLIPER, "", "Fliper");
|
|
|
// 对位
|
m_pGraph->AddIndicateBox(INDICATE_ALIGNER, 428, 172, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_ALIGNER, "", "Aligner");
|
|
|
// Load port 4
|
m_pGraph->AddIndicateBox(INDICATE_LPORT4, 518, 172, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_LPORT4, "", "LPort4");
|
|
|
// Load port 3
|
m_pGraph->AddIndicateBox(INDICATE_LPORT3, 606, 172, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_LPORT3, "", "LPort3");
|
|
|
// Load port 2
|
m_pGraph->AddIndicateBox(INDICATE_LPORT2, 690, 172, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_LPORT2, "", "LPort2");
|
|
|
// Load port 1
|
m_pGraph->AddIndicateBox(INDICATE_LPORT1, 774, 172, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_LPORT1, "", "LPort1");
|
|
|
// Robot
|
m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 190, 294, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "", "Robot");
|
m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 243, 294, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "", "Robot");
|
|
|
// Vacuum bake
|
m_pGraph->AddIndicateBox(INDICATE_VACUUM_BAKE, 396, 516, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_VACUUM_BAKE, "", "Vacuum bake");
|
|
|
// Bake cooling
|
m_pGraph->AddIndicateBox(INDICATE_BAKE_COOLING, 566, 516, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_BAKE_COOLING, "", "Bake cooling");
|
|
|
// 精度检
|
m_pGraph->AddIndicateBox(INDICATE_MEASUREMENT, 737, 516, 48, RGB(22, 22, 22),
|
RGB(255, 127, 39), EQ_BOX_OFFLINE);
|
m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "", "Measurement");
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
// 异常: OCX 属性页应返回 FALSE
|
}
|
|
void CPageGraph1::UpdateDeviceStatus(int id, DeviceStatus status)
|
{
|
// 根据状态设置颜色
|
COLORREF newBackgroundColor;
|
COLORREF newFrameColor1;
|
COLORREF newFrameColor2;
|
|
switch (status) {
|
case ONLINE:
|
newBackgroundColor = EQ_BOX_ONLINE;
|
newFrameColor1 = EQ_BOX_FRAME1;
|
newFrameColor2 = EQ_BOX_FRAME2;
|
break;
|
case OFFLINE:
|
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;
|
default:
|
newBackgroundColor = RGB(255, 255, 255); // 默认白色背景
|
newFrameColor1 = RGB(0, 0, 0); // 默认黑色框架1
|
newFrameColor2 = RGB(0, 0, 0); // 默认黑色框架2
|
break;
|
}
|
|
m_pGraph->UpdateIndicateBox1Colors(id, newBackgroundColor, newFrameColor1, newFrameColor2);
|
|
// 刷新界面
|
Invalidate();
|
UpdateWindow();
|
}
|
|
BOOL CPageGraph1::OnEraseBkgnd(CDC* pDC)
|
{
|
// TODO: 在此添加消息处理程序代码和/或调用默认值
|
if (m_bIsRobotMoving) {
|
// 禁止刷新背景,避免闪烁
|
return TRUE;
|
}
|
|
return CDialogEx::OnEraseBkgnd(pDC);
|
}
|
|
HBRUSH CPageGraph1::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
|
{
|
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
|
|
if (nCtlColor == CTLCOLOR_STATIC) {
|
pDC->SetBkColor(m_crBkgnd);
|
pDC->SetTextColor(RGB(0, 0, 0));
|
}
|
|
if (m_hbrBkgnd == nullptr) {
|
m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
|
}
|
|
return m_hbrBkgnd;
|
}
|
|
void CPageGraph1::OnDestroy()
|
{
|
CDialogEx::OnDestroy();
|
|
KillTimer(TIMER_ID_ROBOT_STATUS);
|
|
if (m_hbrBkgnd != nullptr) {
|
::DeleteObject(m_hbrBkgnd);
|
}
|
|
if (m_pObserver != nullptr) {
|
m_pObserver->unsubscribe();
|
m_pObserver = NULL;
|
}
|
|
SaveArmOffset("ARM1", m_arm1Offset);
|
SaveArmOffset("ARM2", m_arm2Offset);
|
}
|
|
void CPageGraph1::OnSize(UINT nType, int cx, int cy)
|
{
|
CDialogEx::OnSize(nType, cx, cy);
|
if (GetDlgItem(IDC_SERVO_GRAPH1) == nullptr) return;
|
|
CRect rcClient;
|
GetClientRect(&rcClient);
|
GetDlgItem(IDC_SERVO_GRAPH1)->MoveWindow(0, 0, rcClient.Width(), rcClient.Height());
|
}
|
|
void CPageGraph1::UpdateRobotPosition(float percentage)
|
{
|
if (percentage < 0.0f) percentage = 0.0f;
|
if (percentage > 1.0f) percentage = 1.0f;
|
|
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 distance = abs(endX - startX);
|
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));
|
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();
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
}
|
|
// 最终位置校正
|
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)
|
{
|
// 获取机器人图片
|
auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
|
if (!pImage) return;
|
|
// 修正角度为 0~360
|
float finalAngle = fmod(angleInDegrees + 360.0f, 360.0f);
|
m_pGraph->UpdateImageAngle(IMAGE_ROBOT, finalAngle);
|
|
// 计算中心点
|
int cx = pImage->x + pImage->bmWidth / 2;
|
int cy = pImage->y + pImage->bmHeight / 2;
|
|
// 转换角度为弧度
|
float radians = angleInDegrees * 3.1415926f / 180.0f;
|
|
// 旋转 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);
|
|
// 旋转 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);
|
|
// 更新指示框
|
m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, cx + rotatedX1, cy + rotatedY1);
|
m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, cx + rotatedX2, cy + rotatedY2);
|
|
Invalidate();
|
}
|
|
void CPageGraph1::BindEquipmentToGraph()
|
{
|
struct EquipmentBindInfo
|
{
|
int nEquipmentID;
|
int nIndicateID;
|
};
|
|
static const EquipmentBindInfo EQUIPMENT_BIND_LIST[] = {
|
{ EQ_ID_EFEM, INDICATE_ROBOT_ARM1 },
|
{ EQ_ID_EFEM, INDICATE_ROBOT_ARM2 },
|
{ EQ_ID_Bonder1, INDICATE_BONDER1 },
|
{ EQ_ID_Bonder2, INDICATE_BONDER2 },
|
{ EQ_ID_LOADPORT1, INDICATE_LPORT1 },
|
{ EQ_ID_LOADPORT2, INDICATE_LPORT2 },
|
{ EQ_ID_LOADPORT3, INDICATE_LPORT3 },
|
{ EQ_ID_LOADPORT4, INDICATE_LPORT4 },
|
{ EQ_ID_FLIPER, INDICATE_FLIPER },
|
{ EQ_ID_VACUUMBAKE, INDICATE_VACUUM_BAKE },
|
{ EQ_ID_ALIGNER, INDICATE_ALIGNER },
|
{ EQ_ID_BAKE_COOLING, INDICATE_BAKE_COOLING }
|
};
|
|
for (const auto& stBindInfo : EQUIPMENT_BIND_LIST)
|
{
|
SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(stBindInfo.nEquipmentID);
|
m_pGraph->SetIndicateBoxData(stBindInfo.nIndicateID, pEquipment);
|
}
|
}
|
|
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);
|
|
// 移动到指定位置 (测试使用)
|
if (pGraphNmhdr->dwData == INDICATE_LPORT1) {
|
|
}
|
|
*pResult = 0;
|
}
|
|
void CPageGraph1::OnTimer(UINT_PTR nIDEvent)
|
{
|
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);
|
}
|
|
{
|
SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_Bonder1);
|
ASSERT(pEquipment);
|
DeviceStatus status = pEquipment->isAlive() ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
|
UpdateDeviceStatus(INDICATE_BONDER1, status);
|
}
|
|
{
|
SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_Bonder2);
|
ASSERT(pEquipment);
|
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);
|
}
|