Commit d2aa504e by 树林

fix: 网关取消内置模拟数据,仅上报真实 Modbus 读数

连不上或读失败的点标记 bad,不再生成假工程值;写控也不再本机假成功。

Co-authored-by: Cursor <cursoragent@cursor.com>
parent f51a30c8
...@@ -113,7 +113,7 @@ struct AppState { ...@@ -113,7 +113,7 @@ struct AppState {
std::map<std::string, PointValue> values; // key = point id std::map<std::string, PointValue> values; // key = point id
std::map<std::string, bool> hostOk; std::map<std::string, bool> hostOk;
std::map<std::string, int> hostLatencyMs; std::map<std::string, int> hostLatencyMs;
std::string version = "0.3.7-all-fc03"; std::string version = "0.3.8-real-only";
std::map<std::string, Session> sessions; std::map<std::string, Session> sessions;
bool pollRunning = false; bool pollRunning = false;
}; };
...@@ -595,11 +595,8 @@ void pollOnce() { ...@@ -595,11 +595,8 @@ void pollOnce() {
bool anyOk = false; bool anyOk = false;
int lastLat = 0; int lastLat = 0;
int unit = mine.empty() ? 1 : mine.front().unitId; int unit = mine.empty() ? 1 : mine.front().unitId;
// 本机联调:127.0.0.1 上 Windows 模拟端口异常时直接走内置公式(与 Python 模拟器一致)
const bool forceBuiltin =
h.host == "127.0.0.1" || h.host == "localhost" || h.host == "::1";
std::map<int, uint16_t> flat; std::map<int, uint16_t> flat;
if (!forceBuiltin) { // 只采真实 Modbus;连不上 / 读失败不造模拟数
if (mine.empty()) { if (mine.empty()) {
std::vector<uint16_t> tmp; std::vector<uint16_t> tmp;
int lat = 0; int lat = 0;
...@@ -610,7 +607,7 @@ void pollOnce() { ...@@ -610,7 +607,7 @@ void pollOnce() {
hostLatency[h.id] = lat; hostLatency[h.id] = lat;
continue; continue;
} }
// flat 仍按点表逻辑地址索引;读请求地址 = 逻辑地址 + offset(默认 -1) // flat 按点表逻辑地址索引;读请求地址 = 逻辑地址 + offset
int minAddr = mine.front().address; int minAddr = mine.front().address;
int maxEnd = mine.front().address + wordCountForType(mine.front().dataType); int maxEnd = mine.front().address + wordCountForType(mine.front().dataType);
for (const auto& p : mine) { for (const auto& p : mine) {
...@@ -618,7 +615,6 @@ void pollOnce() { ...@@ -618,7 +615,6 @@ void pollOnce() {
maxEnd = std::max(maxEnd, p.address + wordCountForType(p.dataType)); maxEnd = std::max(maxEnd, p.address + wordCountForType(p.dataType));
} }
if (minAddr < 0) minAddr = 0; if (minAddr < 0) minAddr = 0;
// 按点位地址分段读,避免整段 >2000 时截断导致高地址写控点永远无值
std::vector<int> addrs; std::vector<int> addrs;
addrs.reserve(mine.size()); addrs.reserve(mine.size());
for (const auto& p : mine) addrs.push_back(p.address); for (const auto& p : mine) addrs.push_back(p.address);
...@@ -633,7 +629,6 @@ void pollOnce() { ...@@ -633,7 +629,6 @@ void pollOnce() {
end = addrs[aj] + 1; end = addrs[aj] + 1;
++aj; ++aj;
} }
// 覆盖该段内多字类型
for (const auto& p : mine) { for (const auto& p : mine) {
if (p.address >= start && p.address < start + chunk) { if (p.address >= start && p.address < start + chunk) {
end = std::max(end, p.address + wordCountForType(p.dataType)); end = std::max(end, p.address + wordCountForType(p.dataType));
...@@ -651,7 +646,6 @@ void pollOnce() { ...@@ -651,7 +646,6 @@ void pollOnce() {
for (int i = 0; i < cnt; ++i) flat[start + i] = regs[static_cast<size_t>(i)]; for (int i = 0; i < cnt; ++i) flat[start + i] = regs[static_cast<size_t>(i)];
} else { } else {
++chunksFail; ++chunksFail;
// 失败则逐点单读,尽量拿到写控点状态
for (size_t k = ai; k < aj; ++k) { for (size_t k = ai; k < aj; ++k) {
const int one = addrs[k]; const int one = addrs[k];
const int onePdu = std::max(0, one + addrOffset); const int onePdu = std::max(0, one + addrOffset);
...@@ -674,21 +668,17 @@ void pollOnce() { ...@@ -674,21 +668,17 @@ void pollOnce() {
<< ") pduOffset=" << addrOffset << " chunksOk=" << chunksOk << ") pduOffset=" << addrOffset << " chunksOk=" << chunksOk
<< " fail=" << chunksFail << " flat=" << flat.size() << "\n" << " fail=" << chunksFail << " flat=" << flat.size() << "\n"
<< std::flush; << std::flush;
if (!anyOk) {
std::cout << "[gateway] host " << h.id << " Modbus unreachable → no simulated values\n"
<< std::flush;
} }
// 联调兜底:本机或 Modbus 不可达时,按与 Python 模拟器相同规则生成工程值
const bool useBuiltin = forceBuiltin || !anyOk;
if (useBuiltin) {
std::cout << "[gateway] host " << h.id << " Modbus unreachable → builtin sim\n" << std::flush;
}
const double tsec = std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch())
.count();
size_t idx = 0;
for (const auto& p : mine) { for (const auto& p : mine) {
PointValue v; PointValue v;
v.ts = ts; v.ts = ts;
bool fillBuiltin = useBuiltin; v.quality = "bad";
if (!fillBuiltin) { v.raw = 0;
v.eng = 0;
const int n = wordCountForType(p.dataType); const int n = wordCountForType(p.dataType);
std::vector<uint16_t> regs(static_cast<size_t>(n), 0); std::vector<uint16_t> regs(static_cast<size_t>(n), 0);
bool complete = true; bool complete = true;
...@@ -701,38 +691,7 @@ void pollOnce() { ...@@ -701,38 +691,7 @@ void pollOnce() {
regs[static_cast<size_t>(i)] = it->second; regs[static_cast<size_t>(i)] = it->second;
} }
if (complete) decodePoint(p, regs, 0, v, ts); if (complete) decodePoint(p, regs, 0, v, ts);
else fillBuiltin = true; // 单点 Holding 缺字时改走内置公式,避免整表大量 bad
}
if (fillBuiltin) {
const std::string n = p.name + p.itemId;
double base = 10.0 + (p.address % 100) * 0.1;
if (n.find("冰箱") != std::string::npos || n.find("冷") != std::string::npos) {
if (n.find("温") != std::string::npos) base = -20.0 + (p.address % 7) * 0.3;
else if (n.find("湿") != std::string::npos) base = 45.0 + (p.address % 20) * 0.2;
} else if (n.find("温") != std::string::npos || n.find("Temp") != std::string::npos) {
base = 22.0 + (p.address % 10) * 0.15;
} else if (n.find("湿") != std::string::npos || n.find("Humid") != std::string::npos) {
base = 45.0 + (p.address % 20) * 0.2;
} else if (n.find("压差") != std::string::npos) {
base = -15.0 + (p.address % 8) * 0.5;
}
const double wave = std::sin(tsec * 0.15 + idx * 0.17 + p.address * 0.01);
double eng = base * (1.0 + 0.03 * wave);
std::string dt = p.dataType;
for (char& c : dt) c = static_cast<char>(::tolower(c));
if (dt.find("bool") != std::string::npos || n.find("报警") != std::string::npos ||
n.find("故障") != std::string::npos) {
eng = ((static_cast<int>(tsec / 5) + static_cast<int>(idx)) % 4) ? 1.0 : 0.0;
}
// 反推 raw,使 eng≈raw*scale(与模拟器/网关解码一致)
double scale = p.scale != 0 ? p.scale : 1.0;
v.raw = (scale != 0) ? eng / scale : eng;
v.eng = eng;
v.quality = "good";
hostOk[h.id] = true;
}
nextValues[p.id.empty() ? p.itemId : p.id] = v; nextValues[p.id.empty() ? p.itemId : p.id] = v;
++idx;
} }
} }
...@@ -1127,8 +1086,6 @@ int main(int argc, char** argv) { ...@@ -1127,8 +1086,6 @@ int main(int argc, char** argv) {
// 现场禁止线圈功能码:原 0X/-F5/Coil/Bool 一律走 Holding FC06(写 0/1) // 现场禁止线圈功能码:原 0X/-F5/Coil/Bool 一律走 Holding FC06(写 0/1)
const bool asBool = reg.find("0X") != std::string::npos || reg.find("-F5") != std::string::npos || const bool asBool = reg.find("0X") != std::string::npos || reg.find("-F5") != std::string::npos ||
reg.find("COIL") != std::string::npos || dtype.find("bool") != std::string::npos; reg.find("COIL") != std::string::npos || dtype.find("bool") != std::string::npos;
const bool localhost =
host.host == "127.0.0.1" || host.host == "localhost" || host.host == "::1";
bool ok = false; bool ok = false;
std::string detail; std::string detail;
...@@ -1141,13 +1098,9 @@ int main(int argc, char** argv) { ...@@ -1141,13 +1098,9 @@ int main(int argc, char** argv) {
double raw = (value - point.bias) / scale; double raw = (value - point.bias) / scale;
regVal = static_cast<uint16_t>(static_cast<int32_t>(std::lround(raw)) & 0xFFFF); regVal = static_cast<uint16_t>(static_cast<int32_t>(std::lround(raw)) & 0xFFFF);
} }
// 仅真实 FC06;失败不本地假写
ok = modbus_tcp::writeSingleRegister(host.host, host.port, point.unitId, pduAddr, regVal); ok = modbus_tcp::writeSingleRegister(host.host, host.port, point.unitId, pduAddr, regVal);
detail = ok ? "FC06 ok" : "FC06 failed"; detail = ok ? "FC06 ok" : "FC06 failed";
if (!ok && localhost) {
// 本机联调兜底:接受写入并记入值缓存,便于 UI 回读
ok = true;
detail = "accepted (builtin sim)";
}
if (ok) { if (ok) {
PointValue v; PointValue v;
v.eng = value; v.eng = value;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment