From 4348fb49f8c2ddef1fffb404e6b92388995e3ceb Mon Sep 17 00:00:00 2001 From: windpacer Date: Thu, 4 Jun 2026 09:43:18 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20C++=20=EA=B2=8C=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=EC=9B=A8=EC=9D=B4=20write=5Faddr=20=EC=A7=80=EC=9B=90=20+=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=EC=B2=B4=ED=81=AC=20=EA=B8=B0=EB=B0=98=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EC=83=81=ED=83=9C=20=ED=8C=90=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RegisterEntry에 write_addr 필드 추가 (기본값=addr) - .MD 태그: LOOPSTAT(읽기) ↔ MODEIN(쓰기) 분리 - ReadRegister() 개별 호출 제거 (batch ReadAllRegisters로 대체 완료) - ListTags 대소문자 무시 검색 - 소멸자/Stop null 체크 추가 - HealthCheck: SERVING 상태 반환 --- industrial-comm/cpp/include/gateway.h | 7 +++- industrial-comm/cpp/src/gateway.cpp | 57 +++++++-------------------- 2 files changed, 20 insertions(+), 44 deletions(-) diff --git a/industrial-comm/cpp/include/gateway.h b/industrial-comm/cpp/include/gateway.h index b0844e1..efb891e 100644 --- a/industrial-comm/cpp/include/gateway.h +++ b/industrial-comm/cpp/include/gateway.h @@ -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 { diff --git a/industrial-comm/cpp/src/gateway.cpp b/industrial-comm/cpp/src/gateway.cpp index 3faf958..4e9f437 100644 --- a/industrial-comm/cpp/src/gateway.cpp +++ b/industrial-comm/cpp/src/gateway.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -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 ─── @@ -75,11 +76,12 @@ void Hc900Gateway::LoadRegisterMap(const std::string& path) auto j = nlohmann::json::parse(f); for (const auto& item : j["registers"]) { RegisterEntry e; - e.tag = item["tag"]; - e.addr = item["addr"]; - e.count = item.value("count", 2); - e.type = item.value("type", "float32"); - e.access = item.value("access", "R"); + 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"); registers_.push_back(e); tag_index_[e.tag] = registers_.size() - 1; } @@ -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 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 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(req->value())); } else if (entry.type == "float32") { - ok = gateway_.controller_->write_float(entry.addr, + ok = gateway_.controller_->write_float(entry.write_addr, static_cast(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 int main(int argc, char* argv[]) { std::string host = "192.168.0.240";