【问题标题】:Google IOT per device heartbeat alert using Stackdriver使用 Stackdriver 的 Google IOT 每个设备的心跳警报
【发布时间】:2018-03-16 21:28:43
【问题描述】:

我想提醒大量 Google IOT 核心设备中的任何一个都没有心跳(或接收到 0 个字节)。我似乎无法在 Stackdriver 中执行此操作。相反,它似乎让我在整个设备注册表上发出警报,这并没有给我我正在寻找的东西(我怎么知道特定设备已断开连接?)

那么如何做到这一点呢?

【问题讨论】:

    标签: google-cloud-platform stackdriver google-cloud-iot


    【解决方案1】:

    我不知道为什么这个问题被认为“过于宽泛”。

    事实上,Google IOT 没有针对每个设备的警报,而是仅在整个设备注册表上提供警报。如果这不是真的,请回复此帖子。明确说明这一点的页面is here

    Cloud IoT Core 导出可监控的使用指标 以编程方式或通过 Stackdriver Monitoring 访问。这些指标 在设备注册表级别聚合。您可以使用 Stackdriver 创建仪表板或设置警报。

    this statement 中假设的承诺中内置了每个设备警报的重要性:

    有关设备运行状况和功能的操作信息是 确保您的数据收集结构健康且 表现良好。设备可能位于恶劣的环境中或 难以访问的位置。监控您的运营情报 IoT 设备是保留业务相关数据流的关键。

    因此,如果全球分散的众多设备中的一个失去连接,今天要获得警报并不容易。一个人需要构建它,并且根据一个人想要做什么,它需要不同的解决方案。

    就我而言,我想提醒上次心跳时间或上次事件状态发布是否超过 5 分钟。为此,我需要运行一个循环函数来扫描设备注册表并定期执行此操作。此 API 的用法在另一个 SO 帖子中进行了概述:Google iot core connection status

    【讨论】:

    • 很棒的帖子和答案。我只是花了 1 个小时试图弄清楚这一点,似乎他们有一个可以在特定设备上发出警报的设备资源,但是唯一可用的指标是 log_bytes,这没有多大意义,因为设备没有发送日志去谷歌。也许他们正在改进这一点?
    • 是的,我希望这正是我必须做的,这与我的旧实现一样荒谬,我只是听了 MQTT 断开连接并触发了一个事件......对于谷歌来说并不容易......希望我和另一个物联网服务提供商一起去了:(
    【解决方案2】:

    作为参考,这是我刚刚编写的一个 Firebase 函数,用于检查设备的在线状态,可能需要一些调整和进一步测试,但可以帮助其他人开始做一些事情:

    // Example code to call this function
    // const checkDeviceOnline = functions.httpsCallable('checkDeviceOnline');
    // Include 'current' key for 'current' online status to force update on db with delta
    // const isOnline = await checkDeviceOnline({ deviceID: 'XXXX', current: true })
    export const checkDeviceOnline = functions.https.onCall(async (data, context) => {
    
        if (!context.auth) {
            throw new functions.https.HttpsError('failed-precondition', 'You must be logged in to call this function!');
        }
    
        // deviceID is passed in deviceID object key
        const deviceID = data.deviceID
    
        const dbUpdate = (isOnline) => {
            if (('wasOnline' in data) && data.wasOnline !== isOnline) {
                db.collection("devices").doc(deviceID).update({ online: isOnline })
            }
    
            return isOnline
        }
    
        const deviceLastSeen = () => {
            // We only want to use these to determine "latest seen timestamp"
            const stamps = ["lastHeartbeatTime", "lastEventTime", "lastStateTime", "lastConfigAckTime", "deviceAckTime"]
            return stamps.map(key => moment(data[key], "YYYY-MM-DDTHH:mm:ssZ").unix()).filter(epoch => !isNaN(epoch) && epoch > 0).sort().reverse().shift()
        }
    
        await dm.setAuth()
    
        const iotDevice: any = await dm.getDevice(deviceID)
    
        if (!iotDevice) {
            throw new functions.https.HttpsError('failed-get-device', 'Failed to get device!');
        }
    
        console.log('iotDevice', iotDevice)
    
        // If there is no error status and there is last heartbeat time, assume device is online
        if (!iotDevice.lastErrorStatus && iotDevice.lastHeartbeatTime) {
            return dbUpdate(true)
        }
    
        // Add iotDevice.config.deviceAckTime to root of object
        // For some reason in all my tests, I NEVER receive anything on lastConfigAckTime, so this is my workaround
        if (iotDevice.config && iotDevice.config.deviceAckTime) iotDevice.deviceAckTime = iotDevice.config.deviceAckTime
    
        // If there is a last error status, let's make sure it's not a stale (old) one
        const lastSeenEpoch = deviceLastSeen()
        const errorEpoch = iotDevice.lastErrorTime ? moment(iotDevice.lastErrorTime, "YYYY-MM-DDTHH:mm:ssZ").unix() : false
    
        console.log('lastSeen:', lastSeenEpoch, 'errorEpoch:', errorEpoch)
    
        // Device should be online, the error timestamp is older than latest timestamp for heartbeat, state, etc
        if (lastSeenEpoch && errorEpoch && (lastSeenEpoch > errorEpoch)) {
            return dbUpdate(true)
        }
    
        // error status code 4 matches
        // lastErrorStatus.code = 4
        // lastErrorStatus.message = mqtt: SERVER: The connection was closed because MQTT keep-alive check failed.
        // will also be 4 for other mqtt errors like command not sent (qos 1 not acknowledged, etc)
        if (iotDevice.lastErrorStatus && iotDevice.lastErrorStatus.code && iotDevice.lastErrorStatus.code === 4) {
            return dbUpdate(false)
        }
    
        return dbUpdate(false)
    })
    

    我还创建了一个与命令一起使用的函数,向设备发送命令以检查它是否在线:

    export const isDeviceOnline = functions.https.onCall(async (data, context) => {
    
        if (!context.auth) {
            throw new functions.https.HttpsError('failed-precondition', 'You must be logged in to call this function!');
        }
    
        // deviceID is passed in deviceID object key
        const deviceID = data.deviceID
    
        await dm.setAuth()
    
        const dbUpdate = (isOnline) => {
            if (('wasOnline' in data) && data.wasOnline !== isOnline) {
                console.log( 'updating db', deviceID, isOnline )
                db.collection("devices").doc(deviceID).update({ online: isOnline })
            } else {
                console.log('NOT updating db', deviceID, isOnline)
            }
    
            return isOnline
        }
    
        try {
            await dm.sendCommand(deviceID, 'alive?', 'alive')
            console.log('Assuming device is online after succesful alive? command')
            return dbUpdate(true)
        } catch (error) {
            console.log("Unable to send alive? command", error)
            return dbUpdate(false)
        }
    })
    

    这也使用了我修改过的DeviceManager 的版本,你可以在这个要点上找到所有的示例代码(以确保使用最新的更新,并在此处保持小规模): https://gist.github.com/tripflex/3eff9c425f8b0c037c40f5744e46c319

    所有这些代码,只是为了检查设备是否在线......这可以通过 Google 发出某种事件或添加一种简单的方法来轻松处理。 来谷歌吧!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-12
      • 2019-10-22
      • 1970-01-01
      • 2018-02-03
      • 1970-01-01
      相关资源
      最近更新 更多