pELoad 连接后从设备回读真实配置同步状态
程序重启/刷新后页面状态与设备实际状态错配,导致按页面流程下发指令反馈错误。 新增 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 增加设备配置同步状态显示
This commit is contained in:
+99
-7
@@ -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<std::mutex> 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<char>(::toupper(static_cast<unsigned char>(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<std::mutex> 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<char>(::toupper(static_cast<unsigned char>(clean[i])));
|
||||
if (u.rfind("CURR", 0) == 0) clean = "CURR";
|
||||
else if (u.rfind("VOLT", 0) == 0) clean = "VOLT";
|
||||
std::lock_guard<std::mutex> 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
|
||||
//============================================================================
|
||||
// 测量
|
||||
//============================================================================
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -154,6 +154,7 @@ td.l,th.l{text-align:left;}
|
||||
<div class="errline" id="measDiagErr" style="display:none"></div>
|
||||
<div class="row"><span class="k">环路优先(回读)</span><span class="v" id="devFunc">-</span></div>
|
||||
<div class="row"><span class="k">功能模式(回读)</span><span class="v" id="devFuncMode">-</span></div>
|
||||
<div class="row"><span class="k">设备配置同步</span><span class="v" id="cfgSync">-</span></div>
|
||||
<div class="row"><span class="k">切换结果</span><span class="v" id="funcModeFeed">-</span></div>
|
||||
<div class="sub">功能模式切换(FUNC:MODE)</div>
|
||||
<div class="inline">
|
||||
@@ -419,6 +420,12 @@ function render(s){
|
||||
? '<b style="font-size:15px">' + fmName + '</b>'
|
||||
+ (dev.funcModeFixWarn ? ' <span class="badge err">非FIX</span>' : ' <span class="badge ok">FIX</span>')
|
||||
: '<span class="dim">回读中...</span>';
|
||||
const cfs = $('cfgSync');
|
||||
if(dev.cfgSynced){
|
||||
cfs.innerHTML = '<span style="color:var(--ok)">已同步</span>('+ageText(now - dev.cfgSyncTime)+')';
|
||||
} else {
|
||||
cfs.innerHTML = '<span style="color:var(--warn)">同步中…</span>';
|
||||
}
|
||||
const fmf = $('funcModeFeed');
|
||||
if(dev.funcModeFeedValid){
|
||||
const ok = dev.funcModeFeedOk ? '已发送成功' : '发送失败(未连接或超时)';
|
||||
|
||||
Reference in New Issue
Block a user