feat: 完成 issue #23 ① 采集配置外置(点位/采样率/协议参数化,去硬编码)

This commit is contained in:
2026-08-04 23:44:04 +08:00
parent 50b51d4a27
commit ecb181a79e
5 changed files with 90 additions and 7 deletions
+33 -3
View File
@@ -2,7 +2,10 @@
"""周期采集调度引擎 —— 只读采集 + 背压保护 + 健康度上报。
模板化封装要点:
- 采集配置全部外置(gateway.yaml),引擎不关心具体协议;
- 采集配置全部外置(gateway.yaml + 点位字典 CSV),引擎不关心具体协议;
- 采样率外置(issue #23):每个点位按点位字典 CSV 的 sampleRate 列调度,
引擎以 gateway.yaml 的 interval_ms 为基准 tick,sampleRate > interval_ms 的
点位按比例降频读取(去硬编码:点位采样率完全由模板配置驱动);
- 点位按驱动实例的 device 匹配规则分组,一次 tick 内按驱动批量读取;
- 严格只读:引擎只调用 Driver.read_points(),不存在任何控制指令路径;
- 背压保护:未确认(spool 待上行)记录超过阈值时丢弃新样本并计入丢失,
@@ -55,6 +58,28 @@ class CollectorEngine:
self._stop = threading.Event()
self._thread: Optional[threading.Thread] = None
self._point_to_driver = self._build_routing()
# 每点位采样调度(issue #23):tick 计数 + 各点位下次应采集的 tick
self._tick = 0
self._next_due_tick: Dict[str, int] = {p.point_id: 0 for p in point_dict.points}
# ------------------------------------------------------------------
def _ticks_per_sample(self, p: Point) -> int:
"""点位采样间隔(以基准 tick 计)。
sampleRate ≤ interval_ms 时每个 tick 都采;
sampleRate > interval_ms 时按比例降频(四舍五入,至少 1 tick)。
"""
if p.sample_rate <= 0:
return 1
return max(1, int(round(p.sample_rate / self.interval_ms)))
def _due_points(self) -> List[Point]:
"""本轮 tick 到期待采集的点位(按点位 sampleRate 调度)。"""
due: List[Point] = []
for p in self.point_dict.points:
if self._tick >= self._next_due_tick.get(p.point_id, 0):
due.append(p)
return due
# ------------------------------------------------------------------
def _build_routing(self) -> Dict[str, Driver]:
@@ -92,13 +117,16 @@ class CollectorEngine:
本轮成功读到的样本数。
"""
started = time.monotonic()
expected = len(self.point_dict.points)
# 本轮 tick 递增 + 取到期待集(按点位 sampleRate 调度,issue #23)
self._tick += 1
due = self._due_points()
expected = len(due)
got = 0
samples: List[dict] = []
# 1) 按驱动分组读取(只读)
by_driver: Dict[Driver, List[Point]] = {}
for p in self.point_dict.points:
for p in due:
drv = self._point_to_driver.get(p.point_id)
if drv is None:
continue
@@ -112,6 +140,8 @@ class CollectorEngine:
self.metrics.record_round(time.monotonic() - started, expected, got, failed=True)
return got
for p in points:
# 无论本轮是否读到,都推进该点位采样调度(降频点位不连续空读)
self._next_due_tick[p.point_id] = self._tick + self._ticks_per_sample(p)
value = values.get(p.point_id)
if value is None:
continue # 未读到 → 计入丢失