iotagent-node-lib 3.2.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +16 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +55 -0
  4. package/.github/advanced-issue-labeler.yml +30 -0
  5. package/.github/workflows/issue-labeler.yml +43 -0
  6. package/README.md +10 -11
  7. package/doc/README.md +16 -0
  8. package/doc/admin.md +565 -0
  9. package/doc/api.md +32 -85
  10. package/doc/deprecated.md +16 -10
  11. package/doc/{architecture.md → devel/architecture.md} +3 -3
  12. package/doc/{Contribution.md → devel/contribution-guidelines.md} +43 -35
  13. package/doc/devel/development.md +1879 -0
  14. package/doc/{northboundinteractions.md → devel/northboundinteractions.md} +18 -33
  15. package/doc/index.md +3 -5
  16. package/doc/requirements.txt +1 -1
  17. package/docker/Mosquitto/Dockerfile +1 -1
  18. package/docker/Mosquitto/README.md +1 -0
  19. package/lib/commonConfig.js +0 -5
  20. package/lib/fiware-iotagent-lib.js +1 -1
  21. package/lib/jexlTranformsMap.js +2 -1
  22. package/lib/model/Device.js +0 -1
  23. package/lib/model/Group.js +0 -1
  24. package/lib/model/dbConn.js +1 -7
  25. package/lib/plugins/jexlParser.js +1 -1
  26. package/lib/request-shim.js +2 -2
  27. package/lib/services/commands/commandService.js +1 -1
  28. package/lib/services/common/genericMiddleware.js +1 -1
  29. package/lib/services/common/iotManagerService.js +0 -1
  30. package/lib/services/devices/deviceRegistryMemory.js +2 -2
  31. package/lib/services/devices/deviceRegistryMongoDB.js +32 -19
  32. package/lib/services/devices/deviceService.js +44 -43
  33. package/lib/services/devices/devices-NGSI-LD.js +14 -2
  34. package/lib/services/devices/devices-NGSI-mixed.js +0 -2
  35. package/lib/services/devices/devices-NGSI-v2.js +23 -104
  36. package/lib/services/groups/groupService.js +1 -1
  37. package/lib/services/ngsi/entities-NGSI-LD.js +3 -3
  38. package/lib/services/ngsi/entities-NGSI-v2.js +28 -19
  39. package/lib/services/northBound/deviceProvisioningServer.js +14 -8
  40. package/lib/templates/createDevice.json +0 -4
  41. package/lib/templates/createDeviceLax.json +0 -4
  42. package/lib/templates/deviceGroup.json +1 -5
  43. package/lib/templates/updateDevice.json +4 -0
  44. package/lib/templates/updateDeviceLax.json +11 -0
  45. package/mkdocs.yml +6 -11
  46. package/package.json +3 -3
  47. package/scripts/legacy_expression_tool/README.md +280 -0
  48. package/scripts/legacy_expression_tool/legacy_expression_tool.py +423 -0
  49. package/scripts/legacy_expression_tool/requirements.txt +3 -0
  50. package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +0 -1
  51. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +5 -15
  52. package/test/unit/mongodb/mongodb-registry-test.js +1 -1
  53. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +66 -65
  54. package/test/unit/ngsi-ld/general/https-support-test.js +1 -1
  55. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +8 -7
  56. package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +31 -30
  57. package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +12 -11
  58. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +41 -39
  59. package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +122 -122
  60. package/test/unit/ngsi-ld/provisioning/device-registration_test.js +28 -28
  61. package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +18 -17
  62. package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +7 -7
  63. package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +8 -7
  64. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +33 -37
  65. package/test/unit/ngsiv2/examples/contextRequests/updateContext.json +2 -0
  66. package/test/unit/ngsiv2/examples/contextRequests/updateContext1.json +3 -1
  67. package/test/unit/ngsiv2/examples/contextRequests/updateContext3WithStatic.json +2 -0
  68. package/test/unit/ngsiv2/examples/contextRequests/updateContext4.json +4 -1
  69. package/test/unit/ngsiv2/examples/contextRequests/updateContext5.json +12 -0
  70. package/test/unit/ngsiv2/examples/contextRequests/updateContext6.json +12 -0
  71. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin1.json +2 -0
  72. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin2.json +3 -1
  73. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin3.json +3 -1
  74. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin4.json +3 -1
  75. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin5.json +3 -1
  76. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin6.json +3 -1
  77. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin7.json +3 -1
  78. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin8.json +3 -1
  79. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin9.json +3 -1
  80. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast1.json +2 -0
  81. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast2.json +2 -0
  82. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast3.json +3 -1
  83. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast4.json +3 -1
  84. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast5.json +3 -1
  85. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast6.json +3 -1
  86. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast7.json +3 -1
  87. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandError.json +3 -1
  88. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandExpired.json +3 -1
  89. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandFinish.json +3 -1
  90. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus.json +2 -0
  91. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +2 -0
  92. package/test/unit/ngsiv2/examples/contextRequests/updateContextCompressTimestamp1.json +3 -1
  93. package/test/unit/ngsiv2/examples/contextRequests/updateContextCompressTimestamp2.json +3 -1
  94. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin1.json +2 -12
  95. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin11.json +2 -4
  96. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin12.json +2 -4
  97. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin13.json +3 -1
  98. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin2.json +2 -12
  99. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin29.json +2 -12
  100. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin3.json +2 -4
  101. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json +2 -0
  102. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +2 -0
  103. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +2 -0
  104. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +2 -0
  105. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +2 -0
  106. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
  107. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
  108. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin4.json +2 -0
  109. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin40.json +1 -1
  110. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +1 -10
  111. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin5.json +2 -4
  112. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin6.json +2 -4
  113. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin7.json +2 -4
  114. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin8.json +2 -12
  115. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin9.json +2 -4
  116. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionSkip.json +12 -0
  117. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json +1 -1
  118. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin1.json +1 -1
  119. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin10.json +1 -1
  120. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +1 -1
  121. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin12.json +1 -1
  122. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin13.json +1 -1
  123. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin14.json +1 -1
  124. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +1 -1
  125. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +1 -1
  126. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +1 -1
  127. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin2.json +1 -1
  128. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +2 -6
  129. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin3.json +1 -1
  130. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin4.json +1 -1
  131. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +1 -1
  132. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +1 -1
  133. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +1 -1
  134. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +1 -1
  135. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin9.json +1 -1
  136. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +1 -1
  137. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +1 -1
  138. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -1
  139. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +2 -0
  140. package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +2 -0
  141. package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributes.json +2 -0
  142. package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributesMetadata.json +3 -1
  143. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestamp.json +3 -1
  144. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampFalse.json +12 -0
  145. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampFalseTimeInstant.json +12 -0
  146. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverride.json +2 -0
  147. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +2 -0
  148. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampTimezone.json +3 -1
  149. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +144 -85
  150. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +20 -53
  151. package/test/unit/ngsiv2/general/https-support-test.js +2 -6
  152. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +4 -10
  153. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +8 -24
  154. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +146 -65
  155. package/test/unit/ngsiv2/ngsiService/autocast-test.js +14 -21
  156. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +3 -5
  157. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +11 -20
  158. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +20 -30
  159. package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +4 -6
  160. package/test/unit/ngsiv2/plugins/custom-plugin_test.js +1 -2
  161. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +3 -5
  162. package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +2 -3
  163. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +2 -3
  164. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +13 -156
  165. package/test/unit/ngsiv2/provisioning/device-registration_test.js +9 -13
  166. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +4 -10
  167. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +0 -11
  168. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +0 -8
  169. package/test/unit/plugins/capture-provision-inPlugins_test.js +0 -6
  170. package/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +0 -1
  171. package/.nyc_output/processinfo/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +0 -1
  172. package/.nyc_output/processinfo/index.json +0 -1
  173. package/doc/config-basic-example.js +0 -20
  174. package/doc/development.md +0 -285
  175. package/doc/howto.md +0 -645
  176. package/doc/installationguide.md +0 -370
  177. package/doc/operations.md +0 -127
  178. package/doc/usermanual.md +0 -900
  179. package/lib/plugins/bidirectionalData.js +0 -356
  180. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +0 -697
  181. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +0 -599
  182. /package/doc/{NorthboundInteractions.postman_collection → devel/NorthboundInteractions.postman_collection} +0 -0
  183. /package/doc/{echo.js → devel/echo.js} +0 -0
  184. /package/doc/{finalResult.js → devel/finalResult.js} +0 -0
package/doc/howto.md DELETED
@@ -1,645 +0,0 @@
1
- # How to develop a new IOTAgent
2
-
3
- - [Overview](#overview)
4
- - [Protocol](#protocol)
5
- - [Requirements](#requirements)
6
- - [Basic IOTA](#basic-iot-agent)
7
- - [IOTA With Active attributes](#iot-agent-with-active-attributes)
8
- - [IOTA With Lazy attributes](#iota-with-lazy-attributes)
9
- - [Previous considerations](#previous-considerations)
10
- - [Implementation](#implementation)
11
- - [IoT Agent in multi-thread mode](#iot-agent-in-multi-thread-mode)
12
- - [Configuration management](#configuration-management)
13
- - [Provisioning handlers](#provisioning-handlers)
14
-
15
- ## Overview
16
-
17
- This document's goal is to show how to develop a new IoT Agent step by step. To do so, a simple invented HTTP protocol
18
- will be used, so it can be tested with simple command-line instructions as `curl` and `nc`.
19
-
20
- ### Protocol
21
-
22
- The invented protocol will be freely adapted from
23
- [Ultralight 2.0](https://github.com/telefonicaid/fiware-IoTAgent-Cplusplus/blob/develop/doc/modules.md#ultra-light-agent).
24
- Whenever a device wants to send an update, it will send a request as the following:
25
-
26
- ```bash
27
- curl -X GET 'http://127.0.0.1:8080/iot/d?i=ULSensor&k=abc&d=t|15,l|19.6' -i
28
- ```
29
-
30
- Where:
31
-
32
- - **i**: is the device ID.
33
- - **k**: the API Key for the device's service.
34
- - **d**: the data payload, consisting of key-value pairs separated by a pipe (`|`), with each pair separated by comma
35
- (`,`);
36
-
37
- ### Requirements
38
-
39
- This tutorial expects a Node.js v8 (at least) installed and working on your machine. It also expects you to have access
40
- to a Context Broker (without any security proxies).
41
-
42
- ## Basic IoT Agent
43
-
44
- In this first chapter, we will just develop an IoT Agent with a fully connected North Port. This will send and receive
45
- NGSI traffic and can be administered using the IoT Agent's Device Provisioning API. The South Port will remain
46
- unconnected and no native protocol traffic will be sent to the devices. This may seem useless (and indeed it is) but it
47
- will serve us well on showing the basic steps in the creation of an IoT Agent.
48
-
49
- First of all, we have to create the Node project. Create a folder to hold your project and type the following
50
- instruction:
51
-
52
- ```bash
53
- npm init
54
- ```
55
-
56
- This will create the `package.json` file for our project. Now, add the following lines to your project file:
57
-
58
- ```json
59
- "dependencies": {
60
- "iotagent-node-lib": "*"
61
- },
62
-
63
- ```
64
-
65
- And install the dependencies, executing, as usual:
66
-
67
- ```bash
68
- npm install
69
- ```
70
-
71
- The first step is to write a configuration file, that will be used to tune the behavior of our IOTA. The contents can be
72
- copied from the `config-basic-example.js` file, in this same folder. Create a `config.js` file with it in the root
73
- folder of your project. Remember to change the Context Broker IP to your local Context Broker.
74
-
75
- Now we can begin with the code of our IoT Agent. The very minimum code we need to start an IoT Agent is the following:
76
-
77
- ```javascript
78
- var iotAgentLib = require("iotagent-node-lib"),
79
- config = require("./config");
80
-
81
- iotAgentLib.activate(config, function (error) {
82
- if (error) {
83
- console.log("There was an error activating the IOTA");
84
- process.exit(1);
85
- }
86
- });
87
- ```
88
-
89
- The IoT Agent is now ready to be used. Execute it with the following command:
90
-
91
- ```bash
92
- node index.js
93
- ```
94
-
95
- The North Port interface should now be fully functional, i.e.: management of device registrations and configurations.
96
-
97
- ## IoT Agent With Active attributes
98
-
99
- In the previous section we created an IoT Agent that exposed just the North Port interface, but that was pretty useless
100
- (aside from its didactic use). In this section we are going to create a simple South Port interface. It's important to
101
- remark that the nature of the traffic South of the IoT Agent itself has nothing to do with the creation process of an
102
- IoT Agent. Each device protocol will use its own mechanisms and it is up to the IoT Agent developer to find any
103
- libraries that would help him in its development. In this example, we will use Express as such library.
104
-
105
- In order to add the Express dependency to your project, add the following line to the `dependencies` section of the
106
- `package.json`:
107
-
108
- ```json
109
- "express": "*",
110
- ```
111
-
112
- The require section would end up like this (the standard `http` module is also needed):
113
-
114
- ```javascript
115
- var iotAgentLib = require("iotagent-node-lib"),
116
- http = require("http"),
117
- express = require("express"),
118
- config = require("./config");
119
- ```
120
-
121
- And install the dependencies as usual with `npm install`. You will have to require both `express` and `http` in your
122
- code as well.
123
-
124
- Now, in order to accept connections in our code, we have to start express first. With this purpose in mind, we will
125
- create a new function `initSouthbound()`, that will be called from the initialization code of our IoT Agent:
126
-
127
- ```javascript
128
- function initSouthbound(callback) {
129
- southboundServer = {
130
- server: null,
131
- app: express(),
132
- router: express.Router(),
133
- };
134
-
135
- southboundServer.app.set("port", 8080);
136
- southboundServer.app.set("host", "0.0.0.0");
137
-
138
- southboundServer.router.get("/iot/d", manageULRequest);
139
- southboundServer.server = http.createServer(southboundServer.app);
140
- southboundServer.app.use("/", southboundServer.router);
141
- southboundServer.server.listen(southboundServer.app.get("port"), southboundServer.app.get("host"), callback);
142
- }
143
- ```
144
-
145
- This Express code sets up a HTTP server, listening in the 8080 port, that will handle incoming requests targeting path
146
- `/iot/d` using the middleware `manageULRequest()`. This middleware will contain all the logic south of the IoT Agent,
147
- and the library methods we need in order to progress the information to the Context Broker. The code of this middleware
148
- would be as follows:
149
-
150
- ```javascript
151
- function manageULRequest(req, res, next) {
152
- var values;
153
-
154
- iotAgentLib.retrieveDevice(req.query.i, req.query.k, function (error, device) {
155
- if (error) {
156
- res.status(404).send({
157
- message: "Couldn't find the device: " + JSON.stringify(error),
158
- });
159
- } else {
160
- values = parseUl(req.query.d, device);
161
- iotAgentLib.update(device.name, device.type, "", values, device, function (error) {
162
- if (error) {
163
- res.status(500).send({
164
- message: "Error updating the device",
165
- });
166
- } else {
167
- res.status(200).send({
168
- message: "Device successfully updated",
169
- });
170
- }
171
- });
172
- }
173
- });
174
- }
175
- ```
176
-
177
- For this middleware we have made use of a function `parseUl()` that parses the data payload and transforms it in the
178
- data object expected by the update function (i.e.: an attribute array with NGSI syntax):
179
-
180
- ```javascript
181
- function parseUl(data, device) {
182
- function findType(name) {
183
- for (var i = 0; i < device.active.length; i++) {
184
- if (device.active[i].name === name) {
185
- return device.active[i].type;
186
- }
187
- }
188
-
189
- return null;
190
- }
191
-
192
- function createAttribute(element) {
193
- var pair = element.split("|"),
194
- attribute = {
195
- name: pair[0],
196
- value: pair[1],
197
- type: findType(pair[0]),
198
- };
199
-
200
- return attribute;
201
- }
202
-
203
- return data.split(",").map(createAttribute);
204
- }
205
- ```
206
-
207
- Here as an example of the output of the function return for the UL payload `t|15,l|19.6`:
208
-
209
- ```json
210
- [
211
- {
212
- "name": "t",
213
- "type": "celsius",
214
- "value": "15"
215
- },
216
- {
217
- "name": "l",
218
- "type": "meters",
219
- "value": "19.6"
220
- }
221
- ]
222
- ```
223
-
224
- The last thing to do is to invoke the initialization function inside the IoT Agent startup function. The next excerpt
225
- show the modifications in the `activate()` function:
226
-
227
- ```javascript
228
- iotAgentLib.activate(config, function (error) {
229
- if (error) {
230
- console.log("There was an error activating the IOTA");
231
- process.exit(1);
232
- } else {
233
- initSouthbound(function (error) {
234
- if (error) {
235
- console.log("Could not initialize South bound API due to the following error: %s", error);
236
- } else {
237
- console.log("Both APIs started successfully");
238
- }
239
- });
240
- }
241
- });
242
- ```
243
-
244
- Some logs were added in this piece of code to help debugging.
245
-
246
- Once the IOTA is finished the last thing to do is to test it. To do so, launch the IoT Agent and provision a new device
247
- (an example for provisioning can be found in the `examples/howtoProvisioning1.json` file). Once the device is
248
- provisioned, send a new measure by using the example command:
249
-
250
- ```bash
251
- curl -X GET 'http://127.0.0.1:8080/iot/d?i=ULSensor&k=abc&d=t|15,l|19.6' -i
252
- ```
253
-
254
- Now you should be able to see the measures in the Context Broker entity of the device.
255
-
256
- ## IOTA With Lazy attributes
257
-
258
- ### Previous considerations
259
-
260
- The IoT Agents also give the possibility for the device to be asked about the value of one of its measures, instead of
261
- reporting it. In order to do so, the device must be capable of receiving messages of some kind. In this case, we are
262
- going to simulate an HTTP server with `nc` in order to see the values sent by the IOTA. We also have to decide a syntax
263
- for the protocol request for asking the device about a measure. For clarity, we will use the same HTTP GET request we
264
- used to report a measure, but indicating the attribute to ask instead of the data payload. Something like:
265
-
266
- ```bash
267
- curl -X GET 'http://127.0.0.1:9999/iot/d?i=ULSensor&k=abc&q=t,l' -i
268
- ```
269
-
270
- In a real implementation, the server will need to know the URL and port where the devices are listening, in order to
271
- send the request to the appropriate device. For this example, we will assume that the device is listening in port 9999
272
- in localhost. For more complex cases, the mechanism to bind devices to addresses would be IoT-Agent-specific (e.g.: the
273
- OMA Lightweight M2M IoT Agent captures the address of the device in the device registration, and stores the
274
- device-specific information in a MongoDB document).
275
-
276
- Being lazy attributes of a read/write nature, another syntax has to be declared for updating. This syntax will mimic the
277
- one used for updating the server:
278
-
279
- ```
280
- curl -X GET 'http://127.0.0.1:9999/iot/d?i=ULSensor&k=abc&d=t|15,l|19.6' -i
281
- ```
282
-
283
- Both types of calls to the device will be distinguished by the presence or absence of the `d` and `q` attributes.
284
-
285
- A HTTP request library will be needed in order to make those calls. To this extent, `mikeal/request` library will be
286
- used. In order to do so, add the following require statement to the initialization code:
287
-
288
- ```javascript
289
- request = require("request");
290
- ```
291
-
292
- and add the `request` dependency to the `package.json` file:
293
-
294
- ```json
295
- "dependencies": [
296
- [...]
297
-
298
- "request": "*",
299
-
300
- ]
301
- ```
302
-
303
- The require section should now look like this:
304
-
305
- ```javascript
306
- var iotAgentLib = require("iotagent-node-lib"),
307
- http = require("http"),
308
- express = require("express"),
309
- request = require("request"),
310
- config = require("./config");
311
- ```
312
-
313
- ### Implementation
314
-
315
- #### QueryContext implementation
316
-
317
- The main step to complete in order to implement the Lazy attributes mechanism in the IoT Agent is to provide handlers
318
- for the context provisioning requests. At this point, we should provide two handlers: the `/v2/op/update` and the
319
- `/v2/op/query` handlers. To do so, we must first define the handlers themselves:
320
-
321
- ```javascript
322
- function queryContextHandler(id, type, service, subservice, attributes, callback) {
323
- var options = {
324
- url: "http://127.0.0.1:9999/iot/d",
325
- method: "GET",
326
- qs: {
327
- q: attributes.join(),
328
- },
329
- };
330
-
331
- request(options, function (error, response, body) {
332
- if (error) {
333
- callback(error);
334
- } else {
335
- callback(null, createResponse(id, type, attributes, body));
336
- }
337
- });
338
- }
339
- ```
340
-
341
- The queryContext handler is called whenever a `/v2/op/query` request arrives at the North port of the IoT Agent. It is
342
- invoked once for each entity requested, passing the entity ID and Type as the parameters, as well as a list of the
343
- attributes that are requested. In our case, the handler uses this parameters to compose a request to the device. Once
344
- the results of the device are returned, the values are returned to the caller, in the NGSI attribute format.
345
-
346
- In order to format the response from the device in a readable way, we created a `createResponse()` function that maps
347
- the values to its correspondent attributes. This function assumes the type of all the attributes is "string" (this will
348
- not be the case in a real scenario, where the IoT Agent should retrieve the associated device to guess the type of its
349
- attributes). Here is the code for the `createResponse()` function:
350
-
351
- ```javascript
352
- function createResponse(id, type, attributes, body) {
353
- var values = body.split(","),
354
- responses = [];
355
-
356
- for (var i = 0; i < attributes.length; i++) {
357
- responses.push({
358
- name: attributes[i],
359
- type: "string",
360
- value: values[i],
361
- });
362
- }
363
-
364
- return {
365
- id: id,
366
- type: type,
367
- attributes: responses,
368
- };
369
- }
370
- ```
371
-
372
- #### UpdateContext implementation
373
-
374
- ```javascript
375
- function updateContextHandler(id, type, service, subservice, attributes, callback) {
376
- var options = {
377
- url: "http://127.0.0.1:9999/iot/d",
378
- method: "GET",
379
- qs: {
380
- d: createQueryFromAttributes(attributes),
381
- },
382
- };
383
-
384
- request(options, function (error, response, body) {
385
- if (error) {
386
- callback(error);
387
- } else {
388
- callback(null, {
389
- id: id,
390
- type: type,
391
- attributes: attributes,
392
- });
393
- }
394
- });
395
- }
396
- ```
397
-
398
- The updateContext handler deals with the modification requests that arrive at the North Port of the IoT Agent via `/v2/op/update`. It is
399
- invoked once for each entity requested (note that a single request can contain multiple entity updates), with the same
400
- parameters used in the queryContext handler. The only difference is the value of the attributes array, now containing a
401
- list of attribute objects, each containing name, type and value. The handler must also make use of the callback to
402
- return a list of updated attributes.
403
-
404
- For this handler we have used a helper function called `createQueryFromAttributes()`, that transforms the NGSI
405
- representation of the attributes to the UL type expected by the device:
406
-
407
- ```javascript
408
- function createQueryFromAttributes(attributes) {
409
- var query = "";
410
-
411
- for (var i in attributes) {
412
- query += attributes[i].name + "|" + attributes[i].value;
413
-
414
- if (i != attributes.length - 1) {
415
- query += ",";
416
- }
417
- }
418
-
419
- return query;
420
- }
421
- ```
422
-
423
- #### Handler registration
424
-
425
- Once both handlers have been defined, they have to be registered in the IoT Agent, adding the following code to the
426
- setup function:
427
-
428
- ```javascript
429
- iotAgentLib.setDataUpdateHandler(updateContextHandler);
430
- iotAgentLib.setDataQueryHandler(queryContextHandler);
431
- ```
432
-
433
- Where necessary, additional handlers to deal with command actuations and merge-patch operations may also be added when
434
- necessary.
435
-
436
- ```javascript
437
- iotAgentLib.setCommandHandler(commandHandler);
438
- iotAgentLib.setMergePatchHandler(mergePatchHandler);
439
- ```
440
-
441
- #### IOTA Testing
442
-
443
- In order to test it, we need to create an HTTP server simulating the device. The quickest way to do that may be using
444
- netcat. In order to start it just run the following command from the command-line (Linux and Mac only):
445
-
446
- ```bash
447
- nc -l 9999
448
- ```
449
-
450
- This will open a simple TCP server listening on port `9999`, where the requests from the IoT Agent will be printed. In
451
- order for the complete workflow to work (and to receive the response in the application side), the HTTP response has to
452
- be written in the `nc` console (although for testing purposes this is not needed).
453
-
454
- While netcat is great to test simple connectivity, you will need something just a bit more complex to get the complete
455
- scenario working (at least without the need to be incredibly fast sending your response). In order to do so, a simple
456
- echo server was created, that answers 42 to any query to its `/iot/d` path. You can use it to test your attributes one
457
- by one (or you can modify it to accept more requests and give more complex responses). Copy the
458
- [Echo Server script](echo.js) to the same folder of your IoTAgent (as it uses the same dependencies). In order to run
459
- the echo server, just execute the following command:
460
-
461
- ```bash
462
- node echo.js
463
- ```
464
-
465
- Once the mock server has been started (either `nc` or the `echo` server), proceed with the following steps to test your
466
- implementation:
467
-
468
- 1. Provision a device with two lazy attributes. The following request can be used as an example:
469
-
470
- ```text
471
- POST /iot/devices HTTP/1.1
472
- Host: localhost:4041
473
- Content-Type: application/json
474
- fiware-service: howtoserv
475
- fiware-servicepath: /test
476
- Cache-Control: no-cache
477
- Postman-Token: 993ac66b-72da-9e96-ab46-779677a5896a
478
-
479
- {
480
- "devices": [
481
- {
482
- "device_id": "ULSensor",
483
- "entity_name": "Sensor01",
484
- "entity_type": "BasicULSensor",
485
- "lazy": [
486
- {
487
- "name": "t",
488
- "type": "celsius"
489
- },
490
- {
491
- "name": "l",
492
- "type": "meters"
493
- }
494
- ],
495
- "attributes": [
496
- ]
497
- }
498
- ]
499
- }
500
- ```
501
-
502
- 2. Execute a `/v2/op/query` or `/v2/op/update` against one of the entity attributes (use a NGSI client of curl command).
503
-
504
- ```text
505
- POST /v2/op/query HTTP/1.1
506
- Host: localhost:1026
507
- Content-Type: application/json
508
- Accept: application/json
509
- Fiware-Service: howtoserv
510
- Fiware-ServicePath: /test
511
- Cache-Control: no-cache
512
-
513
- {
514
- entities: [
515
- {
516
- id: 'Light:light1'
517
- }
518
- ],
519
- attrs: ['dimming']
520
- }
521
- ```
522
-
523
- 3. Check the received request in the nc console is the expected one.
524
-
525
- 4. (In case you use netcat). Answer the request with an appropriate HTTP response and check the result of the
526
- `/v2/op/query` or `/v2/op/update` request is the expected one. An example of HTTP response, for a query to the `t`
527
- and `l` attributes would be:
528
-
529
- ```text
530
- HTTP/1.0 200 OK
531
- Content-Type: text/plain
532
- Content-Length: 3
533
-
534
- 5,6
535
- ```
536
-
537
- This same response can be used both for updates and queries for testing purposes (even though in the former the body
538
- won't be read).
539
-
540
- ## IoT Agent in multi-thread mode
541
-
542
- It is possible that an IoT Agent can be executed in multi-thread approach, which will increase the number of
543
- request/seconds that can be manage by the server. It's important to remark that the nature of this functionality in
544
- included in the IoT Agent Node Lib but it is not mandatory that you activate this functionality. In this example, we
545
- will see how to use this functionality to deploy an IoT Agent in multi-thread environment.
546
-
547
- **WARNING:** it has been observed in Orion-IOTA integration tests some fails in bidirectional plugin usage scenarios in
548
- multi-thread mode. The fail has not been confirmed yet (it could be a glitch of the testing environment). However, take
549
- this into account if you use multi-thread in combination with bidirectional plugin.
550
-
551
- In order to activate the functionality, you have two options, configure the `config.js` file to add the following line:
552
-
553
- ```javascript
554
- /**
555
- * flag indicating whether the node server will be executed in multi-core option (true) or it will be a
556
- * single-thread one (false).
557
- */
558
- config.multiCore = true;
559
- ```
560
-
561
- or you can define the proper IOTA_MULTI_CORE environment variable. By default, the first choice is the environment
562
- variable and afterward the value of the multiCore in the `config.js` file. The require section would end up like this
563
- (the standard `http` module is also needed):
564
-
565
- ```javascript
566
- var iotAgent = require("../lib/iotagent-implementation"),
567
- iotAgentLib = require("iotagent-node-lib"),
568
- config = require("./config");
569
- ```
570
-
571
- It is important to mention the purpose of the `iotAgent` variable. It is the proper implementation of the IoT Agent
572
- based on the IoT Agent Node Lib. We will need this variable just to make a callback to the corresponding `start()`
573
- process from the library. The variable `config` is used to get details of the configuration file and send that
574
- information to the Node Lib. The Node Lib will take the decision of single-thread or multi-thread execution base on the
575
- value of `config.multiCore` attribute.
576
-
577
- Finally, we can call the corresponding [iotagentLib.startServer()](usermanual.md#iotagentlibstartserver) like the
578
- following code with a callback function to show details about any error during the execution or just print the message
579
- about starting the IoTAgent:
580
-
581
- ```javascript
582
- iotAgentLib.startServer(config, iotAgent, function (error) {
583
- if (error) {
584
- console.log(context, "Error starting IoT Agent: [%s] Exiting process", error);
585
- } else {
586
- console.log(context, "IoT Agent started");
587
- }
588
- });
589
- ```
590
-
591
- > Note: `startServer()` initializes the server but it does not activate the library. The function in the Node Lib will
592
- > call the `iotAgent.start()` in order to complete the activation of the library. Therefore, it is expected that the IoT
593
- > Agent implement the `iotAgent.start()` function with the proper invocation to the `iotAgentLib.activate()`.
594
-
595
- ## Configuration management
596
-
597
- For some IoT Agents, it will be useful to know what devices or configurations were registered in the Agent, or to do
598
- some actions whenever a new device is registered. All this configuration and provisioning actions can be performed using
599
- two mechanisms: the provisioning handlers and the provisioning API.
600
-
601
- ### Provisioning handlers
602
-
603
- The handlers provide a way for the IoT Agent to act whenever a new device, or configuration is provisioned. This can be
604
- used for registering the device in external services, for storing important information about the device, or to listen
605
- in new ports in the case of new configuration. For the simple example we are developing, we will just print the
606
- information we are receiving whenever a new device or configuration is provisioned.
607
-
608
- We need to complete two further steps to have a working set of provisioning handlers. First of all, defining the
609
- handlers themselves. Here we can see the definition of the configuration handler:
610
-
611
- ```javascript
612
- function configurationHandler(configuration, callback) {
613
- console.log("\n\n* REGISTERING A NEW CONFIGURATION:\n%s\n\n", JSON.stringify(configuration, null, 4));
614
- callback(null, configuration);
615
- }
616
- ```
617
-
618
- As we can see, the handlers receive the device or configuration that is being provisioned, as well as a callback. The
619
- handler MUST call the callback once in order for the IOTA to work properly. If an error is passed as a parameter to the
620
- callback, the provisioning will be aborted. If no error is passed, the provisioning process will continue. This
621
- mechanism can be used to implement security mechanisms or to filter the provisioning of devices to the IoT Agent.
622
-
623
- Note also that the same `device` or `configuration` object is passed along to the callback. This lets the IoT Agent
624
- change some of the values provisioned by the user, to add or restrict information in the provisioning. To test this
625
- feature, let's use the provisioning handler to change the value of the type of the provisioning device to
626
- `CertifiedType` (reflecting some validation process performed on the provisioning):
627
-
628
- ```javascript
629
- function provisioningHandler(device, callback) {
630
- console.log("\n\n* REGISTERING A NEW DEVICE:\n%s\n\n", JSON.stringify(device, null, 4));
631
- device.type = "CertifiedType";
632
- callback(null, device);
633
- }
634
- ```
635
-
636
- Once the handlers are defined, the new set of handlers has to be registered into the IoT Agent:
637
-
638
- ```javascript
639
- iotAgentLib.setConfigurationHandler(configurationHandler);
640
- iotAgentLib.setProvisioningHandler(provisioningHandler);
641
- ```
642
-
643
- Now we can test our implementation by sending provisioning requests to the North Port of the IoT Agent. If we provision
644
- a new device into the platform, and then we ask for the list of provisioned devices, we shall see the type of the
645
- provisioned device has changed to `CertifiedType`.