- RegisterEntry에 write_addr 필드 추가 (기본값=addr) - .MD 태그: LOOPSTAT(읽기) ↔ MODEIN(쓰기) 분리 - ReadRegister() 개별 호출 제거 (batch ReadAllRegisters로 대체 완료) - ListTags 대소문자 무시 검색 - 소멸자/Stop null 체크 추가 - HealthCheck: SERVING 상태 반환
109 lines
3.6 KiB
C++
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
|