1.修复以下问题:
CEID 校验恒通过:ceidDefined 返回 true,PauseEvent ID 不做有效性检查。Host 若下发无效 CEID,将被接受但运行时无法触发暂停,风险难以察觉。
建议的上线前防护 / 改进(无需真机也可先改)
2.软件侧警告id上抛;
| | |
| | | #include <fstream> |
| | | #include "SerializeUtil.h" |
| | | #include "CServoUtilsTool.h" |
| | | #include "AlarmManager.h" |
| | | #include "ToolUnits.h" |
| | | #include "Model.h" |
| | | |
| | | |
| | | namespace SERVO { |
| | |
| | | m_nContinuousWorkingPort = 0; |
| | | m_nContinuousWorkingSlot = 0; |
| | | m_pControlJob = nullptr; |
| | | m_bPauseAlarmRaised = false; |
| | | m_pModelCtx = nullptr; |
| | | m_nTestFlag = 0; |
| | | InitializeCriticalSection(&m_criticalSection); |
| | | } |
| | |
| | | void CMaster::setListener(MasterListener listener) |
| | | { |
| | | m_listener = listener; |
| | | } |
| | | |
| | | void CMaster::setModelCtx(CModel* pModel) |
| | | { |
| | | m_pModelCtx = pModel; |
| | | } |
| | | |
| | | CRobotTask* CMaster::getActiveRobotTask() |
| | |
| | | m_pActiveRobotTask->place(); |
| | | } |
| | | unlock(); // 等当前任务完成或中止后继续 |
| | | continue; |
| | | } |
| | | |
| | | // 5.5) 暂停状态检查:若 CJ 或在制 PJ 处于 Paused,暂缓调度新的搬送 |
| | | bool pausedByEvent = false; |
| | | if (m_pControlJob != nullptr && m_pControlJob->state() == CJState::Paused) { |
| | | pausedByEvent = true; |
| | | } |
| | | for (auto pj : m_inProcesJobs) { |
| | | if (pj != nullptr && pj->state() == PJState::Paused) { |
| | | pausedByEvent = true; |
| | | break; |
| | | } |
| | | } |
| | | if (!pausedByEvent && m_bPauseAlarmRaised) { |
| | | if (m_pModelCtx != nullptr) { |
| | | m_pModelCtx->clearSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, 0, 0); |
| | | } |
| | | else { |
| | | AlarmManager& alarmManager = AlarmManager::getInstance(); |
| | | alarmManager.clearAlarmByAttributes(ALID_SOFTWARE_PAUSE_EVENT, 0, 0, CToolUnits::getCurrentTimeString()); |
| | | } |
| | | m_bPauseAlarmRaised = false; |
| | | } |
| | | if (pausedByEvent) { |
| | | LOGI("<Master>调度暂停:ControlJob/ProcessJob 处于 Paused 状态(可能由 PauseEvent 触发)"); |
| | | unlock(); |
| | | continue; |
| | | } |
| | | |
| | |
| | | return m_allowedCeids.find(ceid) != m_allowedCeids.end(); |
| | | } |
| | | |
| | | bool CMaster::raiseSoftAlarm(int alarmId, |
| | | const std::string& desc, |
| | | int level/* = -1*/, |
| | | int deviceId/* = 0*/, |
| | | int unitId/* = 0*/, |
| | | const char* deviceName/* = "Software"*/, |
| | | const char* unitName/* = "App"*/) |
| | | { |
| | | AlarmManager& alarmManager = AlarmManager::getInstance(); |
| | | const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId); |
| | | |
| | | int severity = level; |
| | | if (severity < 0 && info != nullptr) { |
| | | severity = info->nAlarmLevel; |
| | | } |
| | | if (severity < 0) severity = 0; // fallback |
| | | |
| | | std::string descText = desc; |
| | | if (descText.empty() && info != nullptr) { |
| | | descText = !info->strDescription.empty() ? info->strDescription : info->strAlarmText; |
| | | } |
| | | if (descText.empty()) { |
| | | descText = CToolUnits::formatString("Alarm %d", alarmId); |
| | | } |
| | | |
| | | AlarmData alarmData; |
| | | alarmData.nId = alarmId; |
| | | alarmData.nSeverityLevel = severity; |
| | | alarmData.nDeviceId = deviceId; |
| | | alarmData.nUnitId = unitId; |
| | | alarmData.strDeviceName = deviceName; |
| | | alarmData.strUnitName = unitName; |
| | | alarmData.strStartTime = CToolUnits::timeToString2(CToolUnits::getTimestamp()); |
| | | alarmData.strEndTime = ""; |
| | | alarmData.strDescription = descText; |
| | | |
| | | int nAlarmEventId = 0; |
| | | return alarmManager.addAlarm(alarmData, nAlarmEventId); |
| | | } |
| | | |
| | | void CMaster::handleCollectionEvent(uint32_t ceid) |
| | | { |
| | | // 遍历当前 PJ,命中 pauseEvents 时可在此扩展暂停动作 |
| | | bool pausedAny = false; |
| | | for (auto pj : m_processJobs) { |
| | | if (pj == nullptr) continue; |
| | | const auto& pauseList = pj->pauseEvents(); |
| | | if (std::find(pauseList.begin(), pauseList.end(), ceid) != pauseList.end()) { |
| | | LOGW("<Master>PauseEvent hit: CEID=%u, PJ=%s, state=%d", ceid, pj->id().c_str(), (int)pj->state()); |
| | | // TODO: 衔接具体暂停策略(如暂停 PJ/CJ、停止调度/搬送),此处仅留桩位 |
| | | if (pj->pause()) { |
| | | LOGI("<Master>PJ paused by CEID=%u", ceid); |
| | | pausedAny = true; |
| | | } |
| | | if (m_pControlJob != nullptr && m_pControlJob->state() == CJState::Executing) { |
| | | if (m_pControlJob->pause()) { |
| | | LOGI("<Master>ControlJob paused by CEID=%u", ceid); |
| | | pausedAny = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if (pausedAny && m_listener.onControlJobChanged) { |
| | | // 通知应用层刷新 UI/按钮状态 |
| | | m_listener.onControlJobChanged(this); |
| | | } |
| | | if (pausedAny && !m_bPauseAlarmRaised) { |
| | | std::string desc = CToolUnits::formatString("<PauseEvent CEID=%u>", ceid); |
| | | bool raised = false; |
| | | if (m_pModelCtx != nullptr) { |
| | | raised = m_pModelCtx->raiseSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, desc); |
| | | } |
| | | else { |
| | | raised = raiseSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, desc); |
| | | } |
| | | if (raised) { |
| | | LOGI("<Master>PauseEvent soft alarm raised, CEID=%u", ceid); |
| | | m_bPauseAlarmRaised = true; |
| | | } |
| | | } |
| | | } |
| | |
| | | #include "../DAQBridge/core/Collector.h" |
| | | #include "CJobDataS.h" |
| | | |
| | | class CModel; |
| | | |
| | | |
| | | #define CTStep_Unknow 0 |
| | | #define CTStep_LoadPort_Aligner 1 |
| | |
| | | |
| | | |
| | | public: |
| | | void setModelCtx(CModel* pModel); |
| | | void setListener(MasterListener listener); |
| | | CRobotTask* getActiveRobotTask(); |
| | | int init(); |
| | |
| | | bool ceidDefined(uint32_t ceid) const override; |
| | | void setAllowedCeids(const std::vector<unsigned int>& ceids); |
| | | void handleCollectionEvent(uint32_t ceid); |
| | | bool raiseSoftAlarm(int alarmId, |
| | | const std::string& desc, |
| | | int level = -1, |
| | | int deviceId = 0, |
| | | int unitId = 0, |
| | | const char* deviceName = "Software", |
| | | const char* unitName = "App"); |
| | | |
| | | public: |
| | | int getLastError(); |
| | |
| | | private: |
| | | bool m_bEnableEventReport; |
| | | bool m_bEnableAlarmReport; |
| | | bool m_bPauseAlarmRaised; |
| | | SERVO::CControlJob* m_pControlJob; |
| | | std::vector<SERVO::CProcessJob*> m_processJobs; |
| | | std::string m_strStatePath; |
| | | CModel* m_pModelCtx; |
| | | |
| | | int m_nTestFlag; |
| | | std::list<CGlass*> m_bufGlass; |
| | |
| | | #define RX_CODE_EQ_ROBOT_TASK 1012 |
| | | #define RX_CODE_LOADPORT_STATUS_CHANGED 1014 |
| | | #define RX_CODE_CONTROL_STATE_CHANGED 1015 |
| | | #define RX_CODE_CONTROLJOB_CHANGED 1016 |
| | | |
| | | /* 软件侧 ALID */ |
| | | #define ALID_SOFTWARE_PAUSE_EVENT 9000 |
| | | |
| | | /* Channel Name */ |
| | | #define MC_CHANNEL1_NAME "McChannel1" |
| | |
| | | |
| | | /* PPID名字最大长度 */ |
| | | #define PPID_NAME_MAX 80 |
| | | |
| | |
| | | m_hsmsPassive.setVariableValue("PJobSpace", (__int64)(m_master.isProcessJobsEmpty() ? 1 : 0)); |
| | | } |
| | | |
| | | void CModel::notifyControlJobChanged() |
| | | { |
| | | // 1) 刷新派生 SV |
| | | refreshDerivedSVs(); |
| | | // 2) 通知上层 UI(RX_CODE_CONTROLJOB_CHANGED) |
| | | notify(RX_CODE_CONTROLJOB_CHANGED); |
| | | } |
| | | |
| | | bool CModel::raiseSoftAlarm(int alarmId, |
| | | const std::string& desc, |
| | | int level /*= -1*/, |
| | | int deviceId /*= 0*/, |
| | | int unitId /*= 0*/, |
| | | const char* deviceName /*= "Software"*/, |
| | | const char* unitName /*= "App"*/) |
| | | { |
| | | AlarmManager& alarmManager = AlarmManager::getInstance(); |
| | | const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId); |
| | | |
| | | int severity = level; |
| | | if (severity < 0 && info != nullptr) severity = info->nAlarmLevel; |
| | | if (severity < 0) severity = 0; |
| | | |
| | | std::string descText = desc; |
| | | if (descText.empty() && info != nullptr) { |
| | | descText = !info->strDescription.empty() ? info->strDescription : info->strAlarmText; |
| | | } |
| | | if (descText.empty()) { |
| | | descText = CToolUnits::formatString("Alarm %d", alarmId); |
| | | } |
| | | |
| | | AlarmData alarmData; |
| | | alarmData.nId = alarmId; |
| | | alarmData.nSeverityLevel = severity; |
| | | alarmData.nDeviceId = deviceId; |
| | | alarmData.nUnitId = unitId; |
| | | alarmData.strDeviceName = deviceName; |
| | | alarmData.strUnitName = unitName; |
| | | // 若未显式提供设备/单元名称,尝试通过 deviceId/unitId 解析(soft alarm 默认均为 0) |
| | | if (alarmData.strDeviceName.empty()) { |
| | | alarmData.strDeviceName = alarmManager.getDeviceNameById(deviceId); |
| | | } |
| | | if (alarmData.strUnitName.empty()) { |
| | | alarmData.strUnitName = alarmManager.getUnitNameById(deviceId, unitId); |
| | | } |
| | | alarmData.strStartTime = CToolUnits::timeToString2(CToolUnits::getTimestamp()); |
| | | alarmData.strEndTime = ""; |
| | | alarmData.strDescription = descText; |
| | | |
| | | int nAlarmEventId = 0; |
| | | bool result = alarmManager.addAlarm(alarmData, nAlarmEventId); |
| | | if (result) { |
| | | notify(RX_CODE_ALARM_SET); |
| | | if (m_master.isAlarmReportEnable()) { |
| | | m_hsmsPassive.requestAlarmReport(1, alarmId, descText.c_str()); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | void CModel::clearSoftAlarm(int alarmId, int deviceId, int unitId) |
| | | { |
| | | AlarmManager& alarmManager = AlarmManager::getInstance(); |
| | | alarmManager.clearAlarmByAttributes(alarmId, deviceId, unitId, CToolUnits::getCurrentTimeString()); |
| | | notify(RX_CODE_ALARM_CLEAR); |
| | | if (m_master.isAlarmReportEnable()) { |
| | | const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId); |
| | | std::string descText; |
| | | if (info != nullptr) descText = info->strAlarmText; |
| | | m_hsmsPassive.requestAlarmReport(0, alarmId, descText.c_str()); |
| | | } |
| | | } |
| | | |
| | | void CModel::setControlState(ControlState newState) |
| | | { |
| | | const auto prev = m_currentControlState; |
| | |
| | | |
| | | // CGlassPool |
| | | m_glassPool.initPool(); |
| | | |
| | | // 将 Model 上下文传递给 Master,便于 Master 触发软件级报警等跨层操作 |
| | | m_master.setModelCtx(this); |
| | | |
| | | |
| | | // Log |
| | |
| | | }; |
| | | masterListener.onControlJobChanged = [this](void* pMaster) { |
| | | (void)pMaster; |
| | | this->refreshDerivedSVs(); |
| | | this->notifyControlJobChanged(); |
| | | }; |
| | | masterListener.onEqAlive = [&](void* pMaster, SERVO::CEquipment* pEquipment, BOOL bAlive) -> void { |
| | | LOGI("<CModel>Equipment onAlive:%s(%s).", pEquipment->getName().c_str(), |
| | |
| | | #include "CMaster.h" |
| | | #include "CGlassPool.h" |
| | | #include <cstdint> |
| | | #include <string> |
| | | |
| | | enum class ControlState : uint8_t { |
| | | OfflineEquipment = 0, |
| | |
| | | |
| | | ControlState getControlState() const noexcept { return m_currentControlState; } |
| | | void setControlState(ControlState newState); |
| | | bool raiseSoftAlarm(int alarmId, |
| | | const std::string& desc = "", |
| | | int level = -1, |
| | | int deviceId = 0, |
| | | int unitId = 0, |
| | | const char* deviceName = "Software", |
| | | const char* unitName = "App"); |
| | | void clearSoftAlarm(int alarmId, int deviceId = 0, int unitId = 0); |
| | | |
| | | private: |
| | | void refreshDerivedSVs(); |
| | | void notifyControlJobChanged(); |
| | | |
| | | public: |
| | | int notify(int code); |
| | |
| | | SetTimer(TIMER_ID_UPDATE_RUMTIME, 500, nullptr); |
| | | } |
| | | } |
| | | else if (RX_CODE_CONTROLJOB_CHANGED == code) { |
| | | auto* cj = theApp.m_model.getMaster().getControlJob(); |
| | | CString text; |
| | | if (cj != nullptr) { |
| | | std::string st = cj->getStateText(); |
| | | text.Format(_T("ControlJob: %S (%S)"), cj->id().c_str(), st.c_str()); |
| | | if (cj->state() == SERVO::CJState::Paused) { |
| | | text += _T(" [Paused]"); |
| | | } |
| | | } |
| | | else { |
| | | text = _T("ControlJob: None"); |
| | | } |
| | | if (m_pMyStatusbar != nullptr) { |
| | | m_pMyStatusbar->setRunTimeText((LPTSTR)(LPCTSTR)text); |
| | | if (cj != nullptr && cj->state() == SERVO::CJState::Paused) { |
| | | m_pMyStatusbar->setBackgroundColor(STATUSBAR_BK_ALARM); |
| | | m_pMyStatusbar->setForegroundColor(RGB(0, 0, 0)); |
| | | } |
| | | } |
| | | } |
| | | else if (RX_CODE_EQ_ROBOT_TASK == code) { |
| | | int exCode; |
| | | if (pAny->getIntValue("exCode", exCode)) { |
| | |
| | | #include <ctime> |
| | | #include <iomanip> |
| | | #include <sstream> |
| | | #include <vector> |
| | | #include <cstdarg> |
| | | |
| | | CToolUnits::CToolUnits() |
| | | { |
| | |
| | | |
| | | return str; |
| | | } |
| | | std::string CToolUnits::formatString(const char* fmt, ...) |
| | | { |
| | | if (fmt == nullptr) return std::string(); |
| | | |
| | | char buf[512] = {0}; |
| | | va_list args; |
| | | va_start(args, fmt); |
| | | int n = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, fmt, args); |
| | | va_end(args); |
| | | |
| | | if (n >= 0 && n < (int)sizeof(buf)) { |
| | | return std::string(buf); |
| | | } |
| | | |
| | | // ????????????????? |
| | | std::vector<char> tmp((n > 0 ? n + 2 : 1024), 0); |
| | | va_start(args, fmt); |
| | | _vsnprintf_s(tmp.data(), tmp.size(), _TRUNCATE, fmt, args); |
| | | va_end(args); |
| | | return std::string(tmp.data()); |
| | | } |
| | |
| | | #include <chrono> |
| | | #include <optional> |
| | | #include <utility> |
| | | #include <vector> |
| | | #include <cstdarg> |
| | | |
| | | enum class QuickRange { Today, Last7Days, ThisMonth, ThisYear }; |
| | | using TP = std::chrono::system_clock::time_point; |
| | |
| | | static std::string NowStrSec(); |
| | | static std::wstring AnsiToWString(const std::string& str); |
| | | static std::string WStringToAnsi(const std::wstring& wstr); |
| | | static std::string formatString(const char* fmt, ...); |
| | | }; |
| | | |