| SourceCode/Bond/Servo/CPanelProduction.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/CPanelProduction.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/Configuration.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/ConfigurationProduction.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/ProductionStats.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/ProductionStats.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/Servo.vcxproj | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/Servo.vcxproj.filters | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/Servo/ServoDlg.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
SourceCode/Bond/Servo/CPanelProduction.cpp
@@ -1,4 +1,4 @@ // CPanelProduction.cpp // CPanelProduction.cpp // #include "stdafx.h" @@ -20,6 +20,8 @@ m_hbrBkgnd = nullptr; m_nPanelWidth = 288; m_hPlaceholder = nullptr; m_bShiftSummaryValid = FALSE; m_pStatsThread = nullptr; } CPanelProduction::~CPanelProduction() @@ -38,6 +40,7 @@ ON_WM_SIZE() ON_NOTIFY(BYVERTICALLINE_MOVEX, IDC_LINE1, &CPanelProduction::OnVLineMoveX) ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CPanelProduction::OnBnClickedButtonClose) ON_WM_TIMER() END_MESSAGE_MAP() int CPanelProduction::getPanelWidth() @@ -58,6 +61,9 @@ pLine1->SetBkgndColor(RGB(225, 225, 225)); pLine1->SetLineColor(RGB(198, 198, 198)); pLine1->EnableResize(); SetTimer(1, 1000 * 10, nullptr); StartStatsThread(); return TRUE; // return TRUE unless you set the focus to a control // Exception: OCX property pages should return FALSE @@ -81,6 +87,8 @@ void CPanelProduction::OnDestroy() { StopStatsThread(); CDialogEx::OnDestroy(); if (m_hbrBkgnd != nullptr) { @@ -123,3 +131,68 @@ pParent->PostMessage(WM_COMMAND, ID_MENU_WND_TEST_PANEL, 0); } } BOOL CPanelProduction::TryGetShiftSummary(ProductionShiftSummary& outSummary) { CSingleLock lock(&m_csShiftSummary, TRUE); if (!m_bShiftSummaryValid) return FALSE; outSummary = m_shiftSummary; return TRUE; } void CPanelProduction::StartStatsThread() { if (m_pStatsThread != nullptr) return; m_evStopStats.ResetEvent(); m_pStatsThread = AfxBeginThread(&CPanelProduction::StatsThreadProc, this, THREAD_PRIORITY_BELOW_NORMAL, 0, 0); if (m_pStatsThread != nullptr) { m_pStatsThread->m_bAutoDelete = FALSE; } } void CPanelProduction::StopStatsThread() { if (m_pStatsThread == nullptr) return; m_evStopStats.SetEvent(); const DWORD rc = WaitForSingleObject(m_pStatsThread->m_hThread, 5000); if (rc == WAIT_OBJECT_0) { delete m_pStatsThread; } m_pStatsThread = nullptr; } UINT CPanelProduction::StatsThreadProc(LPVOID pParam) { CPanelProduction* self = reinterpret_cast<CPanelProduction*>(pParam); if (self == nullptr) return 0; const DWORD intervalMs = 5000; for (;;) { if (self->m_evStopStats.Lock(intervalMs)) break; ProductionShiftSummary summary; if (ProductionStats::ComputeCurrentShiftSummary(theApp.m_model.m_configuration, summary)) { CSingleLock lock(&self->m_csShiftSummary, TRUE); self->m_shiftSummary = std::move(summary); self->m_bShiftSummaryValid = TRUE; } } return 0; } void CPanelProduction::OnTimer(UINT_PTR nIDEvent) { // TODO: 卿¤æ·»å æ¶æ¯å¤çç¨åºä»£ç å/æè°ç¨é»è®¤å¼ if (nIDEvent == 1) { ProductionShiftSummary outSummary; if (TryGetShiftSummary(outSummary)) { TRACE("OnTimer outSummary.output.pairsPass:%d\n", outSummary.output.pairsPass); } } CDialogEx::OnTimer(nIDEvent); } SourceCode/Bond/Servo/CPanelProduction.h
@@ -1,5 +1,8 @@ #pragma once #pragma once #include "BlButton.h" #include <afxmt.h> #include "ProductionStats.h" // CPanelProduction dialog class CPanelProduction : public CDialogEx @@ -19,10 +22,17 @@ CBlButton m_btnClose; HWND m_hPlaceholder; // Production shift summary (updated by background thread) ProductionShiftSummary m_shiftSummary; BOOL m_bShiftSummaryValid; CCriticalSection m_csShiftSummary; CWinThread* m_pStatsThread; CEvent m_evStopStats; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // å¯¹è¯æ¡æ°æ® // å¯¹è¯æ¡æ°æ® #ifdef AFX_DESIGN_TIME enum { IDD = IDD_PANEL_PRODUCTION }; #endif @@ -35,4 +45,14 @@ afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnVLineMoveX(NMHDR* nmhdr, LRESULT* result); afx_msg void OnBnClickedButtonClose(); // Thread-safe snapshot for UI timer display BOOL TryGetShiftSummary(ProductionShiftSummary& outSummary); private: static UINT AFX_CDECL StatsThreadProc(LPVOID pParam); void StartStatsThread(); void StopStatsThread(); public: afx_msg void OnTimer(UINT_PTR nIDEvent); }; SourceCode/Bond/Servo/Configuration.h
@@ -34,6 +34,13 @@ int getPortCassetteSnSeed(int port); void setPortCassetteSnSeed(int port, int seed); // Production shift settings // Reads shift start times from ini. // - [Production] DayShiftStart=HH:MM (default 08:00) // - [Production] NightShiftStart=HH:MM (default DayShiftStart+12h) // Returns TRUE if both values are valid (or derived); otherwise FALSE and falls back to defaults. BOOL getProductionShiftStartMinutes(int& dayStartMinutes, int& nightStartMinutes); public: void setP2RemoteEqReconnectInterval(int second); int getP2RemoteEqReconnectInterval(); SourceCode/Bond/Servo/ConfigurationProduction.cpp
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,75 @@ #include "stdafx.h" #include "Configuration.h" #include <mutex> #include <string> #include <unordered_map> static bool TryParseHHMM(const std::string& text, int& outMinutes) { int hour = 0; int minute = 0; if (sscanf_s(text.c_str(), "%d:%d", &hour, &minute) != 2) return false; if (hour < 0 || hour >= 24) return false; if (minute < 0 || minute >= 60) return false; outMinutes = hour * 60 + minute; return true; } BOOL CConfiguration::getProductionShiftStartMinutes(int& dayStartMinutes, int& nightStartMinutes) { struct CachedShift { BOOL ok = FALSE; int day = 0; int night = 0; bool inited = false; }; static std::mutex s_mtx; static std::unordered_map<std::string, CachedShift> s_cache; const std::string filePath((LPCSTR)(LPCTSTR)m_strFilepath); { std::lock_guard<std::mutex> g(s_mtx); auto it = s_cache.find(filePath); if (it != s_cache.end() && it->second.inited) { dayStartMinutes = it->second.day; nightStartMinutes = it->second.night; return it->second.ok; } } char buf[64] = {}; GetPrivateProfileStringA("Production", "DayShiftStart", "08:00", buf, (DWORD)sizeof(buf), m_strFilepath); std::string dayStr(buf); GetPrivateProfileStringA("Production", "NightShiftStart", "", buf, (DWORD)sizeof(buf), m_strFilepath); std::string nightStr(buf); const int kDefaultDay = 8 * 60; const int kDefaultNight = 20 * 60; bool okDay = TryParseHHMM(dayStr, dayStartMinutes); bool okNight = false; if (!nightStr.empty()) okNight = TryParseHHMM(nightStr, nightStartMinutes); if (!okDay) dayStartMinutes = kDefaultDay; if (!okNight) nightStartMinutes = (dayStartMinutes + 12 * 60) % (24 * 60); if (dayStartMinutes == nightStartMinutes) { dayStartMinutes = kDefaultDay; nightStartMinutes = kDefaultNight; { std::lock_guard<std::mutex> g(s_mtx); s_cache[filePath] = CachedShift{ FALSE, dayStartMinutes, nightStartMinutes, true }; } return FALSE; } const BOOL ok = (okDay && (nightStr.empty() ? TRUE : okNight)) ? TRUE : FALSE; { std::lock_guard<std::mutex> g(s_mtx); s_cache[filePath] = CachedShift{ ok, dayStartMinutes, nightStartMinutes, true }; } return ok; } SourceCode/Bond/Servo/ProductionStats.cpp
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,377 @@ #include "stdafx.h" #include "ProductionStats.h" #include <algorithm> #include <ctime> #include <iomanip> #include <sstream> #include <unordered_map> #include "Configuration.h" #include "Log.h" #include "sqlite3.h" #ifdef min #undef min #endif #ifdef max #undef max #endif static std::string FormatLocal(const std::chrono::system_clock::time_point& tp) { const std::time_t tt = std::chrono::system_clock::to_time_t(tp); std::tm tm{}; localtime_s(&tm, &tt); std::ostringstream oss; oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); return oss.str(); } static std::string FormatUtcIso(const std::chrono::system_clock::time_point& tp) { const std::time_t tt = std::chrono::system_clock::to_time_t(tp); std::tm tm{}; gmtime_s(&tm, &tt); std::ostringstream oss; oss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%SZ"); return oss.str(); } static bool TryParseLocalTime(const std::string& text, std::chrono::system_clock::time_point& outTp) { if (text.empty()) return false; std::tm tm{}; std::istringstream iss(text); iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); if (iss.fail()) return false; tm.tm_isdst = -1; const std::time_t tt = mktime(&tm); if (tt == (time_t)-1) return false; outTp = std::chrono::system_clock::from_time_t(tt); return true; } static std::string GetExeDir() { char path[MAX_PATH] = {}; GetModuleFileNameA(nullptr, path, MAX_PATH); std::string exePath(path); const size_t pos = exePath.find_last_of("\\/"); return (pos == std::string::npos) ? std::string() : exePath.substr(0, pos); } static bool FileExistsA(const std::string& path) { const DWORD attr = GetFileAttributesA(path.c_str()); return (attr != INVALID_FILE_ATTRIBUTES) && ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0); } static std::string PickDbPath(const std::string& rel1, const std::string& rel2) { const std::string base = GetExeDir(); const std::string p1 = base + "\\" + rel1; if (FileExistsA(p1)) return p1; return base + "\\" + rel2; } static void ComputeOutputFromProcessDb( const ProductionShiftWindow& win, ProductionOutputSummary& out) { const std::string dbPath = PickDbPath("db\\process.db", "DB\\process.db"); sqlite3* db = nullptr; if (sqlite3_open_v2(dbPath.c_str(), &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) { if (db) sqlite3_close(db); return; } const char* sql = "SELECT class_id, buddy_id, aoi_result " "FROM glass_log " "WHERE t_end IS NOT NULL AND t_end >= ? AND t_end < ?;"; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) { sqlite3_close(db); return; } sqlite3_bind_text(stmt, 1, win.startUtcIso.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, win.endUtcIso.c_str(), -1, SQLITE_TRANSIENT); struct PairAgg { bool hasPass = false; bool hasFail = false; bool hasNo = false; }; std::unordered_map<std::string, PairAgg> pairs; for (;;) { const int rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { const char* classId = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)); const char* buddyId = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)); const int aoi = sqlite3_column_int(stmt, 2); const std::string a = classId ? classId : ""; const std::string b = buddyId ? buddyId : ""; std::string key; if (!b.empty()) { if (a <= b) key = a + "|" + b; else key = b + "|" + a; } else { key = a; } auto& agg = pairs[key]; if (aoi == 1) agg.hasPass = true; else if (aoi == 2) agg.hasFail = true; else agg.hasNo = true; } else if (rc == SQLITE_DONE) { break; } else { break; } } sqlite3_finalize(stmt); sqlite3_close(db); out.pairsTotal = static_cast<long long>(pairs.size()); for (const auto& kv : pairs) { const auto& agg = kv.second; if (agg.hasFail) out.pairsFail++; else if (agg.hasPass) out.pairsPass++; else out.pairsNoResult++; } const long long denom = out.pairsPass + out.pairsFail; out.yield = (denom > 0) ? (static_cast<double>(out.pairsPass) / static_cast<double>(denom)) : 0.0; } static void ComputeAlarmSummaryFromDb( const ProductionShiftWindow& win, ProductionAlarmSummary& out) { const std::string dbPath = PickDbPath("DB\\AlarmManager.db", "DB\\AlarmManager.db"); sqlite3* db = nullptr; if (sqlite3_open_v2(dbPath.c_str(), &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) { if (db) sqlite3_close(db); return; } // 1) triggered within shift { const char* sql = "SELECT COUNT(1) FROM alarms " "WHERE start_time >= ? AND start_time < ?;"; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_text(stmt, 1, win.startLocal.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, win.endLocal.c_str(), -1, SQLITE_TRANSIENT); if (sqlite3_step(stmt) == SQLITE_ROW) out.alarmsTriggered = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); } } // 2) overlapping (including active) { const char* sql = "SELECT severity_level, start_time, end_time " "FROM alarms " "WHERE start_time < ? AND (end_time IS NULL OR end_time >= ?);"; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_text(stmt, 1, win.endLocal.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, win.startLocal.c_str(), -1, SQLITE_TRANSIENT); const auto now = std::chrono::system_clock::now(); for (;;) { const int rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { const int severity = sqlite3_column_int(stmt, 0); const char* sStart = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)); const char* sEnd = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)); std::chrono::system_clock::time_point aStart{}; if (!TryParseLocalTime(sStart ? sStart : "", aStart)) continue; std::chrono::system_clock::time_point aEnd{}; bool hasEnd = TryParseLocalTime(sEnd ? sEnd : "", aEnd); if (!hasEnd) aEnd = std::min(now, win.end); const auto clipStart = std::max(aStart, win.start); const auto clipEnd = std::min(aEnd, win.end); if (clipEnd > clipStart) { const auto secs = std::chrono::duration_cast<std::chrono::seconds>(clipEnd - clipStart).count(); out.downtimeMinutes += static_cast<double>(secs) / 60.0; } out.bySeverity[severity] += 1; out.alarmsOverlapping += 1; } else if (rc == SQLITE_DONE) { break; } else { break; } } sqlite3_finalize(stmt); } } sqlite3_close(db); } static void ComputeTransferSummaryFromDb( const ProductionShiftWindow& win, ProductionTransferSummary& out) { const std::string dbPath = PickDbPath("DB\\TransferManager.db", "DB\\TransferManager.db"); sqlite3* db = nullptr; if (sqlite3_open_v2(dbPath.c_str(), &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) { if (db) sqlite3_close(db); return; } const char* sql = "SELECT status, create_time, end_time " "FROM transfers " "WHERE end_time >= ? AND end_time < ? AND end_time != '';"; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) { sqlite3_close(db); return; } sqlite3_bind_text(stmt, 1, win.startLocal.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, win.endLocal.c_str(), -1, SQLITE_TRANSIENT); long long totalSecs = 0; long long cntSecs = 0; for (;;) { const int rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { const char* sStatus = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)); const char* sCreate = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)); const char* sEnd = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)); const std::string status = sStatus ? sStatus : ""; out.byStatus[status] += 1; out.transfersFinished += 1; std::chrono::system_clock::time_point tpCreate{}, tpEnd{}; if (TryParseLocalTime(sCreate ? sCreate : "", tpCreate) && TryParseLocalTime(sEnd ? sEnd : "", tpEnd) && tpEnd > tpCreate) { totalSecs += std::chrono::duration_cast<std::chrono::seconds>(tpEnd - tpCreate).count(); cntSecs += 1; } } else if (rc == SQLITE_DONE) { break; } else { break; } } sqlite3_finalize(stmt); sqlite3_close(db); out.avgCreateToEndSeconds = (cntSecs > 0) ? (static_cast<double>(totalSecs) / static_cast<double>(cntSecs)) : 0.0; } bool ProductionStats::GetCurrentShiftWindow(CConfiguration& config, ProductionShiftWindow& outWindow) { int dayMin = 8 * 60; int nightMin = 20 * 60; config.getProductionShiftStartMinutes(dayMin, nightMin); const auto now = std::chrono::system_clock::now(); const std::time_t ttNow = std::chrono::system_clock::to_time_t(now); std::tm tmNow{}; localtime_s(&tmNow, &ttNow); std::tm tmMid = tmNow; tmMid.tm_hour = 0; tmMid.tm_min = 0; tmMid.tm_sec = 0; tmMid.tm_isdst = -1; const std::time_t ttMid = mktime(&tmMid); if (ttMid == (time_t)-1) return false; const auto midnight = std::chrono::system_clock::from_time_t(ttMid); const auto startDayToday = midnight + std::chrono::minutes(dayMin); const auto startNightToday = midnight + std::chrono::minutes(nightMin); const auto startDay = (now >= startDayToday) ? startDayToday : (startDayToday - std::chrono::hours(24)); const auto startNight = (now >= startNightToday) ? startNightToday : (startNightToday - std::chrono::hours(24)); ProductionShiftType type = ProductionShiftType::Day; auto start = startDay; if (startNight > startDay) { type = ProductionShiftType::Night; start = startNight; } const int durationMin = (type == ProductionShiftType::Day) ? ((nightMin - dayMin + 24 * 60) % (24 * 60)) : ((dayMin - nightMin + 24 * 60) % (24 * 60)); if (durationMin <= 0) return false; outWindow.type = type; outWindow.start = start; outWindow.end = start + std::chrono::minutes(durationMin); outWindow.startLocal = FormatLocal(outWindow.start); outWindow.endLocal = FormatLocal(outWindow.end); outWindow.startUtcIso = FormatUtcIso(outWindow.start); outWindow.endUtcIso = FormatUtcIso(outWindow.end); return true; } bool ProductionStats::ComputeCurrentShiftSummary(CConfiguration& config, ProductionShiftSummary& outSummary) { ProductionShiftWindow win; if (!GetCurrentShiftWindow(config, win)) return false; outSummary = ProductionShiftSummary{}; outSummary.window = win; ComputeOutputFromProcessDb(win, outSummary.output); ComputeAlarmSummaryFromDb(win, outSummary.alarms); ComputeTransferSummaryFromDb(win, outSummary.transfers); return true; } void ProductionStats::LogCurrentShiftSummary(CConfiguration& config) { ProductionShiftSummary s; if (!ComputeCurrentShiftSummary(config, s)) { LOGE("<ProductionStats>Failed to compute shift summary."); return; } const char* shiftName = (s.window.type == ProductionShiftType::Day) ? "Day" : "Night"; LOGI("<ProductionStats>Shift=%s, [%s ~ %s]", shiftName, s.window.startLocal.c_str(), s.window.endLocal.c_str()); LOGI("<ProductionStats>Output(pairs): total=%lld, pass=%lld, fail=%lld, no_result=%lld, yield=%.2f%%", s.output.pairsTotal, s.output.pairsPass, s.output.pairsFail, s.output.pairsNoResult, s.output.yield * 100.0); LOGI("<ProductionStats>Alarms: triggered=%d, overlapping=%d, downtime=%.1f min", s.alarms.alarmsTriggered, s.alarms.alarmsOverlapping, s.alarms.downtimeMinutes); if (!s.alarms.bySeverity.empty()) { std::ostringstream oss; oss << "<ProductionStats>AlarmsBySeverity:"; for (const auto& kv : s.alarms.bySeverity) oss << " L" << kv.first << "=" << kv.second; LOGI("%s", oss.str().c_str()); } LOGI("<ProductionStats>Transfers: finished=%d, avg(create->end)=%.1fs", s.transfers.transfersFinished, s.transfers.avgCreateToEndSeconds); if (!s.transfers.byStatus.empty()) { std::ostringstream oss; oss << "<ProductionStats>TransfersByStatus:"; for (const auto& kv : s.transfers.byStatus) oss << " " << kv.first << "=" << kv.second; LOGI("%s", oss.str().c_str()); } } SourceCode/Bond/Servo/ProductionStats.h
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,58 @@ #pragma once #include <chrono> #include <map> #include <string> class CConfiguration; enum class ProductionShiftType : int { Day = 1, Night = 2 }; struct ProductionShiftWindow { ProductionShiftType type{ ProductionShiftType::Day }; std::chrono::system_clock::time_point start{}; std::chrono::system_clock::time_point end{}; std::string startLocal; // "YYYY-MM-DD HH:MM:SS" std::string endLocal; // "YYYY-MM-DD HH:MM:SS" std::string startUtcIso; // "YYYY-MM-DDTHH:MM:SSZ" std::string endUtcIso; // "YYYY-MM-DDTHH:MM:SSZ" }; struct ProductionOutputSummary { long long pairsTotal = 0; // "对æ°" long long pairsPass = 0; long long pairsFail = 0; long long pairsNoResult = 0; double yield = 0.0; // pairsPass / (pairsPass + pairsFail), 0 if denom==0 }; struct ProductionAlarmSummary { int alarmsTriggered = 0; // start_time within shift window int alarmsOverlapping = 0; // overlaps shift window (including active) double downtimeMinutes = 0.0; // overlap minutes (best-effort) std::map<int, int> bySeverity; // severity_level -> count (overlapping) }; struct ProductionTransferSummary { int transfersFinished = 0; // end_time within shift window std::map<std::string, int> byStatus; double avgCreateToEndSeconds = 0.0; }; struct ProductionShiftSummary { ProductionShiftWindow window; ProductionOutputSummary output; ProductionAlarmSummary alarms; ProductionTransferSummary transfers; }; class ProductionStats { public: static bool GetCurrentShiftWindow(CConfiguration& config, ProductionShiftWindow& outWindow); static bool ComputeCurrentShiftSummary(CConfiguration& config, ProductionShiftSummary& outSummary); static void LogCurrentShiftSummary(CConfiguration& config); }; SourceCode/Bond/Servo/Servo.vcxproj
@@ -246,6 +246,7 @@ <ClInclude Include="CPageReport.h" /> <ClInclude Include="CPageVarialbles.h" /> <ClInclude Include="CPanelProduction.h" /> <ClInclude Include="ProductionStats.h" /> <ClInclude Include="CParam.h" /> <ClInclude Include="CCjPage1.h" /> <ClInclude Include="CProcessDataListDlg.h" /> @@ -467,7 +468,9 @@ <ClCompile Include="CPageLinkSignal.cpp" /> <ClCompile Include="CPageReport.cpp" /> <ClCompile Include="CPageVarialbles.cpp" /> <ClCompile Include="ConfigurationProduction.cpp" /> <ClCompile Include="CPanelProduction.cpp" /> <ClCompile Include="ProductionStats.cpp" /> <ClCompile Include="CParam.cpp" /> <ClCompile Include="CCjPage1.cpp" /> <ClCompile Include="CProcessDataListDlg.cpp" /> @@ -659,4 +662,4 @@ <Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> </Target> </Project> </Project> SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -236,7 +236,9 @@ <ClCompile Include="CVariableEditDlg2.cpp" /> <ClCompile Include="CEventEditDlg.cpp" /> <ClCompile Include="CReportEditDlg.cpp" /> <ClCompile Include="ConfigurationProduction.cpp" /> <ClCompile Include="CPanelProduction.cpp" /> <ClCompile Include="ProductionStats.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="AlarmManager.h" /> @@ -258,6 +260,7 @@ <ClInclude Include="stdafx.h" /> <ClInclude Include="targetver.h" /> <ClInclude Include="TerminalDisplayDlg.h" /> <ClInclude Include="ProductionStats.h" /> <ClInclude Include="SECSRuntimeManager.h" /> <ClInclude Include="CCLinkPerformance\CCLinkIEControl.h"> <Filter>CCLinkPerformance</Filter> @@ -560,4 +563,4 @@ <UniqueIdentifier>{885738f6-3122-4bb9-8308-46b7f692fb13}</UniqueIdentifier> </Filter> </ItemGroup> </Project> </Project> SourceCode/Bond/Servo/ServoDlg.cpp
@@ -708,7 +708,7 @@ void CServoDlg::OnUpdateMenuWndTestPanel(CCmdUI* pCmdUI) { pCmdUI->Enable(TRUE); pCmdUI->SetRadio(m_nLeftPanelType == 1); pCmdUI->SetCheck(m_nLeftPanelType == 1); } void CServoDlg::OnMenuWndProPanel() @@ -719,7 +719,7 @@ void CServoDlg::OnUpdateMenuWndProPanel(CCmdUI* pCmdUI) { pCmdUI->Enable(TRUE); pCmdUI->SetRadio(m_nLeftPanelType == 2); pCmdUI->SetCheck(m_nLeftPanelType == 2); } void CServoDlg::OnMenuHelpAbout()