iotagent-node-lib 4.6.0 → 4.8.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.
- package/.github/workflows/ci.yml +2 -3
- package/CHANGES_NEXT_RELEASE +1 -0
- package/Changelog +20 -0
- package/config.js +3 -1
- package/doc/README.md +1 -0
- package/doc/admin.md +39 -5
- package/doc/api.md +74 -9
- package/doc/devel/northboundinteractions.md +122 -15
- package/doc/models/models.md +260 -0
- package/doc/requirements.txt +1 -1
- package/docker/Mosquitto/Dockerfile +1 -1
- package/lib/commonConfig.js +21 -2
- package/lib/fiware-iotagent-lib.js +15 -11
- package/lib/jexlTranformsMap.js +182 -35
- package/lib/model/Command.js +11 -2
- package/lib/model/Device.js +8 -3
- package/lib/model/Group.js +5 -3
- package/lib/model/dbConn.js +53 -112
- package/lib/services/commands/commandRegistryMongoDB.js +130 -76
- package/lib/services/commands/commandService.js +3 -3
- package/lib/services/common/iotManagerService.js +3 -1
- package/lib/services/devices/deviceRegistryMemory.js +36 -0
- package/lib/services/devices/deviceRegistryMongoDB.js +161 -88
- package/lib/services/devices/deviceService.js +44 -5
- package/lib/services/devices/devices-NGSI-v2.js +6 -1
- package/lib/services/groups/groupRegistryMongoDB.js +120 -83
- package/lib/services/ngsi/entities-NGSI-v2.js +14 -1
- package/lib/services/ngsi/ngsiService.js +32 -1
- package/lib/services/northBound/contextServer-NGSI-v2.js +12 -3
- package/lib/services/northBound/contextServer.js +2 -1
- package/lib/services/northBound/deviceProvisioningServer.js +12 -3
- package/lib/services/northBound/northboundServer.js +1 -0
- package/lib/templates/createDevice.json +4 -0
- package/lib/templates/updateDevice.json +16 -0
- package/lib/templates/updateDeviceLax.json +12 -0
- package/package.json +4 -4
- package/test/functional/config-test.js +3 -2
- package/test/functional/testUtils.js +15 -4
- package/test/unit/examples/groupProvisioningRequests/provisionFullGroup.json +1 -0
- package/test/unit/expressions/jexlExpression-test.js +165 -1
- package/test/unit/general/config-multi-core-test.js +1 -2
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +5 -4
- package/test/unit/general/deviceService-test.js +6 -5
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +6 -5
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +7 -39
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1 -2
- package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +1 -2
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +8 -5
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +42 -41
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +11 -10
- package/test/unit/ngsiv2/general/deviceService-test.js +6 -5
- package/test/unit/ngsiv2/general/https-support-test.js +1 -1
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +4 -3
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +6 -5
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +17 -16
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +10 -18
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +21 -20
- package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +8 -7
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +12 -11
- package/test/unit/ngsiv2/plugins/custom-plugin_test.js +3 -2
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +28 -27
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +6 -4
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -11
- package/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +11 -10
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +5 -4
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +6 -5
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -1
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +5 -4
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +8 -7
- package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +0 -326
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# MongoDB Datamodel
|
|
2
|
+
|
|
3
|
+
This file displays IoT Agent datamodel stored in the MongoDB database.
|
|
4
|
+
|
|
5
|
+
## Collections
|
|
6
|
+
|
|
7
|
+
### Devices
|
|
8
|
+
|
|
9
|
+
The collection 'devices' sotres information about the iotagent devices
|
|
10
|
+
|
|
11
|
+
Fields:
|
|
12
|
+
|
|
13
|
+
- **\_ID** _ObjectId_: unique object ID used by mongoDB
|
|
14
|
+
- **id** _string_: id of device
|
|
15
|
+
- **type** _string_: entity type used
|
|
16
|
+
- **name** _string_: entity name used
|
|
17
|
+
- **lazy** _array_: array of lazy attributes of device
|
|
18
|
+
- **active** _array_: array of active attributes of device
|
|
19
|
+
- **commands** _array_: array of commands of device
|
|
20
|
+
- **apikey** _string_: apikey of device
|
|
21
|
+
- **endpoint** _string_: endpoint used by push commands when http
|
|
22
|
+
- **resource** _string_: iotagent resource
|
|
23
|
+
- **protocol** _string_: device protocol (JSON)
|
|
24
|
+
- **transport** _string_: device transport (http, mqtt, amqp)
|
|
25
|
+
- **staticAttributes** _array_: array of static attributes of device
|
|
26
|
+
- **subscriptions** _array_: subscriptions of device
|
|
27
|
+
- **service** _string_: service which the device belongs to
|
|
28
|
+
- **subservice** _string_: subservice which the rule belongs to.
|
|
29
|
+
- **polling** _boolean_: if device uses polling for commands
|
|
30
|
+
- **timezone** _string_: timezone of device
|
|
31
|
+
- **timestamp** _boolean_ timestamp of device
|
|
32
|
+
- **registrationId** _string_: registrationId of device
|
|
33
|
+
- **internalId** _string_: internalId of device
|
|
34
|
+
- **creationDate** _date_: creationDate of device
|
|
35
|
+
- **internalAttributes** _object_: internalAttributes of device
|
|
36
|
+
- **autoprovision** _boolean_: if device support autoprovision
|
|
37
|
+
- **explicitAttrs** _enum_:
|
|
38
|
+
- **ngsiVersion** _string_: ngsi version used by device
|
|
39
|
+
- **payloadType** _string_: payloadType used by device
|
|
40
|
+
- **useCBflowControl** _boolean_: if CBFlow will be used by device when updates in CB
|
|
41
|
+
- **storeLastMeasure** _boolean_: if device store last measure received
|
|
42
|
+
- **lastMeasure** _object_: last measure received by device
|
|
43
|
+
- **oldCtxt** _object_: jexl Ctxt used at last measure
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"_id": {
|
|
50
|
+
"$oid": "680b4b338d0e60f98718a8b2"
|
|
51
|
+
},
|
|
52
|
+
"lazy": [],
|
|
53
|
+
"commands": [
|
|
54
|
+
{
|
|
55
|
+
"name": "reset",
|
|
56
|
+
"type": "command",
|
|
57
|
+
"value": "",
|
|
58
|
+
"expression": "{ set: brand + '_' + reset }",
|
|
59
|
+
"object_id": "reset"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "cmd1",
|
|
63
|
+
"type": "command",
|
|
64
|
+
"value": "",
|
|
65
|
+
"expression": "brand",
|
|
66
|
+
"object_id": "cmd1"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "cmd2",
|
|
70
|
+
"type": "command",
|
|
71
|
+
"value": "",
|
|
72
|
+
"expression": "reset",
|
|
73
|
+
"object_id": "cmd2"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"staticAttributes": [],
|
|
77
|
+
"creationDate": {
|
|
78
|
+
"$date": {
|
|
79
|
+
"$numberLong": "1745570611491"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"id": "disp",
|
|
83
|
+
"type": "thing",
|
|
84
|
+
"name": "thing:disp",
|
|
85
|
+
"service": "smartcity",
|
|
86
|
+
"subservice": "/",
|
|
87
|
+
"registrationId": "680b4b33956cc1ed0205840a",
|
|
88
|
+
"apikey": "APIKEY",
|
|
89
|
+
"protocol": "IoTA-JSON",
|
|
90
|
+
"transport": "HTTP",
|
|
91
|
+
"polling": false,
|
|
92
|
+
"active": [],
|
|
93
|
+
"oldCtxt": {
|
|
94
|
+
"level": "33",
|
|
95
|
+
"brand": "o1",
|
|
96
|
+
"id": "disp",
|
|
97
|
+
"type": "thing",
|
|
98
|
+
"service": "smartcity",
|
|
99
|
+
"subservice": "/",
|
|
100
|
+
"entity_name": "thing:disp",
|
|
101
|
+
"TimeInstant": "2025-04-25T08:43:31.496Z"
|
|
102
|
+
},
|
|
103
|
+
"subscriptions": []
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Groups
|
|
108
|
+
|
|
109
|
+
The collection groups' stores information about the iotagent groups of devices
|
|
110
|
+
|
|
111
|
+
Fields:
|
|
112
|
+
|
|
113
|
+
- **\_ID** _ObjectId_: unique object ID used by mongoDB
|
|
114
|
+
- **url** _string_: url used by group of devices
|
|
115
|
+
- **resource** _string_: iotagent resource
|
|
116
|
+
- **apikey** _string_: apikey used by group of devices
|
|
117
|
+
- **endpoint** _string_: endpoint used by push commands when http
|
|
118
|
+
- **transport** _string_: group transport (http, mqtt, amqp)
|
|
119
|
+
- **type** _string_: entity type used
|
|
120
|
+
- **service** _string_: service which the group of device belongs to
|
|
121
|
+
- **subservice** _string_: subservice which the group of devices belongs to
|
|
122
|
+
- **description** _string_: description of group of devices
|
|
123
|
+
- **trust** _string_: keystone trust id used when devices of this group request to CB
|
|
124
|
+
- **cbHost** _string_: CB endpoint used by devices of this group
|
|
125
|
+
- **timezone** _string_: timezone used by group of devices
|
|
126
|
+
- **timestamp** _boolean_: timestamp of group
|
|
127
|
+
- **commands** _array_: array of commands of device group
|
|
128
|
+
- **staticAttributes** _array_: array of static attributes of device group
|
|
129
|
+
- **lazy** _array_: array of lazy attributes of device group
|
|
130
|
+
- **attributes** _array_: array of active attributes of device group
|
|
131
|
+
- **internalAttributes** _array_: array of internal attributes used by devices of group
|
|
132
|
+
- **autoprovision** _boolean_: if devices of group supports autoprovision
|
|
133
|
+
- **explicitAttrs** _enum_: explicit attributes configuration used by devices of group
|
|
134
|
+
- **defaultEntityNameConjunction** _string_:
|
|
135
|
+
- **ngsiVersion** _string_: ngsi version used by devices of group
|
|
136
|
+
- **entityNameExp** _string_: entity name expression used by devics of group
|
|
137
|
+
- **payloadType** _string_: payloadType used by devices of group
|
|
138
|
+
- **useCBflowControl** _boolean_: payloadType used by device group
|
|
139
|
+
- **storeLastMeasure** _boolean_: if devices of group store last measure received
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"_id": {
|
|
146
|
+
"$oid": "67a1e6447ae8b4ba4478f019"
|
|
147
|
+
},
|
|
148
|
+
"commands": [
|
|
149
|
+
{
|
|
150
|
+
"name": "reset",
|
|
151
|
+
"type": "command",
|
|
152
|
+
"value": "",
|
|
153
|
+
"expression": "{ set: brand + '_' + reset }"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "cmd1",
|
|
157
|
+
"type": "command",
|
|
158
|
+
"value": "",
|
|
159
|
+
"expression": "brand"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "cmd2",
|
|
163
|
+
"type": "command",
|
|
164
|
+
"value": "",
|
|
165
|
+
"expression": "reset"
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"staticAttributes": [
|
|
169
|
+
{
|
|
170
|
+
"name": "brand",
|
|
171
|
+
"type": "Text",
|
|
172
|
+
"value": "o1",
|
|
173
|
+
"metadata": {}
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
"attributes": [],
|
|
177
|
+
"resource": "/iot/json",
|
|
178
|
+
"apikey": "APIKEY",
|
|
179
|
+
"type": "thing",
|
|
180
|
+
"service": "smartcity",
|
|
181
|
+
"subservice": "/",
|
|
182
|
+
"description": "miJSON",
|
|
183
|
+
"timestamp": true,
|
|
184
|
+
"internalAttributes": [],
|
|
185
|
+
"lazy": [],
|
|
186
|
+
"transport": "HTTP",
|
|
187
|
+
"endpoint": "'https://eoykcmmm.m.pipedream.net' + '/' + service + '/' + subservice + '/' + id + '/' + type"
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Commands
|
|
192
|
+
|
|
193
|
+
The collection 'commands' stores information about the commands
|
|
194
|
+
|
|
195
|
+
Fields:
|
|
196
|
+
|
|
197
|
+
- **\_ID** _ObjectId_: unique object ID used by mongoDB
|
|
198
|
+
- **deviceId** _string_: device ID of the device
|
|
199
|
+
- **type** _string_: type of the command
|
|
200
|
+
- **name** _string_: name of the command
|
|
201
|
+
- **value** _object_: value of the command
|
|
202
|
+
- **service** _string_: service which the device command belongs to
|
|
203
|
+
- **subservice** _string_: subservice which the device command belongs to
|
|
204
|
+
- **execTs** _date_: related with new commands functionality (stored but not yet in use)
|
|
205
|
+
- **status** _string_: related with new commands functionality (stored but not yet in use)
|
|
206
|
+
- **info** _string_: related with new commands functionality (stored but not yet in use)
|
|
207
|
+
- **onDelivered** _Object_: related with new commands functionality (stored but not yet in use)
|
|
208
|
+
- **onOk**: _Object_: related with new commands functionality (stored but not yet in use)
|
|
209
|
+
- **onError** _Object_: related with new commands functionality (stored but not yet in use)
|
|
210
|
+
- **onInfo** _Object_: related with new commands functionality (stored but not yet in use)
|
|
211
|
+
- **cmdExecution** \_Boolean\_\_: related with new commands functionality (stored but not yet in use)
|
|
212
|
+
- **dateExpiration**: { type: Date }: related with new commands functionality (stored but not yet in use)
|
|
213
|
+
- **creationDate** _date_: creation date of command
|
|
214
|
+
|
|
215
|
+
Example:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"_id": {
|
|
220
|
+
"$oid": "680b4b538d0e60f98718a8eb"
|
|
221
|
+
},
|
|
222
|
+
"creationDate": {
|
|
223
|
+
"$date": {
|
|
224
|
+
"$numberLong": "1745570643252"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"name": "cmd1",
|
|
228
|
+
"type": "command",
|
|
229
|
+
"value": "on",
|
|
230
|
+
"deviceId": "disp3",
|
|
231
|
+
"service": "smartcity",
|
|
232
|
+
"subservice": "/"
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Indexes
|
|
237
|
+
|
|
238
|
+
### Devices
|
|
239
|
+
|
|
240
|
+
An index guarantees that every device is identified by the tuple (service, subservice, apikey, id)
|
|
241
|
+
|
|
242
|
+
The index is created/ensured when iotagent starts, but it can be created from a mongoDB shell with
|
|
243
|
+
|
|
244
|
+
```javascript
|
|
245
|
+
db.devices.ensureIndex({ service: 1, subservice: 1, apikey: 1, id: 1 }, { unique: true });
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Groups
|
|
249
|
+
|
|
250
|
+
An index guarantees that every group is identified by the tuple (apikey, resource)
|
|
251
|
+
|
|
252
|
+
The index is created/ensured when iotagent starts, but it can be created from a mongoDB shell with
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
db.groups.ensureIndex({ apikey: 1, resource: 1 }, { unique: true });
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Commands
|
|
259
|
+
|
|
260
|
+
None index is defined
|
package/doc/requirements.txt
CHANGED
package/lib/commonConfig.js
CHANGED
|
@@ -157,7 +157,9 @@ function processEnvironmentVariables() {
|
|
|
157
157
|
'IOTA_FALLBACK_PATH',
|
|
158
158
|
'IOTA_LD_SUPPORT_NULL',
|
|
159
159
|
'IOTA_LD_SUPPORT_DATASET_ID',
|
|
160
|
-
'IOTA_EXPRESS_LIMIT'
|
|
160
|
+
'IOTA_EXPRESS_LIMIT',
|
|
161
|
+
'IOTA_USE_CB_FLOW_CONTROL',
|
|
162
|
+
'IOTA_STORE_LAST_MEASURE'
|
|
161
163
|
];
|
|
162
164
|
const iotamVariables = [
|
|
163
165
|
'IOTA_IOTAM_URL',
|
|
@@ -350,6 +352,11 @@ function processEnvironmentVariables() {
|
|
|
350
352
|
config.defaultResource = process.env.IOTA_DEFAULT_RESOURCE;
|
|
351
353
|
}
|
|
352
354
|
|
|
355
|
+
// Default transport
|
|
356
|
+
if (process.env.IOTA_DEFAULT_TRANSPORT !== undefined) {
|
|
357
|
+
config.defaultTransport = process.env.IOTA_DEFAULT_TRANSPORT;
|
|
358
|
+
}
|
|
359
|
+
|
|
353
360
|
// Default explicitAttrs
|
|
354
361
|
if (process.env.IOTA_EXPLICIT_ATTRS !== undefined) {
|
|
355
362
|
config.explicitAttrs = process.env.IOTA_EXPLICIT_ATTRS;
|
|
@@ -474,6 +481,16 @@ function processEnvironmentVariables() {
|
|
|
474
481
|
} else {
|
|
475
482
|
config.expressLimit = config.expressLimit ? config.expressLimit : '1mb';
|
|
476
483
|
}
|
|
484
|
+
if (process.env.IOTA_USE_CB_FLOW_CONTROL) {
|
|
485
|
+
config.useCBflowControl = process.env.IOTA_USE_CB_FLOW_CONTROL === 'true';
|
|
486
|
+
} else {
|
|
487
|
+
config.useCBflowControl = config.useCBflowControl === true;
|
|
488
|
+
}
|
|
489
|
+
if (process.env.IOTA_STORE_LAST_MEASURE) {
|
|
490
|
+
config.storeLastMeasure = process.env.IOTA_STORE_LAST_MEASURE === 'true';
|
|
491
|
+
} else {
|
|
492
|
+
config.storeLastMeasure = config.storeLastMeasure === true;
|
|
493
|
+
}
|
|
477
494
|
}
|
|
478
495
|
|
|
479
496
|
function setConfig(newConfig) {
|
|
@@ -503,7 +520,9 @@ function getConfigForTypeInformation() {
|
|
|
503
520
|
multiCore: config.multiCore,
|
|
504
521
|
relaxTemplateValidation: config.relaxTemplateValidation,
|
|
505
522
|
defaultEntityNameConjunction: config.defaultEntityNameConjunction,
|
|
506
|
-
defaultType: config.defaultType
|
|
523
|
+
defaultType: config.defaultType,
|
|
524
|
+
useCBflowControl: config.useCBflowControl,
|
|
525
|
+
storeLastMeasure: config.storeLastMeasure
|
|
507
526
|
};
|
|
508
527
|
return conf;
|
|
509
528
|
}
|
|
@@ -47,17 +47,20 @@ const context = {
|
|
|
47
47
|
|
|
48
48
|
/* eslint-disable-next-line no-unused-vars */
|
|
49
49
|
function activateStatLogs(newConfig, callback) {
|
|
50
|
-
async.series(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
async.series(
|
|
51
|
+
[
|
|
52
|
+
apply(statsRegistry.globalLoad, {
|
|
53
|
+
deviceCreationRequests: 0,
|
|
54
|
+
deviceRemovalRequests: 0,
|
|
55
|
+
measureRequests: 0,
|
|
56
|
+
raiseAlarm: 0,
|
|
57
|
+
releaseAlarm: 0,
|
|
58
|
+
updateEntityRequestsOk: 0,
|
|
59
|
+
updateEntityRequestsError: 0
|
|
60
|
+
})
|
|
61
|
+
],
|
|
62
|
+
callback
|
|
63
|
+
);
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
/**
|
|
@@ -314,6 +317,7 @@ exports.setDataUpdateHandler = contextServer.setUpdateHandler;
|
|
|
314
317
|
exports.setCommandHandler = contextServer.setCommandHandler;
|
|
315
318
|
exports.setMergePatchHandler = contextServer.setMergePatchHandler;
|
|
316
319
|
exports.setDataQueryHandler = contextServer.setQueryHandler;
|
|
320
|
+
exports.executeUpdateSideEffects = contextServer.executeUpdateSideEffects;
|
|
317
321
|
exports.setConfigurationHandler = contextServer.setConfigurationHandler;
|
|
318
322
|
exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler;
|
|
319
323
|
exports.setProvisioningHandler = contextServer.setProvisioningHandler;
|
package/lib/jexlTranformsMap.js
CHANGED
|
@@ -26,63 +26,210 @@
|
|
|
26
26
|
JEXL avaliable transformations*/
|
|
27
27
|
|
|
28
28
|
const map = {
|
|
29
|
-
jsonparse: (
|
|
30
|
-
|
|
29
|
+
jsonparse: (val) => {
|
|
30
|
+
const safeOperation =
|
|
31
|
+
(fn) =>
|
|
32
|
+
(...args) => {
|
|
33
|
+
try {
|
|
34
|
+
return fn(...args);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
return safeOperation(JSON.parse)(val);
|
|
40
|
+
},
|
|
41
|
+
jsonstringify: (val) => {
|
|
42
|
+
const safeOperation =
|
|
43
|
+
(fn) =>
|
|
44
|
+
(...args) => {
|
|
45
|
+
try {
|
|
46
|
+
return fn(...args);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return safeOperation(JSON.stringify)(val);
|
|
52
|
+
},
|
|
31
53
|
indexOf: (val, char) => String(val).indexOf(char),
|
|
32
54
|
length: (val) => String(val).length,
|
|
33
55
|
trim: (val) => String(val).trim(),
|
|
34
56
|
substr: (val, int1, int2) => String(val).substr(int1, int2),
|
|
35
|
-
addreduce: (arr) =>
|
|
57
|
+
addreduce: (arr) => {
|
|
58
|
+
const safeOperation =
|
|
59
|
+
(fn) =>
|
|
60
|
+
(...args) => {
|
|
61
|
+
try {
|
|
62
|
+
return fn(...args);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
return safeOperation((arr) => arr.reduce((i, v) => i + v))(arr);
|
|
68
|
+
},
|
|
36
69
|
lengtharray: (arr) => arr.length,
|
|
37
70
|
typeof: (val) => typeof val,
|
|
38
|
-
isarray:
|
|
39
|
-
isnan:
|
|
40
|
-
parseint: (val) =>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
71
|
+
isarray: Array.isArray,
|
|
72
|
+
isnan: isNaN,
|
|
73
|
+
parseint: (val) => {
|
|
74
|
+
const safeParseNumber = (fn) => (val) => {
|
|
75
|
+
const result = fn(val);
|
|
76
|
+
return isNaN(result) ? null : result;
|
|
77
|
+
};
|
|
78
|
+
return safeParseNumber((val) => parseInt(val, 10))(val);
|
|
79
|
+
},
|
|
80
|
+
parsefloat: (val) => {
|
|
81
|
+
const safeParseNumber = (fn) => (val) => {
|
|
82
|
+
const result = fn(val);
|
|
83
|
+
return isNaN(result) ? null : result;
|
|
84
|
+
};
|
|
85
|
+
return safeParseNumber(parseFloat)(val);
|
|
86
|
+
},
|
|
87
|
+
toisodate: (val) => {
|
|
88
|
+
const safeDateOperation = (fn) => (val) => {
|
|
89
|
+
const date = new Date(val);
|
|
90
|
+
return isNaN(date.getTime()) ? null : fn(date);
|
|
91
|
+
};
|
|
92
|
+
return safeDateOperation((date) => date.toISOString())(val);
|
|
93
|
+
},
|
|
94
|
+
timeoffset: (val) => {
|
|
95
|
+
const safeDateOperation = (fn) => (val) => {
|
|
96
|
+
const date = new Date(val);
|
|
97
|
+
return isNaN(date.getTime()) ? null : fn(date);
|
|
98
|
+
};
|
|
99
|
+
return safeDateOperation((date) => date.getTimezoneOffset())(val);
|
|
100
|
+
},
|
|
101
|
+
tostring: (val) => {
|
|
102
|
+
const safeOperation =
|
|
103
|
+
(fn) =>
|
|
104
|
+
(...args) => {
|
|
105
|
+
try {
|
|
106
|
+
return fn(...args);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return safeOperation((val) => val.toString())(val);
|
|
112
|
+
},
|
|
113
|
+
urlencode: encodeURI,
|
|
114
|
+
urldecode: (val) => {
|
|
115
|
+
const safeOperation =
|
|
116
|
+
(fn) =>
|
|
117
|
+
(...args) => {
|
|
118
|
+
try {
|
|
119
|
+
return fn(...args);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
return safeOperation(decodeURI)(val);
|
|
125
|
+
},
|
|
47
126
|
replacestr: (str, from, to) => str.replace(from, to),
|
|
48
|
-
replaceregexp: (str, reg, to) =>
|
|
49
|
-
|
|
50
|
-
|
|
127
|
+
replaceregexp: (str, reg, to) => {
|
|
128
|
+
const safeOperation =
|
|
129
|
+
(fn) =>
|
|
130
|
+
(...args) => {
|
|
131
|
+
try {
|
|
132
|
+
return fn(...args);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
return safeOperation((str, reg, to) => str.replace(new RegExp(reg), to))(str, reg, to);
|
|
138
|
+
},
|
|
139
|
+
replaceallregexp: (str, reg, to) => {
|
|
140
|
+
const safeOperation =
|
|
141
|
+
(fn) =>
|
|
142
|
+
(...args) => {
|
|
143
|
+
try {
|
|
144
|
+
return fn(...args);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
return safeOperation((str, reg, to) => str.replace(new RegExp(reg, 'g'), to))(str, reg, to);
|
|
150
|
+
},
|
|
51
151
|
split: (str, ch) => str.split(ch),
|
|
52
152
|
joinarrtostr: (arr, ch) => arr.join(ch),
|
|
53
153
|
concatarr: (arr, arr2) => arr.concat(arr2),
|
|
54
154
|
mapper: (val, values, choices) => choices[values.findIndex((target) => target === val)],
|
|
55
155
|
thmapper: (val, values, choices) =>
|
|
56
|
-
choices[
|
|
57
|
-
values.reduce((acc, curr, i) => (acc === 0 || acc ? acc : val <= curr ? (acc = i) : (acc = null)), null)
|
|
58
|
-
],
|
|
156
|
+
choices[values.reduce((acc, curr, i) => (acc !== null ? acc : val <= curr ? i : null), null)],
|
|
59
157
|
bitwisemask: (i, mask, op, shf) =>
|
|
60
158
|
(op === '&' ? parseInt(i) & mask : op === '|' ? parseInt(i) | mask : op === '^' ? parseInt(i) ^ mask : i) >>
|
|
61
159
|
shf,
|
|
62
160
|
slice: (arr, init, end) => arr.slice(init, end),
|
|
63
|
-
addset: (arr, x) =>
|
|
64
|
-
return Array.from(new Set(arr).add(x));
|
|
65
|
-
},
|
|
161
|
+
addset: (arr, x) => Array.from(new Set(arr).add(x)),
|
|
66
162
|
removeset: (arr, x) => {
|
|
67
|
-
|
|
163
|
+
const s = new Set(arr);
|
|
68
164
|
s.delete(x);
|
|
69
165
|
return Array.from(s);
|
|
70
166
|
},
|
|
71
167
|
touppercase: (val) => String(val).toUpperCase(),
|
|
72
168
|
tolowercase: (val) => String(val).toLowerCase(),
|
|
73
|
-
floor:
|
|
74
|
-
ceil:
|
|
75
|
-
round:
|
|
76
|
-
tofixed: (val, decimals) =>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
169
|
+
floor: Math.floor,
|
|
170
|
+
ceil: Math.ceil,
|
|
171
|
+
round: Math.round,
|
|
172
|
+
tofixed: (val, decimals) => {
|
|
173
|
+
const num = Number.parseFloat(val);
|
|
174
|
+
const dec = Number.parseInt(decimals);
|
|
175
|
+
return isNaN(num) || isNaN(dec) ? null : num.toFixed(dec);
|
|
176
|
+
},
|
|
177
|
+
gettime: (val) => {
|
|
178
|
+
const safeDateOperation = (fn) => (val) => {
|
|
179
|
+
const date = new Date(val);
|
|
180
|
+
return isNaN(date.getTime()) ? null : fn(date);
|
|
181
|
+
};
|
|
182
|
+
return safeDateOperation((date) => date.getTime())(val);
|
|
183
|
+
},
|
|
184
|
+
toisostring: (val) => {
|
|
185
|
+
const safeDateOperation = (fn) => (val) => {
|
|
186
|
+
const date = new Date(val);
|
|
187
|
+
return isNaN(date.getTime()) ? null : fn(date);
|
|
188
|
+
};
|
|
189
|
+
return safeDateOperation((date) => date.toISOString())(val);
|
|
190
|
+
},
|
|
191
|
+
localestring: (d, timezone, options) => {
|
|
192
|
+
const safeOperation =
|
|
193
|
+
(fn) =>
|
|
194
|
+
(...args) => {
|
|
195
|
+
try {
|
|
196
|
+
return fn(...args);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
return safeOperation((d, timezone, options) => new Date(d).toLocaleString(timezone, options))(
|
|
202
|
+
d,
|
|
203
|
+
timezone,
|
|
204
|
+
options
|
|
205
|
+
);
|
|
206
|
+
},
|
|
207
|
+
now: Date.now,
|
|
208
|
+
hextostring: (val) => {
|
|
209
|
+
const safeOperation =
|
|
210
|
+
(fn) =>
|
|
211
|
+
(...args) => {
|
|
212
|
+
try {
|
|
213
|
+
return fn(...args);
|
|
214
|
+
} catch (e) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
return safeOperation((val) => {
|
|
219
|
+
if (typeof val !== 'string' || !/^[0-9a-fA-F]+$/.test(val) || val.length % 2 !== 0) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
return new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))));
|
|
223
|
+
})(val);
|
|
224
|
+
},
|
|
225
|
+
valuePicker: (val, pick) =>
|
|
226
|
+
Object.entries(val)
|
|
227
|
+
.filter(([, v]) => v === pick)
|
|
228
|
+
.map(([k]) => k),
|
|
229
|
+
valuePickerMulti: (val, pick) =>
|
|
230
|
+
Object.entries(val)
|
|
231
|
+
.filter(([, v]) => pick.includes(v))
|
|
232
|
+
.map(([k]) => k)
|
|
86
233
|
};
|
|
87
234
|
|
|
88
235
|
exports.map = map;
|
package/lib/model/Command.js
CHANGED
|
@@ -31,11 +31,20 @@ const Command = new Schema({
|
|
|
31
31
|
value: Object,
|
|
32
32
|
service: { type: String, lowercase: true },
|
|
33
33
|
subservice: String,
|
|
34
|
+
execTs: { type: Date },
|
|
35
|
+
status: String,
|
|
36
|
+
info: String,
|
|
37
|
+
onDelivered: Object,
|
|
38
|
+
onOk: Object,
|
|
39
|
+
onError: Object,
|
|
40
|
+
onInfo: Object,
|
|
41
|
+
cmdExecution: Boolean,
|
|
42
|
+
dateExpiration: { type: Date },
|
|
34
43
|
creationDate: { type: Date, default: Date.now }
|
|
35
44
|
});
|
|
36
45
|
|
|
37
|
-
function load(
|
|
38
|
-
module.exports.model =
|
|
46
|
+
function load() {
|
|
47
|
+
module.exports.model = mongoose.model('Command', Command);
|
|
39
48
|
module.exports.internalSchema = Command;
|
|
40
49
|
}
|
|
41
50
|
|
package/lib/model/Device.js
CHANGED
|
@@ -53,11 +53,16 @@ const Device = new Schema({
|
|
|
53
53
|
autoprovision: Boolean,
|
|
54
54
|
explicitAttrs: Group.ExplicitAttrsType,
|
|
55
55
|
ngsiVersion: String,
|
|
56
|
-
payloadType: String
|
|
56
|
+
payloadType: String,
|
|
57
|
+
useCBflowControl: Boolean,
|
|
58
|
+
storeLastMeasure: Boolean,
|
|
59
|
+
lastMeasure: Object,
|
|
60
|
+
oldCtxt: Object
|
|
57
61
|
});
|
|
58
62
|
|
|
59
|
-
function load(
|
|
60
|
-
|
|
63
|
+
function load() {
|
|
64
|
+
Device.index({ service: 1, subservice: 1, apikey: 1, id: 1 }, { unique: true });
|
|
65
|
+
module.exports.model = mongoose.model('Device', Device);
|
|
61
66
|
module.exports.internalSchema = Device;
|
|
62
67
|
}
|
|
63
68
|
|
package/lib/model/Group.js
CHANGED
|
@@ -65,12 +65,14 @@ const Group = new Schema({
|
|
|
65
65
|
defaultEntityNameConjunction: String,
|
|
66
66
|
ngsiVersion: String,
|
|
67
67
|
entityNameExp: String,
|
|
68
|
-
payloadType: String
|
|
68
|
+
payloadType: String,
|
|
69
|
+
useCBflowControl: Boolean,
|
|
70
|
+
storeLastMeasure: Boolean
|
|
69
71
|
});
|
|
70
72
|
|
|
71
|
-
function load(
|
|
73
|
+
function load() {
|
|
72
74
|
Group.index({ apikey: 1, resource: 1 }, { unique: true });
|
|
73
|
-
module.exports.model =
|
|
75
|
+
module.exports.model = mongoose.model('Group', Group);
|
|
74
76
|
module.exports.internalSchema = Group;
|
|
75
77
|
}
|
|
76
78
|
|