#ifndef HC900_GATEWAY_H #define HC900_GATEWAY_H #include #include #include #include #include #include #include #include #include "modbus_gateway.grpc.pb.h" #include "modbus_gateway.pb.h" 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" }; struct CachedValue { float float32_val; uint32_t uint16_val; bool is_float; uint32_t quality; // 192=good, 0=bad 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, const std::string& map_path, int poll_interval_ms = 1000, int grpc_port = 50051); ~Hc900Gateway(); bool Start(); void Stop(); private: void PollLoop(); void LoadRegisterMap(const std::string& path); void ReadAllRegisters(); // gRPC service implementation class GatewayServiceImpl final : public hc900::ModbusGateway::Service { public: GatewayServiceImpl(Hc900Gateway& gateway); grpc::Status ReadTags(grpc::ServerContext* ctx, const hc900::ReadTagsRequest* req, hc900::ReadTagsResponse* resp) override; grpc::Status WriteTag(grpc::ServerContext* ctx, const hc900::WriteTagRequest* req, hc900::WriteTagResponse* resp) override; grpc::Status ListTags(grpc::ServerContext* ctx, const hc900::ListTagsRequest* req, hc900::ListTagsResponse* resp) override; grpc::Status HealthCheck(grpc::ServerContext* ctx, const hc900::HealthCheckRequest* req, hc900::HealthCheckResponse* resp) override; grpc::Status StreamTags(grpc::ServerContext* ctx, const hc900::StreamTagsRequest* req, grpc::ServerWriter* writer) override; private: Hc900Gateway& gateway_; }; std::string host_; uint16_t port_; int poll_interval_ms_; std::unique_ptr controller_; // Register map std::vector registers_; std::unordered_map tag_index_; std::vector sorted_indices_; // indices into registers_, sorted by address // Cache std::unordered_map cache_; mutable std::mutex cache_mutex_; // Transport — shared between poll thread and gRPC handlers mutable std::mutex transport_mutex_; // Poller std::atomic running_; std::thread poll_thread_; uint64_t poll_count_{0}; std::chrono::milliseconds last_poll_duration_{0}; // gRPC server std::unique_ptr grpc_server_; std::unique_ptr grpc_service_; std::string grpc_listen_; }; #endif // HC900_GATEWAY_H