feat: C++ 게이트웨이 write_addr 지원 + 헬스체크 기반 연결 상태 판정
- RegisterEntry에 write_addr 필드 추가 (기본값=addr) - .MD 태그: LOOPSTAT(읽기) ↔ MODEIN(쓰기) 분리 - ReadRegister() 개별 호출 제거 (batch ReadAllRegisters로 대체 완료) - ListTags 대소문자 무시 검색 - 소멸자/Stop null 체크 추가 - HealthCheck: SERVING 상태 반환
This commit is contained in:
@@ -18,6 +18,8 @@ class Controller;
|
||||
struct RegisterEntry {
|
||||
std::string tag;
|
||||
uint32_t addr;
|
||||
uint32_t write_addr; // address used for writes; defaults to addr. Differs for loop MODE
|
||||
// (read=Loop Status / LOOPSTAT, write=Auto/Manual State / MODEIN)
|
||||
uint32_t count; // register count (1 or 2)
|
||||
std::string type; // "float32" or "uint16"
|
||||
std::string access; // "R" or "RW"
|
||||
@@ -31,6 +33,10 @@ struct CachedValue {
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
};
|
||||
|
||||
// One gateway process serves exactly one HC900 controller. The C# ControllerProcessManager
|
||||
// launches one process per controller (each on its own gRPC port), so tag namespaces are
|
||||
// naturally isolated per controller — the same SignalTag name can exist on several
|
||||
// controllers (peer comms) without collision.
|
||||
class Hc900Gateway final {
|
||||
public:
|
||||
Hc900Gateway(const std::string& host, uint16_t port,
|
||||
@@ -46,7 +52,6 @@ private:
|
||||
void PollLoop();
|
||||
void LoadRegisterMap(const std::string& path);
|
||||
void ReadAllRegisters();
|
||||
CachedValue ReadRegister(const RegisterEntry& entry);
|
||||
|
||||
// gRPC service implementation
|
||||
class GatewayServiceImpl final : public hc900::ModbusGateway::Service {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <grpcpp/server.h>
|
||||
#include <grpcpp/server_builder.h>
|
||||
@@ -59,7 +60,7 @@ void Hc900Gateway::Stop()
|
||||
running_ = false;
|
||||
if (poll_thread_.joinable()) poll_thread_.join();
|
||||
if (grpc_server_) grpc_server_->Shutdown();
|
||||
controller_->disconnect();
|
||||
if (controller_) controller_->disconnect();
|
||||
}
|
||||
|
||||
// ─── Register Map Loading ───
|
||||
@@ -77,6 +78,7 @@ void Hc900Gateway::LoadRegisterMap(const std::string& path)
|
||||
RegisterEntry e;
|
||||
e.tag = item["tag"];
|
||||
e.addr = item["addr"];
|
||||
e.write_addr = item.value("write_addr", e.addr); // default: write where we read
|
||||
e.count = item.value("count", 2);
|
||||
e.type = item.value("type", "float32");
|
||||
e.access = item.value("access", "R");
|
||||
@@ -176,33 +178,6 @@ void Hc900Gateway::ReadAllRegisters()
|
||||
}
|
||||
}
|
||||
|
||||
CachedValue Hc900Gateway::ReadRegister(const RegisterEntry& entry)
|
||||
{
|
||||
CachedValue cv{};
|
||||
cv.is_float = (entry.type == "float32");
|
||||
cv.timestamp = std::chrono::system_clock::now();
|
||||
cv.quality = 0;
|
||||
|
||||
if (!controller_->is_connected()) return cv;
|
||||
|
||||
std::lock_guard<std::mutex> lock(transport_mutex_);
|
||||
|
||||
if (entry.type == "uint16" && entry.count == 1) {
|
||||
uint16_t v = 0;
|
||||
if (controller_->read_register(entry.addr, v)) {
|
||||
cv.uint16_val = v;
|
||||
cv.quality = 192;
|
||||
}
|
||||
} else if (entry.type == "float32") {
|
||||
float v = 0;
|
||||
if (controller_->read_float(entry.addr, v, VendorFormat::HC900_FLOAT)) {
|
||||
cv.float32_val = v;
|
||||
cv.quality = 192;
|
||||
}
|
||||
}
|
||||
return cv;
|
||||
}
|
||||
|
||||
// ─── gRPC Service Implementation ───
|
||||
|
||||
Hc900Gateway::GatewayServiceImpl::GatewayServiceImpl(Hc900Gateway& gateway)
|
||||
@@ -238,13 +213,6 @@ grpc::Status Hc900Gateway::GatewayServiceImpl::ReadTags(
|
||||
} else {
|
||||
for (const auto& name : req->tag_names()) {
|
||||
auto it = gateway_.cache_.find(name);
|
||||
if (it == gateway_.cache_.end()) {
|
||||
auto idx = gateway_.tag_index_.find(name);
|
||||
if (idx != gateway_.tag_index_.end()) {
|
||||
auto& entry = gateway_.registers_[idx->second];
|
||||
it = gateway_.cache_.find(entry.tag);
|
||||
}
|
||||
}
|
||||
if (it != gateway_.cache_.end()) {
|
||||
TagValueFromCache(resp->add_values(), it->first, it->second);
|
||||
}
|
||||
@@ -276,10 +244,10 @@ grpc::Status Hc900Gateway::GatewayServiceImpl::WriteTag(
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(gateway_.transport_mutex_);
|
||||
if (entry.type == "uint16") {
|
||||
ok = gateway_.controller_->write_register(entry.addr,
|
||||
ok = gateway_.controller_->write_register(entry.write_addr,
|
||||
static_cast<uint16_t>(req->value()));
|
||||
} else if (entry.type == "float32") {
|
||||
ok = gateway_.controller_->write_float(entry.addr,
|
||||
ok = gateway_.controller_->write_float(entry.write_addr,
|
||||
static_cast<float>(req->value()),
|
||||
VendorFormat::HC900_FLOAT);
|
||||
}
|
||||
@@ -307,6 +275,7 @@ grpc::Status Hc900Gateway::GatewayServiceImpl::ListTags(
|
||||
const hc900::ListTagsRequest* req,
|
||||
hc900::ListTagsResponse* resp)
|
||||
{
|
||||
// ListTags 필터: 대소문자 무시 검색 (ReadTags/WriteTag는 exact-match, register-map 원형 표기 필요)
|
||||
int count = 0;
|
||||
for (const auto& entry : gateway_.registers_) {
|
||||
if (!req->filter().empty()) {
|
||||
@@ -388,7 +357,9 @@ grpc::Status Hc900Gateway::GatewayServiceImpl::StreamTags(
|
||||
}
|
||||
|
||||
// ─── main ───
|
||||
|
||||
//
|
||||
// One process per controller. The C# ControllerProcessManager launches this with:
|
||||
// hc900_gateway <host> <register-map> <poll_ms> <grpc_port> <modbus_port>
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::string host = "192.168.0.240";
|
||||
|
||||
Reference in New Issue
Block a user