From 7ddca21fdb798123239eab9daa390e2702afdff7 Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期五, 10 十月 2025 18:02:19 +0800
Subject: [PATCH] 1.ProcessStart和ProcessEnd加调上层时加上SlotNo, 状态也关联到SlotNo, 因为多腔可能 并行工作。 2.加入曲线采集服务端到项目中。
---
SourceCode/Bond/DAQBridge/buffer/BufferRegistry.h | 24
SourceCode/Bond/DAQBridge/buffer/BufferManager.h | 72 +
SourceCode/Bond/Servo/Servo.vcxproj | 41
SourceCode/Bond/DAQBridge/core/Collector.h | 163 +++
SourceCode/Bond/Servo/CMaster.h | 5
SourceCode/Bond/DAQBridge/proto/ProtocolCodec.cpp | 663 +++++++++++++
SourceCode/Bond/DAQBridge/buffer/BufferManager.cpp | 128 ++
SourceCode/Bond/DAQBridge/core/Display.cpp | 266 +++++
SourceCode/Bond/Servo/CEquipment.cpp | 39
SourceCode/Bond/DAQBridge/core/DataTypes.h | 9
SourceCode/Bond/DAQBridge/proto/Protocol.h | 168 +++
SourceCode/Bond/Servo/CMaster.cpp | 175 +++
SourceCode/Bond/DAQBridge/core/Display.h | 113 ++
SourceCode/Bond/DAQBridge/net/SocketComm.h | 48
SourceCode/Bond/Servo/CBonder.h | 2
SourceCode/Bond/DAQBridge/net/FrameAssembler.h | 58 +
SourceCode/Bond/DAQBridge/buffer/SampleBuffer.h | 57 +
SourceCode/Bond/DAQBridge/core/ConnEvents.h | 40
SourceCode/Bond/DAQBridge/net/SocketComm.cpp | 105 ++
SourceCode/Bond/DAQBridge/proto/ProtocolCodec.h | 49
SourceCode/Bond/DAQBridge/DAQConfig.h | 7
SourceCode/Bond/DAQBridge/buffer/SampleBuffer.cpp | 116 ++
SourceCode/Bond/Servo/Servo.vcxproj.filters | 63 +
SourceCode/Bond/DAQBridge/buffer/BufferRegistry.cpp | 35
SourceCode/Bond/DAQBridge/core/CommBase.h | 29
SourceCode/Bond/Servo/CEquipment.h | 8
SourceCode/Bond/Servo/CBonder.cpp | 4
SourceCode/Bond/DAQBridge/core/Collector.cpp | 537 ++++++++++
SourceCode/Bond/Servo/CParam.cpp | 7
29 files changed, 3,005 insertions(+), 26 deletions(-)
diff --git a/SourceCode/Bond/DAQBridge/DAQConfig.h b/SourceCode/Bond/DAQBridge/DAQConfig.h
new file mode 100644
index 0000000..27fff9e
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/DAQConfig.h
@@ -0,0 +1,7 @@
+#pragma once
+#include <cstdint>
+namespace DAQCfg {
+ inline constexpr const char* Version = "1.0.1";
+ inline constexpr uint32_t KickIfNoVersionSec = 5;
+ inline constexpr uint32_t RecvLoopIntervalMs = 5;
+}
diff --git a/SourceCode/Bond/DAQBridge/buffer/BufferManager.cpp b/SourceCode/Bond/DAQBridge/buffer/BufferManager.cpp
new file mode 100644
index 0000000..7c0ec72
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/buffer/BufferManager.cpp
@@ -0,0 +1,128 @@
+// BufferManager.cpp
+#include "BufferManager.h"
+
+BufferManager::BufferManager(uint32_t id, std::string name, RetentionPolicy defaultPolicy)
+ : id_(id), name_(std::move(name)), defPolicy_(defaultPolicy) {}
+
+std::string BufferManager::name() const {
+ std::shared_lock lk(mtx_);
+ return name_;
+}
+void BufferManager::rename(const std::string& newName) {
+ std::unique_lock lk(mtx_);
+ name_ = newName;
+}
+
+void BufferManager::start() {
+ std::unique_lock lk(mtx_);
+ for (auto& kv : map_) kv.second->clear();
+ running_.store(true);
+}
+void BufferManager::stop() {
+ running_.store(false);
+}
+void BufferManager::clearAll() {
+ std::unique_lock lk(mtx_);
+ for (auto& kv : map_) kv.second->clear();
+}
+
+SampleBuffer& BufferManager::getOrCreate(uint32_t channelId) {
+ auto it = map_.find(channelId);
+ if (it != map_.end()) return *it->second;
+ auto buf = std::make_unique<SampleBuffer>(defPolicy_);
+ auto& ref = *buf;
+ map_[channelId] = std::move(buf);
+ return ref;
+}
+
+void BufferManager::push(uint32_t channelId, int64_t ts_ms, double v) {
+ if (!running_.load()) return; // 停止时丢弃新数据
+ std::unique_lock lk(mtx_);
+ getOrCreate(channelId).push(ts_ms, v);
+}
+
+std::vector<Sample> BufferManager::getSince(uint32_t channelId, int64_t tsExclusive, size_t maxCount) const {
+ std::shared_lock lk(mtx_);
+ auto it = map_.find(channelId);
+ if (it == map_.end()) return {};
+ return it->second->getSince(tsExclusive, maxCount);
+}
+std::vector<Sample> BufferManager::getRange(uint32_t channelId, int64_t from_ts, int64_t to_ts, size_t maxCount) const {
+ std::shared_lock lk(mtx_);
+ auto it = map_.find(channelId);
+ if (it == map_.end()) return {};
+ return it->second->getRange(from_ts, to_ts, maxCount);
+}
+
+void BufferManager::setDefaultPolicy(const RetentionPolicy& p, bool applyToExisting) {
+ std::unique_lock lk(mtx_);
+ defPolicy_ = p;
+ if (applyToExisting) {
+ for (auto& kv : map_) kv.second->setPolicy(p);
+ }
+}
+void BufferManager::setPolicy(uint32_t channelId, const RetentionPolicy& p) {
+ std::unique_lock lk(mtx_);
+ getOrCreate(channelId).setPolicy(p);
+}
+RetentionPolicy BufferManager::getPolicy(uint32_t channelId) const {
+ std::shared_lock lk(mtx_);
+ auto it = map_.find(channelId);
+ if (it == map_.end()) return defPolicy_;
+ return it->second->getPolicy();
+}
+
+std::vector<uint32_t> BufferManager::listChannels() const {
+ std::shared_lock lk(mtx_);
+ std::vector<uint32_t> ids; ids.reserve(map_.size());
+ for (auto& kv : map_) ids.push_back(kv.first);
+ return ids;
+}
+
+void BufferManager::setChannelName(uint32_t channelId, const std::string& name) {
+ std::unique_lock lk(mtx_);
+ channelNames_[channelId] = name;
+ // 强制创建对应的 SampleBuffer(可选)
+ (void)getOrCreate(channelId);
+}
+
+std::string BufferManager::getChannelName(uint32_t channelId) const {
+ std::shared_lock lk(mtx_);
+ auto it = channelNames_.find(channelId);
+ if (it != channelNames_.end() && !it->second.empty()) return it->second;
+ // 默认名
+ return "Ch-" + std::to_string(channelId);
+}
+
+std::vector<BufferManager::ChannelInfo> BufferManager::listChannelInfos() const {
+ std::shared_lock lk(mtx_);
+ std::vector<ChannelInfo> out;
+ out.reserve(map_.size());
+ // 以“已有缓冲器的通道”为基准列出;如需列出所有“命名过但尚未产生数据”的通道,也可遍历 channelNames_ 合并
+ for (auto& kv : map_) {
+ const uint32_t ch = kv.first;
+ auto it = channelNames_.find(ch);
+ out.push_back(ChannelInfo{ ch, (it != channelNames_.end() && !it->second.empty())
+ ? it->second
+ : ("Ch-" + std::to_string(ch)) });
+ }
+ return out;
+}
+
+BufferManager::ChannelStat BufferManager::getChannelStat(uint32_t channelId) const {
+ std::shared_lock lk(mtx_);
+ auto it = map_.find(channelId);
+ if (it == map_.end()) return ChannelStat{ channelId, 0, 0, 0 };
+ const auto& buf = *it->second;
+ return ChannelStat{ channelId, buf.size(), buf.earliestTs(), buf.latestTs() };
+}
+
+std::vector<BufferManager::ChannelStat> BufferManager::listChannelStats() const {
+ std::shared_lock lk(mtx_);
+ std::vector<ChannelStat> out; out.reserve(map_.size());
+ for (auto& kv : map_) {
+ const auto& buf = *kv.second;
+ out.push_back(ChannelStat{ kv.first, buf.size(), buf.earliestTs(), buf.latestTs() });
+ }
+ return out;
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/DAQBridge/buffer/BufferManager.h b/SourceCode/Bond/DAQBridge/buffer/BufferManager.h
new file mode 100644
index 0000000..564e16b
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/buffer/BufferManager.h
@@ -0,0 +1,72 @@
+#pragma once
+#include "SampleBuffer.h"
+#include <unordered_map>
+#include <memory>
+#include <shared_mutex>
+#include <string>
+#include <atomic>
+#include <vector>
+
+class BufferManager {
+public:
+ struct ChannelStat {
+ uint32_t id;
+ size_t size;
+ int64_t earliestTs;
+ int64_t latestTs;
+ };
+
+public:
+ BufferManager(uint32_t id, std::string name, RetentionPolicy defaultPolicy = {});
+
+ // 标识
+ uint32_t id() const { return id_; }
+ std::string name() const;
+ void rename(const std::string& newName);
+
+ // 采集控制
+ void start();
+ void stop();
+ bool isRunning() const { return running_.load(); }
+ void clearAll();
+
+ // 数据写读
+ void push(uint32_t channelId, int64_t ts_ms, double v);
+ std::vector<Sample> getSince(uint32_t channelId, int64_t tsExclusive, size_t maxCount = 4096) const;
+ std::vector<Sample> getRange(uint32_t channelId, int64_t from_ts, int64_t to_ts, size_t maxCount = 4096) const;
+
+ // 策略
+ void setDefaultPolicy(const RetentionPolicy& p, bool applyToExisting = false);
+ void setPolicy(uint32_t channelId, const RetentionPolicy& p);
+ RetentionPolicy getPolicy(uint32_t channelId) const;
+
+ // 通道管理
+ std::vector<uint32_t> listChannels() const;
+
+ // ===== 新增:通道(曲线)名称 =====
+ void setChannelName(uint32_t channelId, const std::string& name); // 设置/更新
+ std::string getChannelName(uint32_t channelId) const; // 若未设置,返回 "Ch-<id>"
+ struct ChannelInfo { uint32_t id; std::string name; };
+ std::vector<ChannelInfo> listChannelInfos() const; // 方便 UI 列表
+
+ std::vector<ChannelStat> listChannelStats() const;
+ // 单个通道
+ ChannelStat getChannelStat(uint32_t channelId) const;
+private:
+ SampleBuffer& getOrCreate(uint32_t channelId);
+
+ mutable std::shared_mutex mtx_;
+ std::unordered_map<uint32_t, std::unique_ptr<SampleBuffer>> map_;
+
+ // ===== 新增:通道名称表 =====
+ std::unordered_map<uint32_t, std::string> channelNames_;
+
+ uint32_t id_;
+ std::string name_;
+ RetentionPolicy defPolicy_;
+ std::atomic<bool> running_{ false };
+
+
+
+
+};
diff --git a/SourceCode/Bond/DAQBridge/buffer/BufferRegistry.cpp b/SourceCode/Bond/DAQBridge/buffer/BufferRegistry.cpp
new file mode 100644
index 0000000..19e76ef
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/buffer/BufferRegistry.cpp
@@ -0,0 +1,35 @@
+// BufferRegistry.cpp
+#include "BufferRegistry.h"
+
+BufferManager& BufferRegistry::getOrCreate(uint32_t managerId, const std::string& name, const RetentionPolicy& defPolicy) {
+ std::unique_lock lk(mtx_);
+ auto it = managers_.find(managerId);
+ if (it != managers_.end()) return *it->second;
+ auto bm = std::make_unique<BufferManager>(managerId, name, defPolicy);
+ auto& ref = *bm;
+ managers_[managerId] = std::move(bm);
+ return ref;
+}
+
+std::vector<uint32_t> BufferRegistry::listManagers() const {
+ std::shared_lock lk(mtx_);
+ std::vector<uint32_t> ids; ids.reserve(managers_.size());
+ for (auto& kv : managers_) ids.push_back(kv.first);
+ return ids;
+}
+
+BufferManager* BufferRegistry::find(uint32_t managerId) {
+ std::shared_lock lk(mtx_);
+ auto it = managers_.find(managerId);
+ return (it == managers_.end()) ? nullptr : it->second.get();
+}
+const BufferManager* BufferRegistry::find(uint32_t managerId) const {
+ std::shared_lock lk(mtx_);
+ auto it = managers_.find(managerId);
+ return (it == managers_.end()) ? nullptr : it->second.get();
+}
+
+void BufferRegistry::remove(uint32_t managerId) {
+ std::unique_lock lk(mtx_);
+ managers_.erase(managerId);
+}
diff --git a/SourceCode/Bond/DAQBridge/buffer/BufferRegistry.h b/SourceCode/Bond/DAQBridge/buffer/BufferRegistry.h
new file mode 100644
index 0000000..2cc44d0
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/buffer/BufferRegistry.h
@@ -0,0 +1,24 @@
+// BufferRegistry.h
+#pragma once
+#include "BufferManager.h"
+#include <unordered_map>
+#include <memory>
+#include <shared_mutex>
+
+class BufferRegistry {
+public:
+ // 获取/创建某台机器的 BufferManager
+ BufferManager& getOrCreate(uint32_t managerId, const std::string& name, const RetentionPolicy& defPolicy = {});
+
+ // 查询
+ std::vector<uint32_t> listManagers() const;
+ BufferManager* find(uint32_t managerId);
+ const BufferManager* find(uint32_t managerId) const;
+
+ // 移除一个管理器(可选)
+ void remove(uint32_t managerId);
+
+private:
+ mutable std::shared_mutex mtx_;
+ std::unordered_map<uint32_t, std::unique_ptr<BufferManager>> managers_;
+};
diff --git a/SourceCode/Bond/DAQBridge/buffer/SampleBuffer.cpp b/SourceCode/Bond/DAQBridge/buffer/SampleBuffer.cpp
new file mode 100644
index 0000000..be7145e
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/buffer/SampleBuffer.cpp
@@ -0,0 +1,116 @@
+// SampleBuffer.cpp
+#include "SampleBuffer.h"
+#include <algorithm>
+
+void SampleBuffer::push(int64_t ts_ms, double v) {
+ std::unique_lock lk(mtx_);
+ if (!data_.empty() && ts_ms < data_.back().ts_ms) {
+ ts_ms = data_.back().ts_ms; // 简单纠正为非递减;严格场景可改为丢弃/插入排序
+ }
+ data_.push_back({ ts_ms, v });
+ pruneUnlocked(ts_ms); // 用当前写入时间作为参考时间
+}
+
+std::vector<Sample> SampleBuffer::getSince(int64_t tsExclusive, size_t maxCount) const {
+ std::shared_lock lk(mtx_);
+ std::vector<Sample> out;
+ if (data_.empty()) return out;
+ const size_t start = lowerBoundIndex(tsExclusive);
+ if (start >= data_.size()) return out;
+ const size_t n = std::min(maxCount, data_.size() - start);
+ out.reserve(n);
+ for (size_t i = 0; i < n; ++i) out.push_back(data_[start + i]);
+ return out;
+}
+
+std::vector<Sample> SampleBuffer::getRange(int64_t from_ts, int64_t to_ts, size_t maxCount) const {
+ if (to_ts < from_ts) return {};
+ std::shared_lock lk(mtx_);
+ std::vector<Sample> out;
+ if (data_.empty()) return out;
+ const size_t L = lowerBoundInclusive(from_ts);
+ const size_t R = upperBoundInclusive(to_ts);
+ if (L >= R) return out;
+ const size_t n = std::min(maxCount, R - L);
+ out.reserve(n);
+ for (size_t i = 0; i < n; ++i) out.push_back(data_[L + i]);
+ return out;
+}
+
+size_t SampleBuffer::size() const { std::shared_lock lk(mtx_); return data_.size(); }
+bool SampleBuffer::empty() const { std::shared_lock lk(mtx_); return data_.empty(); }
+int64_t SampleBuffer::latestTs() const {
+ std::shared_lock lk(mtx_);
+ return data_.empty() ? 0 : data_.back().ts_ms;
+}
+void SampleBuffer::clear() {
+ std::unique_lock lk(mtx_);
+ data_.clear();
+}
+
+void SampleBuffer::setPolicy(const RetentionPolicy& p) {
+ std::unique_lock lk(mtx_);
+ policy_ = p;
+ // 立即按新策略清一次(以“当前最新 ts”为参考)
+ pruneUnlocked(data_.empty() ? 0 : data_.back().ts_ms);
+}
+RetentionPolicy SampleBuffer::getPolicy() const {
+ std::shared_lock lk(mtx_);
+ return policy_;
+}
+
+void SampleBuffer::pruneUnlocked(int64_t ref_now_ms) {
+ switch (policy_.mode) {
+ case RetainMode::ByCount:
+ if (policy_.maxSamples > 0) {
+ while (data_.size() > policy_.maxSamples) data_.pop_front();
+ }
+ break;
+ case RetainMode::ByRollingAge: {
+ if (policy_.maxAge.count() > 0) {
+ const int64_t cutoff = ref_now_ms - policy_.maxAge.count();
+ while (!data_.empty() && data_.front().ts_ms < cutoff) data_.pop_front();
+ }
+ break;
+ }
+ case RetainMode::ByAbsoluteRange: {
+ const bool valid = (policy_.absTo >= policy_.absFrom);
+ if (valid) {
+ // 丢弃窗口之外的数据(两端都裁)
+ while (!data_.empty() && data_.front().ts_ms < policy_.absFrom) data_.pop_front();
+ while (!data_.empty() && data_.back().ts_ms > policy_.absTo) data_.pop_back();
+ }
+ break;
+ }
+ }
+}
+
+size_t SampleBuffer::lowerBoundIndex(int64_t tsExclusive) const {
+ size_t L = 0, R = data_.size();
+ while (L < R) {
+ size_t m = (L + R) >> 1;
+ if (data_[m].ts_ms <= tsExclusive) L = m + 1; else R = m;
+ }
+ return L; // 第一个 > tsExclusive
+}
+size_t SampleBuffer::lowerBoundInclusive(int64_t tsInclusive) const {
+ size_t L = 0, R = data_.size();
+ while (L < R) {
+ size_t m = (L + R) >> 1;
+ if (data_[m].ts_ms < tsInclusive) L = m + 1; else R = m;
+ }
+ return L; // 第一个 >= tsInclusive
+}
+size_t SampleBuffer::upperBoundInclusive(int64_t tsInclusive) const {
+ size_t L = 0, R = data_.size();
+ while (L < R) {
+ size_t m = (L + R) >> 1;
+ if (data_[m].ts_ms <= tsInclusive) L = m + 1; else R = m;
+ }
+ return L; // 最后一个 <= tsInclusive 的下一个位置
+}
+
+int64_t SampleBuffer::earliestTs() const {
+ std::shared_lock lk(mtx_);
+ return data_.empty() ? 0 : data_.front().ts_ms;
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/DAQBridge/buffer/SampleBuffer.h b/SourceCode/Bond/DAQBridge/buffer/SampleBuffer.h
new file mode 100644
index 0000000..12d44f8
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/buffer/SampleBuffer.h
@@ -0,0 +1,57 @@
+// SampleBuffer.h
+#pragma once
+#include "../core/DataTypes.h"
+#include <deque>
+#include <vector>
+#include <shared_mutex>
+#include <chrono>
+
+enum class RetainMode {
+ ByCount, // 按样本数上限
+ ByRollingAge, // 按滚动时间窗口(maxAge)
+ ByAbsoluteRange // 按绝对时间段 [absFrom, absTo]
+};
+
+struct RetentionPolicy {
+ RetainMode mode = RetainMode::ByCount;
+ size_t maxSamples = 100000; // ByCount 用
+ std::chrono::milliseconds maxAge{ std::chrono::hours(1) }; // ByRollingAge 用
+ int64_t absFrom = 0; // ByAbsoluteRange 用
+ int64_t absTo = 0; // ByAbsoluteRange 用(absTo>=absFrom 有效)
+};
+
+class SampleBuffer {
+public:
+ explicit SampleBuffer(RetentionPolicy policy = {}) : policy_(policy) {}
+
+ // 写:按时间戳递增推入(乱序会被简单纠正为非递减)
+ void push(int64_t ts_ms, double v);
+
+ // 读:按“tsExclusive 之后”的新数据
+ std::vector<Sample> getSince(int64_t tsExclusive, size_t maxCount = 4096) const;
+
+ // 读:按区间 [from, to](包含边界)
+ std::vector<Sample> getRange(int64_t from_ts, int64_t to_ts, size_t maxCount = 4096) const;
+
+ // 查询 / 维护
+ size_t size() const;
+ bool empty() const;
+ int64_t latestTs() const;
+ void clear();
+
+ // 配置
+ void setPolicy(const RetentionPolicy& p);
+ RetentionPolicy getPolicy() const;
+
+ int64_t earliestTs() const; // 无数据时返回 0
+
+private:
+ void pruneUnlocked(int64_t ref_now_ms); // 按策略清理
+ size_t lowerBoundIndex(int64_t tsExclusive) const; // 二分:第一个 > tsExclusive
+ size_t lowerBoundInclusive(int64_t tsInclusive) const; // 第一个 >= tsInclusive
+ size_t upperBoundInclusive(int64_t tsInclusive) const; // 最后一个 <= tsInclusive 的下一个位置
+
+ mutable std::shared_mutex mtx_;
+ std::deque<Sample> data_;
+ RetentionPolicy policy_;
+};
diff --git a/SourceCode/Bond/DAQBridge/core/Collector.cpp b/SourceCode/Bond/DAQBridge/core/Collector.cpp
new file mode 100644
index 0000000..7cb2d3d
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/core/Collector.cpp
@@ -0,0 +1,537 @@
+#include "Collector.h"
+#include <iostream>
+#include <chrono>
+
+#include "../DAQConfig.h"
+#include "../core/ConnEvents.h"
+using namespace DAQEvt;
+
+// 协议编解码
+#include "../proto/Protocol.h"
+#include "../proto/ProtocolCodec.h"
+
+using namespace Proto;
+
+Collector::Collector() {}
+
+void Collector::startLoop(uint32_t intervalMs) {
+ if (running.load()) return;
+ running.store(true);
+ worker = std::thread([this, intervalMs]() {
+ while (running.load()) {
+ this->poll();
+ std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
+ }
+ });
+}
+
+void Collector::stopLoop() {
+ if (!running.load()) return;
+ running.store(false);
+ if (worker.joinable()) worker.join();
+}
+
+Collector::~Collector() {
+ stopLoop();
+ disconnect();
+}
+
+std::vector<Collector::ClientSummary> Collector::getClientList() {
+ std::lock_guard<std::mutex> lk(mClients);
+ std::vector<ClientSummary> out;
+ out.reserve(clients.size());
+ for (const auto& c : clients) {
+ out.push_back(ClientSummary{ c.ip, c.port, c.versionOk, c.sock });
+ }
+ return out;
+}
+
+void Collector::createServer(uint16_t port) {
+ if (socketComm.createServerSocket(port)) {
+ if (cbStatus) cbStatus(static_cast<int>(DAQEvt::ConnCode::ServerListening),
+ "Collector server listening on port " + std::to_string(port));
+ onConnectionEstablished();
+ }
+}
+
+void Collector::disconnect() {
+ {
+ std::lock_guard<std::mutex> lk(mClients);
+ for (auto& c : clients) {
+ socketComm.closeClient(c.sock);
+ if (cbClientEvent) cbClientEvent(c.ip, c.port, /*connected*/false);
+ }
+ clients.clear();
+ }
+ socketComm.closeSocket();
+ if (cbStatus) cbStatus(static_cast<int>(DAQEvt::ConnCode::ServerStopped), "Collector server stopped");
+ onConnectionLost();
+}
+
+void Collector::poll() {
+ // 1) 接新客户端
+ while (true) {
+ SOCKET cs; std::string ip; uint16_t port;
+ if (!socketComm.acceptOne(cs, ip, port)) break;
+
+ ClientInfo ci;
+ ci.sock = cs; ci.ip = ip; ci.port = port;
+ ci.tsConnected = std::chrono::steady_clock::now();
+
+ {
+ std::lock_guard<std::mutex> lk(mClients);
+ clients.push_back(ci);
+ }
+
+ if (cbStatus) cbStatus(static_cast<int>(DAQEvt::ConnCode::ClientAccepted),
+ "Client connected: " + ip + ":" + std::to_string(port));
+ if (cbClientEvent) cbClientEvent(ip, port, /*connected*/true);
+ }
+
+ // 2) 轮询各客户端
+ auto now = std::chrono::steady_clock::now();
+ std::lock_guard<std::mutex> lk(mClients);
+ for (size_t i = 0; i < clients.size(); ) {
+ auto& c = clients[i];
+
+ // 2a) 版本握手超时
+ if (!c.versionOk) {
+ auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - c.tsConnected).count();
+ if (elapsed >= DAQCfg::KickIfNoVersionSec) {
+ if (cbStatus) cbStatus(static_cast<int>(DAQEvt::ConnCode::VersionTimeoutKick),
+ "Kick client (no version within 5s): " + c.ip + ":" + std::to_string(c.port));
+ socketComm.closeClient(c.sock);
+ if (cbClientEvent) cbClientEvent(c.ip, c.port, /*connected*/false);
+ clients.erase(clients.begin() + i);
+ continue;
+ }
+ }
+
+ // 2b) 收数据
+ std::vector<uint8_t> buf;
+ bool peerClosed = false;
+ if (socketComm.recvFrom(c.sock, buf, peerClosed)) {
+ if (!buf.empty()) {
+ if (rawDumpEnabled && cbRaw) cbRaw(buf);
+
+ // 帧组装
+ c.fr.push(buf);
+ std::vector<uint8_t> frame;
+ while (c.fr.nextFrame(frame)) {
+ if (rawDumpEnabled && cbRaw) cbRaw(frame);
+ handleClientData(c, frame);
+ }
+ }
+ }
+ else if (peerClosed) {
+ // 对端主动断开
+ if (cbStatus) cbStatus(static_cast<int>(DAQEvt::ConnCode::ClientDisconnected),
+ "Client disconnected: " + c.ip + ":" + std::to_string(c.port));
+ socketComm.closeClient(c.sock);
+ if (cbClientEvent) cbClientEvent(c.ip, c.port, /*connected*/false);
+ clients.erase(clients.begin() + i);
+ continue;
+ }
+
+ ++i;
+ }
+}
+
+void Collector::sendSampleData(double sample) {
+ (void)sample;
+#ifdef _DEBUG
+ if (cbStatus) cbStatus(0, "sendSampleData() no-op.");
+#endif
+}
+
+void Collector::sendWindowData(const std::vector<std::string>& fields) {
+ (void)fields;
+#ifdef _DEBUG
+ if (cbStatus) cbStatus(0, "sendWindowData() no-op.");
+#endif
+}
+
+void Collector::onConnectionEstablished() {
+ std::cout << "[Collector] ready.\n";
+}
+void Collector::onConnectionLost() {
+ std::cout << "[Collector] stopped.\n";
+}
+
+// 兼容旧门禁函数
+bool Collector::isVersionRequest(const std::vector<uint8_t>& data) const {
+ if (data.size() < 4 + 4 + 2 + 2 + 1) return false;
+ if (!(data[0] == 0x11 && data[1] == 0x88 && data[2] == 0x11 && data[3] == 0x88)) return false;
+ size_t cmdPos = 4 + 4 + 2;
+ uint16_t cmd = (uint16_t(data[cmdPos]) << 8) | data[cmdPos + 1];
+ return (cmd == CMD_REQ_VERSION);
+}
+
+// ========== BufferRegistry 直通 ==========
+BufferManager& Collector::registryAddMachine(uint32_t machineId, const std::string& name,
+ const RetentionPolicy& defPolicy) {
+ return registry_.getOrCreate(machineId, name, defPolicy);
+}
+BufferManager* Collector::registryFind(uint32_t machineId) { return registry_.find(machineId); }
+const BufferManager* Collector::registryFind(uint32_t machineId) const { return registry_.find(machineId); }
+std::vector<uint32_t> Collector::registryListMachines() const { return registry_.listManagers(); }
+
+void Collector::buffersPush(uint32_t machineId, uint32_t channelId, int64_t ts_ms, double v) {
+ auto& bm = registry_.getOrCreate(machineId, "Machine-" + std::to_string(machineId));
+ bm.push(channelId, ts_ms, v); // 未 start() 时将被内部忽略
+}
+
+std::vector<Sample> Collector::buffersGetSince(uint32_t machineId, uint32_t channelId,
+ int64_t tsExclusive, size_t maxCount) const {
+ auto* bm = registry_.find(machineId);
+ if (!bm) return {};
+ return bm->getSince(channelId, tsExclusive, maxCount);
+}
+std::vector<Sample> Collector::buffersGetRange(uint32_t machineId, uint32_t channelId,
+ int64_t from_ts, int64_t to_ts, size_t maxCount) const {
+ auto* bm = registry_.find(machineId);
+ if (!bm) return {};
+ return bm->getRange(channelId, from_ts, to_ts, maxCount);
+}
+
+void Collector::buffersStart(uint32_t machineId) {
+ auto& bm = registry_.getOrCreate(machineId, "Machine-" + std::to_string(machineId));
+ bm.start(); // 清空 + 允许写
+}
+void Collector::buffersStop(uint32_t machineId) {
+ if (auto* bm = registry_.find(machineId)) bm->stop();
+}
+void Collector::buffersClear(uint32_t machineId) {
+ if (auto* bm = registry_.find(machineId)) bm->clearAll();
+}
+bool Collector::buffersIsRunning(uint32_t machineId) const {
+ auto* bm = registry_.find(machineId);
+ return bm ? bm->isRunning() : false;
+}
+
+void Collector::buffersSetDefaultPolicy(uint32_t machineId, const RetentionPolicy& p, bool applyExisting) {
+ auto& bm = registry_.getOrCreate(machineId, "Machine-" + std::to_string(machineId));
+ bm.setDefaultPolicy(p, applyExisting);
+}
+void Collector::buffersSetPolicy(uint32_t machineId, uint32_t channelId, const RetentionPolicy& p) {
+ auto& bm = registry_.getOrCreate(machineId, "Machine-" + std::to_string(machineId));
+ bm.setPolicy(channelId, p);
+}
+void Collector::buffersSetChannelName(uint32_t machineId, uint32_t channelId, const std::string& name) {
+ auto& bm = registry_.getOrCreate(machineId, "Machine-" + std::to_string(machineId));
+ bm.setChannelName(channelId, name);
+}
+std::string Collector::buffersGetChannelName(uint32_t machineId, uint32_t channelId) const {
+ auto* bm = registry_.find(machineId);
+ if (!bm) return {};
+ return bm->getChannelName(channelId);
+}
+std::vector<BufferManager::ChannelInfo> Collector::buffersListChannelInfos(uint32_t machineId) const {
+ auto* bm = registry_.find(machineId);
+ if (!bm) return {};
+ return bm->listChannelInfos();
+}
+std::vector<BufferManager::ChannelStat> Collector::buffersListChannelStats(uint32_t machineId) const {
+ auto* bm = registry_.find(machineId);
+ if (!bm) return {};
+ return bm->listChannelStats();
+}
+BufferManager::ChannelStat Collector::buffersGetChannelStat(uint32_t machineId, uint32_t channelId) const {
+ auto* bm = registry_.find(machineId);
+ if (!bm) return BufferManager::ChannelStat{ channelId, 0, 0, 0 };
+ return bm->getChannelStat(channelId);
+}
+
+// ====== 批次管理 ======
+// 新实现:根据 expectedDurationMs 计算 expectedEndTs
+void Collector::batchStart(uint32_t machineId,
+ const std::string& batchId,
+ uint64_t expectedDurationMs)
+{
+ const uint64_t startTs = nowMs();
+ const uint64_t expectedEndTs = (expectedDurationMs > 0) ? (startTs + expectedDurationMs) : 0;
+
+ {
+ std::lock_guard<std::mutex> lk(mBatches);
+ auto& br = batches_[machineId];
+ br.state = BatchState::Active;
+ br.activeBatchId = batchId;
+ br.activeStartTs = startTs;
+ br.expectedEndTs = expectedEndTs;
+ }
+
+ // 批次开始:清空并开始采集
+ buffersStart(machineId);
+
+ if (cbStatus) cbStatus(0, "batchStart: machine " + std::to_string(machineId) +
+ " batch=" + batchId +
+ " durMs=" + std::to_string(expectedDurationMs));
+}
+
+// 兼容旧签名:忽略 startTs;把 expectedEndTs 当“时长”
+void Collector::batchStart(uint32_t machineId,
+ const std::string& batchId,
+ uint64_t /*startTs_ignored*/,
+ uint64_t expectedEndTs_orDuration)
+{
+ // 若调用方误传了绝对的“结束时间戳”,这里会被当作“时长”;
+ // 如需严格区分,你也可以加一个阈值判断(比如大于 10^12 认为是绝对时间戳)
+ // 然后转换:duration = max(0, endTs - now)
+
+ uint64_t durationMs = expectedEndTs_orDuration;
+
+ // 可选:做个简单的“像绝对时间戳”的判断并自动转为时长
+ // 以毫秒时间戳阈值举例(~2001-09-09):1e12
+ if (expectedEndTs_orDuration > 1000000000000ULL) {
+ uint64_t now = nowMs();
+ if (expectedEndTs_orDuration > now)
+ durationMs = expectedEndTs_orDuration - now;
+ else
+ durationMs = 0; // 已过期,当未知处理
+ }
+
+ batchStart(machineId, batchId, durationMs);
+}
+
+void Collector::batchStop(uint32_t machineId, uint64_t /*endTs*/) {
+ {
+ std::lock_guard<std::mutex> lk(mBatches);
+ auto it = batches_.find(machineId);
+ if (it != batches_.end()) {
+ it->second.state = BatchState::Idle;
+ it->second.activeBatchId.clear();
+ it->second.activeStartTs = 0;
+ it->second.expectedEndTs = 0;
+ }
+ else {
+ batches_[machineId] = BatchRec{}; // 生成一条 Idle
+ }
+ }
+
+ // 停止采集,但不清数据
+ buffersStop(machineId);
+
+ if (cbStatus) cbStatus(0, "batchStop: machine " + std::to_string(machineId));
+}
+
+Collector::BatchRec Collector::getBatch(uint32_t machineId) const {
+ std::lock_guard<std::mutex> lk(mBatches);
+ auto it = batches_.find(machineId);
+ if (it != batches_.end()) return it->second;
+ return BatchRec{}; // 默认 Idle
+}
+
+// ========== 业务分发 ==========
+void Collector::handleClientData(ClientInfo& c, const std::vector<uint8_t>& data) {
+ // 版本校验
+ if (!c.versionOk) {
+ ReqVersion vreq{};
+ if (decodeRequestVersion(data, vreq)) {
+ c.versionOk = true;
+ if (cbStatus) cbStatus(static_cast<int>(DAQEvt::ConnCode::VersionOk),
+ "Client " + c.ip + ":" + std::to_string(c.port) + " version OK");
+
+ RspVersion vrst;
+ vrst.dataId = vreq.dataId; // 回显
+ vrst.version = DAQCfg::Version; // 集中配置
+ auto rsp = encodeResponseVersion(vrst);
+ socketComm.sendTo(c.sock, rsp);
+ }
+ return; // 未通过版本校验,其它忽略
+ }
+
+ // 通过版本校验后:按 cmd 分发
+ const uint16_t cmd = peek_cmd(data);
+
+ // 0x0101 —— 单通道增量拉取
+ if (cmd == CMD_REQ_SINCE) {
+ ReqSince req;
+ if (!decodeRequestSince(data, req)) return;
+
+ auto vec = buffersGetSince(req.machineId, req.channelId,
+ static_cast<int64_t>(req.sinceTsExclusive), req.maxCount);
+
+ uint64_t lastTsSent = req.sinceTsExclusive;
+ if (!vec.empty()) lastTsSent = static_cast<uint64_t>(vec.back().ts_ms);
+
+ auto stat = buffersGetChannelStat(req.machineId, req.channelId);
+ uint8_t more = (stat.latestTs > static_cast<int64_t>(lastTsSent)) ? 1 : 0;
+
+ RspSince rsp;
+ rsp.dataId = req.dataId; // 回显
+ rsp.machineId = req.machineId;
+ rsp.channelId = req.channelId;
+ rsp.lastTsSent = lastTsSent;
+ rsp.more = more;
+ rsp.samples.reserve(vec.size());
+ for (auto& s : vec) rsp.samples.push_back({ static_cast<uint64_t>(s.ts_ms), s.value });
+
+ auto pkt = encodeResponseSince(rsp);
+ socketComm.sendTo(c.sock, pkt);
+ return;
+ }
+
+ // 0x0103 —— 统计/通道列表
+ if (cmd == CMD_REQ_STATS) {
+ ReqStats req;
+ if (!decodeRequestStats(data, req)) return;
+
+ auto* bm = registry_.find(req.machineId);
+ if (!bm) {
+ // 示例:可回错误帧
+ // sendErrorTo(c, req.dataId, CMD_REQ_STATS, req.machineId, ErrCode::NoActiveBatch, "machine not found");
+ return;
+ }
+
+ RspStats rsp;
+ rsp.dataId = req.dataId;
+ rsp.machineId = req.machineId;
+
+ auto stats = bm->listChannelStats();
+ rsp.channels.reserve(stats.size());
+ for (auto& s : stats) {
+ ChannelStatInfo ci;
+ ci.channelId = s.id;
+ ci.earliestTs = static_cast<uint64_t>(s.earliestTs);
+ ci.latestTs = static_cast<uint64_t>(s.latestTs);
+ ci.size = static_cast<uint32_t>(s.size);
+ ci.name = bm->getChannelName(s.id); // UTF-8
+ rsp.channels.push_back(std::move(ci));
+ }
+
+ auto pkt = encodeResponseStats(rsp);
+ socketComm.sendTo(c.sock, pkt);
+ return;
+ }
+
+ // 0x0104 —— 机台列表
+ if (cmd == CMD_REQ_MACHINES) {
+ ReqMachines req;
+ if (!decodeRequestMachines(data, req)) return;
+
+ RspMachines rsp;
+ rsp.dataId = req.dataId;
+
+ auto mids = registry_.listManagers();
+ rsp.machines.reserve(mids.size());
+ for (auto id : mids) {
+ auto* bm = registry_.find(id);
+ if (!bm) continue;
+ rsp.machines.push_back(MachineInfo{ id, bm->name() });
+ }
+
+ auto pkt = encodeResponseMachines(rsp);
+ socketComm.sendTo(c.sock, pkt);
+ return;
+ }
+
+ // 0x0105 —— 整机多通道增量拉取
+ if (cmd == CMD_REQ_SINCE_ALL) {
+ ReqSinceAll req;
+ if (!decodeRequestSinceAll(data, req)) return;
+
+ RspSinceAll rsp;
+ rsp.dataId = req.dataId; // 回显
+ rsp.machineId = req.machineId;
+ rsp.moreAny = 0;
+
+ auto* bm = registry_.find(req.machineId);
+ if (!bm) {
+ auto pkt = encodeResponseSinceAll(rsp);
+ socketComm.sendTo(c.sock, pkt);
+ return;
+ }
+
+ // 避免超过 2 字节长度:正文 ~60KB
+ constexpr size_t kMaxBody = 60 * 1024;
+
+ rsp.blocks.clear();
+ auto stats = bm->listChannelStats();
+ for (auto& s : stats) {
+ auto vec = bm->getSince(s.id, static_cast<int64_t>(req.sinceTsExclusive), req.maxPerChannel);
+ if (vec.empty()) continue;
+
+ ChannelBlock blk;
+ blk.channelId = s.id;
+ blk.lastTsSent = static_cast<uint64_t>(vec.back().ts_ms);
+
+ auto st = bm->getChannelStat(s.id);
+ blk.more = (st.latestTs > static_cast<int64_t>(blk.lastTsSent)) ? 1 : 0;
+
+ blk.samples.reserve(vec.size());
+ for (auto& sm : vec) {
+ blk.samples.push_back({ static_cast<uint64_t>(sm.ts_ms), sm.value });
+ }
+
+ rsp.blocks.push_back(std::move(blk));
+ auto tentative = encodeResponseSinceAll(rsp);
+ if (tentative.size() > (4 + 4 + 2) + kMaxBody + 1) {
+ // 超体积:把最后一块拿出来,先发前面的
+ auto last = std::move(rsp.blocks.back());
+ rsp.blocks.pop_back();
+
+ auto pkt = encodeResponseSinceAll(rsp);
+ socketComm.sendTo(c.sock, pkt);
+
+ rsp.blocks.clear();
+ rsp.blocks.push_back(std::move(last));
+ }
+ }
+
+ rsp.moreAny = 0;
+ for (const auto& b : rsp.blocks) {
+ if (b.more) { rsp.moreAny = 1; break; }
+ }
+
+ auto pkt = encodeResponseSinceAll(rsp);
+ socketComm.sendTo(c.sock, pkt);
+ return;
+ }
+
+ // 0x0120 —— 批次信息
+ if (cmd == CMD_REQ_BATCH_INFO) {
+ ReqBatchInfo req;
+ if (!decodeRequestBatchInfo(data, req)) return;
+
+ RspBatchInfo rsp;
+ rsp.dataId = req.dataId;
+ rsp.machineId = req.machineId;
+
+ const auto br = getBatch(req.machineId);
+ rsp.state = br.state;
+ rsp.activeBatchId = br.activeBatchId;
+ rsp.activeStartTs = br.activeStartTs;
+ rsp.expectedEndTs = br.expectedEndTs;
+
+ auto pkt = encodeResponseBatchInfo(rsp);
+ socketComm.sendTo(c.sock, pkt);
+ return;
+ }
+
+ // 其它指令:按需扩展
+}
+
+// 统一错误回包
+void Collector::sendErrorTo(ClientInfo& c,
+ uint32_t dataId,
+ uint16_t refCmd,
+ uint32_t machineId,
+ ErrCode code,
+ const std::string& message)
+{
+ RspError er;
+ er.dataId = dataId;
+ er.refCmd = refCmd;
+ er.machineId = machineId;
+ er.code = code;
+ er.message = message;
+
+ auto pkt = encodeResponseError(er);
+ socketComm.sendTo(c.sock, pkt);
+}
+
+// 工具:当前 epoch 毫秒
+uint64_t Collector::nowMs() {
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+}
diff --git a/SourceCode/Bond/DAQBridge/core/Collector.h b/SourceCode/Bond/DAQBridge/core/Collector.h
new file mode 100644
index 0000000..b595905
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/core/Collector.h
@@ -0,0 +1,163 @@
+#pragma once
+#ifndef COLLECTOR_H
+#define COLLECTOR_H
+
+#include "CommBase.h"
+#include "../net/SocketComm.h"
+
+#include <functional>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <vector>
+#include <string>
+#include <unordered_map>
+
+#include "../buffer/BufferRegistry.h"
+#include "../net/FrameAssembler.h"
+
+// 协议常量/结构(必须包含,供本头文件使用 Proto:: 类型)
+#include "../proto/Protocol.h"
+
+class Collector : public CommBase {
+public:
+ Collector();
+ ~Collector();
+
+ // ===== CommBase 接口 =====
+ void sendSampleData(double sample) override;
+ void sendWindowData(const std::vector<std::string>& dataFields) override;
+
+ void connectServer(const std::string& /*ip*/, uint16_t /*port*/) override {} // 采集端不作为客户端
+ void createServer(uint16_t port) override;
+ void disconnect() override;
+
+ void onConnectionEstablished() override;
+ void onConnectionLost() override;
+
+ // 连接/原始数据回调(UI)
+ void setConnectionStatusCallback(std::function<void(int, std::string)> cb) override { cbStatus = std::move(cb); }
+ void setRawDataCallback(std::function<void(const std::vector<uint8_t>&)> cb) override { cbRaw = std::move(cb); }
+ void setRawDumpEnabled(bool enabled) override { rawDumpEnabled = enabled; }
+
+ // 后台轮询(不阻塞 UI)
+ void startLoop(uint32_t intervalMs = 10);
+ void stopLoop();
+ void poll();
+
+ // 客户端进入/断开事件
+ void setClientEventCallback(std::function<void(const std::string& ip, uint16_t port, bool connected)> cb) {
+ cbClientEvent = std::move(cb);
+ }
+
+ // 客户端快照
+ struct ClientSummary {
+ std::string ip;
+ uint16_t port = 0;
+ bool versionOk = false;
+ SOCKET sock = INVALID_SOCKET;
+ };
+ std::vector<ClientSummary> getClientList();
+
+ // ===== 缓冲/通道相关 =====
+ void buffersSetChannelName(uint32_t machineId, uint32_t channelId, const std::string& name);
+ std::string buffersGetChannelName(uint32_t machineId, uint32_t channelId) const;
+ std::vector<BufferManager::ChannelInfo> buffersListChannelInfos(uint32_t machineId) const;
+
+ BufferManager& registryAddMachine(uint32_t machineId, const std::string& name,
+ const RetentionPolicy& defPolicy = {});
+ BufferManager* registryFind(uint32_t machineId);
+ const BufferManager* registryFind(uint32_t machineId) const;
+ std::vector<uint32_t> registryListMachines() const;
+
+ void buffersPush(uint32_t machineId, uint32_t channelId, int64_t ts_ms, double v);
+
+ std::vector<Sample> buffersGetSince(uint32_t machineId, uint32_t channelId,
+ int64_t tsExclusive, size_t maxCount = 4096) const;
+ std::vector<Sample> buffersGetRange(uint32_t machineId, uint32_t channelId,
+ int64_t from_ts, int64_t to_ts, size_t maxCount = 4096) const;
+
+ void buffersStart(uint32_t machineId); // 清空该机历史并开始
+ void buffersStop(uint32_t machineId); // 暂停(push 忽略)
+ void buffersClear(uint32_t machineId); // 清空历史,不改 running 状态
+ bool buffersIsRunning(uint32_t machineId) const;
+
+ void buffersSetDefaultPolicy(uint32_t machineId, const RetentionPolicy& p, bool applyExisting = true);
+ void buffersSetPolicy(uint32_t machineId, uint32_t channelId, const RetentionPolicy& p);
+
+ std::vector<BufferManager::ChannelStat> buffersListChannelStats(uint32_t machineId) const;
+ BufferManager::ChannelStat buffersGetChannelStat(uint32_t machineId, uint32_t channelId) const;
+
+ // ===== 批次管理(新增) =====
+ struct BatchRec {
+ Proto::BatchState state = Proto::BatchState::Idle; // Idle / Active
+ std::string activeBatchId; // Idle 时为空
+ uint64_t activeStartTs = 0; // ms epoch
+ uint64_t expectedEndTs = 0; // 0: 未知
+ };
+
+ // 新:仅传“预计时长ms”(0=未知),开始时间内部取 nowMs()
+ void batchStart(uint32_t machineId,
+ const std::string& batchId,
+ uint64_t expectedDurationMs = 0);
+
+ // (可选兼容)旧签名保留,但标记为弃用:内部转调新签名
+ void batchStart(uint32_t machineId,
+ const std::string& batchId,
+ uint64_t startTs /*ignored*/,
+ uint64_t expectedEndTs /*treated as duration or absolute?*/);
+
+// 结束当前批次(保持数据,状态转 Idle)
+ void batchStop(uint32_t machineId,
+ uint64_t endTs = 0); // 备用
+
+// 查询该机当前批次(若不存在,返回 Idle 空记录)
+ BatchRec getBatch(uint32_t machineId) const;
+
+private:
+ struct ClientInfo {
+ SOCKET sock = INVALID_SOCKET;
+ std::string ip;
+ uint16_t port = 0;
+ std::chrono::steady_clock::time_point tsConnected{};
+ bool versionOk = false;
+ FrameAssembler fr; // 每个客户端一个帧组装器
+ };
+
+ SocketComm socketComm;
+ std::vector<ClientInfo> clients;
+
+ std::function<void(int, std::string)> cbStatus;
+ std::function<void(const std::vector<uint8_t>&)> cbRaw;
+ bool rawDumpEnabled = true;
+
+ std::function<void(const std::string&, uint16_t, bool)> cbClientEvent;
+
+ std::thread worker;
+ std::atomic<bool> running{ false };
+ std::mutex mClients; // 保护 clients
+
+ BufferRegistry registry_; // 一个 Collector 管所有机台
+
+ // 批次状态
+ std::unordered_map<uint32_t, BatchRec> batches_;
+ mutable std::mutex mBatches;
+
+ // 内部函数
+ void handleClientData(ClientInfo& c, const std::vector<uint8_t>& data);
+ bool isVersionRequest(const std::vector<uint8_t>& data) const;
+
+ // 统一错误回包(6 参数)
+ void sendErrorTo(ClientInfo& c,
+ uint32_t dataId,
+ uint16_t refCmd,
+ uint32_t machineId,
+ Proto::ErrCode code,
+ const std::string& message);
+
+ // 工具:当前 epoch 毫秒
+ static uint64_t nowMs();
+};
+
+#endif // COLLECTOR_H
diff --git a/SourceCode/Bond/DAQBridge/core/CommBase.h b/SourceCode/Bond/DAQBridge/core/CommBase.h
new file mode 100644
index 0000000..5795299
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/core/CommBase.h
@@ -0,0 +1,29 @@
+#pragma once
+#ifndef COMM_BASE_H
+#define COMM_BASE_H
+
+#include <string>
+#include <vector>
+#include <functional>
+
+class CommBase {
+public:
+ virtual void sendSampleData(double sample) = 0;
+ virtual void sendWindowData(const std::vector<std::string>& dataFields) = 0;
+ virtual void connectServer(const std::string& ip, uint16_t port) = 0;
+ virtual void createServer(uint16_t port) = 0;
+ virtual void disconnect() = 0;
+
+ // 连接状态回调
+ virtual void onConnectionEstablished() = 0;
+ virtual void onConnectionLost() = 0;
+ virtual void setConnectionStatusCallback(std::function<void(int, std::string)> callback) = 0;
+
+ // 新增:原始数据上抛(收到的“字节流”直接回调给应用层)
+ virtual void setRawDataCallback(std::function<void(const std::vector<uint8_t>&)> callback) = 0;
+
+ // 新增:开关(默认 true)
+ virtual void setRawDumpEnabled(bool enabled) = 0;
+};
+
+#endif // COMM_BASE_H
diff --git a/SourceCode/Bond/DAQBridge/core/ConnEvents.h b/SourceCode/Bond/DAQBridge/core/ConnEvents.h
new file mode 100644
index 0000000..2e29fda
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/core/ConnEvents.h
@@ -0,0 +1,40 @@
+#pragma once
+#include <string>
+
+namespace DAQEvt {
+
+ // 用枚举定义,外部回调仍以 int 传递
+ enum class ConnCode : int {
+ // Collector(服务端)
+ ServerListening = 100, // createServer 成功
+ ServerStopped = 101, // disconnect/close
+ ClientAccepted = 110, // 新客户端接入
+ ClientDisconnected = 111, // 客户端主动断开
+ VersionOk = 120, // 客户端版本校验通过
+ VersionTimeoutKick = 121, // 5 秒未握手被踢
+
+ // Display(客户端)
+ DisplayConnected = 200, // connectServer 成功(已建连)
+ DisplayDisconnected = 201, // disconnect/close
+
+ // 通用/错误
+ SocketError = 900
+ };
+
+ // 可选:把 code 转成默认字符串(备用)
+ inline const char* ToString(ConnCode c) {
+ switch (c) {
+ case ConnCode::ServerListening: return "Server listening";
+ case ConnCode::ServerStopped: return "Server stopped";
+ case ConnCode::ClientAccepted: return "Client accepted";
+ case ConnCode::ClientDisconnected: return "Client disconnected";
+ case ConnCode::VersionOk: return "Version OK";
+ case ConnCode::VersionTimeoutKick: return "Version timeout kick";
+ case ConnCode::DisplayConnected: return "Display connected";
+ case ConnCode::DisplayDisconnected: return "Display disconnected";
+ case ConnCode::SocketError: return "Socket error";
+ default: return "Unknown";
+ }
+ }
+
+} // namespace DAQEvt
diff --git a/SourceCode/Bond/DAQBridge/core/DataTypes.h b/SourceCode/Bond/DAQBridge/core/DataTypes.h
new file mode 100644
index 0000000..8943adc
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/core/DataTypes.h
@@ -0,0 +1,9 @@
+// DataTypes.h
+#pragma once
+#include <cstdint>
+
+struct Sample {
+ int64_t ts_ms = 0; // 时间戳:毫秒
+ double value = 0.0;
+};
+#pragma once
diff --git a/SourceCode/Bond/DAQBridge/core/Display.cpp b/SourceCode/Bond/DAQBridge/core/Display.cpp
new file mode 100644
index 0000000..9b3b71b
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/core/Display.cpp
@@ -0,0 +1,266 @@
+#include "Display.h"
+#include <iostream>
+#include "../DAQConfig.h"
+#include "../core/ConnEvents.h"
+using namespace DAQEvt;
+
+// 公共协议头
+#include "../proto/Protocol.h"
+#include "../proto/ProtocolCodec.h"
+#include "../net/FrameAssembler.h"
+
+using namespace Proto;
+
+Display::Display() {}
+Display::~Display() {
+ stopRecvLoop();
+ disconnect();
+}
+
+void Display::connectServer(const std::string& ip, uint16_t port) {
+ if (socketComm.createClientSocket(ip, port)) {
+ if (cbStatus) cbStatus(static_cast<int>(ConnCode::DisplayConnected), "Display connected to server");
+ onConnectionEstablished();
+
+ // 连接后立刻发送版本请求(0x0001),带 dataId 回显
+ ReqVersion vreq;
+ vreq.dataId = m_nextDataId++;
+ auto pkt = encodeRequestVersion(vreq);
+ socketComm.sendDataSingle(pkt);
+
+ startRecvLoop(DAQCfg::RecvLoopIntervalMs);
+ }
+}
+
+void Display::disconnect() {
+ stopRecvLoop();
+ socketComm.closeSocket();
+ if (cbStatus) cbStatus(static_cast<int>(ConnCode::DisplayDisconnected), "Display disconnected");
+ onConnectionLost();
+}
+
+void Display::startRecvLoop(uint32_t intervalMs) {
+ if (recvRunning.load()) return;
+ recvRunning.store(true);
+ recvThread = std::thread([this, intervalMs]() {
+ std::vector<uint8_t> chunk;
+ std::vector<uint8_t> frame;
+ FrameAssembler fr;
+
+ while (recvRunning.load()) {
+ chunk.clear();
+ if (socketComm.recvSingle(chunk) && !chunk.empty()) {
+ if (rawDumpEnabled && cbRaw) cbRaw(chunk);
+ fr.push(chunk);
+ while (fr.nextFrame(frame)) {
+ if (rawDumpEnabled && cbRaw) cbRaw(frame);
+ handleRawData(frame);
+ }
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
+ }
+ });
+}
+
+void Display::stopRecvLoop() {
+ if (!recvRunning.load()) return;
+ recvRunning.store(false);
+ if (recvThread.joinable()) recvThread.join();
+}
+
+void Display::sendSampleData(double) { /* 客户端这边暂不发 */ }
+void Display::sendWindowData(const std::vector<std::string>&) { /* 同上 */ }
+
+void Display::onConnectionEstablished() { std::cout << "[Display] connected\n"; }
+void Display::onConnectionLost() { std::cout << "[Display] disconnected\n"; }
+
+// —— 低层接口:显式 dataId ——
+
+// 发送 0x0101(增量拉取)— 原版(不带 batchId)
+void Display::requestSince(uint32_t dataId, uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, uint16_t maxCount) {
+ // 调到带 batchId 的重载,传空串 => 不附加 batchId
+ requestSince(dataId, machineId, channelId, sinceTsExclusive, std::string(), maxCount);
+}
+
+// 发送 0x0101(增量拉取)— ★ 新:带 batchId
+void Display::requestSince(uint32_t dataId, uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, const std::string& batchId, uint16_t maxCount) {
+ ReqSince req;
+ req.dataId = dataId;
+ req.machineId = machineId;
+ req.channelId = channelId;
+ req.sinceTsExclusive = sinceTsExclusive;
+ req.maxCount = maxCount;
+ if (!batchId.empty()) {
+ req.flags = SINCE_FLAG_HAS_BATCH;
+ req.batchId = batchId;
+ }
+ else {
+ req.flags = 0;
+ req.batchId.clear();
+ }
+ auto pkt = encodeRequestSince(req);
+ socketComm.sendDataSingle(pkt);
+}
+
+void Display::requestMachines(uint32_t dataId) {
+ ReqMachines req; req.dataId = dataId;
+ auto pkt = encodeRequestMachines(req);
+ socketComm.sendDataSingle(pkt);
+}
+
+void Display::requestStats(uint32_t dataId, uint32_t machineId) {
+ ReqStats req; req.dataId = dataId; req.machineId = machineId; req.flags = 0;
+ auto pkt = encodeRequestStats(req);
+ socketComm.sendDataSingle(pkt);
+}
+
+// 整机多通道增量 — 原版(不带 batchId)
+void Display::requestSinceAll(uint32_t dataId, uint32_t machineId, uint64_t sinceTsExclusive,
+ uint16_t maxPerChannel) {
+ // 调到带 batchId 的重载,传空串
+ requestSinceAll(dataId, machineId, sinceTsExclusive, std::string(), maxPerChannel);
+}
+
+// 整机多通道增量 — ★ 新:带 batchId
+void Display::requestSinceAll(uint32_t dataId, uint32_t machineId, uint64_t sinceTsExclusive,
+ const std::string& batchId, uint16_t maxPerChannel) {
+ ReqSinceAll req;
+ req.dataId = dataId;
+ req.machineId = machineId;
+ req.sinceTsExclusive = sinceTsExclusive;
+ req.maxPerChannel = maxPerChannel;
+ if (!batchId.empty()) {
+ req.flags = SINCE_FLAG_HAS_BATCH;
+ req.batchId = batchId;
+ }
+ else {
+ req.flags = 0;
+ req.batchId.clear();
+ }
+ socketComm.sendDataSingle(encodeRequestSinceAll(req));
+}
+
+// —— 新增:批次信息拉取 ——
+// 显式 dataId
+void Display::requestBatchInfo(uint32_t dataId, uint32_t machineId) {
+ ReqBatchInfo req; req.dataId = dataId; req.machineId = machineId;
+ socketComm.sendDataSingle(encodeRequestBatchInfo(req));
+}
+// 便捷:自动 dataId
+void Display::requestBatchInfo(uint32_t machineId) {
+ requestBatchInfo(m_nextDataId++, machineId);
+}
+
+// —— 便捷接口:自动分配 dataId ——
+// 三组只是简单地在内部调用上述显式版
+
+void Display::requestMachines() {
+ requestMachines(m_nextDataId++);
+}
+
+void Display::requestStats(uint32_t machineId) {
+ requestStats(m_nextDataId++, machineId);
+}
+
+void Display::requestSince(uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, uint16_t maxCount) {
+ requestSince(m_nextDataId++, machineId, channelId, sinceTsExclusive, maxCount);
+}
+
+void Display::requestSince(uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, const std::string& batchId, uint16_t maxCount) {
+ requestSince(m_nextDataId++, machineId, channelId, sinceTsExclusive, batchId, maxCount);
+}
+
+void Display::requestSinceAll(uint32_t machineId, uint64_t sinceTsExclusive, uint16_t maxPerChannel) {
+ requestSinceAll(m_nextDataId++, machineId, sinceTsExclusive, maxPerChannel);
+}
+
+void Display::requestSinceAll(uint32_t machineId, uint64_t sinceTsExclusive,
+ const std::string& batchId, uint16_t maxPerChannel) {
+ requestSinceAll(m_nextDataId++, machineId, sinceTsExclusive, batchId, maxPerChannel);
+}
+
+// 收包分发(在接收线程里被调用)
+void Display::handleRawData(const std::vector<uint8_t>& rawData) {
+ // F001 版本响应
+ {
+ RspVersion vrsp;
+ if (decodeResponseVersion(rawData, vrsp)) {
+ if (cbStatus) cbStatus(static_cast<int>(ConnCode::VersionOk),
+ std::string("Server version: ") + vrsp.version);
+ // m_versionOk = true;
+ return;
+ }
+ }
+
+ // F101 —— since 拉取响应
+ {
+ RspSince rsp;
+ if (decodeResponseSince(rawData, rsp)) {
+ if (cbSamples) cbSamples(rsp.machineId, rsp.channelId, rsp.lastTsSent, rsp.more, rsp.samples);
+ return;
+ }
+ }
+
+ // F103 —— 统计/通道
+ {
+ RspStats st;
+ if (decodeResponseStats(rawData, st)) {
+ if (cbStats) cbStats(st.machineId, st.channels);
+ return;
+ }
+ }
+
+ // F104 —— 机台列表
+ {
+ RspMachines ms;
+ if (decodeResponseMachines(rawData, ms)) {
+ if (cbMachines) cbMachines(ms.machines);
+ return;
+ }
+ }
+
+ // F105 —— 多通道增量
+ {
+ RspSinceAll ra;
+ if (decodeResponseSinceAll(rawData, ra)) {
+ if (cbSamplesMulti) cbSamplesMulti(ra.machineId, ra.moreAny, ra.blocks);
+ return;
+ }
+ }
+
+ // ★ 新增:F120 —— 批次信息
+ {
+ RspBatchInfo bi;
+ if (decodeResponseBatchInfo(rawData, bi)) {
+ if (cbBatchInfo) cbBatchInfo(bi);
+ return;
+ }
+ }
+
+ // ★ 新增:E100 —— 统一错误(自愈)
+ {
+ RspError er;
+ if (decodeResponseError(rawData, er)) {
+ if (cbStatus) {
+ std::string s = "ERR ref=0x" + [](uint16_t x) {
+ char buf[8]; std::snprintf(buf, sizeof(buf), "%04X", (unsigned)x); return std::string(buf);
+ }(er.refCmd) +
+ " mid=" + std::to_string(er.machineId) +
+ " code=" + std::to_string((unsigned)er.code) +
+ " msg=" + er.message;
+ cbStatus(static_cast<int>(ConnCode::SocketError), s);
+ }
+ // 错误自愈:NoActive / Mismatch → 拉一次 BatchInfo
+ if (er.code == ErrCode::NoActiveBatch || er.code == ErrCode::BatchMismatch) {
+ requestBatchInfo(er.machineId);
+ }
+ return;
+ }
+ }
+
+ // 其它类型(将来扩展)……
+}
diff --git a/SourceCode/Bond/DAQBridge/core/Display.h b/SourceCode/Bond/DAQBridge/core/Display.h
new file mode 100644
index 0000000..79d789b
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/core/Display.h
@@ -0,0 +1,113 @@
+#pragma once
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include "CommBase.h"
+#include "../net/SocketComm.h"
+#include <functional>
+#include <thread>
+#include <atomic>
+#include "../proto/Protocol.h"
+#include "../proto/ProtocolCodec.h"
+
+// 回调:收到样本的上抛(UI 用它画图/存本地)
+using SamplesCallback = std::function<void(uint32_t machineId, uint32_t channelId,
+ uint64_t lastTsSent, uint8_t more,
+ const std::vector<Proto::SamplePair>& samples)>;
+using SamplesMultiCallback = std::function<void(
+ uint32_t machineId, uint8_t moreAny,
+ const std::vector<Proto::ChannelBlock>& blocks)>;
+using MachinesCallback = std::function<void(const std::vector<Proto::MachineInfo>& machines)>;
+using StatsCallback = std::function<void(uint32_t machineId, const std::vector<Proto::ChannelStatInfo>& channels)>;
+
+// ★ 新增:批次信息回调
+using BatchInfoCallback = std::function<void(const Proto::RspBatchInfo&)>;
+
+class Display : public CommBase {
+public:
+ Display();
+ ~Display();
+
+ void sendSampleData(double sample) override;
+ void sendWindowData(const std::vector<std::string>& dataFields) override;
+
+ void connectServer(const std::string& ip, uint16_t port) override;
+ void createServer(uint16_t /*port*/) override {}
+ void disconnect() override;
+
+ void onConnectionEstablished() override;
+ void onConnectionLost() override;
+
+ void handleRawData(const std::vector<uint8_t>& rawData);
+
+ void setConnectionStatusCallback(std::function<void(int, std::string)> cb) override { cbStatus = std::move(cb); }
+ void setRawDataCallback(std::function<void(const std::vector<uint8_t>&)> cb) override { cbRaw = std::move(cb); }
+ void setRawDumpEnabled(bool enabled) override { rawDumpEnabled = enabled; }
+
+ void startRecvLoop(uint32_t intervalMs = 10);
+ void stopRecvLoop();
+
+ void setSamplesCallback(SamplesCallback cb) { cbSamples = std::move(cb); }
+ void setSamplesMultiCallback(SamplesMultiCallback cb) { cbSamplesMulti = std::move(cb); }
+ void setMachinesCallback(MachinesCallback cb) { cbMachines = std::move(cb); }
+ void setStatsCallback(StatsCallback cb) { cbStats = std::move(cb); }
+ void setBatchInfoCallback(BatchInfoCallback cb) { cbBatchInfo = std::move(cb); } // ★ 新增
+
+ // —— 原有“需显式 dataId”的低层接口(保留以兼容)——
+ void requestMachines(uint32_t dataId);
+ void requestStats(uint32_t dataId, uint32_t machineId);
+ void requestSince(uint32_t dataId, uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, uint16_t maxCount = 1024);
+ // ★ 新增:带 batchId 的显式 dataId 版
+ void requestSince(uint32_t dataId, uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, const std::string& batchId, uint16_t maxCount = 1024);
+
+ // 便捷:整机多通道增量(显式 dataId)
+ void requestSinceAll(uint32_t dataId, uint32_t machineId, uint64_t sinceTsExclusive,
+ uint16_t maxPerChannel = 1024);
+ // ★ 新增:带 batchId 的显式 dataId 版
+ void requestSinceAll(uint32_t dataId, uint32_t machineId, uint64_t sinceTsExclusive,
+ const std::string& batchId, uint16_t maxPerChannel = 1024);
+
+ // —— 新增:批次信息拉取(显式/便捷)——
+ void requestBatchInfo(uint32_t dataId, uint32_t machineId); // 显式 dataId
+ void requestBatchInfo(uint32_t machineId); // 便捷:自动 m_nextDataId++
+
+ // —— 新增:便捷高层接口(自动分配 dataId)——
+ void requestMachines(); // 自动 m_nextDataId++
+ void requestStats(uint32_t machineId);
+ void requestSince(uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, uint16_t maxCount = 1024);
+ // ★ 新增:便捷带 batchId
+ void requestSince(uint32_t machineId, uint32_t channelId,
+ uint64_t sinceTsExclusive, const std::string& batchId, uint16_t maxCount = 1024);
+
+ // 便捷:整机多通道增量
+ void requestSinceAll(uint32_t machineId, uint64_t sinceTsExclusive,
+ uint16_t maxPerChannel = 1024);
+ // ★ 新增:便捷带 batchId
+ void requestSinceAll(uint32_t machineId, uint64_t sinceTsExclusive,
+ const std::string& batchId, uint16_t maxPerChannel = 1024);
+
+private:
+ SocketComm socketComm;
+ std::function<void(int, std::string)> cbStatus;
+
+ // 原始数据回调 & 开关
+ std::function<void(const std::vector<uint8_t>&)> cbRaw;
+ bool rawDumpEnabled = true;
+
+ std::thread recvThread;
+ std::atomic<bool> recvRunning{ false };
+ SamplesCallback cbSamples;
+ SamplesMultiCallback cbSamplesMulti;
+ MachinesCallback cbMachines;
+ StatsCallback cbStats;
+ BatchInfoCallback cbBatchInfo; // ★ 新增
+
+ // dataId 递增,用于配对请求/响应
+ uint32_t m_nextDataId = 1;
+ bool m_versionOk = false;
+};
+
+#endif // DISPLAY_H
diff --git a/SourceCode/Bond/DAQBridge/net/FrameAssembler.h b/SourceCode/Bond/DAQBridge/net/FrameAssembler.h
new file mode 100644
index 0000000..dd68684
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/net/FrameAssembler.h
@@ -0,0 +1,58 @@
+#pragma once
+#include <vector>
+#include <cstdint>
+#include <algorithm>
+
+class FrameAssembler {
+public:
+ // 追加一块原始字节
+ void push(const std::vector<uint8_t>& chunk) {
+ buf_.insert(buf_.end(), chunk.begin(), chunk.end());
+ }
+ void push(const uint8_t* p, size_t n) {
+ buf_.insert(buf_.end(), p, p + n);
+ }
+
+ // 提取下一帧;返回 true 表示 out 拿到了一帧完整数据
+ bool nextFrame(std::vector<uint8_t>& out) {
+ const uint8_t HEAD[4] = { 0x11,0x88,0x11,0x88 };
+ const uint8_t TAIL = 0x88;
+ for (;;) {
+ // 需要至少 4B 头 + 4B dataId + 2B len + 1B 尾 = 11B
+ if (buf_.size() < 11) return false;
+
+ // 找头同步
+ size_t i = 0;
+ while (i + 4 <= buf_.size() && !std::equal(HEAD, HEAD + 4, buf_.begin() + i)) ++i;
+ if (i + 4 > buf_.size()) { // 没找到头,清空
+ buf_.clear();
+ return false;
+ }
+ if (i > 0) buf_.erase(buf_.begin(), buf_.begin() + i); // 丢弃头前噪声
+
+ if (buf_.size() < 11) return false; // 还不够最小帧
+
+ // 读取正文长度(大端)
+ uint16_t len = (uint16_t(buf_[8]) << 8) | buf_[9];
+ size_t total = 4 + 4 + 2 + size_t(len) + 1; // 整帧长度
+ if (buf_.size() < total) return false; // 半帧,等下次
+
+ // 校验尾
+ if (buf_[total - 1] != TAIL) {
+ // 尾不对:丢弃一个字节,重新找头(避免死锁)
+ buf_.erase(buf_.begin());
+ continue;
+ }
+
+ // 取出完整帧
+ out.assign(buf_.begin(), buf_.begin() + total);
+ buf_.erase(buf_.begin(), buf_.begin() + total);
+ return true;
+ }
+ }
+
+ void clear() { buf_.clear(); }
+
+private:
+ std::vector<uint8_t> buf_;
+};
diff --git a/SourceCode/Bond/DAQBridge/net/SocketComm.cpp b/SourceCode/Bond/DAQBridge/net/SocketComm.cpp
new file mode 100644
index 0000000..43eac34
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/net/SocketComm.cpp
@@ -0,0 +1,105 @@
+#include "SocketComm.h"
+
+SocketComm::SocketComm() {
+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
+ std::cerr << "WSAStartup failed\n";
+ }
+}
+SocketComm::~SocketComm() {
+ closeSocket();
+ WSACleanup();
+}
+
+bool SocketComm::setNonBlocking(SOCKET s, bool nb) {
+ u_long mode = nb ? 1UL : 0UL;
+ return ioctlsocket(s, FIONBIO, &mode) == 0;
+}
+
+bool SocketComm::createClientSocket(const std::string& serverIP, uint16_t serverPort) {
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == INVALID_SOCKET) return false;
+
+ sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(serverPort);
+ if (InetPton(AF_INET, serverIP.c_str(), &addr.sin_addr) <= 0) return false;
+
+ if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
+ std::cerr << "connect failed: " << WSAGetLastError() << "\n";
+ closesocket(sock); sock = INVALID_SOCKET; return false;
+ }
+ return true;
+}
+
+bool SocketComm::createServerSocket(uint16_t port) {
+ listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (listenSock == INVALID_SOCKET) return false;
+
+ // 复用地址
+ BOOL yes = 1; setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
+
+ sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port);
+ if (bind(listenSock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) return false;
+ if (listen(listenSock, SOMAXCONN) == SOCKET_ERROR) return false;
+
+ // 非阻塞监听
+ setNonBlocking(listenSock, true);
+ std::cout << "Server listening on port " << port << "\n";
+ return true;
+}
+
+bool SocketComm::acceptOne(SOCKET& outClient, std::string& outIp, uint16_t& outPort) {
+ sockaddr_in caddr{}; int len = sizeof(caddr);
+ SOCKET cs = accept(listenSock, (sockaddr*)&caddr, &len);
+ if (cs == INVALID_SOCKET) {
+ int e = WSAGetLastError();
+ if (e == WSAEWOULDBLOCK) return false; // 当前没有新连接
+ std::cerr << "accept error: " << e << "\n"; return false;
+ }
+ setNonBlocking(cs, true);
+ char ipbuf[INET_ADDRSTRLEN]{};
+ InetNtop(AF_INET, &caddr.sin_addr, ipbuf, INET_ADDRSTRLEN);
+ outIp = ipbuf; outPort = ntohs(caddr.sin_port);
+ outClient = cs;
+ return true;
+}
+
+bool SocketComm::recvFrom(SOCKET s, std::vector<uint8_t>& buffer, bool& peerClosed) {
+ peerClosed = false;
+ char tmp[4096];
+ int r = recv(s, tmp, sizeof(tmp), 0);
+ if (r > 0) {
+ buffer.assign(tmp, tmp + r);
+ return true;
+ }
+ if (r == 0) { // 对端正常关闭
+ peerClosed = true;
+ return false;
+ }
+ int e = WSAGetLastError();
+ if (e == WSAEWOULDBLOCK) return false; // 暂无数据
+ // 其它错误也认为连接不可用了
+ peerClosed = true;
+ return false;
+}
+
+bool SocketComm::sendTo(SOCKET s, const std::vector<uint8_t>& data) {
+ int sent = send(s, reinterpret_cast<const char*>(data.data()), (int)data.size(), 0);
+ return sent == (int)data.size();
+}
+
+void SocketComm::closeClient(SOCKET s) {
+ if (s != INVALID_SOCKET) closesocket(s);
+}
+
+void SocketComm::closeSocket() {
+ if (listenSock != INVALID_SOCKET) { closesocket(listenSock); listenSock = INVALID_SOCKET; }
+ if (sock != INVALID_SOCKET) { closesocket(sock); sock = INVALID_SOCKET; }
+}
+
+bool SocketComm::sendDataSingle(const std::vector<uint8_t>& data) {
+ if (sock == INVALID_SOCKET) return false;
+ return sendTo(sock, data);
+}
+bool SocketComm::recvSingle(std::vector<uint8_t>& buffer) {
+ if (sock == INVALID_SOCKET) return false;
+ return recvFrom(sock, buffer);
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/DAQBridge/net/SocketComm.h b/SourceCode/Bond/DAQBridge/net/SocketComm.h
new file mode 100644
index 0000000..7e6746e
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/net/SocketComm.h
@@ -0,0 +1,48 @@
+#pragma once
+#ifndef SOCKET_COMM_H
+#define SOCKET_COMM_H
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <string>
+#include <vector>
+#include <iostream>
+
+#pragma comment(lib, "ws2_32.lib")
+
+class SocketComm {
+public:
+ SocketComm();
+ ~SocketComm();
+
+ bool createClientSocket(const std::string& serverIP, uint16_t serverPort); // 客户端
+ bool createServerSocket(uint16_t port); // 服务端(监听)
+
+ // 新增:非阻塞 accept/收发/关闭指定客户端
+ bool acceptOne(SOCKET& outClient, std::string& outIp, uint16_t& outPort);
+ bool recvFrom(SOCKET s, std::vector<uint8_t>& buffer, bool& peerClosed);
+
+ // 可选:为了兼容旧调用,保留一个内联包装(如果其它地方还用到了旧签名)
+ inline bool recvFrom(SOCKET s, std::vector<uint8_t>& buffer) {
+ bool closed = false;
+ return recvFrom(s, buffer, closed);
+ }
+ bool sendTo(SOCKET s, const std::vector<uint8_t>& data);
+ void closeClient(SOCKET s);
+
+ void closeSocket(); // 关闭监听或单连接
+
+ // 供上层判断本对象当前是否是“监听模式”
+ bool isListening() const { return listenSock != INVALID_SOCKET; }
+ bool sendDataSingle(const std::vector<uint8_t>& data); // 客户端单连接发送
+ bool recvSingle(std::vector<uint8_t>& buffer); // 客户端单连接接收
+
+private:
+ SOCKET listenSock = INVALID_SOCKET; // 监听 socket(服务端)
+ SOCKET sock = INVALID_SOCKET; // 单连接模式(客户端时用)
+ WSADATA wsaData{};
+
+ bool setNonBlocking(SOCKET s, bool nb);
+};
+
+#endif // SOCKET_COMM_H
diff --git a/SourceCode/Bond/DAQBridge/proto/Protocol.h b/SourceCode/Bond/DAQBridge/proto/Protocol.h
new file mode 100644
index 0000000..ce98f98
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/proto/Protocol.h
@@ -0,0 +1,168 @@
+#pragma once
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace Proto {
+
+ // 固定帧
+ inline constexpr uint8_t kHead[4] = { 0x11, 0x88, 0x11, 0x88 };
+ inline constexpr uint8_t kTail = 0x88;
+
+ // 指令码
+ enum : uint16_t {
+ // 握手
+ CMD_REQ_VERSION = 0x0001,
+ CMD_RSP_VERSION = 0xF001,
+
+ // 单通道增量拉取
+ CMD_REQ_SINCE = 0x0101,
+ CMD_RSP_SINCE = 0xF101,
+
+ // 统计/通道列表
+ CMD_REQ_STATS = 0x0103,
+ CMD_RSP_STATS = 0xF103,
+
+ // 机台列表
+ CMD_REQ_MACHINES = 0x0104,
+ CMD_RSP_MACHINES = 0xF104,
+
+ // 整机多通道增量拉取
+ CMD_REQ_SINCE_ALL = 0x0105,
+ CMD_RSP_SINCE_ALL = 0xF105,
+
+ // 批次信息
+ CMD_REQ_BATCH_INFO = 0x0120,
+ CMD_RSP_BATCH_INFO = 0xF120,
+
+ // 错误
+ CMD_RSP_ERROR = 0xE100,
+ };
+
+ // === since* 请求里附加 batchId 的标志位 ===
+ inline constexpr uint16_t SINCE_FLAG_HAS_BATCH = 0x0001;
+
+ // ===== 数据结构(两端共用) =====
+
+ // 0x0001 / 0xF001
+ struct ReqVersion {
+ uint32_t dataId = 0;
+ };
+ struct RspVersion {
+ uint32_t dataId = 0;
+ std::string version; // UTF-8,例如 "1.0.1"
+ };
+
+ // 0x0104 / 0xF104
+ struct MachineInfo {
+ uint32_t id = 0;
+ std::string name;
+ };
+ struct ReqMachines {
+ uint32_t dataId = 0;
+ };
+ struct RspMachines {
+ uint32_t dataId = 0;
+ std::vector<MachineInfo> machines;
+ };
+
+ // 0x0103 / 0xF103
+ struct ReqStats {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ uint16_t flags = 0;
+ };
+ struct ChannelStatInfo {
+ uint32_t channelId = 0;
+ uint64_t earliestTs = 0;
+ uint64_t latestTs = 0;
+ uint32_t size = 0;
+ std::string name; // UTF-8
+ };
+ struct RspStats {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ std::vector<ChannelStatInfo> channels;
+ };
+
+ // 0x0101 / 0xF101(单通道增量)
+ struct ReqSince {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ uint32_t channelId = 0;
+ uint64_t sinceTsExclusive = 0; // ms
+ uint16_t maxCount = 1024;
+ uint16_t flags = 0; // 按位,见 SINCE_FLAG_HAS_BATCH
+ std::string batchId; // flags & SINCE_FLAG_HAS_BATCH 时有效
+ };
+ struct SamplePair {
+ uint64_t ts_ms = 0;
+ double value = 0.0;
+ };
+ struct RspSince {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ uint32_t channelId = 0;
+ uint64_t lastTsSent = 0;
+ uint8_t more = 0; // 该通道是否还有未发
+ std::vector<SamplePair> samples;
+ };
+
+ // 0x0105 / 0xF105(整机多通道增量)
+ struct ChannelBlock {
+ uint32_t channelId = 0;
+ uint64_t lastTsSent = 0; // 该通道本次最后样本时间戳
+ uint8_t more = 0; // 该通道是否还有未发
+ std::vector<SamplePair> samples;
+ };
+ struct ReqSinceAll {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ uint64_t sinceTsExclusive = 0; // 对所有通道统一 since
+ uint16_t maxPerChannel = 1024; // 每条曲线上限
+ uint16_t flags = 0; // 按位,见 SINCE_FLAG_HAS_BATCH
+ std::string batchId; // flags & SINCE_FLAG_HAS_BATCH 时有效
+ };
+ struct RspSinceAll {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ uint8_t moreAny = 0; // 是否还有任意通道有剩余
+ std::vector<ChannelBlock> blocks; // 多个通道的增量
+ };
+
+ // === 批次状态 ===
+ enum class BatchState : uint8_t {
+ Idle = 0, // 无活动批次:activeBatchId="", activeStartTs=0, expectedEndTs=0
+ Active = 1,
+ };
+
+ // === 错误码 ===
+ enum class ErrCode : uint16_t {
+ NoActiveBatch = 0x0001, // 当前无活动批次
+ BatchMismatch = 0x0002, // 请求携带的 batchId 与当前活动批次不一致
+ };
+
+ // 0x0120 / 0xF120(批次信息)
+ struct ReqBatchInfo {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ };
+ struct RspBatchInfo {
+ uint32_t dataId = 0;
+ uint32_t machineId = 0;
+ BatchState state = BatchState::Idle;
+ std::string activeBatchId; // state=Idle 时应为空
+ uint64_t activeStartTs = 0;
+ uint64_t expectedEndTs = 0; // =0 表示未知/Idle
+ };
+
+ // 0xE100 错误帧
+ struct RspError {
+ uint32_t dataId = 0;
+ uint16_t refCmd = 0; // 出错请求的指令:如 0x0101/0x0105/0x0120...
+ uint32_t machineId = 0;
+ ErrCode code = ErrCode::NoActiveBatch;
+ std::string message; // 简短文本
+ };
+
+} // namespace Proto
diff --git a/SourceCode/Bond/DAQBridge/proto/ProtocolCodec.cpp b/SourceCode/Bond/DAQBridge/proto/ProtocolCodec.cpp
new file mode 100644
index 0000000..6a1d4a3
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/proto/ProtocolCodec.cpp
@@ -0,0 +1,663 @@
+#include "ProtocolCodec.h"
+#include <cstring> // std::memcpy
+#include <cstdio> // std::snprintf (如需日志)
+#include <vector>
+
+namespace Proto {
+
+ // ---------------------------
+ // Big-endian 基础编解码工具
+ // ---------------------------
+ void put_u16(std::vector<uint8_t>& v, uint16_t x) {
+ v.push_back(static_cast<uint8_t>((x >> 8) & 0xFF));
+ v.push_back(static_cast<uint8_t>(x & 0xFF));
+ }
+ void put_u32(std::vector<uint8_t>& v, uint32_t x) {
+ v.push_back(static_cast<uint8_t>((x >> 24) & 0xFF));
+ v.push_back(static_cast<uint8_t>((x >> 16) & 0xFF));
+ v.push_back(static_cast<uint8_t>((x >> 8) & 0xFF));
+ v.push_back(static_cast<uint8_t>(x & 0xFF));
+ }
+ void put_u64(std::vector<uint8_t>& v, uint64_t x) {
+ for (int i = 7; i >= 0; --i) v.push_back(static_cast<uint8_t>((x >> (i * 8)) & 0xFF));
+ }
+ uint16_t get_u16(const uint8_t* p) {
+ return static_cast<uint16_t>((uint16_t(p[0]) << 8) | uint16_t(p[1]));
+ }
+ uint32_t get_u32(const uint8_t* p) {
+ return (uint32_t(p[0]) << 24) | (uint32_t(p[1]) << 16) | (uint32_t(p[2]) << 8) | uint32_t(p[3]);
+ }
+ uint64_t get_u64(const uint8_t* p) {
+ uint64_t x = 0;
+ for (int i = 0; i < 8; ++i) { x = (x << 8) | p[i]; }
+ return x;
+ }
+ void put_f64_be(std::vector<uint8_t>& v, double d) {
+ static_assert(sizeof(double) == 8, "double must be 8 bytes");
+ uint64_t u;
+ std::memcpy(&u, &d, 8);
+ for (int i = 7; i >= 0; --i) v.push_back(static_cast<uint8_t>((u >> (i * 8)) & 0xFF));
+ }
+ double get_f64_be(const uint8_t* p) {
+ uint64_t u = 0;
+ for (int i = 0; i < 8; ++i) { u = (u << 8) | p[i]; }
+ double d;
+ std::memcpy(&d, &u, 8);
+ return d;
+ }
+
+ // 快速窥探正文 cmd(不做完整长度核对,保留你原本风格)
+ uint16_t peek_cmd(const std::vector<uint8_t>& f) {
+ if (f.size() < 12) return 0;
+ if (!(f[0] == kHead[0] && f[1] == kHead[1] && f[2] == kHead[2] && f[3] == kHead[3])) return 0;
+ return (uint16_t(f[10]) << 8) | f[11];
+ }
+
+ // 包装帧:4B头 + 4B dataId + 2B bodyLen + body + 1B尾
+ static inline void frame_wrap(uint32_t dataId, const std::vector<uint8_t>& body, std::vector<uint8_t>& out) {
+ out.clear();
+ out.reserve(4 + 4 + 2 + body.size() + 1);
+ out.push_back(kHead[0]); out.push_back(kHead[1]); out.push_back(kHead[2]); out.push_back(kHead[3]);
+ put_u32(out, dataId);
+ put_u16(out, static_cast<uint16_t>(body.size()));
+ out.insert(out.end(), body.begin(), body.end());
+ out.push_back(kTail);
+ }
+
+ // ---------------------------
+ // 0x0104 / 0xF104 Machines
+ // ---------------------------
+ std::vector<uint8_t> encodeRequestMachines(const ReqMachines& req) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_REQ_MACHINES);
+
+ std::vector<uint8_t> out;
+ frame_wrap(req.dataId, body, out);
+ return out;
+ }
+
+ std::vector<uint8_t> encodeResponseMachines(const RspMachines& rsp) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_RSP_MACHINES);
+ put_u16(body, static_cast<uint16_t>(rsp.machines.size()));
+ for (auto& m : rsp.machines) {
+ put_u32(body, m.id);
+ const uint16_t n = static_cast<uint16_t>(m.name.size());
+ put_u16(body, n);
+ body.insert(body.end(), m.name.begin(), m.name.end());
+ }
+
+ std::vector<uint8_t> out;
+ frame_wrap(rsp.dataId, body, out);
+ return out;
+ }
+
+ bool decodeRequestMachines(const std::vector<uint8_t>& f, ReqMachines& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ return (get_u16(b) == CMD_REQ_MACHINES);
+ }
+
+ bool decodeResponseMachines(const std::vector<uint8_t>& f, RspMachines& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 + 2 > e) return false;
+ if (get_u16(b) != CMD_RSP_MACHINES) return false;
+ b += 2;
+ const uint16_t cnt = get_u16(b); b += 2;
+
+ out.machines.clear();
+ out.machines.reserve(cnt);
+ for (uint16_t i = 0; i < cnt; ++i) {
+ if (b + 4 + 2 > e) return false;
+ const uint32_t id = get_u32(b); b += 4;
+ const uint16_t n = get_u16(b); b += 2;
+ if (b + n > e) return false;
+ out.machines.push_back({ id, std::string(reinterpret_cast<const char*>(b), n) });
+ b += n;
+ }
+ return (b == e);
+ }
+
+ // ---------------------------
+ // 0x0001 / 0xF001 Version
+ // ---------------------------
+ std::vector<uint8_t> encodeRequestVersion(const ReqVersion& req) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_REQ_VERSION);
+
+ std::vector<uint8_t> out;
+ frame_wrap(req.dataId, body, out);
+ return out;
+ }
+
+ std::vector<uint8_t> encodeResponseVersion(const RspVersion& rsp) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_RSP_VERSION);
+ const uint16_t n = static_cast<uint16_t>(rsp.version.size());
+ put_u16(body, n);
+ body.insert(body.end(), rsp.version.begin(), rsp.version.end()); // UTF-8
+
+ std::vector<uint8_t> out;
+ frame_wrap(rsp.dataId, body, out);
+ return out;
+ }
+
+ bool decodeRequestVersion(const std::vector<uint8_t>& f, ReqVersion& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+ const uint8_t* b = p + 10;
+ return (get_u16(b) == CMD_REQ_VERSION);
+ }
+
+ bool decodeResponseVersion(const std::vector<uint8_t>& f, RspVersion& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 + 2 > e) return false;
+ if (get_u16(b) != CMD_RSP_VERSION) return false;
+ b += 2;
+
+ const uint16_t n = get_u16(b); b += 2;
+ if (b + n > e) return false;
+ out.version.assign(reinterpret_cast<const char*>(b), n);
+ b += n;
+
+ return (b == e);
+ }
+
+ // ---------------------------
+ // 0x0103 / 0xF103 Stats
+ // ---------------------------
+ std::vector<uint8_t> encodeRequestStats(const ReqStats& req) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_REQ_STATS);
+ put_u32(body, req.machineId);
+ put_u16(body, req.flags);
+
+ std::vector<uint8_t> out;
+ frame_wrap(req.dataId, body, out);
+ return out;
+ }
+
+ std::vector<uint8_t> encodeResponseStats(const RspStats& rsp) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_RSP_STATS);
+ put_u32(body, rsp.machineId);
+ put_u16(body, static_cast<uint16_t>(rsp.channels.size()));
+
+ for (auto& c : rsp.channels) {
+ put_u32(body, c.channelId);
+ put_u64(body, c.earliestTs);
+ put_u64(body, c.latestTs);
+ put_u32(body, c.size);
+ const uint16_t n = static_cast<uint16_t>(c.name.size());
+ put_u16(body, n);
+ body.insert(body.end(), c.name.begin(), c.name.end());
+ }
+
+ std::vector<uint8_t> out;
+ frame_wrap(rsp.dataId, body, out);
+ return out;
+ }
+
+ bool decodeRequestStats(const std::vector<uint8_t>& f, ReqStats& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+ if (b + 2 + 4 + 2 > e) return false;
+ if (get_u16(b) != CMD_REQ_STATS) return false;
+ b += 2;
+
+ out.machineId = get_u32(b); b += 4;
+ out.flags = get_u16(b); b += 2;
+
+ return (b == e);
+ }
+
+ bool decodeResponseStats(const std::vector<uint8_t>& f, RspStats& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 + 4 + 2 > e) return false;
+ if (get_u16(b) != CMD_RSP_STATS) return false;
+ b += 2;
+
+ out.machineId = get_u32(b); b += 4;
+ const uint16_t cnt = get_u16(b); b += 2;
+
+ out.channels.clear();
+ out.channels.reserve(cnt);
+ for (uint16_t i = 0; i < cnt; ++i) {
+ if (b + 4 + 8 + 8 + 4 + 2 > e) return false;
+ ChannelStatInfo ci{};
+ ci.channelId = get_u32(b); b += 4;
+ ci.earliestTs = get_u64(b); b += 8;
+ ci.latestTs = get_u64(b); b += 8;
+ ci.size = get_u32(b); b += 4;
+
+ const uint16_t n = get_u16(b); b += 2;
+ if (b + n > e) return false;
+ ci.name.assign(reinterpret_cast<const char*>(b), n);
+ b += n;
+
+ out.channels.push_back(std::move(ci));
+ }
+ return (b == e);
+ }
+
+ // ---------------------------------------
+ // 0x0101 / 0xF101 Since(支持可选 batchId)
+ // ---------------------------------------
+ std::vector<uint8_t> encodeRequestSince(const ReqSince& req) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_REQ_SINCE);
+ put_u32(body, req.machineId);
+ put_u32(body, req.channelId);
+ put_u64(body, req.sinceTsExclusive);
+ put_u16(body, req.maxCount);
+ put_u16(body, req.flags);
+ if (req.flags & SINCE_FLAG_HAS_BATCH) {
+ const uint16_t L = static_cast<uint16_t>(req.batchId.size());
+ put_u16(body, L);
+ body.insert(body.end(), req.batchId.begin(), req.batchId.end());
+ }
+
+ std::vector<uint8_t> out;
+ frame_wrap(req.dataId, body, out);
+ return out;
+ }
+
+ std::vector<uint8_t> encodeResponseSince(const RspSince& rsp) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_RSP_SINCE);
+ put_u32(body, rsp.machineId);
+ put_u32(body, rsp.channelId);
+ put_u64(body, rsp.lastTsSent);
+ body.push_back(rsp.more ? 1u : 0u);
+ put_u16(body, static_cast<uint16_t>(rsp.samples.size()));
+ for (auto& s : rsp.samples) {
+ put_u64(body, s.ts_ms);
+ put_f64_be(body, s.value);
+ }
+
+ std::vector<uint8_t> out;
+ frame_wrap(rsp.dataId, body, out);
+ return out;
+ }
+
+ bool decodeRequestSince(const std::vector<uint8_t>& f, ReqSince& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 > e) return false;
+ if (get_u16(b) != CMD_REQ_SINCE) return false;
+ b += 2;
+
+ if (b + 4 + 4 + 8 + 2 + 2 > e) return false;
+ out.machineId = get_u32(b); b += 4;
+ out.channelId = get_u32(b); b += 4;
+ out.sinceTsExclusive = get_u64(b); b += 8;
+ out.maxCount = get_u16(b); b += 2;
+ out.flags = get_u16(b); b += 2;
+
+ out.batchId.clear();
+ if (out.flags & SINCE_FLAG_HAS_BATCH) {
+ if (b + 2 > e) return false;
+ const uint16_t L = get_u16(b); b += 2;
+ if (b + L > e) return false;
+ out.batchId.assign(reinterpret_cast<const char*>(b), L);
+ b += L;
+ }
+
+ return (b == e);
+ }
+
+ bool decodeResponseSince(const std::vector<uint8_t>& f, RspSince& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 > e) return false;
+ if (get_u16(b) != CMD_RSP_SINCE) return false;
+ b += 2;
+
+ if (b + 4 + 4 + 8 + 1 + 2 > e) return false;
+ out.machineId = get_u32(b); b += 4;
+ out.channelId = get_u32(b); b += 4;
+ out.lastTsSent = get_u64(b); b += 8;
+ out.more = *b++; // u8
+ const uint16_t cnt = get_u16(b); b += 2;
+
+ out.samples.clear();
+ out.samples.reserve(cnt);
+ for (uint16_t i = 0; i < cnt; ++i) {
+ if (b + 8 + 8 > e) return false;
+ const uint64_t ts = get_u64(b); b += 8;
+ const double v = get_f64_be(b); b += 8;
+ out.samples.push_back({ ts, v });
+ }
+ return (b == e);
+ }
+
+ // ---------------------------------------
+ // 0x0105 / 0xF105 SinceAll(整机多通道增量)
+ // ---------------------------------------
+ std::vector<uint8_t> encodeRequestSinceAll(const ReqSinceAll& req) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_REQ_SINCE_ALL);
+ put_u32(body, req.machineId);
+ put_u64(body, req.sinceTsExclusive);
+ put_u16(body, req.maxPerChannel);
+ put_u16(body, req.flags);
+ if (req.flags & SINCE_FLAG_HAS_BATCH) {
+ const uint16_t L = static_cast<uint16_t>(req.batchId.size());
+ put_u16(body, L);
+ body.insert(body.end(), req.batchId.begin(), req.batchId.end());
+ }
+
+ std::vector<uint8_t> out;
+ frame_wrap(req.dataId, body, out);
+ return out;
+ }
+
+ std::vector<uint8_t> encodeResponseSinceAll(const RspSinceAll& rsp) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_RSP_SINCE_ALL);
+ put_u32(body, rsp.machineId);
+ body.push_back(rsp.moreAny ? 1u : 0u);
+ put_u16(body, static_cast<uint16_t>(rsp.blocks.size()));
+
+ for (const auto& b : rsp.blocks) {
+ put_u32(body, b.channelId);
+ put_u64(body, b.lastTsSent);
+ body.push_back(b.more ? 1u : 0u);
+ put_u16(body, static_cast<uint16_t>(b.samples.size()));
+ for (const auto& sp : b.samples) {
+ put_u64(body, sp.ts_ms);
+ put_f64_be(body, sp.value);
+ }
+ }
+
+ std::vector<uint8_t> out;
+ frame_wrap(rsp.dataId, body, out);
+ return out;
+ }
+
+ bool decodeRequestSinceAll(const std::vector<uint8_t>& f, ReqSinceAll& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 > e) return false;
+ if (get_u16(b) != CMD_REQ_SINCE_ALL) return false;
+ b += 2;
+
+ if (b + 4 + 8 + 2 + 2 > e) return false;
+ out.machineId = get_u32(b); b += 4;
+ out.sinceTsExclusive = get_u64(b); b += 8;
+ out.maxPerChannel = get_u16(b); b += 2;
+ out.flags = get_u16(b); b += 2;
+
+ out.batchId.clear();
+ if (out.flags & SINCE_FLAG_HAS_BATCH) {
+ if (b + 2 > e) return false;
+ const uint16_t L = get_u16(b); b += 2;
+ if (b + L > e) return false;
+ out.batchId.assign(reinterpret_cast<const char*>(b), L);
+ b += L;
+ }
+ return (b == e);
+ }
+
+ bool decodeResponseSinceAll(const std::vector<uint8_t>& f, RspSinceAll& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 > e) return false;
+ if (get_u16(b) != CMD_RSP_SINCE_ALL) return false;
+ b += 2;
+
+ if (b + 4 + 1 + 2 > e) return false;
+ out.machineId = get_u32(b); b += 4;
+ out.moreAny = *b++; // u8
+ const uint16_t N = get_u16(b); b += 2;
+
+ out.blocks.clear();
+ out.blocks.reserve(N);
+ for (uint16_t i = 0; i < N; ++i) {
+ if (b + 4 + 8 + 1 + 2 > e) return false;
+ ChannelBlock blk;
+ blk.channelId = get_u32(b); b += 4;
+ blk.lastTsSent = get_u64(b); b += 8;
+ blk.more = *b++; // u8
+ const uint16_t M = get_u16(b); b += 2;
+
+ blk.samples.clear();
+ blk.samples.reserve(M);
+ for (uint16_t j = 0; j < M; ++j) {
+ if (b + 8 + 8 > e) return false;
+ SamplePair sp;
+ sp.ts_ms = get_u64(b); b += 8;
+ sp.value = get_f64_be(b); b += 8;
+ blk.samples.push_back(sp);
+ }
+ out.blocks.push_back(std::move(blk));
+ }
+ return (b == e);
+ }
+
+ // ---------------------------
+ // 0x0120 / 0xF120 BatchInfo
+ // ---------------------------
+ std::vector<uint8_t> encodeRequestBatchInfo(const ReqBatchInfo& req) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_REQ_BATCH_INFO);
+ put_u32(body, req.machineId);
+
+ std::vector<uint8_t> out;
+ frame_wrap(req.dataId, body, out);
+ return out;
+ }
+
+ bool decodeRequestBatchInfo(const std::vector<uint8_t>& f, ReqBatchInfo& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 + 4 > e) return false;
+ if (get_u16(b) != CMD_REQ_BATCH_INFO) return false;
+ b += 2;
+
+ out.machineId = get_u32(b); b += 4;
+ return (b == e);
+ }
+
+ std::vector<uint8_t> encodeResponseBatchInfo(const RspBatchInfo& rsp) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_RSP_BATCH_INFO);
+ put_u32(body, rsp.machineId);
+ body.push_back(static_cast<uint8_t>(rsp.state));
+
+ const uint16_t L = static_cast<uint16_t>(rsp.activeBatchId.size());
+ put_u16(body, L);
+ body.insert(body.end(), rsp.activeBatchId.begin(), rsp.activeBatchId.end());
+
+ put_u64(body, rsp.activeStartTs);
+ put_u64(body, rsp.expectedEndTs);
+
+ std::vector<uint8_t> out;
+ frame_wrap(rsp.dataId, body, out);
+ return out;
+ }
+
+ bool decodeResponseBatchInfo(const std::vector<uint8_t>& f, RspBatchInfo& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 + 4 + 1 + 2 > e) return false;
+ if (get_u16(b) != CMD_RSP_BATCH_INFO) return false;
+ b += 2;
+
+ out.machineId = get_u32(b); b += 4;
+ out.state = static_cast<BatchState>(*b++);
+
+ const uint16_t L = get_u16(b); b += 2;
+ if (b + L > e) return false;
+ out.activeBatchId.assign(reinterpret_cast<const char*>(b), L); b += L;
+
+ if (b + 8 + 8 > e) return false;
+ out.activeStartTs = get_u64(b); b += 8;
+ out.expectedEndTs = get_u64(b); b += 8;
+
+ return (b == e);
+ }
+
+ // ---------------------------
+ // 0xE100 Error
+ // ---------------------------
+ std::vector<uint8_t> encodeResponseError(const RspError& rsp) {
+ std::vector<uint8_t> body;
+ put_u16(body, CMD_RSP_ERROR);
+ put_u16(body, rsp.refCmd);
+ put_u32(body, rsp.machineId);
+ put_u16(body, static_cast<uint16_t>(rsp.code));
+ const uint16_t L = static_cast<uint16_t>(rsp.message.size());
+ put_u16(body, L);
+ body.insert(body.end(), rsp.message.begin(), rsp.message.end());
+
+ std::vector<uint8_t> out;
+ frame_wrap(rsp.dataId, body, out);
+ return out;
+ }
+
+ bool decodeResponseError(const std::vector<uint8_t>& f, RspError& out) {
+ if (f.size() < 11) return false;
+ const uint8_t* p = f.data();
+ if (p[0] != kHead[0] || p[1] != kHead[1] || p[2] != kHead[2] || p[3] != kHead[3]) return false;
+ if (f.back() != kTail) return false;
+
+ out.dataId = get_u32(p + 4);
+ const uint16_t bodyLen = get_u16(p + 8);
+ if (10u + bodyLen + 1u != f.size()) return false;
+
+ const uint8_t* b = p + 10;
+ const uint8_t* e = b + bodyLen;
+
+ if (b + 2 + 2 + 4 + 2 + 2 > e) return false;
+ if (get_u16(b) != CMD_RSP_ERROR) return false;
+ b += 2;
+
+ out.refCmd = get_u16(b); b += 2;
+ out.machineId = get_u32(b); b += 4;
+ out.code = static_cast<ErrCode>(get_u16(b)); b += 2;
+
+ const uint16_t L = get_u16(b); b += 2;
+ if (b + L > e) return false;
+ out.message.assign(reinterpret_cast<const char*>(b), L); b += L;
+
+ return (b == e);
+ }
+
+} // namespace Proto
diff --git a/SourceCode/Bond/DAQBridge/proto/ProtocolCodec.h b/SourceCode/Bond/DAQBridge/proto/ProtocolCodec.h
new file mode 100644
index 0000000..308898f
--- /dev/null
+++ b/SourceCode/Bond/DAQBridge/proto/ProtocolCodec.h
@@ -0,0 +1,49 @@
+#pragma once
+#include "Protocol.h"
+
+namespace Proto {
+
+ // 大端工具
+ void put_u16(std::vector<uint8_t>& v, uint16_t x);
+ void put_u32(std::vector<uint8_t>& v, uint32_t x);
+ void put_u64(std::vector<uint8_t>& v, uint64_t x);
+ uint16_t get_u16(const uint8_t* p);
+ uint32_t get_u32(const uint8_t* p);
+ uint64_t get_u64(const uint8_t* p);
+ void put_f64_be(std::vector<uint8_t>& v, double d);
+ double get_f64_be(const uint8_t* p);
+
+ // 帮助:peek cmd
+ uint16_t peek_cmd(const std::vector<uint8_t>& frame);
+
+ // 编码(构帧:头 + dataId + len + body + 尾)
+ std::vector<uint8_t> encodeRequestVersion(const ReqVersion& req);
+ std::vector<uint8_t> encodeResponseVersion(const RspVersion& rsp);
+ std::vector<uint8_t> encodeRequestMachines(const ReqMachines& req);
+ std::vector<uint8_t> encodeResponseMachines(const RspMachines& rsp);
+ std::vector<uint8_t> encodeRequestStats(const ReqStats& req);
+ std::vector<uint8_t> encodeResponseStats(const RspStats& rsp);
+ std::vector<uint8_t> encodeRequestSince(const ReqSince& req);
+ std::vector<uint8_t> encodeResponseSince(const RspSince& rsp);
+ std::vector<uint8_t> encodeRequestSinceAll(const ReqSinceAll& req);
+ std::vector<uint8_t> encodeResponseSinceAll(const RspSinceAll& rsp);
+ std::vector<uint8_t> encodeRequestBatchInfo(const ReqBatchInfo& req);
+ std::vector<uint8_t> encodeResponseBatchInfo(const RspBatchInfo& rsp);
+
+ // 解码
+ bool decodeRequestVersion(const std::vector<uint8_t>& frame, ReqVersion& out);
+ bool decodeResponseVersion(const std::vector<uint8_t>& frame, RspVersion& out);
+ bool decodeRequestMachines(const std::vector<uint8_t>& frame, ReqMachines& out);
+ bool decodeResponseMachines(const std::vector<uint8_t>& frame, RspMachines& out);
+ bool decodeRequestStats(const std::vector<uint8_t>& frame, ReqStats& out);
+ bool decodeResponseStats(const std::vector<uint8_t>& frame, RspStats& out);
+ bool decodeRequestSince(const std::vector<uint8_t>& frame, ReqSince& out);
+ bool decodeResponseSince(const std::vector<uint8_t>& frame, RspSince& out);
+ bool decodeRequestSinceAll(const std::vector<uint8_t>& frame, ReqSinceAll& out);
+ bool decodeResponseSinceAll(const std::vector<uint8_t>& frame, RspSinceAll& out);
+ bool decodeRequestBatchInfo(const std::vector<uint8_t>& frame, ReqBatchInfo& out);
+ bool decodeResponseBatchInfo(const std::vector<uint8_t>& frame, RspBatchInfo& out);
+ std::vector<uint8_t> encodeResponseError(const RspError& rsp);
+ bool decodeResponseError(const std::vector<uint8_t>& frame, RspError& out);
+
+} // namespace Proto
diff --git a/SourceCode/Bond/Servo/CBonder.cpp b/SourceCode/Bond/Servo/CBonder.cpp
index 797dada..b9f8343 100644
--- a/SourceCode/Bond/Servo/CBonder.cpp
+++ b/SourceCode/Bond/Servo/CBonder.cpp
@@ -487,9 +487,9 @@
return 0;
}
- int CBonder::onProcessStateChanged(PROCESS_STATE state)
+ int CBonder::onProcessStateChanged(int slotNo, PROCESS_STATE state)
{
- CEquipment::onProcessStateChanged(state);
+ CEquipment::onProcessStateChanged(slotNo, state);
if (state == PROCESS_STATE::Complete) {
// 检查数据,当前两片玻璃,一片为G1, 一片为G2, 且pProcessData中的id能匹配G1或G2
diff --git a/SourceCode/Bond/Servo/CBonder.h b/SourceCode/Bond/Servo/CBonder.h
index b8a9f08..89631dc 100644
--- a/SourceCode/Bond/Servo/CBonder.h
+++ b/SourceCode/Bond/Servo/CBonder.h
@@ -22,7 +22,7 @@
virtual void getAttributeVector(CAttributeVector& attrubutes);
virtual int recvIntent(CPin* pPin, CIntent* pIntent);
virtual int onProcessData(CProcessData* pProcessData);
- virtual int onProcessStateChanged(PROCESS_STATE state);
+ virtual int onProcessStateChanged(int slotNo, PROCESS_STATE state);
virtual int getIndexerOperationModeBaseValue();
virtual int parsingParams(const char* pszData, size_t size, std::vector<CParam>& parsms);
virtual int parsingProcessData(const char* pszData, size_t size, std::vector<CParam>& parsms);
diff --git a/SourceCode/Bond/Servo/CEquipment.cpp b/SourceCode/Bond/Servo/CEquipment.cpp
index d725241..f9d930a 100644
--- a/SourceCode/Bond/Servo/CEquipment.cpp
+++ b/SourceCode/Bond/Servo/CEquipment.cpp
@@ -26,7 +26,6 @@
m_pCclink = nullptr;
m_nBaseAlarmId = 0;
m_pArm = nullptr;
- m_processState = PROCESS_STATE::Ready;
m_blockReadBit = { 0 };
m_nTestFlag = 0;
InitializeCriticalSection(&m_criticalSection);
@@ -144,13 +143,15 @@
return 0;
}
- void CEquipment::setProcessState(PROCESS_STATE state)
+ void CEquipment::setProcessState(int nSlotNo, PROCESS_STATE state)
{
- m_processState = state;
- onProcessStateChanged(m_processState);
+ if (nSlotNo <= 0 || nSlotNo > 8) return;
+
+ m_processState[nSlotNo - 1] = state;
+ onProcessStateChanged(nSlotNo, m_processState[nSlotNo - 1]);
if (m_listener.onProcessStateChanged != nullptr) {
- m_listener.onProcessStateChanged(this, m_processState);
+ m_listener.onProcessStateChanged(this, nSlotNo, m_processState[nSlotNo - 1]);
}
}
@@ -909,8 +910,8 @@
Unlock();
- if (m_processState != PROCESS_STATE::Ready) {
- setProcessState(PROCESS_STATE::Ready);
+ if (m_processState[port] != PROCESS_STATE::Ready) {
+ setProcessState(port, PROCESS_STATE::Ready);
}
if (m_listener.onDataChanged != nullptr) {
@@ -941,9 +942,11 @@
pGlass->release(); // tempFetchOut需要调用一次release
Unlock();
+ /*
if (m_processState != PROCESS_STATE::Processing) {
setProcessState(PROCESS_STATE::Processing);
}
+ */
if (m_listener.onDataChanged != nullptr) {
m_listener.onDataChanged(this, EDCC_STORED_JOB);
@@ -1929,12 +1932,17 @@
year, month, day, hour, minute, second
);
+ CGlass* pGlass = getGlassFromSlot(slotNo);
+ if (pGlass == nullptr) {
+ LOGE("<CEquipment-%s>decodeJobProcessStartReport, 找不到对应glass", getName().c_str());
+ }
+ if (slotNo <= 0 || slotNo > 8) return -1;
- if (m_processState != PROCESS_STATE::Processing) {
+ if (m_processState[slotNo -1] != PROCESS_STATE::Processing) {
Lock();
m_svDatas.clear();
Unlock();
- setProcessState(PROCESS_STATE::Processing);
+ setProcessState(slotNo, PROCESS_STATE::Processing);
}
@@ -2002,11 +2010,11 @@
);
- if (m_processState != PROCESS_STATE::Complete) {
- setProcessState(PROCESS_STATE::Complete);
- }
-
CGlass* pGlass = getGlassFromSlot(slotNo);
+ if (m_processState[slotNo - 1] != PROCESS_STATE::Complete) {
+ setProcessState(slotNo, PROCESS_STATE::Complete);
+ }
+
if (pGlass == nullptr) {
LOGE("<CEquipment-%s>decodeJobProcessEndReport, 找不到对应glass", getName().c_str());
}
@@ -2015,9 +2023,6 @@
if (pJs->getCassetteSequenceNo() == cassetteNo
&& pJs->getJobSequenceNo() == jobSequenceNo) {
pGlass->processEnd(m_nID, getSlotUnit(slotNo));
- if (m_processState != PROCESS_STATE::Complete) {
- setProcessState(PROCESS_STATE::Complete);
- }
}
else {
LOGE("<CEquipment-%s>decodeJobProcessEndReport, jobSequenceNo或jobSequenceNo不匹配",
@@ -2136,7 +2141,7 @@
return 0;
}
- int CEquipment::onProcessStateChanged(PROCESS_STATE state)
+ int CEquipment::onProcessStateChanged(int nSlotNo, PROCESS_STATE state)
{
return 0;
}
diff --git a/SourceCode/Bond/Servo/CEquipment.h b/SourceCode/Bond/Servo/CEquipment.h
index ff16693..36a05c9 100644
--- a/SourceCode/Bond/Servo/CEquipment.h
+++ b/SourceCode/Bond/Servo/CEquipment.h
@@ -55,7 +55,7 @@
typedef std::function<void(void* pEiuipment, void* pReport)> ONVCREVENTREPORT;
typedef std::function<BOOL(void* pEiuipment, int port, CJobDataB* pJobDataB)> ONPREFETCHEDOUTJOB;
typedef std::function<BOOL(void* pEiuipment, int port, CJobDataB* pJobDataB, short& putSlot)> ONPRESTOREDJOB;
- typedef std::function<void(void* pEiuipment, PROCESS_STATE state)> ONPROCESSSTATE;
+ typedef std::function<void(void* pEiuipment, int nSlotNo, PROCESS_STATE state)> ONPROCESSSTATE;
typedef std::function<void(void* pEiuipment, short scanMap, short downMap)> ONMAPMISMATCH;
typedef std::function<void(void* pEiuipment, short status, __int64 data)> ONPORTSTATUSCHANGED;
@@ -140,7 +140,7 @@
virtual int onProcessData(CProcessData* pProcessData);
virtual int onSendAble(int port);
virtual int onReceiveAble(int port);
- virtual int onProcessStateChanged(PROCESS_STATE state);
+ virtual int onProcessStateChanged(int nSlotNo, PROCESS_STATE state);
virtual int getIndexerOperationModeBaseValue();
virtual bool isSlotProcessed(int slot) { return true; };
bool isAlarmStep(SERVO::CStep* pStep);
@@ -267,7 +267,7 @@
int decodeJobProcessStartReport(CStep* pStep, const char* pszData, size_t size);
int decodeJobProcessEndReport(CStep* pStep, const char* pszData, size_t size);
BOOL compareJobData(CJobDataB* pJobDataB, CJobDataS* pJobDataS);
- void setProcessState(PROCESS_STATE state);
+ void setProcessState(int nSlotNo, PROCESS_STATE state);
float toFloat(const char* pszAddr);
protected:
@@ -307,7 +307,7 @@
int m_nBaseAlarmId;
CRecipesManager m_recipesManager;
CSlot m_slot[SLOT_MAX];
- PROCESS_STATE m_processState;
+ PROCESS_STATE m_processState[SLOT_MAX] = { PROCESS_STATE::Ready };
std::vector<SERVO::CSVData> m_svDatas;
private:
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index 960294c..037d0f5 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -9,6 +9,11 @@
namespace SERVO {
+ static inline int64_t now_ms_epoch() {
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+ }
+
CMaster* g_pMaster = NULL;
unsigned __stdcall DispatchThreadFunction(LPVOID lpParam)
@@ -262,6 +267,12 @@
}
m_listEquipment.clear();
+
+ if (m_pCollector != nullptr) {
+ m_pCollector->stopLoop();
+ delete m_pCollector;
+ m_pCollector = nullptr;
+ }
return 0;
}
@@ -1520,8 +1531,20 @@
unlock();
}
};
- listener.onProcessStateChanged = [&](void* pEquipment, PROCESS_STATE state) -> void {
+ listener.onProcessStateChanged = [&](void* pEquipment, int slotNo, PROCESS_STATE state) -> void {
+ ASSERT(1 <= slotNo && slotNo <= 8);
+ int eqid = ((CEquipment*)pEquipment)->getID();
+ CGlass* pGlass = ((CEquipment*)pEquipment)->getGlassFromSlot(slotNo);
LOGI("<Master>onProcessStateChanged<%d>", (int)state);
+ if (state == PROCESS_STATE::Processing) {
+ if (pGlass != nullptr) {
+ m_pCollector->batchStart(eqid,
+ pGlass->getID().c_str(), 10 * 60 * 1000ULL);
+ }
+ }
+ else if (state == PROCESS_STATE::Complete) {
+ m_pCollector->batchStop(eqid);
+ }
};
listener.onMapMismatch = [&](void* pEquipment, short scanMap, short downMap) {
LOGE("<Master-%s>Port InUse, map(%d!=%d)不一致,请检查。",
@@ -1599,6 +1622,61 @@
std::vector<CParam> params;
((CEquipment*)pEquipment)->parsingSVData((const char*)rawData.data(), rawData.size(), params);
+
+ // 以下加入到曲线数据中
+ const int64_t ts = now_ms_epoch();
+ int eqid = ((CEquipment*)pEquipment)->getID();
+ if (eqid == EQ_ID_Bonder1 || eqid == EQ_ID_Bonder2) {
+ m_pCollector->buffersPush(eqid, 1, ts, params.at(1).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 2, ts, params.at(2).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 3, ts, params.at(3).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 4, ts, params.at(4).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 5, ts, params.at(5).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 6, ts, params.at(6).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 7, ts, params.at(7).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 8, ts, params.at(8).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 9, ts, params.at(9).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 10, ts, params.at(10).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 11, ts, params.at(11).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 12, ts, params.at(12).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 13, ts, params.at(13).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 14, ts, params.at(14).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 15, ts, params.at(15).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 16, ts, params.at(16).getDoubleValue());
+ }
+ else if (eqid == EQ_ID_VACUUMBAKE) {
+ m_pCollector->buffersPush(eqid, 1, ts, params.at(1).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 2, ts, params.at(2).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 3, ts, params.at(3).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 4, ts, params.at(4).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 5, ts, params.at(5).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 6, ts, params.at(6).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 7, ts, params.at(7).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 8, ts, params.at(10).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 9, ts, params.at(11).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 10, ts, params.at(12).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 11, ts, params.at(13).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 12, ts, params.at(14).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 13, ts, params.at(15).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 14, ts, params.at(16).getDoubleValue());
+ }
+ else if (eqid == EQ_ID_BAKE_COOLING) {
+ m_pCollector->buffersPush(eqid, 1, ts, params.at(1).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 2, ts, params.at(2).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 3, ts, params.at(3).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 4, ts, params.at(4).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 5, ts, params.at(5).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 6, ts, params.at(6).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 7, ts, params.at(11).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 8, ts, params.at(12).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 9, ts, params.at(13).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 10, ts, params.at(14).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 11, ts, params.at(15).getDoubleValue());
+ m_pCollector->buffersPush(eqid, 12, ts, params.at(16).getDoubleValue());
+ }
+
+
+ // 以下是输出测试
std::string strOut;
char szBuffer[256];
for (auto p : params) {
@@ -3008,4 +3086,99 @@
return nullptr;
}
+
+ void CMaster::CreateDAQBridgeServer()
+ {
+ auto connectionStatusCallback = [&](int code, const std::string& status) {
+ LOGI("<DAQBridge>status:", status.c_str());
+ };
+ auto rawDataCallback = [](const std::vector<uint8_t>& bytes) {
+
+ };
+
+ // 事件:有人连入/断开就上日志
+ auto clieintEventCallback = [](const std::string& ip, uint16_t port, bool connected) {
+ LOGI("<DAQBridge>[Client %s] %s:%u", connected ? _T("JOIN") : _T("LEAVE"), ip.c_str(), port);
+ };
+
+ if (m_pCollector == nullptr) {
+ m_pCollector = new Collector();
+ m_pCollector->setConnectionStatusCallback(connectionStatusCallback);
+ m_pCollector->setRawDataCallback(rawDataCallback);
+ m_pCollector->setClientEventCallback(clieintEventCallback);
+ m_pCollector->createServer(8081);
+ m_pCollector->startLoop(10);
+
+ // 1) 注册机台(推荐:先注册 id + 机器名称)
+ RetentionPolicy defP; defP.mode = RetainMode::ByCount; defP.maxSamples = 200;
+ m_pCollector->registryAddMachine(EQ_ID_Bonder1, "Bonder1", defP);
+ m_pCollector->registryAddMachine(EQ_ID_Bonder2, "Bonder2", defP);
+ m_pCollector->registryAddMachine(EQ_ID_VACUUMBAKE, "前烘烤", defP);
+ m_pCollector->registryAddMachine(EQ_ID_BAKE_COOLING, "烘烤冷却", defP);
+
+
+ // 2) 为通道设置“曲线名称”
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 1, "气囊压力");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 2, "上腔压力");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 3, "管道真空规值");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 4, "腔体真空规值");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 5, "上腔温度1");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 6, "上腔温度2");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 7, "上腔温度3");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 8, "上腔温度4");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 9, "上腔温度5");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 10, "上腔温度6");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 11, "下腔温度1");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 12, "下腔温度2");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 13, "下腔温度3");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 14, "下腔温度4");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 15, "下腔温度5");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder1, 16, "下腔温度6");
+
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 1, "气囊压力");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 2, "上腔压力");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 3, "管道真空规值");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 4, "腔体真空规值");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 5, "上腔温度1");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 6, "上腔温度2");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 7, "上腔温度3");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 8, "上腔温度4");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 9, "上腔温度5");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 10, "上腔温度6");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 11, "下腔温度1");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 12, "下腔温度2");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 13, "下腔温度3");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 14, "下腔温度4");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 15, "下腔温度5");
+ m_pCollector->buffersSetChannelName(EQ_ID_Bonder2, 16, "下腔温度6");
+
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 1, "A腔真空规值");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 2, "A腔温控1");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 3, "A腔温控2");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 4, "A腔温控4");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 5, "A腔温控5");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 6, "A腔温控6");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 7, "A腔温控7");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 8, "B腔真空规值");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 9, "B腔温控1");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 10, "B腔温控2");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 11, "B腔温控4");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 12, "B腔温控5");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 13, "B腔温控6");
+ m_pCollector->buffersSetChannelName(EQ_ID_VACUUMBAKE, 14, "B腔温控7");
+
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 1, "A烘烤温控1");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 2, "A烘烤温控2");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 3, "A烘烤温控4");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 4, "A烘烤温控5");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 5, "A烘烤温控6");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 6, "A烘烤温控7");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 7, "B烘烤温控1");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 8, "B烘烤温控2");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 9, "B烘烤温控4");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 10, "B烘烤温控5");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 11, "B烘烤温控6");
+ m_pCollector->buffersSetChannelName(EQ_ID_BAKE_COOLING, 12, "B烘烤温控7");
+ }
+ }
}
diff --git a/SourceCode/Bond/Servo/CMaster.h b/SourceCode/Bond/Servo/CMaster.h
index be3fea6..21b68d6 100644
--- a/SourceCode/Bond/Servo/CMaster.h
+++ b/SourceCode/Bond/Servo/CMaster.h
@@ -15,6 +15,7 @@
#include "CRobotTask.h"
#include "ProcessJob.h"
#include "CControlJob.h"
+#include "../DAQBridge/core/Collector.h"
#define CTStep_Unknow 0
@@ -261,6 +262,10 @@
int m_nTestFlag;
std::list<CGlass*> m_bufGlass;
+
+ private:
+ Collector* m_pCollector = nullptr;
+ void CreateDAQBridgeServer();
};
}
diff --git a/SourceCode/Bond/Servo/CParam.cpp b/SourceCode/Bond/Servo/CParam.cpp
index 4935b4e..57a8f0c 100644
--- a/SourceCode/Bond/Servo/CParam.cpp
+++ b/SourceCode/Bond/Servo/CParam.cpp
@@ -65,7 +65,12 @@
double CParam::getDoubleValue()
{
- return m_fValue;
+ if(m_nValueType == PVT_DOUBLE)
+ return m_fValue;
+ if (m_nValueType == PVT_INT)
+ return (double)m_nValue;
+
+ return 0.0;
}
void CParam::setDoubleValue(double value)
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index 4dc50af..0b003ae 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -200,6 +200,19 @@
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="..\DAQBridge\buffer\BufferManager.h" />
+ <ClInclude Include="..\DAQBridge\buffer\BufferRegistry.h" />
+ <ClInclude Include="..\DAQBridge\buffer\SampleBuffer.h" />
+ <ClInclude Include="..\DAQBridge\core\Collector.h" />
+ <ClInclude Include="..\DAQBridge\core\CommBase.h" />
+ <ClInclude Include="..\DAQBridge\core\ConnEvents.h" />
+ <ClInclude Include="..\DAQBridge\core\DataTypes.h" />
+ <ClInclude Include="..\DAQBridge\core\Display.h" />
+ <ClInclude Include="..\DAQBridge\DAQConfig.h" />
+ <ClInclude Include="..\DAQBridge\net\FrameAssembler.h" />
+ <ClInclude Include="..\DAQBridge\net\SocketComm.h" />
+ <ClInclude Include="..\DAQBridge\proto\Protocol.h" />
+ <ClInclude Include="..\DAQBridge\proto\ProtocolCodec.h" />
<ClInclude Include="..\jsoncpp\include\json\autolink.h" />
<ClInclude Include="..\jsoncpp\include\json\config.h" />
<ClInclude Include="..\jsoncpp\include\json\features.h" />
@@ -381,6 +394,34 @@
<ClInclude Include="VerticalLine.h" />
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="..\DAQBridge\buffer\BufferManager.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\buffer\BufferRegistry.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\buffer\SampleBuffer.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\core\Collector.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\core\Display.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\net\SocketComm.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\proto\ProtocolCodec.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="..\jsoncpp\lib_json\json_reader.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index b76c3a5..8c439f7 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -206,6 +206,27 @@
<ClCompile Include="CCjPageBase.cpp" />
<ClCompile Include="CCarrierSlotSelector.cpp" />
<ClCompile Include="CCarrierSlotGrid.cpp" />
+ <ClCompile Include="..\DAQBridge\buffer\BufferManager.cpp">
+ <Filter>DAQBridge</Filter>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\buffer\BufferRegistry.cpp">
+ <Filter>DAQBridge</Filter>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\buffer\SampleBuffer.cpp">
+ <Filter>DAQBridge</Filter>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\core\Collector.cpp">
+ <Filter>DAQBridge</Filter>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\core\Display.cpp">
+ <Filter>DAQBridge</Filter>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\net\SocketComm.cpp">
+ <Filter>DAQBridge</Filter>
+ </ClCompile>
+ <ClCompile Include="..\DAQBridge\proto\ProtocolCodec.cpp">
+ <Filter>DAQBridge</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AlarmManager.h" />
@@ -437,6 +458,45 @@
<ClInclude Include="CCjPageBase.h" />
<ClInclude Include="CCarrierSlotSelector.h" />
<ClInclude Include="CCarrierSlotGrid.h" />
+ <ClInclude Include="..\DAQBridge\buffer\BufferManager.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\buffer\BufferRegistry.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\buffer\SampleBuffer.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\core\Collector.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\core\CommBase.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\core\ConnEvents.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\core\DataTypes.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\core\Display.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\net\FrameAssembler.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\net\SocketComm.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\proto\Protocol.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\proto\ProtocolCodec.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
+ <ClInclude Include="..\DAQBridge\DAQConfig.h">
+ <Filter>DAQBridge</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Servo.rc" />
@@ -476,5 +536,8 @@
<Filter Include="JsonCpp">
<UniqueIdentifier>{a877af37-2f78-484f-b1bb-276edbbcd694}</UniqueIdentifier>
</Filter>
+ <Filter Include="DAQBridge">
+ <UniqueIdentifier>{885738f6-3122-4bb9-8308-46b7f692fb13}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
</Project>
\ No newline at end of file
--
Gitblit v1.9.3