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 { ...@@ -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.9-keep-good"; std::string version = "0.3.12-connect-timeout";
std::map<std::string, Session> sessions; std::map<std::string, Session> sessions;
bool pollRunning = false; bool pollRunning = false;
}; };
...@@ -601,15 +601,27 @@ void pollOnce() { ...@@ -601,15 +601,27 @@ void pollOnce() {
int lastLat = 0; int lastLat = 0;
int unit = mine.empty() ? 1 : mine.front().unitId; int unit = mine.empty() ? 1 : mine.front().unitId;
std::map<int, uint16_t> flat; 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;连不上 / 读失败不造模拟数 // 只采真实 Modbus;连不上 / 读失败不造模拟数
if (mine.empty()) { if (mine.empty()) {
std::vector<uint16_t> tmp; std::vector<uint16_t> tmp;
int lat = 0; int lat = 0;
const int probeLogical = 1; const int probeLogical = 1;
const int probePdu = std::max(0, probeLogical + addrOffset); 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; hostOk[h.id] = ok;
hostLatency[h.id] = lat; hostLatency[h.id] = lat;
sess.close();
continue; continue;
} }
// flat 按点表逻辑地址索引;读请求地址 = 逻辑地址 + offset // flat 按点表逻辑地址索引;读请求地址 = 逻辑地址 + offset
...@@ -626,7 +638,17 @@ void pollOnce() { ...@@ -626,7 +638,17 @@ void pollOnce() {
std::sort(addrs.begin(), addrs.end()); std::sort(addrs.begin(), addrs.end());
addrs.erase(std::unique(addrs.begin(), addrs.end()), addrs.end()); addrs.erase(std::unique(addrs.begin(), addrs.end()), addrs.end());
int chunksOk = 0, chunksFail = 0; 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();) { 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 start = addrs[ai];
int end = start + 1; int end = start + 1;
size_t aj = ai + 1; size_t aj = ai + 1;
...@@ -640,33 +662,47 @@ void pollOnce() { ...@@ -640,33 +662,47 @@ void pollOnce() {
} }
} }
int cnt = std::min(chunk, std::max(1, end - start)); int cnt = std::min(chunk, std::max(1, end - start));
if (cnt > 125) cnt = 125;
const int pduStart = std::max(0, start + addrOffset); const int pduStart = std::max(0, start + addrOffset);
std::vector<uint16_t> regs; std::vector<uint16_t> regs;
int lat = 0; 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) { static_cast<int>(regs.size()) == cnt) {
anyOk = true; anyOk = true;
lastLat = lat; lastLat = lat;
++chunksOk; ++chunksOk;
consecutiveFail = 0;
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) { ++consecutiveFail;
// 块读失败:有限次单点回退(复用同一连接)
bool gotOne = false;
for (size_t k = ai; k < aj && singleOk < kMaxSingleFallback; ++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);
std::vector<uint16_t> oneReg; std::vector<uint16_t> oneReg;
int oneLat = 0; int oneLat = 0;
if (modbus_tcp::readHolding(h.host, h.port, unit, onePdu, 1, oneReg, &oneLat) && if (sess.readHolding(unit, onePdu, 1, oneReg, &oneLat, false) && oneReg.size() == 1) {
oneReg.size() == 1) {
anyOk = true; anyOk = true;
lastLat = oneLat; lastLat = oneLat;
flat[one] = oneReg[0]; flat[one] = oneReg[0];
++chunksOk; ++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; ai = aj > ai ? aj : ai + 1;
} }
sess.close();
hostOk[h.id] = anyOk; hostOk[h.id] = anyOk;
hostLatency[h.id] = lastLat; hostLatency[h.id] = lastLat;
std::cout << "[gateway] host " << h.id << " range=[" << minAddr << "," << maxEnd 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