如何查询 IoT 设备处于活动状态的总时间?

How do I query the total time an IoT device is active?

我是 SQL 和 AWS Timestream 的新手,我想编写一个查询来提供设备处于活动状态的总时间。然后我想根据设备的千瓦时额定值将其转化为能源使用量。

数据点的时间间隔不是固定的。数据看起来像这样:

timestamp (s) active (boolean)
1617697080 (10h18) false
1617697920 (10h32) true
1617698280 (10h38) false (active for 6 minutes)
1617699000 (10h50) true
1617699120 (10h52) false (active for 2 minutes)
etc.

以上总活动时间为8分钟。

我想提出的问题是,

什么查询会给我这个信息并容忍可变间隔?

我正在寻找两条路径,但还没有完全弄清楚,

  1. 对数据进行插值并填充值以获得具有一致间隔的新时间流(然后就像计算值一样简单),或者
  2. 使用一些 date/time 函数来查看数据点之间的时间戳,并将其处于活动状态的总时间相加。

我一直在尝试获取插入数据的查询权限,但尚未成功。我遵循 AWS Timestream SQL 文档中的模式,但还不太了解它。

我什至不知道从哪里开始或到哪里寻找求和时间戳差异的示例。逻辑过程是这样的,

if (this_point == true) then
    total_active_time += next_point_timestamp - this_point_timestamp

可能的解决方案 #1:插值

插值在某些范围内效果很好,尽管对于解决方案来说感觉有点矫枉过正。 on/off 设备的分辨率是 10s,所以如果插值采样时间太大,数据就会开始倾斜。当内插时间序列超过 10k 点时,这会出现问题 - AWS Timestream 会抛出错误“序列函数的结果不得超过 10000 个条目”。所以在短时间内,这是有效的,但我想计算一个月的能源使用情况,那么它永远不会成功(至少在 10 秒的分辨率下不会)。使用 1m 的分辨率可以让我在 6 天的时间里工作。可以在不同的日期范围内多次使用此查询来获取数据。

WITH active_timeseries AS (
SELECT time, COUNT_IF(measure_value::boolean) AS Active
FROM "my_db"."data"
WHERE measure_name = 'active'
  AND time > ago(6d)
GROUP BY time
), interpolated_timeseries AS (
SELECT INTERPOLATE_LOCF(
  CREATE_TIME_SERIES(time, Active),
      SEQUENCE(min(time), max(time), 1m)) AS interpolated_active
FROM active_timeseries
), new_timeseries AS (
SELECT time, value
FROM interpolated_timeseries
CROSS JOIN UNNEST(interpolated_active)
)
-- where "1" = 1kWh
SELECT bin(time, 1d) as binned_ts, COUNT_IF(value > 0) / 60.0 * 1 as Daily
FROM new_timeseries
GROUP BY bin(time, 1d)
ORDER BY binned_ts

可能的解决方案#2:计算时差

在应用程序中,开启和关闭时间可能有多个“开启”和多个“关闭”样本。可以使用 LAG 函数确定从开到关和关到开的转换样本。起初我无法让 LAG 和 LEAD 时间函数在时间戳上工作,但它突然似乎在没有我改变某些东西的情况下工作......不知道该怎么做。然后可以使用 LEAD 来确定时间间隔。将它们放在一起看起来像这样:

with active_timeseries AS (
  SELECT time, 
    measure_value::boolean as active, 
    LAG(measure_value::boolean, 1, NULL) OVER (ORDER BY time ASC) AS last_active
  FROM "my_db"."data"
  where measure_name = 'active'
    AND time > ago(6d)
  ORDER by time ASC
)
SELECT time, active, last_active,
  (LEAD(time, 1, NULL) OVER (ORDER BY time ASC) - time) AS time_interval
FROM active_timeseries
where (active = true AND last_active = false) OR
      (active = false AND last_active = true)

这会为您提供一个间隔,数据看起来像这样(但时间戳为本机“时间戳”类型)

timestamp (s) active (boolean) time interval
1617697080 (10h18) false 840
1617697920 (10h32) true 360
1617698280 (10h38) false (active for 6 minutes) 720
1617699000 (10h50) true 120
1617699120 (10h52) false (active for 2 minutes) etc.
etc.

太棒了!几乎就是我要找的东西....但现在我无法将时间间隔(“时间戳”类型)转换为可用的东西。我需要对其进行一些基本操作以将其计算为 kWh 使用量,例如

kWh = number_of_seconds_active(s) / seconds_in_an_hour(s) * power(kW)

经过一些调整和测试后,我发现 SQL EXTRACT() 可以让我提取天数、小时数、分钟数和秒数。所以我可以这样做:

with active_timeseries AS (
  SELECT time, 
    measure_value::boolean as active, 
    LAG(measure_value::boolean, 1, NULL) OVER (ORDER BY time ASC) AS last_active
  FROM "my_db"."data"
  where measure_name = 'active'
    AND time > ago(6d)
  ORDER by time ASC
), interval_timeseries AS (
SELECT time, active, last_active,
  (LEAD(time, 1, NULL) OVER (ORDER BY time ASC) - time) AS time_interval
FROM active_timeseries
where (active = true AND last_active = false) OR
      (active = false AND last_active = true)
)
SELECT time, active, last_active, time_interval,
  EXTRACT(hour from time_interval) * 3600 + EXTRACT(minute from time_interval) * 60 + EXTRACT(second from time_interval) as interval_time,
  (EXTRACT(hour from time_interval) * 3600 + EXTRACT(minute from time_interval) * 60 + EXTRACT(second from time_interval)) / 3600.0 * 3.0 as kWh
from interval_timeseries
where active = true

这给了我能源使用量!

您可以使用 LEAD 函数计算到下一个样本的时间差。这为您提供了一个可以转换为能源使用的时间间隔。按您想要的分辨率对数据进行分类,并在 active 为真时简单地累加所有能源使用量。

此示例获取 3kW 设备在过去 30 天内的每日能源使用情况。

with active_timeseries AS (
  SELECT time,
    measure_value::boolean as active,
    (LEAD(time, 1, NULL) OVER (ORDER BY time ASC) - time) AS time_interval
  FROM "my_db"."data"
  where measure_name = 'active'
    AND time > ago(30d)
    ORDER by time ASC
)
SELECT bin(time, 1d) as binned_ts,
  SUM((EXTRACT(hour from time_interval) * 3600 + EXTRACT(minute from time_interval) * 60 + EXTRACT(second from time_interval)) / 3600.0 * 3.0) as kWh
from active_timeseries
where active = true
GROUP BY bin(time, 1d)
ORDER BY binned_ts

您可能会产生窗口效应,具体取决于样本的具体下降方式和 bin 大小,例如如果活动时间从一天开始到另一天结束,并且只有开始和结束的样本,那么整个时间间隔的使用量将在第一天汇总。