随着家庭自动化越来越普及,记录统计数据的传感器数量和提供该数据所需的信息也越来越多。通过使用Bluemix 中的 Time Series Database,您可以轻松地记录记录了时间的数据,并查询和报告该数据。在本教程中,我们将介绍如何使用 Time Series Database 来创建、存储并最终报告信息。我们还将使用该数据库跨多个传感器关联数据点,跟踪一个多区域住宅里的供热系统的效率。
我们的应用程序的核心是一个 Node.js 应用程序,它被连接到后端(也就是 Time Series Database)。
首先,在 Bluemix 仪表板中创建一个新的 Node.js 应用程序:
创建您的应用程序并为其命名后,添加一个新的服务,即 Time Series Database。
完成这个步骤后,需要访问该代码。我喜欢下载软件包,然后使用 cf
命令行工具与该流程交互。这为我提供了快速更改,然后使用 cf push
更新正在运行的 Bluemix 实例上的版本的功能。
无论您希望如何与代码交互和处理它,您应用程序的核心都是在应用程序包的顶级目录中创建的 app.js 文件。
在该文件中有不同的部分。顶部的一节包含配置信息和核心集成,用于确定应用程序应如何连接其他组件。以下代码显示了这个标头部分的示例。
1234567891011121314151617181920212223242526272829303132333435363738/*jshint node:true*/ // app.js // This file contains the server-side JavaScript code for your application. // This sample application uses express as the web application framework ( http://expressjs.com/ ), // and jade as the template engine ( http://jade-lang.com/ ). var express = require( 'express' ); // setup middleware var app = express(); var fs = require( 'fs' ); app.use(app.router); app.use(express.errorHandler()); app.use(express. static (__dirname + '/public' )); //setup static public directory app.set( 'view engine' , 'jade' ); app.set( 'views' , __dirname + '/views' ); //optional because express defaults to CWD/views // render index page app.get( '/' , function (req, res){ res.render( 'index' ); }); // There are many useful environment variables available in process.env. // VCAP_APPLICATION contains useful information about a deployed application. var appInfo = JSON.parse(process.env.VCAP_APPLICATION || "{}" ); // TODO : Get application information and use it in your app. // VCAP_SERVICES contains all the credentials of services bound to // this application. For details of its content, please refer to // the document or sample of each service. var services = JSON.parse(process.env.VCAP_SERVICES || "{}" ); // TODO : Get service credentials and communicate with bluemix services. // The IP address of the Cloud Foundry DEA (Droplet Execution Agent) that hosts this application: var host = (process.env.VCAP_APP_HOST || 'localhost' ); // The port on the DEA for communication with the application: var port = (process.env.VCAP_APP_PORT || 3000 );
这里有很多行,其中许多行仅在初始化时使用。最重要的一行将会获取当前服务的信息:
1var services = JSON.parse(process.env.VCAP_SERVICES || "{}" );
此过程抓取的信息包括您应用程序中所有已配置的服务的访问信息。具体地讲,它将包含用于访问您的 Time Series Database 的 URL 和凭据。
另一个重要的代码段将会确定在访问您应用程序中某个特定 URL 时执行的操作:
1234// render index page app.get( '/' , function (req, res){ res.render( 'index' ); });
在本例中,只要 myapp.bluemix.net/(根或索引)被访问,Node.js 应用程序就会直接响应或调用另一个函数。在这里,我们使用了调用 render()
函数的内联 JavaScript 函数调用另一个函数。这是 res (response)
对象上的一个方法,基本上讲,它会读取该文件的内容并返回一个索引文档。
请求传递到该函数,展示对采用这种方法调用所有函数都很有用的一个元素。请求包含 URL 信息和任何可能包含在 URL 内的查询参数。本教程后面将讨论如何处理该信息。
无论对每个元素的响应是什么,一个重要事实是:app.get()
函数定义一个路线来将访问的 URL 映射到一个需要返回的操作、函数或页面。请记住,我们实质上是在一个 JavaScript 应用程序的界限内运行此应用程序,所以我们必须拥有一种在访问的 URL 与响应之间建立连接的方法。app.get()
方法是一个注册 URL 和该 URL 对应用程序的含义的方法。
app.js 文件的最后一部分包含启动应用程序(并开始监听传入请求)的函数,以及一个记录已启动的应用程序的行:
12app.listen(port, host); console.log( 'App started on port ' + port);
再次声明,请记住这是一个 JavaScript 应用程序,所以没有 stdout
或 stderr
需要讲解。您使用 console.log()
方法记录错误和信息。对于 BlueMix 应用程序,此信息可通过仪表板提供,可以使用对 cf 命令行工具的 'cf log appname'
调用来获得。
在开始处理数据之前,需要打开一个与 Time Series Database 的连接。
Bluemix 中的 Time Series Database 可以通过许多不同的接口提供,具体情况取决于应用程序环境。对于 Node.js 应用程序,有两个解决方案:使用 REST API 或使用 Mongo API。如果想要为基础数据提供一个直接转交接口,例如在提取图表和其他信息时,那么 REST 接口可能很有用。
我们使用了 Mongo API,因为它提供了非常简单的方式来访问 Node.js 应用程序中的数据,也那就是说,使用 JSON 执行调用来描述、分割和定义记录和信息。
要创建一个接口,必须知道连接数据库的凭据(URL,包括用户名和密码)。此信息在服务变量内提供,该变量本身已在之前在 Node.js 脚本中自动填充。事实上,要处理多个服务,需要连接到主要服务对象中的 timeseriesdatabase
对象类的第一个 URL,类似以下代码:
1services.timeseriesdatabase[ 0 ].credentials.json_url
要实际访问数据库,可以将整个流程嵌入在一个连接调用中:
123require( 'mongodb' ).connect(services.timeseriesdatabase[ 0 ].credentials.json_url, function (err, conn) { … }
这将使用所提取的凭据,通过 MongoDB API 打开与 Time Series Database 的一个连接,调用一个函数来提供错误和连接对象。尽管这看似有点浪费,但它提供了跟踪任何问题或错误并从中恢复的最佳方法。
需要通过 MongoDB 连接来访问数据。MongoDB 中的数据存储在集合中,集合大体上相当于典型 SQL 数据库中的一个数据表。
存储在 Time Series Database 中的信息是为了引用和链接到某个特定的度量结果和环境而设计的。可以将 Time Series Database 视为一个日记条目,您在其中记录您在何处、度量类型和值。因为它是一个时序数据库,所以您能够不断查看同一个值,确定度量结果趋势是上升还是下降,或者最新的度量结果比一个历史值更高还是更低。
得益于 Time Series Database 的工作方式,必须为每个度量结果提供一些限制和参数,包括:
measure_unit
)。在此应用程序,该单位为摄氏度,但它也可以是 MB、秒或 mm,只要存储的是这类数据。loc_esi_id
)。此信息定义了此条目的标识符。direction
),也就是说,它是一个生产者(即创建者或值)还是一个使用者(它们的使用者)。这是一个字节值:P 或 C。value
)。无法记录一个 'empty'
或 NULL 值。该值实际记录为小数类型,最多精确到小数点后 3 位。tstamp
)。时间戳必须具有格式 2014-01-01 00:00:00.00000。与传统数据库不同,时间戳必须以 15 为单位。所以,在过去的每个小时,可在 00、15、30 或 45 分钟时记录一个值。也可仅为时间戳、方向、度量单位和位置记录一个值。例如,如果记录在特定的一天 16:11:00 来自休息室中的一个温度传感器的值,这是惟一的数据点。但是,同时可记录来自车库的温度,因为这是一个不同的位置。
所有数据都写入一个叫做 ts_data_v
的集合中。因为我们可将不同的位置记录在此表中,所以我们只需一个表即可,因为我们可能仅使用一个不同的位置或度量类型。但是,请记住您不能对同一个位置(比如休息室)记录两个温度(度量类型 C),不过完全可以使用'lounge_front'
和 'lounge_back'
。
所以,可以创建一条像这样的记录:
1234567{ "direction" : "P" , "tstamp" : "2014-09-16 11:17:15.00000" , "measure_unit" : "C" , "value" : 21 , "loc_esi_id" : "lounge" }
要记录该值,需要将一个 URL 解析到一个应用程序,这样才能提取该位置和值。可以通过处理来自路线调用(来自 app.get()
)的请求对象来实现此目的:
1234var parsedUrl = require( 'url' ).parse(req.url, true ); var queryObject = parsedUrl.query; var name = (queryObject[ "name" ] || 'lounge' ); var temppoint = parseFloat((queryObject[ "temp" ] || 0 ));
首先,解析来自请求对象的 URL。这会生成一个 queryObject
,您可以从中提取 URL 值,在本例中为位置和温度点。所以,下面这行:
1http: //mcslptds.mybluemix.net/reading?name=kitchen&temp=23
为我们提供了位置 'kitchen'
和值 '23'
。
接下来,我们需要构造一种合适的时间戳格式。您一定还记得,它必须与数据库需要的格式准确匹配。这意味着您需要在其中填充 0,构造一种合适的字符串与日期/时间组合。
与提取的 URL 值相结合,将得到:
点击查看代码清单
这会创建 create_datapoint()
函数,需要在此应用程序中添加连接到该函数的路线,所以我们还添加了一个适合其他路线的条目:
123app.get( "/reading" , function (req, res) { create_datapoint(req,res); });
现在您已经有了传入数据的方式,您可以通过再次将应用程序部署到 Bluemix 来测试它,然后,使用 curl
记录一个点:
1$ curl 'http://mcslptds.mybluemix.net/reading?name=kitchen&temp=23'
我们已拥有传入数据的方式,接下来看看如何传出数据。
要获取一个条目列表,可以使用 MongoDB 接口连接到数据库,然后,使用collection.find()
函数提取存储在数据库中的值:
点击查看代码清单
首先,返回一个有效的标头 (HTTP 200
)。然后,连接到数据库,使用所提供的名称或默认的'lounge'
在数据库上执行查询。
为了简便起见,我们使用 URL /dumplist 在应用程序中注册此函数:
123app.get( "/dumplist" , function (req, res) { list_datapoint(req,res); });
然后会返回一个值列表。当然,这不是很有用。返回一个输出曲线图是个一种不错的方法,这样我们就可更轻松地了解趋势和值随时间的变化。毕竟这是一个时序数据库。
提供温度数据的输出曲线图实际上包含两步:
要首先完成第二步,我们的方法是创建一个静态 HTML 页面,在调用一个特定 URL 时,我们将会解析该页面并将它显示给用户:
12345var graph_view = function (req, res) { fs.readFile( './public/graph.html' , function (err, data) { res.end(data); }); }
这会从应用程序目录中返回 /public/graph.html 的内容。要显示该页面,我们向此函数添加一个路径 /graph
:
123app.get( "/graph" , function (req, res) { graph_view(req,res); });
该 HTML 本身非常简单。加载 JQuery,然后加载 JQuery FLOT 库来打印图表信息:
1234567891011121314151617181920212223242526<html> <head> <title>Graph</title> <link rel= "stylesheet" type= "text/css" href= "stylesheets/style.css" /> </head> <body> <script src= "jquery.js" ></script> <script src= "jquery.flot.js" ></script> <script src= "jquery.flot.time.js" ></script> <script> $(document).ready( function () { var dataseries = []; $.getJSON( "/graphpoints" , function (json) { $.plot($( "#plot" ) , [json],{ xaxis: { mode: "time" }}); }); }); </script> <div id= "plot" ></div> <div id= "plotdata" ></div> </body> </html>
确保您将此信息添加到正确的位置,使之与之前调用的 parse()
函数相匹配。
关键部分是定义该数据的内联函数。在本例中,我们通过 URL /graphpoints 调用我们自己的应用程序,并通过 Ajax 来加载它:
123$.getJSON( "/graphpoints" , function (json) { $.plot($( "#plot" ) , [json],{ xaxis: { mode: "time" }}); });
该信息需要具有特定的格式,包含一个嵌套数组,其中每个嵌套的数组是一对 X 和 Y 轴。例如:
1[[ 1 , 21 ],[ 2 , 23 ],…]
第一个元素是一个顺序号或日期/时间字符串,可由 Flot 库解析为数据。
要输出此数据,我们需要创建 /dumplist
函数的一个修改版本,以正确的格式生成该信息。
整个函数类似于:
点击查看代码清单
该函数的核心是调用 Mongo API 来访问该集合。该集合按时间戳字段,以降序自动排序。然后,对于每个返回的条目,会创建一个包含日期时间字符串(从时间戳解析而来)的数组:
12timeint = ( new Date(items[i].tstamp).getTime())/ 1000 ; dataseries.push([timeint,items[i].value]);
访问曲线图页面时,将返回 HTML 页面,并且 Ajax 代码会加载 /graphpoints URL,这会从 Time Series Database 返回数据,描绘这些值并为我们提供一个曲线图。
使用 Bluemix 和 Node.js 创建一个新应用程序实际上非常简单。通过使用样板内容和现成的应用程序,您可以非常轻松地创建一个基本应用程序。对于 Node.js 应用程序,关键元素包括类似于 REST 的 API(可创建它来提交和返回信息),以及解析内容以显示传统的 HTML 页面的能力。通过将这些元素与一个后端数据库(比如 Time Series Database)相组合,可以非常轻松地存储和检索信息。使用 Time Series Database,数据的顺序和排序的复杂性已得到处理,您获得的是正确排序的信息。
点击这里,查看原文。