chenluhua1980
2025-12-26 0222ece90a4caa554b1dd09aa37b95ad394bf893
1.显示生产数据和tt
已修改5个文件
137 ■■■■ 文件已修改
SourceCode/Bond/Servo/CPageProdOverview.cpp 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPanelProduction.cpp 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPanelProduction.h 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ProductionStats.cpp 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ProductionStats.h 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageProdOverview.cpp
@@ -5,11 +5,12 @@
#include "Servo.h"
#include "CPageProdOverview.h"
#include "afxdialogex.h"
#include "CPanelProduction.h"
namespace
{
    constexpr UINT_PTR kTimerRefreshId = 2001;
    constexpr UINT kTimerRefreshIntervalMs = 5000;
    constexpr UINT kTimerRefreshIntervalMs = 10000;
}
IMPLEMENT_DYNAMIC(CPageProdOverview, CDialogEx)
@@ -129,12 +130,36 @@
void CPageProdOverview::RefreshData()
{
    auto* pPanel = dynamic_cast<CPanelProduction*>(GetParent());
    if (pPanel == nullptr) {
        pPanel = dynamic_cast<CPanelProduction*>(GetParent() ? GetParent()->GetParent() : nullptr);
    }
    if (pPanel == nullptr) {
        m_labelDayOut.setText(_T("--"));
        m_labelNightOut.setText(_T("--"));
        m_labelDayTakt.setText(_T("--"));
        m_labelNightTakt.setText(_T("--"));
        return;
    }
    ProductionShiftSummary day;
    ProductionShiftSummary night;
    if (!pPanel->TryGetDayNightSummaries(day, night)) {
        m_labelDayOut.setText(_T("--"));
        m_labelNightOut.setText(_T("--"));
        m_labelDayTakt.setText(_T("--"));
        m_labelNightTakt.setText(_T("--"));
        return;
    }
    CString text;
    text.Format(_T("%d"), 123);
    text.Format(_T("%lld"), day.output.pairsTotal);
    m_labelDayOut.setText(text);
    text.Format(_T("%d"), 1235);
    text.Format(_T("%lld"), night.output.pairsTotal);
    m_labelNightOut.setText(text);
    m_labelDayTakt.setText(_T("1236"));
    m_labelNightTakt.setText(_T("1238"));
    text.Format(_T("%.1fs"), day.output.avgTaktSeconds);
    m_labelDayTakt.setText(text);
    text.Format(_T("%.1fs"), night.output.avgTaktSeconds);
    m_labelNightTakt.setText(text);
}
SourceCode/Bond/Servo/CPanelProduction.cpp
@@ -156,11 +156,12 @@
    }
}
BOOL CPanelProduction::TryGetShiftSummary(ProductionShiftSummary& outSummary)
BOOL CPanelProduction::TryGetDayNightSummaries(ProductionShiftSummary& outDay, ProductionShiftSummary& outNight)
{
    CSingleLock lock(&m_csShiftSummary, TRUE);
    if (!m_bShiftSummaryValid) return FALSE;
    outSummary = m_shiftSummary;
    outDay = m_daySummary;
    outNight = m_nightSummary;
    return TRUE;
}
@@ -197,10 +198,12 @@
    for (;;) {
        if (self->m_evStopStats.Lock(intervalMs)) break;
        ProductionShiftSummary summary;
        if (ProductionStats::ComputeCurrentShiftSummary(theApp.m_model.m_configuration, summary)) {
        ProductionShiftSummary daySummary;
        ProductionShiftSummary nightSummary;
        if (ProductionStats::ComputeDayNightSummaries(theApp.m_model.m_configuration, daySummary, nightSummary)) {
            CSingleLock lock(&self->m_csShiftSummary, TRUE);
            self->m_shiftSummary = std::move(summary);
            self->m_daySummary = std::move(daySummary);
            self->m_nightSummary = std::move(nightSummary);
            self->m_bShiftSummaryValid = TRUE;
        }
    }
@@ -211,12 +214,5 @@
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
@@ -24,8 +24,9 @@
    HWND m_hPlaceholder;
    CAccordionWnd* m_pAccordionWnd;
    // Production shift summary (updated by background thread)
    ProductionShiftSummary m_shiftSummary;
    // Production shift summaries (updated by background thread)
    ProductionShiftSummary m_daySummary;
    ProductionShiftSummary m_nightSummary;
    BOOL m_bShiftSummaryValid;
    CCriticalSection m_csShiftSummary;
    CWinThread* m_pStatsThread;
@@ -49,8 +50,8 @@
    afx_msg void OnVLineMoveX(NMHDR* nmhdr, LRESULT* result);
    afx_msg void OnBnClickedButtonClose();
    // Thread-safe snapshot for UI timer display
    BOOL TryGetShiftSummary(ProductionShiftSummary& outSummary);
    // Thread-safe snapshots for UI timer display
    BOOL TryGetDayNightSummaries(ProductionShiftSummary& outDay, ProductionShiftSummary& outNight);
private:
    static UINT AFX_CDECL StatsThreadProc(LPVOID pParam);
SourceCode/Bond/Servo/ProductionStats.cpp
@@ -88,7 +88,9 @@
    }
    const char* sql =
        "SELECT class_id, buddy_id, aoi_result "
        "SELECT class_id, buddy_id, aoi_result, "
        "IFNULL(strftime('%Y-%m-%d %H:%M:%S', t_start, 'localtime'), ''),"
        "IFNULL(strftime('%Y-%m-%d %H:%M:%S', t_end,   'localtime'), '') "
        "FROM glass_log "
        "WHERE t_end IS NOT NULL AND t_end >= ? AND t_end < ?;";
@@ -105,6 +107,7 @@
        bool hasPass = false;
        bool hasFail = false;
        bool hasNo = false;
        long long maxTaktSeconds = -1;
    };
    std::unordered_map<std::string, PairAgg> pairs;
@@ -114,6 +117,8 @@
            const char* classId = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
            const char* buddyId = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
            const int aoi = sqlite3_column_int(stmt, 2);
            const char* sStart = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
            const char* sEnd = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 4));
            const std::string a = classId ? classId : "";
            const std::string b = buddyId ? buddyId : "";
@@ -130,6 +135,12 @@
            if (aoi == 1) agg.hasPass = true;
            else if (aoi == 2) agg.hasFail = true;
            else agg.hasNo = true;
            std::chrono::system_clock::time_point tpStart{}, tpEnd{};
            if (TryParseLocalTime(sStart ? sStart : "", tpStart) && TryParseLocalTime(sEnd ? sEnd : "", tpEnd) && tpEnd > tpStart) {
                const auto secs = std::chrono::duration_cast<std::chrono::seconds>(tpEnd - tpStart).count();
                if (secs > agg.maxTaktSeconds) agg.maxTaktSeconds = secs;
            }
        }
        else if (rc == SQLITE_DONE) {
            break;
@@ -143,14 +154,23 @@
    sqlite3_close(db);
    out.pairsTotal = static_cast<long long>(pairs.size());
    long long sumTakt = 0;
    long long cntTakt = 0;
    for (const auto& kv : pairs) {
        const auto& agg = kv.second;
        if (agg.hasFail) out.pairsFail++;
        else if (agg.hasPass) out.pairsPass++;
        else out.pairsNoResult++;
        if (agg.maxTaktSeconds >= 0) {
            sumTakt += agg.maxTaktSeconds;
            cntTakt += 1;
        }
    }
    const long long denom = out.pairsPass + out.pairsFail;
    out.yield = (denom > 0) ? (static_cast<double>(out.pairsPass) / static_cast<double>(denom)) : 0.0;
    out.taktSamplePairs = cntTakt;
    out.avgTaktSeconds = (cntTakt > 0) ? (static_cast<double>(sumTakt) / static_cast<double>(cntTakt)) : 0.0;
}
static void ComputeAlarmSummaryFromDb(
@@ -359,6 +379,7 @@
    LOGI("<ProductionStats>Shift=%s, [%s ~ %s]", shiftName, s.window.startLocal.c_str(), s.window.endLocal.c_str());
    LOGI("<ProductionStats>Output(pairs): total=%lld, pass=%lld, fail=%lld, no_result=%lld, yield=%.2f%%",
        s.output.pairsTotal, s.output.pairsPass, s.output.pairsFail, s.output.pairsNoResult, s.output.yield * 100.0);
    LOGI("<ProductionStats>Takt: avg=%.1fs, samples=%lld", s.output.avgTaktSeconds, s.output.taktSamplePairs);
    LOGI("<ProductionStats>Alarms: triggered=%d, overlapping=%d, downtime=%.1f min",
        s.alarms.alarmsTriggered, s.alarms.alarmsOverlapping, s.alarms.downtimeMinutes);
    if (!s.alarms.bySeverity.empty()) {
@@ -375,3 +396,43 @@
        LOGI("%s", oss.str().c_str());
    }
}
bool ProductionStats::ComputeDayNightSummaries(CConfiguration& config, ProductionShiftSummary& outDay, ProductionShiftSummary& outNight)
{
    ProductionShiftSummary cur;
    if (!ComputeCurrentShiftSummary(config, cur)) return false;
    // Determine previous adjacent window for the other shift.
    ProductionShiftSummary other = cur;
    if (cur.window.type == ProductionShiftType::Day) {
        other.window.type = ProductionShiftType::Night;
        other.window.end = cur.window.start;
        other.window.start = cur.window.start - (cur.window.end - cur.window.start);
    }
    else {
        other.window.type = ProductionShiftType::Day;
        other.window.end = cur.window.start;
        other.window.start = cur.window.start - (cur.window.end - cur.window.start);
    }
    other.window.startLocal = FormatLocal(other.window.start);
    other.window.endLocal = FormatLocal(other.window.end);
    other.window.startUtcIso = FormatUtcIso(other.window.start);
    other.window.endUtcIso = FormatUtcIso(other.window.end);
    other.output = ProductionOutputSummary{};
    other.alarms = ProductionAlarmSummary{};
    other.transfers = ProductionTransferSummary{};
    ComputeOutputFromProcessDb(other.window, other.output);
    ComputeAlarmSummaryFromDb(other.window, other.alarms);
    ComputeTransferSummaryFromDb(other.window, other.transfers);
    if (cur.window.type == ProductionShiftType::Day) {
        outDay = std::move(cur);
        outNight = std::move(other);
    }
    else {
        outNight = std::move(cur);
        outDay = std::move(other);
    }
    return true;
}
SourceCode/Bond/Servo/ProductionStats.h
@@ -27,6 +27,10 @@
    long long pairsFail = 0;
    long long pairsNoResult = 0;
    double yield = 0.0;              // pairsPass / (pairsPass + pairsFail), 0 if denom==0
    // Average takt time derived from glass_log.t_start/t_end (per pair, seconds)
    double avgTaktSeconds = 0.0;
    long long taktSamplePairs = 0;
};
struct ProductionAlarmSummary {
@@ -54,5 +58,9 @@
    static bool GetCurrentShiftWindow(CConfiguration& config, ProductionShiftWindow& outWindow);
    static bool ComputeCurrentShiftSummary(CConfiguration& config, ProductionShiftSummary& outSummary);
    static void LogCurrentShiftSummary(CConfiguration& config);
};
    // Computes "current shift" and its adjacent other shift, so UI can always show Day+Night numbers.
    // - If current is Day: day=current day shift, night=previous night shift.
    // - If current is Night: night=current night shift, day=previous day shift.
    static bool ComputeDayNightSummaries(CConfiguration& config, ProductionShiftSummary& outDay, ProductionShiftSummary& outNight);
};