Commit fd50d6bd by James Yuan

fix: 网关 Modbus 复用连接并加连接超时,避免单主机挂死拖垮其它网关

mb-102 connect 阻塞会导致 mb-104 轮询超时被标成读失败;0.3.12 非阻塞 connect + Session 复用后 fail-fast。

Co-authored-by: Cursor <cursoragent@cursor.com>
parent 367d9363
......@@ -113,7 +113,7 @@ struct AppState {
std::map<std::string, PointValue> values; // key = point id
std::map<std::string, bool> hostOk;
std::map<std::string, int> hostLatencyMs;
std::string version = "0.3.9-keep-good";
std::string version = "0.3.12-connect-timeout";
std::map<std::string, Session> sessions;
bool pollRunning = false;
};
......@@ -601,15 +601,27 @@ void pollOnce() {
int lastLat = 0;
int unit = mine.empty() ? 1 : mine.front().unitId;
std::map<int, uint16_t> flat;
// 同一主机整轮共用一条 TCP,避免每点新建连接把从站打满
modbus_tcp::Session sess;
const bool linked = sess.open(h.host, h.port);
if (!linked) {
hostOk[h.id] = false;
hostLatency[h.id] = 0;
std::cout << "[gateway] host " << h.id << " TCP connect failed " << h.host << ":" << h.port
<< "\n"
<< std::flush;
continue;
}
// 只采真实 Modbus;连不上 / 读失败不造模拟数
if (mine.empty()) {
std::vector<uint16_t> tmp;
int lat = 0;
const int probeLogical = 1;
const int probePdu = std::max(0, probeLogical + addrOffset);
bool ok = modbus_tcp::readHolding(h.host, h.port, unit, probePdu, 1, tmp, &lat);
bool ok = sess.readHolding(unit, probePdu, 1, tmp, &lat);
hostOk[h.id] = ok;
hostLatency[h.id] = lat;
sess.close();
continue;
}
// flat 按点表逻辑地址索引;读请求地址 = 逻辑地址 + offset
......@@ -626,7 +638,17 @@ void pollOnce() {
std::sort(addrs.begin(), addrs.end());
addrs.erase(std::unique(addrs.begin(), addrs.end()), addrs.end());
int chunksOk = 0, chunksFail = 0;
int singleOk = 0;
int consecutiveFail = 0;
constexpr int kMaxSingleFallback = 20; // 块失败后最多单点回退
constexpr int kAbortAfterFails = 3; // 连续失败则放弃本主机,避免拖死其它网关
for (size_t ai = 0; ai < addrs.size();) {
if (consecutiveFail >= kAbortAfterFails) {
std::cout << "[gateway] host " << h.id << " abort after " << consecutiveFail
<< " consecutive Modbus failures\n"
<< std::flush;
break;
}
int start = addrs[ai];
int end = start + 1;
size_t aj = ai + 1;
......@@ -640,33 +662,47 @@ void pollOnce() {
}
}
int cnt = std::min(chunk, std::max(1, end - start));
if (cnt > 125) cnt = 125;
const int pduStart = std::max(0, start + addrOffset);
std::vector<uint16_t> regs;
int lat = 0;
if (modbus_tcp::readHolding(h.host, h.port, unit, pduStart, cnt, regs, &lat) &&
if (sess.readHolding(unit, pduStart, cnt, regs, &lat, consecutiveFail == 0) &&
static_cast<int>(regs.size()) == cnt) {
anyOk = true;
lastLat = lat;
++chunksOk;
consecutiveFail = 0;
for (int i = 0; i < cnt; ++i) flat[start + i] = regs[static_cast<size_t>(i)];
} else {
++chunksFail;
for (size_t k = ai; k < aj; ++k) {
++consecutiveFail;
// 块读失败:有限次单点回退(复用同一连接)
bool gotOne = false;
for (size_t k = ai; k < aj && singleOk < kMaxSingleFallback; ++k) {
const int one = addrs[k];
const int onePdu = std::max(0, one + addrOffset);
std::vector<uint16_t> oneReg;
int oneLat = 0;
if (modbus_tcp::readHolding(h.host, h.port, unit, onePdu, 1, oneReg, &oneLat) &&
oneReg.size() == 1) {
if (sess.readHolding(unit, onePdu, 1, oneReg, &oneLat, false) && oneReg.size() == 1) {
anyOk = true;
lastLat = oneLat;
flat[one] = oneReg[0];
++chunksOk;
++singleOk;
gotOne = true;
consecutiveFail = 0;
}
}
if (!gotOne && consecutiveFail >= kAbortAfterFails) {
std::cout << "[gateway] host " << h.id << " abort after " << consecutiveFail
<< " consecutive Modbus failures\n"
<< std::flush;
break;
}
}
ai = aj > ai ? aj : ai + 1;
}
sess.close();
hostOk[h.id] = anyOk;
hostLatency[h.id] = lastLat;
std::cout << "[gateway] host " << h.id << " range=[" << minAddr << "," << maxEnd
......
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