Honeywell HC900을 Modbus TCP로 직접 폴링 → gRPC → C# 크롤러 → PostgreSQL. 기존 Experion OPC UA 데이터 경로를 HC900 직접 통신으로 대체. - industrial-comm/cpp: C++ Modbus 게이트웨이 (gRPC 서버) - src: C# .NET 8 ASP.NET Core 크롤러 + 웹 UI (3-Layer) - mcp-server: Python FastMCP (RAG/NL2SQL/P&ID) - 다중 컨트롤러(N-Controller) 지원 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
3.2 KiB
C++
104 lines
3.2 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 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;
|
|
};
|
|
|
|
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();
|
|
CachedValue ReadRegister(const RegisterEntry& entry);
|
|
|
|
// 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
|