From c610190c54d4c2168e77846cd559f6f6730694d4 Mon Sep 17 00:00:00 2001 From: zjk <1553836110@qq.com> Date: Mon, 7 Sep 2026 13:03:08 +0800 Subject: [PATCH] =?UTF-8?q?pELoad=20=E8=BF=9E=E6=8E=A5=E5=90=8E=E4=BB=8E?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=9B=9E=E8=AF=BB=E7=9C=9F=E5=AE=9E=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=90=8C=E6=AD=A5=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 程序重启/刷新后页面状态与设备实际状态错配,导致按页面流程下发指令反馈错误。 新增 syncDeviceConfigOnce:连接建立进入远控后同步查询设备 OUTP/FUNC/FUNC:MODE 及各限值/设定值,以设备真实回读为准覆盖内存状态;设备输出仍为开时置 loadOn=true 并由 applyEnable 重新下发完整使能序列(接管恢复控制)。 - IT6000C 新增 7 个配置回读查询封装(CURR?/VOLT?/VOLT:LIM?/CURR:LIM?*2/POW:LIM?*2) - ELoad 实现 syncDeviceConfigOnce 并接入 doControlCycle(连接后自动,覆盖重连/复位/本控→远控) - 抽出 normFunc 辅助函数复用于 pollError;快照新增 cfgSynced/cfgSyncTime - 页面卡2 增加设备配置同步状态显示 --- src/pELoad/ELoad.cpp | 106 +++++++++++++++++++++++++++++++--- src/pELoad/ELoad.h | 3 + src/pELoad/driver/IT6000C.cpp | 8 +++ src/pELoad/driver/IT6000C.h | 9 +++ src/pELoad/web/pages/index.h | 7 +++ 5 files changed, 126 insertions(+), 7 deletions(-) diff --git a/src/pELoad/ELoad.cpp b/src/pELoad/ELoad.cpp index 9420ac4..f401dc5 100644 --- a/src/pELoad/ELoad.cpp +++ b/src/pELoad/ELoad.cpp @@ -519,6 +519,7 @@ void ELoad::doControlCycle(double now) { // 每次连接后进入远控(仅当需要远控;用户切本控后不自动切回) if (m_remoteRequested && !m_remoteApplied) { + m_devCfgSyncDone = false; // 重连/复位/本控后需重读设备真实配置 if (m_dev.remote()) { m_remoteApplied = true; std::lock_guard lock(m_stateMutex); @@ -532,6 +533,12 @@ void ELoad::doControlCycle(double now) { } } + // 连接建立并进入远控后,回读一次设备真实配置(接管恢复控制) + if (m_remoteApplied && !m_devCfgSyncDone) { + syncDeviceConfigOnce(now); + m_devCfgSyncDone = true; + } + // 输出状态切换 bool needOn = m_loadOn; if (needOn && !m_cfgApplied) @@ -768,8 +775,97 @@ std::string normFuncMode(const std::string& m) { if (u.rfind("CAR", 0) == 0) return "CAR"; return u; } + +// 规范化环路优先 FUNC 回读:CURRent/VOLTage 长格式 -> CURR/VOLT。 +std::string normFunc(const std::string& m) { + std::string u; + u.reserve(m.size()); + for (size_t i = 0; i < m.size(); ++i) + u += static_cast(::toupper(static_cast(m[i]))); + if (u.rfind("CURR", 0) == 0) return "CURR"; + if (u.rfind("VOLT", 0) == 0) return "VOLT"; + return u; +} } // namespace +//--------------------------------------------------------- +// syncDeviceConfigOnce:连接后从设备回读真实配置并接管状态。 +// 程序重启/重连/复位后,设备可能保持着与 .moos 默认值不同的实际状态; +// 这里以设备真实回读为准覆盖内存状态,避免页面与设备错配。 +// 在控制线程内同步调用(仅连接建立时执行一次,低频);查询在锁外进行, +// 全部收集完毕后一次性加锁写回共享状态。 + +void ELoad::syncDeviceConfigOnce(double now) { + bool outpOn = false; + std::string func, mode; + double voltLimit = 0, currPos = 0, currNeg = 0, powPos = 0, powNeg = 0; + double curr = 0, volt = 0; + bool hasOutp = false, hasFunc = false, hasMode = false; + bool hasVoltLim = false, hasCurrPos = false, hasCurrNeg = false; + bool hasPowPos = false, hasPowNeg = false; + bool hasCurr = false, hasVolt = false; + + if (m_dev.queryOutput(outpOn)) hasOutp = true; + if (m_dev.queryFunction(func)) hasFunc = true; + if (m_dev.queryFunctionMode(mode)) hasMode = true; + if (m_dev.queryVoltageLimit(voltLimit)) hasVoltLim = true; + if (m_dev.queryCurrentLimitPos(currPos)) hasCurrPos = true; + if (m_dev.queryCurrentLimitNeg(currNeg)) hasCurrNeg = true; + if (m_dev.queryPowerLimitPos(powPos)) hasPowPos = true; + if (m_dev.queryPowerLimitNeg(powNeg)) hasPowNeg = true; + if (m_dev.queryCurrent(curr)) hasCurr = true; + if (m_dev.queryVoltage(volt)) hasVolt = true; + + std::lock_guard lock(m_stateMutex); + + // 输出:设备保持开 -> 接管恢复控制(置 loadOn=true,cfgApplied=false, + // 由 applyEnable 重新下发完整使能序列) + if (hasOutp) { + m_loadOn = outpOn; + m_cfgApplied = false; + } + + // 环路优先 FUNC:以设备为准,镜像 WCMD_MODE_CC/CV 的联动逻辑 + if (hasFunc) { + std::string nf = normFunc(func); + if (!nf.empty()) { + bool cc = (nf != "VOLT"); + m_devFunc = nf; + if (m_ccPriority != cc) { + m_ccPriority = cc; + if (!cc) m_mode = "volt"; + else if (m_mode == "volt") m_mode = "follow"; + } + } + } + + // 功能模式 FUNC:MODE + if (hasMode) { + std::string nm = normFuncMode(trimScpiStr(mode)); + if (!nm.empty()) { + m_devFuncMode = nm; + m_funcModeFixWarn = (nm != "FIX"); + } + } + + // 限值 / 设定值:以设备实际值为准 + if (hasVoltLim) m_voltLimit = voltLimit; + if (hasCurrPos) m_currLimitPos = currPos; + if (hasCurrNeg) m_currLimitNeg = currNeg; + if (hasPowPos) m_powLimitPos = powPos; + if (hasPowNeg) m_powLimitNeg = powNeg; + if (hasCurr) m_cmdCurrent = curr; + if (hasVolt) m_voltSet = volt; + + m_devCfgSyncTime = now; + LOG_F(INFO, "[ELoad] device config synced: outp=%s func=%s mode=%s " + "voltLim=%.1f currLim=%.3f/%.3f powLim=%.1f/%.1f curr=%.3f volt=%.1f", + (hasOutp ? (outpOn ? "ON" : "OFF") : "?"), + (hasFunc ? m_devFunc.c_str() : "?"), + (hasMode ? m_devFuncMode.c_str() : "?"), + voltLimit, currPos, currNeg, powPos, powNeg, curr, volt); +} + void ELoad::pollDevice() { if (m_measPending) return; // 上一轮查询仍在 IO 线程处理中 if (!m_scpi.isConnected()) { @@ -820,14 +916,8 @@ void ELoad::pollError(double now) { case 0: m_scpi.queryAsync("FUNC?", [this](bool ok, const std::string& r) { if (!ok) return; - std::string clean = trimScpiStr(r); + std::string clean = normFunc(trimScpiStr(r)); if (clean.empty()) return; - // 兼容 CURRent/VOLTage 长格式 -> CURR/VOLT - std::string u; - for (size_t i = 0; i < clean.size(); ++i) - u += static_cast(::toupper(static_cast(clean[i]))); - if (u.rfind("CURR", 0) == 0) clean = "CURR"; - else if (u.rfind("VOLT", 0) == 0) clean = "VOLT"; std::lock_guard lock(m_stateMutex); m_devFunc = clean; }); @@ -993,6 +1083,8 @@ std::string ELoad::buildSnapshot() { dev["funcModeFeedOk"] = m_funcModeFeedOk ? 1 : 0; dev["funcModeFeedTime"]= m_funcModeFeedTime; dev["funcModeFeedValid"] = m_funcModeFeedTime > 0 ? 1 : 0; + dev["cfgSynced"] = m_devCfgSyncDone ? 1 : 0; + dev["cfgSyncTime"] = m_devCfgSyncTime; dev["remoteState"] = m_remoteState; // remote / local / unknown dev["remoteFeed"] = m_remoteFeed; // 最近远控/本控指令执行结果 dev["remoteFeedTime"] = m_remoteFeedTime; diff --git a/src/pELoad/ELoad.h b/src/pELoad/ELoad.h index 334d35e..c91c16d 100644 --- a/src/pELoad/ELoad.h +++ b/src/pELoad/ELoad.h @@ -54,6 +54,7 @@ private: void applyDisable(); // 关输出 double computeTargetPower(double now); // 目标功率(含超时/斜率处理) double computeCmdCurrent(double targetP); // 功率折算电流(含限幅) + void syncDeviceConfigOnce(double now); // 连接后从设备回读真实配置并接管 void pollDevice(); // 异步测量轮询(不阻塞控制循环) void pollError(double now); // 异步错误/FUNC 回读(1Hz 节流) void requestFuncModeReadback(); // 异步回读 FUNC:MODE?(真实反馈驱动显示) @@ -166,6 +167,8 @@ private: //---- 控制线程私有(无需加锁)---- bool m_remoteApplied = false; bool m_cfgApplied = false; + bool m_devCfgSyncDone = false; // 连接后是否已回读设备真实配置 + double m_devCfgSyncTime = 0; // 最近一次配置回读时刻 double m_lastSentCurrent = 0; double m_lastSentVolt = 0; double m_lastSendTime = 0; diff --git a/src/pELoad/driver/IT6000C.cpp b/src/pELoad/driver/IT6000C.cpp index dc3c325..a177761 100644 --- a/src/pELoad/driver/IT6000C.cpp +++ b/src/pELoad/driver/IT6000C.cpp @@ -100,6 +100,14 @@ bool IT6000C::setCurrentLimitNeg(double amp) { return setDouble("CURR:LIM:NEG", bool IT6000C::setPowerLimitPos(double watt) { return setDouble("POW:LIM", watt); } bool IT6000C::setPowerLimitNeg(double watt) { return setDouble("POW:LIM:NEG", watt); } +bool IT6000C::queryCurrent(double& amp) { return queryDouble("CURR?", amp); } +bool IT6000C::queryVoltage(double& volt) { return queryDouble("VOLT?", volt); } +bool IT6000C::queryVoltageLimit(double& volt) { return queryDouble("VOLT:LIM?", volt); } +bool IT6000C::queryCurrentLimitPos(double& amp) { return queryDouble("CURR:LIM?", amp); } +bool IT6000C::queryCurrentLimitNeg(double& amp) { return queryDouble("CURR:LIM:NEG?", amp); } +bool IT6000C::queryPowerLimitPos(double& watt) { return queryDouble("POW:LIM?", watt); } +bool IT6000C::queryPowerLimitNeg(double& watt) { return queryDouble("POW:LIM:NEG?", watt); } + //============================================================================ // 测量 //============================================================================ diff --git a/src/pELoad/driver/IT6000C.h b/src/pELoad/driver/IT6000C.h index 63fee1c..1669dd9 100644 --- a/src/pELoad/driver/IT6000C.h +++ b/src/pELoad/driver/IT6000C.h @@ -62,6 +62,15 @@ public: bool setPowerLimitPos(double watt); // POW:LIM bool setPowerLimitNeg(double watt); // POW:LIM:NEG + // 配置回读(程序重启/重连后从设备同步真实状态) + bool queryCurrent(double& amp); // CURR? + bool queryVoltage(double& volt); // VOLT? + bool queryVoltageLimit(double& volt); // VOLT:LIM? + bool queryCurrentLimitPos(double& amp); // CURR:LIM? + bool queryCurrentLimitNeg(double& amp); // CURR:LIM:NEG? + bool queryPowerLimitPos(double& watt); // POW:LIM? + bool queryPowerLimitNeg(double& watt); // POW:LIM:NEG? + // 测量 bool measureVoltage(double& v); // MEAS:VOLT?(触发一次测量,较慢) bool measureCurrent(double& i); // MEAS:CURR? diff --git a/src/pELoad/web/pages/index.h b/src/pELoad/web/pages/index.h index f1ee18a..689811f 100644 --- a/src/pELoad/web/pages/index.h +++ b/src/pELoad/web/pages/index.h @@ -154,6 +154,7 @@ td.l,th.l{text-align:left;}
环路优先(回读)-
功能模式(回读)-
+
设备配置同步-
切换结果-
功能模式切换(FUNC:MODE)
@@ -419,6 +420,12 @@ function render(s){ ? '' + fmName + '' + (dev.funcModeFixWarn ? ' 非FIX' : ' FIX') : '回读中...'; + const cfs = $('cfgSync'); + if(dev.cfgSynced){ + cfs.innerHTML = '已同步('+ageText(now - dev.cfgSyncTime)+')'; + } else { + cfs.innerHTML = '同步中…'; + } const fmf = $('funcModeFeed'); if(dev.funcModeFeedValid){ const ok = dev.funcModeFeedOk ? '已发送成功' : '发送失败(未连接或超时)';