Files
HC900-Crawler/industrial-comm/cpp/include/gateway.h
windpacer 4348fb49f8 feat: C++ 게이트웨이 write_addr 지원 + 헬스체크 기반 연결 상태 판정
- RegisterEntry에 write_addr 필드 추가 (기본값=addr)
  - .MD 태그: LOOPSTAT(읽기) ↔ MODEIN(쓰기) 분리
- ReadRegister() 개별 호출 제거 (batch ReadAllRegisters로 대체 완료)
- ListTags 대소문자 무시 검색
- 소멸자/Stop null 체크 추가
- HealthCheck: SERVING 상태 반환
2026-06-04 09:43:18 +09:00

109 lines
3.6 KiB
C++

#ifndef HC900_GATEWAY_H
#define HC900_GATEWAY_H
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <atomic>
#include <thread>
#include <mutex>
#include <chrono>
#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<hc900::TagValue>* writer) override;
private:
Hc900Gateway& gateway_;
};
std::string host_;
uint16_t port_;
int poll_interval_ms_;
std::unique_ptr<Controller> controller_;
// Register map
std::vector<RegisterEntry> registers_;
std::unordered_map<std::string, size_t> tag_index_;
std::vector<size_t> sorted_indices_; // indices into registers_, sorted by address
// Cache
std::unordered_map<std::string, CachedValue> cache_;
mutable std::mutex cache_mutex_;
// Transport — shared between poll thread and gRPC handlers
mutable std::mutex transport_mutex_;
// Poller
std::atomic<bool> running_;
std::thread poll_thread_;
uint64_t poll_count_{0};
std::chrono::milliseconds last_poll_duration_{0};
// gRPC server
std::unique_ptr<grpc::Server> grpc_server_;
std::unique_ptr<GatewayServiceImpl> grpc_service_;
std::string grpc_listen_;
};
#endif // HC900_GATEWAY_H