Initial commit: HC900 Crawler
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>
This commit is contained in:
103
industrial-comm/cpp/include/gateway.h
Normal file
103
industrial-comm/cpp/include/gateway.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user