From 7aa8f838a6f1ef4251b3c5fa66234b5df16fd03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A1=E5=8F=A3=E6=B5=AA=E4=BA=BA?= Date: Wed, 14 Oct 2026 23:43:40 +0800 Subject: [PATCH] feat: sample Raspberry Pi primary interface traffic rates --- README.md | 4 ++++ SHA256SUMS | 9 ++++++--- app/network_rates.py | 29 ++++++++++++++++++++++++++++ app/pigway_pi_control.py | 8 +++++++- docs/DEPLOYMENT-v3.8.0-2026-10-14.md | 5 +++++ install.sh | 3 ++- tests/test_network_rates.py | 23 ++++++++++++++++++++++ 7 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 app/network_rates.py create mode 100644 docs/DEPLOYMENT-v3.8.0-2026-10-14.md create mode 100644 tests/test_network_rates.py diff --git a/README.md b/README.md index 06d63f0..e5dd0ab 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,7 @@ Logs follow the system journal retention policy; no additional database is creat ## Naming and compatibility This project targets Raspberry Pi. Other Linux platforms are not covered by its support claim and may have separately maintained adapters. Repository naming does not change the existing `pigway-agent` adapter ID or `pigway-pi-control` service, configuration and API identifiers. + +## v3.8.0 网络流量 + +主接口每个网络采集周期读取 sysfs 的 rx_bytes/tx_bytes,以单调时间差计算字节/秒,API 新增 rx_bytes_per_second 和 tx_bytes_per_second。首次采样、接口切换、计数器重置或读取失败返回空值;不修改监控告警、硬件插件控制或 API 可选安装行为。2026-10-14 已在 Gateway 部署,实机上下行速率与 Console 标准历史指标验证通过。 diff --git a/SHA256SUMS b/SHA256SUMS index 6433d75..2909784 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -1,14 +1,17 @@ ecf1884bb89b86ed4573d4d922a971abde43772bf7b2ffa6a999a12fbfb72caf .gitignore -d0e219b4b02b073f83e17ba3bb6e8c96dd4152ecdf11d946d53319613097e294 README.md +56b7a3ed6a6a8dbbcf02a80b6219a5e6aab0df2ef04cdff90895784c311e5cfa README.md 64a9708701f8c8d872a587e8a2845afff463797b06a2791319daadd705933d31 README.zh-CN.md -852889cbbf57d5e857794432426e3343b5d08b1395b3098a6e19fd648b4f4267 app/pigway_pi_control.py +c5853f283c0951b43f2bacbc49e66c26d3a605a184d427828e042eb9236d2740 app/network_rates.py +003e6766552823e8cd8ec56888c385d0607df0c14ab7dff166472fcf6914ff9c app/pigway_pi_control.py 757eb6a83ca07740a2389af9b51c6fd5d940b4eb86abbc603f231409416d45db app/pigway_pi_control_api.py 3a8a368bbf299eb2d6cccc968a4aacf070058bddebdc5176083a97ac579d720d app/plugin_api.py d5a783b64a10fa43191a813ef73ef8f762e359b0bd42ec155724fa46216298b0 config/pigway-pi-control.conf +9d45f22221012a7fdb3406ce74a6113f618e9930fe460efb3869efd099e3b1d8 docs/DEPLOYMENT-v3.8.0-2026-10-14.md e3bf3e7070ea62c0526791bed922688c3fb49fee36ca9c375d080119968feb72 docs/HARDWARE_PLUGIN_API.md b54ec1f3e9fb15473910686c54142a85d387e44ea5df427a4cf1b06052e789b3 docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.md 9222a2b6a38f6b6d7d88b30c05413c68c05f69ee499c8479eca05ee7c62e87c3 docs/RASPBERRY_PI_OS_TRIXIE_FRESH_INSTALL.zh-CN.md -d2706de954d0c3f1b4311c1bac52ef09b912919b96126fa6000a88786e969438 install.sh +c104443a4c951ea7916f74473510362f684df7d916bf2a436b779513ba3fe396 install.sh 8a593c290da5bd0d037aee86c35264512afd574a2a0f60f2c29e9db63e0109bd systemd/pigway-pi-control-api.service 1cb55de6b104a233066495e9786319305fa4f1b6f25483c108ab4f5ac9aa5700 systemd/pigway-pi-control.service +8b97229abfb31c68464eb5754bc3a8fec7a6a78e216d74a86b7cc89486700740 tests/test_network_rates.py 9f59e7313ee58e687ae6dd29d175c67082674254ac5c5f54168b2735b64c20a4 uninstall.sh diff --git a/app/network_rates.py b/app/network_rates.py new file mode 100644 index 0000000..0fb91ea --- /dev/null +++ b/app/network_rates.py @@ -0,0 +1,29 @@ +"""Sample interface counters without summing physical and virtual interfaces.""" +import re +from pathlib import Path + + +class NetworkRates: + def __init__(self, root='/sys/class/net'): + self.root = Path(root) + self.previous = None + + def sample(self, interface, now): + try: + if not re.fullmatch(r'[\w.:-]{1,64}', interface): + raise ValueError('invalid interface') + directory = self.root / interface / 'statistics' + rx = int((directory / 'rx_bytes').read_text()) + tx = int((directory / 'tx_bytes').read_text()) + if rx < 0 or tx < 0: + raise ValueError('negative counter') + except (OSError, ValueError, TypeError): + self.previous = None + return None, None + previous = self.previous + self.previous = interface, now, rx, tx + if previous is None or previous[0] != interface or now <= previous[1]: + return None, None + elapsed = now - previous[1] + return (None if rx < previous[2] else (rx - previous[2]) / elapsed, + None if tx < previous[3] else (tx - previous[3]) / elapsed) diff --git a/app/pigway_pi_control.py b/app/pigway_pi_control.py index 6f82854..c9da073 100755 --- a/app/pigway_pi_control.py +++ b/app/pigway_pi_control.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 import time,socket,subprocess,configparser,signal,re,argparse,json,os,platform from pathlib import Path -APP_VERSION="3.7.2" +APP_VERSION="3.8.0" import sys sys.path.append("/usr/local/share/pigway-pi-control") from shutil import disk_usage from plugin_api import Publisher,registered,discover +from network_rates import NetworkRates CFG="/etc/pigway-pi-control.conf" STATUS_PATH="/run/pigway-pi-control/status.json" @@ -104,6 +105,8 @@ prev_cpu=None page=0; page_since=time.monotonic() cpu_hi=mem_hi=0 net_ok=link_ok=True +network_rates=NetworkRates() +net_rx_bytes_per_second=None; net_tx_bytes_per_second=None net_iface=""; net_kind="NET"; net_metric="DOWN"; net_speed_mbps=None; ip_label="IP4"; ip="NO IP" link_failure_since=internet_failure_since=None wifi_weak_since=None; wifi_weak_active=False @@ -435,6 +438,8 @@ def write_status(cpu,temp,mem,disk): "power":health, "network":{"interface":net_iface,"kind":net_kind,"metric":net_metric, "link_speed_mbps":net_speed_mbps, + "rx_bytes_per_second":net_rx_bytes_per_second, + "tx_bytes_per_second":net_tx_bytes_per_second, "signal_percent":signal, "ip_label":ip_label,"ip":ip,"link":bool(link_ok),"internet":bool(net_ok)}, "watches":[{"type":w[0],"name":w[1],"target":w[2],"up":bool(ok)} @@ -484,6 +489,7 @@ try: if now>=monitor_ready_at and now-last_net >= NETWORK_INTERVAL: old_id=(net_iface,ip) net_iface,net_kind,net_metric,net_speed_mbps,ip_label,ip=primary_network() + net_rx_bytes_per_second,net_tx_bytes_per_second=network_rates.sample(net_iface,now) raw_link_ok=ip!="NO IP"; raw_net_ok=internet() if raw_link_ok else False if raw_link_ok: link_failure_since=None;link_ok=True diff --git a/docs/DEPLOYMENT-v3.8.0-2026-10-14.md b/docs/DEPLOYMENT-v3.8.0-2026-10-14.md new file mode 100644 index 0000000..2aa67e1 --- /dev/null +++ b/docs/DEPLOYMENT-v3.8.0-2026-10-14.md @@ -0,0 +1,5 @@ +# Agent v3.8.0 网络流量 + +2026-10-14 Gateway 已部署。单独的 NetworkRates 模块读取主接口 sysfs 计数器;网络更新周期计算速率,写入已有状态/API。首次采样、接口切换、复位、读取失败不伪造零值。idle 返回真实 0。安装器加入模块依赖,保持 API 可选安装。采样测试覆盖初始、零流量、传输、复位、切换及失效;Python 编译、安装器语法检查通过。实机服务 active,状态版本 3.8.0、eth0 上传/下载数值有效,未发现本轮循环错误;Console 两设备在线,接入插件生成对应历史指标。 + +旧程序备份 `/home/way/pigway-agent-backup-v3.8.0-20261014/pigway-pi-control`,目录 0700。仅更新监控程序和新增模块并重启监控服务,没有变更硬件插件或 API 配置。 diff --git a/install.sh b/install.sh index d38638a..187730a 100755 --- a/install.sh +++ b/install.sh @@ -12,13 +12,14 @@ for arg in "$@"; do *) echo "ERROR: unknown option: $arg (hardware is installed separately)" >&2; exit 2 ;; esac done -for required in app/pigway_pi_control.py app/plugin_api.py app/pigway_pi_control_api.py config/pigway-pi-control.conf systemd/pigway-pi-control.service systemd/pigway-pi-control-api.service; do +for required in app/pigway_pi_control.py app/plugin_api.py app/network_rates.py app/pigway_pi_control_api.py config/pigway-pi-control.conf systemd/pigway-pi-control.service systemd/pigway-pi-control-api.service; do [ -f "$required" ] || { echo "ERROR: required file missing: $required" >&2; exit 1; } done [ "$(id -u)" -eq 0 ] || { echo 'ERROR: run installer as root' >&2; exit 1; } # The monitor has no I2C, GPIO, Pillow, smbus2 or cooling-board requirement. command -v python3 >/dev/null || { apt-get update; apt-get install -y python3; } mkdir -p /usr/local/share/pigway-pi-control +install -m 0644 app/network_rates.py /usr/local/share/pigway-pi-control/network_rates.py install -m 0644 app/plugin_api.py /usr/local/share/pigway-pi-control/plugin_api.py install -m 0755 app/pigway_pi_control.py /usr/local/sbin/pigway-pi-control install -m 0755 app/pigway_pi_control_api.py /usr/local/sbin/pigway-pi-control-api diff --git a/tests/test_network_rates.py b/tests/test_network_rates.py new file mode 100644 index 0000000..ddd9ebe --- /dev/null +++ b/tests/test_network_rates.py @@ -0,0 +1,23 @@ +import sys,unittest,tempfile +from pathlib import Path +sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'app')) +from network_rates import NetworkRates + +class RatesTests(unittest.TestCase): + def test_initial_idle_transfer_reset_and_switch(self): + with tempfile.TemporaryDirectory() as root: + def write(interface,rx,tx): + p=Path(root)/interface/'statistics';p.mkdir(parents=True,exist_ok=True) + (p/'rx_bytes').write_text(str(rx));(p/'tx_bytes').write_text(str(tx)) + write('eth0',1000,2000);sampler=NetworkRates(root) + self.assertEqual(sampler.sample('eth0',10),(None,None)) + self.assertEqual(sampler.sample('eth0',15),(0,0)) + write('eth0',1500,3000) + self.assertEqual(sampler.sample('eth0',20),(100,200)) + write('eth0',10,20) + self.assertEqual(sampler.sample('eth0',25),(None,None)) + write('wlan0',100,200) + self.assertEqual(sampler.sample('wlan0',30),(None,None)) + self.assertEqual(sampler.sample('missing',35),(None,None)) + self.assertEqual(sampler.sample('eth0',40),(None,None)) + self.assertEqual(sampler.sample('../eth0',45),(None,None))