2 个版本 (1 个稳定版)

使用旧的 Rust 2015

1.0.0 2020年9月25日
0.1.0 2018年5月12日

#314硬件支持

MirOS 许可证

52KB
845

sensorlog

sensorlog 是一个轻量级的数据记录服务。

Build Status

数据模型

接口非常简单,只包含两个主要操作

  • insertMeasurement(sensor_id, time, data) - 存储一个测量值
  • fetchMeasurements(sensor_id, from, until) - 获取指定时间范围内的所有测量值

测量值是一个包含 timesensor_iddata 的三元组。 time 字段应包含测量时的实际时钟时间。 sensor_id 字段用于标识一系列连续的测量值。 data 字段被视为不透明的数据块;其内容完全由用户定义。

安装

将库添加到您的 Cargo.toml

[dependencies]
sensorlog = "1.0.0"

入门

首先,您需要创建一个日志文件配置,描述数据如何存储,特别是存储配额(有关更多信息,请参阅“保留 & 配额”部分)

let mut logfile_config = sensorlog::logfile_config::LogfileConfig::new();
// This sets the default qoate, i.e. how much storage space a sensor can use
// You can either directly initiate the enum
logfile_config.set_default_storage_quota(sensorlog::quota::StorageQuota::Unlimited);
// Or use the parse_string utility function
logfile_config.set_default_storage_quota(sensorlog::quota::StorageQuota::parse_string("unlimited")?);

然后,您需要创建一个 Sensorlog 实例

let datadir = PathBuf::from("/tmp/sensordata");
let service = sensorlog::Sensorlog::new(&datadir, logfile_config)?;

现在您可以插入一些数据。运行以下代码将测量值“3250”插入传感器's1.hydraulic_pressure_psi'(如果时间参数为 None,它将默认为当前的实际时钟时间)

service.store_measurement(None, "s1.hydraulic_pressure_psi", "3250")?;

之后,运行以下代码从 's1.hydraulic_pressure_psi' 传感器检索最后 10 分钟的测量值

let now = sensorlog::time::get_unix_microseconds()?;
let ten_minutes_ago = now - 10 * 60 * 1000000;
let measurements = service.fetch_measurements("s1.hydraulic_pressure_psi", None, Some(ten_minutes_ago), None)?;
println!("Fetched measurements: {:?}", measurements);

输出应类似于以下内容

Fetched measurements: [Measurement { time: 1600872248099558, data: [51, 50, 53, 48] }]

保留 & 配额

sensorlog 的保留和垃圾回收系统基于为每个 sensor_id 分配的简单存储配额。一旦给定传感器的配额被用完,当为该传感器添加新测量值时,旧测量值将被丢弃。配额以字节为单位表示,测量值的旋转始终是先入先出。

您可以设置适用于所有传感器的默认配额值,以及每个单个传感器 ID 的配额覆盖。

例如,要在“允许列表”配置中使用sensorlog,将默认配额设置为零,然后为每个传感器显式分配存储空间。

let mut logfile_config = sensorlog::logfile_config::LogfileConfig::new();
logfile_config.set_default_storage_quota(sensorlog::quota::StorageQuota::Zero);

let datadir = PathBuf::from("/tmp/sensordata");
let mut service = sensorlog::Sensorlog::new(&datadir, logfile_config)?;

service.set_storage_quota_for("my.first.key", sensorlog::quota::StorageQuota::parse_string("1MB")?);
service.set_storage_quota_for("some/other/ley", sensorlog::quota::StorageQuota::parse_string("4MB")?);

在上述配置中,sensorlog使用的总磁盘空间将被限制,但您不能插入未预先配置的传感器的数据。相反的配置是将默认配额设置为无限。此配置允许您存储来自未知的传感器的数据,但可能会使用无限量的磁盘空间。

let mut logfile_config = sensorlog::logfile_config::LogfileConfig::new();
logfile_config.set_default_storage_quota(sensorlog::quota::StorageQuota::Unlimited);

let datadir = PathBuf::from("/tmp/sensordata");
let service = sensorlog::Sensorlog::new(&datadir, logfile_config)?;

时钟看门狗

默认情况下,sensorlog将存储每次测量的当前、绝对系统时间。当运行在无网络连接的系统上,例如没有访问GPS或互联网的真实时钟参考时,这可能是个问题。

假设我们运行在一个需要由现场操作员配置系统时间的离线系统上。考虑操作员错误地输入了一个远在未来的系统时间,后来发现错误后,将系统时间更改为正确值的情况。在这种情况下,sensorlog之类的服务应该如何处理?

简单的行为可能是简单地打开失败并继续提供现有记录的数据。当然,这意味着会提供错误的数据,用户无法判断其是否错误。这显然不是好的解决方案!

因此,sensorlog实现了时钟看门狗选项,允许您在系统时间以意外方式更改时关闭失败。启用时钟看门狗后,sensorlog将监视系统时钟,并在检测到时间的大幅跳跃时触发看门狗。

看门狗可以在两种模式下运行,称为“恐慌”和“擦除”。在“恐慌”模式下,当看门狗被触发时,sensorlogd将简单地退出并显示错误信息。在“擦除”模式下,触发看门狗将导致所有存储的测量数据被删除。

⚠️ 目前看门狗总是以“擦除”模式运行!!!

设计目标

由于sensorlog是为半嵌入式用例设计的,即在高可靠性环境中运行在受限系统上,主要设计目标是简单性、健壮性和有限资源使用。

当这些目标与其他竞争目标之间存在冲突时,我们必须做出权衡。因此,sensorlog没有特别高的操作吞吐量、低操作延迟或最小资源使用。

注意事项

  • sensorlog需要具有相同sensor_id的连续测量的时间字段单调递增。如果您尝试插入一个比已存储的具有相同sensor_id的测量更旧的测量,则将刷新该传感器的现有数据。

  • 指定的存储配额应用于包括sensorlog元数据在内的总使用存储空间,但不包括文件系统开销。这意味着可用于测量有效负载数据的实际存储量略低于配置配额。此外,一旦考虑了文件系统开销,实际使用的磁盘空间略多于配置配额。

  • 在系统时间快速变化的A-B-A场景中,时钟看门狗可能无法触发。然而,这在实践中并不是一个问题,因为看门狗未触发的唯一情况是在间歇性时钟变化期间没有存储或检索测量。

  • 虽然sensorlog对象应该是线程安全的,但存储不是。不要同时创建多个引用相同数据目录的sensorlog实例。虽然这不应该破坏数据文件,但它可能导致数据丢失。

考虑的替代方案

  • 另一种处理时钟变化的策略是优雅地处理它们,通过重新写入现有数据以纠正时间偏移。然而,由于这种方法在实践中被认为是成本高昂且易出错的,因此没有选择用于sensorlog。

许可证

Copyright © 2018 nyantec GmbH <[email protected]>

Authors:
  Paul Asmuth <[email protected]>
  Karl Engelhardt <[email protected]>

Provided that these terms and disclaimer and all copyright notices
are retained or reproduced in an accompanying document, permission
is granted to deal in this work without restriction, including un‐
limited rights to use, publicly perform, distribute, sell, modify,
merge, give away, or sublicence.

This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
the utmost extent permitted by applicable law, neither express nor
implied; without malicious intent or gross negligence. In no event
may a licensor, author or contributor be held liable for indirect,
direct, other damage, loss, or other issues arising in any way out
of dealing in the work, even if advised of the possibility of such
damage or existence of a defect, except proven that it results out
of said person’s immediate fault when using the work as intended.

依赖项

~11-22MB
~280K SLoC