大家好,我是 Ivan Fardin,我是罗马 Sapienza 大学计算机科学工程硕士的学生,这个项目是作为物联网 19-20课程的一部分而开发的。
在本文中,我将向您展示如何使用用户设备的加速度传感器根据人群感应技术收集的值来设置一个基于 IoT MQTT 的基于云的人类活动识别 Web 应用程序系统。
该项目包括:
假设运动最多为 0.5 Hz(即每分钟 30 步),理论上 1 Hz(即每秒 1 条消息)的采样频率足以识别用户是否静止不动。
所有代码都可以在GitHub 存储库中找到。
让我们通过分析图中的每个组件开始详细介绍。
用户可以通过以下网站上的浏览器访问Web 应用程序,并且该应用程序独立于操作系统,因此可以将手机、平板电脑、台式机和笔记本电脑等不同设备连接到后端。它利用通用传感器 API从移动设备的加速传感器收集数据。通用传感器 API 是一种传感器框架,可在安全上下文(即 HTTPS)中将传感器设备暴露给 Web 平台。
传感器值或由此产生的人类活动通过MQTT (一种轻量级且被广泛采用的针对特定主题的受限设备设计的消息传递协议)使用与网站访问相关联的唯一 ID(身份)传输到云基础设施。
MQTT 是使用Eclipse Paho JavaScript Client实现的,这是一个用Javascript编写的基于浏览器的 MQTT 客户端库,它使用 WebSockets(一种通信协议,通过单个 TCP/IP 连接提供全双工通信通道)连接到 MQTT 代理。
为什么使用 WebSocket 上的 MQTT ?由于该应用程序是 Web 应用程序,因此它存在于浏览器中,并且基于 WebSocket 的 MQTT 允许将 MQTT 数据直接发送和接收到 Web 浏览器中。
通过 Paho 发送的消息取决于用户选择的模式来识别活动,它们是:
后端是使用AWS IoT实现的,它在 Internet 连接的设备和 AWS 云之间提供安全的双向通信。设备与 AWS 云之间的通信由AWS IoT 消息代理根据发布-订阅模式进行处理。
AWS IoT 消息代理为设备和 AWS IoT 应用程序提供了一种安全机制,可以通过将消息从发布客户端发送到订阅客户端来相互发布和接收连接 AWS IoT 客户端的消息。客户端通过在主题上发布消息来发送数据,并通过订阅主题来接收消息。当消息代理接收到来自发布客户端的消息时,它会将消息转发给所有订阅该主题的客户端。
此外,AWS IoT 提供了创建规则的可能性,这些规则定义了一个或多个要根据 MQTT 消息主题执行的操作。通过这种方式,我通过创建一个规则来实现云计算,在该规则中,代理将来自 Web 应用程序设备的所有传入消息转发到指定的AWS Lambda函数。这会详细说明传入的数据并将它们插入到代表架构持久层的AWS DynamoDB表中。
最后,Web 应用程序连接到 AWS DynamoDB 服务,以根据所选模式检索和显示设备数据和最后一小时的结果活动,并连接到代理发送数据,如果启用了云计算, 从 Lambda 函数接收实时数据。
首先,如果您没有 AWS 账户,您需要创建一个。作为一名学生,我有一个AWS 教育,它免费提供对云资源的有限访问。
如果您像我一样拥有 AWS 教育账户,请确保所选区域是 us-east-1(北弗吉尼亚),这是此类账户唯一可用的区域,否则后端将无法工作。
注册或登录后,请转到下一部分。
后端实施的一个良好起点可能是创建AWS DynamoDB表。
因此,在 AWS 控制台中找到 DynamoDB 服务并单击它。然后点击创建表并填写如下表格,然后按创建。
为了与 web 应用部分中的代码保持一致,我将使用Id和dateTime但当然您可以将任何属性作为分区和排序键,以及决定不使用排序键。
创建表后,您可以滚动概览选项卡获取关联的 ARN
通过单击项目选项卡,您可以查看表中存在哪些元素。
由于加速度传感器的采样频率为 1 Hz,并且值会立即传输以进行实时识别,因此表格的大小将随着使用而大幅增加。这可能是一个性能问题,但要记住 Web 应用程序的功能,我只需要持久性来显示最后一小时的数据,因此我可以在这段时间间隔后删除它们以提高性能。为此,DynamoDB 提供了生存时间(TTL) 功能,该功能允许定义表中的项目何时过期,以便可以自动从数据库中删除它们。
因此,在项目选项卡中单击操作和管理 TTL。
在这里放置您决定用作 TTL 的属性,然后单击Continue
没有什么比这更简单了,您的数据库已准备就绪。
既然你的数据库已经准备好了,让我们看看如何填充它。请记住,当 MQTT 消息到达AWS IoT Broker时,它会检查主题是否与您选择调用 Lambda 函数的主题匹配。AWS Lambda是一项计算服务,让您无需预置或管理服务器即可运行代码,并且仅在需要时执行您的代码并自动扩展。
创建 Lambda 函数搜索并在 AWS 控制台中选择 Lambda 服务。
在其主页中单击创建功能
然后为它选择一个名称,作者从头开始选项,因为我的实现非常简单,以及编程语言(在我的例子中我选择了Python 3.7 )。最后点击创建函数
好的,现在您必须在编辑器中输入以下代码:
import json
import boto3
import time
dynamodb = boto3.resource('dynamodb', region_name='')
table = dynamodb.Table('')
def lambda_handler(event, context):
# Data expires after 1 hour (3600 s) and few minutes (400 s)
# Time in Unix epoch
ttl = int(time.time()) + 4000
# Check if edge computation
if "isStanding" in event:
table.put_item(
Item={
"Id": event["clientID"],
"dateTime": event["dateTime"],
"isStanding": event["isStanding"],
"computation": "edge",
"TTL": ttl
}
);
# Else cloud computation
else:
isStanding = True
x2 = event["x2"]
y2 = event["y2"]
z2 = event["z2"]
if((abs(x2 - event["x1"])*0.67 > 0.292) or
(abs(y2 - event["y1"])*0.7 > 0.145) or
(abs(z2 - event["z1"])*0.67 > 0.45)):
isStanding = False
clientID = event["clientID"]
table.put_item(
Item={
"Id": clientID,
"dateTime": event["dateTime"],
"x": str(x2), # DynamoDB does not support float type
"y": str(y2),
"z": str(z2),
"isStanding": isStanding,
"computation": "cloud",
"TTL": ttl
}
);
client = boto3.client('iot-data', region_name='')
# Change topic, qos and payload
response = client.publish(
topic = "" + clientID,
qos = 1,
payload = json.dumps({
"x": x2,
"y": y2,
"z": z2,
"isStanding": isStanding
})
)
现在,您需要创建一个AWS Cognito Identity Poll,授予用户访问 DynamoDB 服务和使用 AWS IoT 的 MQTT 操作的权限。
在 AWS 控制台中搜索并选择 Cognito 服务。
在其主页中,按Create Identity Poll按钮并填写如下表格,然后单击Create Pool ,然后单击Allow 。
因此,单击示例代码选项卡以查看您的池 ID。
现在移动到IAM 服务页面并单击角色
在这里搜索刚刚创建的新的未经授权的 Cognito 角色并单击它。
然后按附加策略按钮
并搜索DynamoDBFullAccess ,选择它并按下附加策略按钮。
您的Cognito 身份投票现已准备好用于访问您的 DynamoDB。
要完成,您必须创建并附加 IoT 策略,以执行 MQTT 消息的连接、订阅、发布等操作。
要创建策略,请再次单击附加策略按钮,然后单击创建策略( IAM步骤 3 和 4 的图片可能很有用)。
在这里移动JSON选项卡
并在编辑器中粘贴以下 JSON,将区域、帐户 ID 和主题字段替换为您的
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:::client/${iot:ClientId}"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": [
"arn:aws:iot:::topic/"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:::topicfilter/"
]
}
]
}
然后,按Review policy并插入策略名称和描述,然后单击Create policy 。
最终重复 IAM 步骤 3 和 4 以将此新策略附加到未经授权的角色。
大功告成,您的 Cognito Identity Poll 已准备好用于访问您的 DynamoDB 和 IoT Core。
您几乎已经完成了后端的设置,只是错过了使用AWS IoT Core创建事物对象的过程。
在 AWS 控制台中找到 IoT Core 服务并单击它。
在这里,您必须将您的设备连接到平台,因此请转到Onboard并单击Onboard a device中的Get started 。
然后选择您连接到 AWS IoT 的方式,在本例中,我使用Linux平台和Java编程语言,然后按 N ext 。
输入你的东西的名字,然后继续
然后下载证书和私钥和公钥,然后进行下一步。
您将在此处显示配置和测试设备的教程,因此请按完成。你的东西已经创建了:)
之后,记下唯一标识您的事物的Amazon 资源名称(ARN) 中的区域和账户 ID 。
ARN 具有以下一般格式:
因此,从 IoT Core 初始页面转到Things ,选择新创建的并单击它。
单击设置并记下您的自定义端点,该端点允许您通过 REST API 连接到 AWS IoT(稍后将非常有用)。
现在您需要编辑在创建事物时生成的策略,以允许您的客户端通过 MQTT 连接并与代理通信。
因此,从 IoT Core 初始页面转到Secure ,然后转到Policies ,然后单击与您的事物关联的策略
在此处单击Edit policydocument 并重用 IAM 控制台中使用的 JSON 将区域和账户 ID 字段替换为您之前使用的字段
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:::client/${iot:ClientId}"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": [
"arn:aws:iot:::topic/"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:::topicfilter/"
]
}
]
}
在哪里:
有关 IoT 策略的更多信息,请参阅相关文档。
现在,您必须向代理添加一条规则,以在传入消息发布到指定主题时调用之前定义的 Lambda 函数。记下主题,稍后您将使用它。
为此,请转到Act并按Create a rule按钮。然后使用规则名称和描述以及将过滤有关主题的传入消息的 SQL 查询填写表格
滚动页面并按添加操作按钮为规则设置操作
并选择向 Lambda 函数发送消息。
然后点击页面底部的Configure action ,选择之前创建的函数
最后,单击添加操作,然后单击创建规则以得出结论:您的后端终于准备好了 :)
一旦您设置了基于云的后端,就该进行具有双重作用的 Web 应用程序开发:生成和显示数据。如果选择边缘计算,它还必须在本地处理数据。
要实现这一点,您需要一些HTML5和Javascript的基础知识(也可以选择CSS )。
让我们从index.html文件开始,该文件包含 Javascript 可以使用的元素。
首先,您必须导入可选的 CSS 文件styles.css以及必要的SDK和脚本,包括AWS和Paho MQTT的那些。
html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixeden-stroke-7-icon@1.2.3/pe-icon-7-stroke/dist/pe-icon-7-stroke.min.css">
<link rel="stylesheet" href="css/styles.css">
<script src="js/main.js">script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.1">script>
<script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8">script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@0.7.7">script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.7.16.min.js">script>
<script src="js/aws.js">script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript">script>
<script src="js/pahoMQTTClient.js">script>
head>
然后,在我的实现的主体部分,我使用了两个按钮允许用户在Cloud 和 Edge Computing之间进行选择,另一个用于检索最后一小时的数据
id="btns" class="buttonsAnimation"> id="cloudBtn" class="btn btn-primary btn-lg btn-custom">Cloud Computing id="edgeBtn" class="btn btn-primary btn-lg btn-custom">Edge Computing
id="historyBtn" class="hide"> class="btn btn-secondary btn-lg btn-custom">Show last hour values
在云计算的情况下显示人类活动识别结果和加速度计值的段落
id="measure" class="measure hide">
和四张卡片,以显示以下形式的最后一小时值和活动(当然具有不同的 ID)
"historyChart" class="chart hide"> <div class="card"> <div class="card-body"> <div class="stat-widget-five"> <div class="stat-icon dib green-color"> <i class="pe-7s-graph2">i> div> <div class="stat-content"> <div class="dib"> <div class="stat-text">History Chartdiv> div> div> div> <canvas id="historyCanvas">canvas> div> div> </div> ... html>
我省略了一些不是成功完成任务的基本元素,但如果您有兴趣,可以在我的GitHub 存储库中找到这些元素。
好吧,现在是时候讨论由三个 Javascript 文件组成的 Web 应用程序的逻辑了:
第一个包含两个函数,用于在具有 AWS IoT Core 的 Web 应用程序中通过 WebSocket 协议使用 MQTT,以便使用AWS Signature Version 4指定凭证。
第二个是一个 Javascript 类,它使用 WebSockets 执行与服务器的连接。此外,它还提供订阅或取消订阅 MQTT 主题、发布和接收 MQTT 消息以及与服务器断开连接的功能。实现非常简单,有关 Paho MQTT 的更多信息可以在官方页面上阅读。
类构造函数在输入中有两个参数:
class PahoMQTTClient {
constructor(requestUrl, clientId) {
this.requestUrl = requestUrl;
this.clientId = clientId;
this.client = null;
this.isConnected = false;
}
...
}
为了将客户端连接到服务器,我编写了以下函数,它接收两个回调作为参数,当连接成功或失败时以及订阅主题后收到 MQTT 消息时将调用它们。连接是使用SSL和可用于 AWS IoT 的更新版本的 MQTT 执行的。请注意,在连接选项中还指定了订阅后传入消息的回调,我添加了接收回调。
// Connect to the server
conn(callbackConnection, callbackReceive) {
this.client = new Paho.MQTT.Client(this.requestUrl, this.clientId);
var connectOptions = {
onSuccess: () => {
// Connect succeeded
console.log("onConnect: connect succeeded");
// In an arrow function "this" represents the owner of the function
// while in a regular function "this" represents the object that calls the function
this.isConnected = true;
callbackConnection();
},
useSSL: true,
timeout: 3,
mqttVersion: 4,
onFailure: function() {
// Connect failed
console.log("onFailure: connect failed");
callbackConnection();
}
};
// Set callback handlers
this.client.onConnectionLost = onConnectionLost;
this.client.onMessageArrived = onMessageArrived;
// Connect the client
this.client.connect(connectOptions);
// Called when the client loses its connection
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0)
console.log("onConnectionLost:" + responseObject.errorMessage);
}
// Called when a message arrives
function onMessageArrived(message) {
console.log("onMessageArrived:" + message.payloadString);
callbackReceive(message.payloadString);
}
}
订阅和取消订阅是很琐碎的函数
// Subscribe to a topic
sub(topic) {
console.log("Subscribing on topic " + topic);
this.client.subscribe(topic);
}
// Unsubscribe to a topic
unsub(topic) {
console.log("Unsubscribing on topic " + topic);
this.client.unsubscribe(topic);
}
以及发布的
// Publish a message on a topic
pub(message, topic) {
console.log("Publishing message on topic " + topic);
// QOS = 0 => Best effort, retained = false => Message delivered only to current subscriptions
this.client.send(topic, message, 0, false);
}
我们错过与 AWS 后端连接的最后一步是提供凭证。我们可以直接使用IAM的
var host = "", region = "";
var credentials = new AWS.Credentials("", "", "");
var requestUrl = SigV4Utils.getSignedUrl(host, region, credentials);
或认知
// Initialize Amazon Cognito credentials provider
AWS.config.region = '';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: '',
});
// Obtain credentials
AWS.config.credentials.get(function(){
// Credentials will be available when this function is called
var host = "";
var requestUrl = SigV4Utils.getSignedUrl(host, AWS.config.region, AWS.config.credentials);
...
});
选择哪一个?Cognito 当然是因为我不想公开我的凭据。
因此在main.js文件中,当获取凭证时,使用上述 Javascript 文件的功能执行 WebSocket 上的 MQTT 连接
var uuid = createUUID();
var mqttClient = null;
// Initialize Amazon Cognito credentials provider
AWS.config.region = '';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: '',
});
// Obtain credentials
AWS.config.credentials.get(function(){
// Credentials will be available when this function is called
var host = "";
var requestUrl = SigV4Utils.getSignedUrl(host, AWS.config.region, AWS.config.credentials);
mqttClient = new PahoMQTTClient(requestUrl, uuid);
mqttClient.conn(callbackConnection, callbackReceive);
});
这两个回调函数非常简单,但由于操作是异步的,所以是必要的。第一个通知全局环境连接尝试已结束,第二个用于在启用云计算时接收 MQTT 数据实时显示给用户。
// Callback for the MQTT client connection
function callbackConnection() {
fired = true;
// Check if the callback is invoked after that a button is pressed (slow connection)
if(cloudBtnActivated || edgeBtnActivated)
main();
}
// Callback for the MQTT client when a message is received after subscription
function callbackReceive(msg) {
if(!cloudBtnActivated) return;
msg = JSON.parse(msg);
setMeasureText('x: ' + msg.x + '
y: ' + msg.y + '
z: ' + msg.z + "
" + (msg.isStanding ? "You're standing" : "You're moving"));
}
其中cloudBtnActivated和edgeBtnActivated是两个全局标志,分别表示是否激活了云计算按钮或边缘计算按钮。
Web 应用程序的一个关键软件组件由通用传感器 API构成,它允许在上下文安全 (HTTPS) 时从用户移动设备的加速度计传感器收集数据。
用法很简单,但是要读取传感器我们需要用户的权限,所以首先检查它是否被授予
navigator.permissions.query({ name: "accelerometer" }).then(result => {
if (result.state != 'granted') {
setMeasureText("Sorry, we're not allowed to access sensors on your device");
return;
}
start();
}).catch(err => {
setMeasureText("Integration with Permissions API is not enabled");
});
如果成功,调用 start 函数,该函数将创建读取数据的 Javascript 对象
var accelerometer = null;
var topic = "" + uuid;
function start() {
if(accelerometer != null) {
accelerometer.start();
return;
}
try {
// For cloud computing
mqttClient.sub("" + uuid);
// Read once per second
accelerometer = new Accelerometer({ frequency: 1 });
accelerometer.addEventListener('error', errorListener);
accelerometer.addEventListener('reading', readListener);
accelerometer.start();
} catch (error) {
// Handle construction errors
setMeasureText(error.message);
}
}
如架构部分所述,我以 1 Hz 的频率采样并立即订阅该主题以接收云计算结果消息。请注意,try代码块在应用程序的整个生命周期中执行一次,以避免多次创建。
错误监听器非常简单;它只是在应显示 HAR 结果的区域显示错误消息
function errorListener(event) {
// Handle runtime errors
setMeasureText(event.error.message);
}
相反,读取侦听器更有趣,因为除了在两种模式下发送 MQTT 消息之外,它还包含传感器值的处理和 HAR 结果的后续显示(在边缘计算的情况下)。
function readListener(event) {
var now = { x:event.target.x, y:event.target.y, z:event.target.z };
values.push(now);
// Cloud-based Deployment
if(cloudBtnActivated && values.length > 1) {
mqttClient.pub(createJsonString(values), topic);
values.shift();
}
// Edge-based Deployment
else if(edgeBtnActivated && values.length > 1) {
var check = isStanding(now);
if(check)
setMeasureText('x: ' + event.target.x + '
y: ' + event.target.y + '
z: ' + event.target.z + "
You're standing");
else
setMeasureText('x: ' + event.target.x + '
y: ' + event.target.y + '
z: ' + event.target.z + "
You're moving");
mqttClient.pub(createJsonString(check), topic);
}
}
为了停止读取,我编写了一个简单的函数,它不会消除传感器对象(请记住 start 函数),但会清理专用于显示 HAR 结果的区域。
function stop() {
if(accelerometer != null)
accelerometer.stop();
setMeasureText("");
}
好吧,让我们看看HAR 模型中最期待的部分代码。实际上,您在编写 Lambda 函数时已经看到了它,但我没有说任何关于它的内容。它是如何工作的?它对两个连续的措施进行简单检查,以检测用户是否在移动。如果移动超过指定阈值,则结果是移动,否则不是。阈值和归一化因子是根据经验计算的,以尽量减少噪声,结果非常好。
// Check if the user is standing (do side effect on values array removing the old element)
function isStanding(now) {
var before = values.shift();
// One decides for all
if((Math.abs(now.x - before.x)*0.67 > 0.292)
|| (Math.abs(now.y - before.y)*0.7 > 0.145)
|| (Math.abs(now.z - before.z)*0.67 > 0.45))
return false;
return true;
}
最后但同样重要的是,要查看最后一小时的活动(如果启用了云计算,还有传感器值),您可以单击显示最后一小时的值,该值将对数据库执行查询以检索数据,然后通过可缩放和可平移的图表正确显示它们。这些是使用Chart.js库实现的,Hammer.js用于手势识别,chartjs-plugin-zoom用于缩放和平移。
与 DynamoDB的连接是通过如下修改之前的凭证块来执行的
var uuid = createUUID();
var mqttClient = null;
// Initialize Amazon Cognito credentials provider
AWS.config.region = '';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: '',
});
// Obtain credentials
AWS.config.credentials.get(function(){
// Credentials will be available when this function is called
var host = "";
var requestUrl = SigV4Utils.getSignedUrl(host, AWS.config.region, AWS.config.credentials);
mqttClient = new PahoMQTTClient(requestUrl, uuid);
mqttClient.conn(callbackConnection, callbackReceive);
});
var docClient = new AWS.DynamoDB.DocumentClient();
由于 DynamoDB 是NoSQL数据库,因此您必须使用以下语法
var params = {
TableName : "",
ProjectionExpression: ", ..., ",
KeyConditionExpression: "",
FilterExpression: "",
ExpressionAttributeNames:{
"": "",
...,
"": ""
},
ExpressionAttributeValues: {
"": "",
...,
"": ""
}
};
在哪里:
有关更多详细信息,请参阅AWS DynamoDB 文档。
因此,为了满足该特征,查询如下
var params = {
TableName : "",
ProjectionExpression: cloudBtnActivated ? "Id, #dt, x, y, z, isStanding" : "Id, #dt, isStanding",
KeyConditionExpression: "Id = :clientID and #dt between :start_h and :end_h",
FilterExpression: "computation = :computation",
ExpressionAttributeNames:{
"#dt": "dateTime"
},
ExpressionAttributeValues: {
":clientID": uuid,
":start_h": dateTime[1],
":end_h": dateTime[0],
":computation": cloudBtnActivated ? "cloud" : "edge"
}
};
docClient.query(params, function(err, data) {
if (err) {
// Error
} else {
// Success, do stuff
}
});
其中cloudBtnActivated是一个全局标志,用于表示云计算按钮是否被激活。将功能分开就足够了,因为它们是互斥的(即您不能同时使用两者)。
警告:我使用Id作为分区键,而作为排序键dateTime ,它们必须与您在 DynamoDB 表中定义的一致。因此,如果您使用不同的密钥,请在代码中将我的密钥替换为您的密钥。
如果成功,您必须迭代响应对象以检索数据并显示它们
var sensorValues = [];
var dateTimes = [];
// Additional arrays for cloud computing
var xValues = [];
var yValues = [];
var zValues = [];
// Check if cloud computing
if(cloudBtnActivated)
data.Items.forEach(function(data) {
sensorValues.push(data.isStanding ? 0 : 1);
// DynamoDB does not support float type so, in the table, the value is stored as string
xValues.push(parseFloat(data.x));
yValues.push(parseFloat(data.y));
zValues.push(parseFloat(data.z));
dateTimes.push(data.dateTime);
});
else
data.Items.forEach(function(data) {
sensorValues.push(data.isStanding ? 0 : 1);
dateTimes.push(data.dateTime);
});
// Continue scanning if we have more data (per scan 1MB limitation)
if (typeof data.LastEvaluatedKey != "undefined") {
params.ExclusiveStartKey = data.LastEvaluatedKey;
docClient.scan(params, onScan);
}
if (sensorValues.length == 0)
setTimeout(function() {
alert("No data sent in the past hour");
}, 100);
else {
if(cloudBtnActivated) {
drawLineChart(dateTimes, sensorValues, "historyCanvas", "Activity Cloud Computing");
drawLineChart(dateTimes, xValues, "historyXCanvas", "x Cloud Computing");
drawLineChart(dateTimes, yValues, "historyYCanvas", "y Cloud Computing");
drawLineChart(dateTimes, zValues, "historyZCanvas", "z Cloud Computing");
}
else
drawLineChart(dateTimes, sensorValues, "historyCanvas", "Activity Edge Computing");
}
有关非重点代码的更多详细信息,请参阅我的GitHub 存储库。
就是这样,享受它,如果您欣赏我的工作,请通过喜欢或评论告诉我。谢谢!
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !