iotagent-node-lib 2.21.0 → 2.24.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/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
- package/.nyc_output/processinfo/{76bc24ff-5fac-4b5a-997d-de2799342eb0.json → 33364de2-1199-4ec2-b33c-cae063ef8cc4.json} +1 -1
- package/.nyc_output/processinfo/index.json +1 -1
- package/CHANGES_NEXT_RELEASE +0 -1
- package/doc/Contribution.md +3 -3
- package/doc/advanced-topics.md +73 -9
- package/doc/api.md +7 -5
- package/doc/architecture.md +52 -5
- package/doc/expressionLanguage.md +18 -3
- package/doc/operations.md +8 -5
- package/doc/usermanual.md +18 -16
- package/docker/Mosquitto/Dockerfile +1 -1
- package/lib/errors.js +9 -1
- package/lib/model/Group.js +2 -1
- package/lib/model/dbConn.js +4 -0
- package/lib/plugins/bidirectionalData.js +104 -6
- package/lib/plugins/expressionPlugin.js +18 -7
- package/lib/plugins/multiEntity.js +42 -29
- package/lib/plugins/pluginUtils.js +17 -0
- package/lib/request-shim.js +2 -1
- package/lib/services/commands/commandService.js +29 -2
- package/lib/services/common/iotManagerService.js +2 -1
- package/lib/services/devices/deviceService.js +35 -9
- package/lib/services/groups/groupRegistryMongoDB.js +13 -12
- package/lib/services/ngsi/entities-NGSI-LD.js +7 -0
- package/lib/services/ngsi/entities-NGSI-v2.js +70 -11
- package/lib/services/ngsi/ngsiService.js +1 -1
- package/lib/services/northBound/contextServer-NGSI-LD.js +110 -15
- package/lib/services/northBound/contextServer-NGSI-v2.js +8 -3
- package/lib/services/northBound/contextServerUtils.js +9 -9
- package/lib/services/northBound/deviceProvisioningServer.js +2 -1
- package/lib/templates/createDevice.json +1 -2
- package/lib/templates/createDeviceLax.json +2 -3
- package/lib/templates/updateDevice.json +1 -2
- package/package.json +24 -24
- package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +51 -0
- package/test/unit/mongodb/mongoDBUtils.js +2 -2
- package/test/unit/mongodb/mongodb-group-registry-test.js +25 -1
- package/test/unit/mongodb/mongodb-registry-test.js +51 -3
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +2 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextLanguageProperties1.json +15 -0
- package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithDatasetId.json +21 -0
- package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +17 -0
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +995 -27
- package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -0
- package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +111 -0
- package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +221 -0
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +1 -1
- package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +8 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +6 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +12 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
- package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +19 -0
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +10 -8
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +106 -0
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -0
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +115 -0
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +63 -0
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +60 -0
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +2 -1
- package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +0 -1
|
@@ -26,8 +26,9 @@
|
|
|
26
26
|
const async = require('async');
|
|
27
27
|
const apply = async.apply;
|
|
28
28
|
const _ = require('underscore');
|
|
29
|
-
const parser = require('./
|
|
29
|
+
const parser = require('./expressionPlugin');
|
|
30
30
|
const logger = require('logops');
|
|
31
|
+
const config = require('../commonConfig');
|
|
31
32
|
const subscriptions = require('../services/ngsi/subscriptionService');
|
|
32
33
|
const deviceService = require('../services/devices/deviceService');
|
|
33
34
|
const context = {
|
|
@@ -180,14 +181,17 @@ function getReverseTransformations(list, values, callback) {
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
/**
|
|
183
|
-
* Apply a list of transformations to the reported values,
|
|
184
|
+
* Apply a list of transformations to the reported values,
|
|
185
|
+
* generating a new array of values with the additional data.
|
|
186
|
+
* For NGSI-v2 this consists of value transformations only
|
|
184
187
|
*
|
|
185
188
|
* @param {Array} values List of reported attributes (with their name, type and value).
|
|
189
|
+
* @param {Object} typeInformation Provisioning information about the device represented by the entity.
|
|
186
190
|
* @param {Array} transformations List of transformations to apply (with their name, type and expression).
|
|
187
191
|
*/
|
|
188
|
-
function
|
|
192
|
+
function processTransformationsNGSIv2(values, typeInformation, transformations, callback) {
|
|
189
193
|
let cleanedExpression;
|
|
190
|
-
const ctx = parser.extractContext(values);
|
|
194
|
+
const ctx = parser.extractContext(values, typeInformation);
|
|
191
195
|
let resultTransformations = [];
|
|
192
196
|
|
|
193
197
|
for (let j = 0; j < 2; j++) {
|
|
@@ -205,13 +209,107 @@ function processTransformations(values, transformations, callback) {
|
|
|
205
209
|
values.push({
|
|
206
210
|
name: resultTransformations[i].object_id,
|
|
207
211
|
type: resultTransformations[i].type,
|
|
208
|
-
value: parser.parse(cleanedExpression, ctx, 'String')
|
|
212
|
+
value: parser.parse(cleanedExpression, ctx, 'String', typeInformation)
|
|
209
213
|
});
|
|
210
214
|
}
|
|
211
215
|
|
|
212
216
|
callback(null, values);
|
|
213
217
|
}
|
|
214
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Apply a list of transformations to the reported values,
|
|
221
|
+
* generating a new array of values with the additional data.
|
|
222
|
+
* For NGSI-LD the output includes value, metadata and datasetId
|
|
223
|
+
*
|
|
224
|
+
* @param {Array} values List of reported attributes (with their name, type and value).
|
|
225
|
+
* @param {Object} typeInformation Provisioning information about the device represented by the entity.
|
|
226
|
+
* @param {Array} transformations List of transformations to apply (with their name, type and expression).
|
|
227
|
+
*/
|
|
228
|
+
function processTransformationsNGSILD(values, typeInformation, transformations, callback) {
|
|
229
|
+
let cleanedExpression;
|
|
230
|
+
const defaultValues = [];
|
|
231
|
+
const datasetIds = {};
|
|
232
|
+
|
|
233
|
+
// Split incoming values into those with and without datasetId
|
|
234
|
+
values.forEach((value) => {
|
|
235
|
+
if (value.datasetId) {
|
|
236
|
+
datasetIds[value.datasetId] = datasetIds[value.datasetId] || [];
|
|
237
|
+
datasetIds[value.datasetId].push(value);
|
|
238
|
+
} else {
|
|
239
|
+
defaultValues.push(value);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
let resultTransformations = [];
|
|
244
|
+
|
|
245
|
+
for (let j = 0; j < 2; j++) {
|
|
246
|
+
if (transformations[j]) {
|
|
247
|
+
resultTransformations = resultTransformations.concat(transformations[j]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Process Transformations using the default datasetId
|
|
252
|
+
// This is the direct equivalent of the NGSI-v2 function
|
|
253
|
+
if (!_.isEmpty(defaultValues)) {
|
|
254
|
+
for (let i = 0; i < resultTransformations.length; i++) {
|
|
255
|
+
cleanedExpression = resultTransformations[i].expression.substr(
|
|
256
|
+
2,
|
|
257
|
+
resultTransformations[i].expression.length - 3
|
|
258
|
+
);
|
|
259
|
+
values.push({
|
|
260
|
+
name: resultTransformations[i].object_id,
|
|
261
|
+
type: resultTransformations[i].type,
|
|
262
|
+
metadata: {},
|
|
263
|
+
value: parser.parse(
|
|
264
|
+
cleanedExpression,
|
|
265
|
+
parser.extractContext(defaultValues, typeInformation),
|
|
266
|
+
'String',
|
|
267
|
+
typeInformation
|
|
268
|
+
)
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Process Transformations using an explicit datasetId
|
|
274
|
+
// There is no equivalent with NGSI-v2
|
|
275
|
+
_.keys(datasetIds).forEach((datasetId) => {
|
|
276
|
+
for (let i = 0; i < resultTransformations.length; i++) {
|
|
277
|
+
cleanedExpression = resultTransformations[i].expression.substr(
|
|
278
|
+
2,
|
|
279
|
+
resultTransformations[i].expression.length - 3
|
|
280
|
+
);
|
|
281
|
+
values.push({
|
|
282
|
+
name: resultTransformations[i].object_id,
|
|
283
|
+
type: resultTransformations[i].type,
|
|
284
|
+
datasetId,
|
|
285
|
+
metadata: {},
|
|
286
|
+
value: parser.parse(
|
|
287
|
+
cleanedExpression,
|
|
288
|
+
parser.extractContext(datasetIds[datasetId], typeInformation),
|
|
289
|
+
'String',
|
|
290
|
+
typeInformation
|
|
291
|
+
)
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
callback(null, values);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Apply a list of transformations to the reported values, generating a new array of values with the additional data.
|
|
300
|
+
*
|
|
301
|
+
* @param {Array} values List of reported attributes (with their name, type and value).
|
|
302
|
+
* @param {Object} typeInformation Provisioning information about the device represented by the entity.
|
|
303
|
+
* @param {Array} transformations List of transformations to apply (with their name, type and expression).
|
|
304
|
+
*/
|
|
305
|
+
function processTransformations(values, typeInformation, transformations, callback) {
|
|
306
|
+
if (config.checkNgsiLD({})) {
|
|
307
|
+
processTransformationsNGSILD(values, typeInformation, transformations, callback);
|
|
308
|
+
} else {
|
|
309
|
+
processTransformationsNGSIv2(values, typeInformation, transformations, callback);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
215
313
|
/**
|
|
216
314
|
* Middleware to handle incoming Configuration group provisions. Should check for the existence of reverse active
|
|
217
315
|
* attributes and create subscriptions for the modifciation of those values.
|
|
@@ -259,7 +357,7 @@ function handleNotification(device, values, callback) {
|
|
|
259
357
|
apply(getReverseTransformations, deviceAttributes, values),
|
|
260
358
|
apply(getReverseTransformations, groupAttributes, values)
|
|
261
359
|
]),
|
|
262
|
-
apply(processTransformations, values)
|
|
360
|
+
apply(processTransformations, values, device)
|
|
263
361
|
],
|
|
264
362
|
function (error, results) {
|
|
265
363
|
callback(error, device, results);
|
|
@@ -45,21 +45,28 @@ function setJEXLTransforms(transformationMap) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function applyExpression(expression, context, typeInformation) {
|
|
48
|
-
let parser =
|
|
49
|
-
if (checkJexl(typeInformation)) {
|
|
50
|
-
parser =
|
|
48
|
+
let parser = jexlParser;
|
|
49
|
+
if (!checkJexl(typeInformation)) {
|
|
50
|
+
parser = legacyParser;
|
|
51
51
|
}
|
|
52
52
|
return parser.applyExpression(expression, context, typeInformation);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function extractContext(attributeList, typeInformation) {
|
|
56
|
-
let parser =
|
|
57
|
-
if (checkJexl(typeInformation)) {
|
|
58
|
-
parser =
|
|
56
|
+
let parser = jexlParser;
|
|
57
|
+
if (!checkJexl(typeInformation)) {
|
|
58
|
+
parser = legacyParser;
|
|
59
59
|
}
|
|
60
60
|
return parser.extractContext(attributeList);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
function parse(expression, context, type, typeInformation) {
|
|
64
|
+
if (!checkJexl(typeInformation)) {
|
|
65
|
+
return legacyParser.parse(expression, context, type);
|
|
66
|
+
}
|
|
67
|
+
return jexlParser.parse(expression, context);
|
|
68
|
+
}
|
|
69
|
+
|
|
63
70
|
function mergeAttributes(attrList1, attrList2) {
|
|
64
71
|
const finalCollection = _.clone(attrList1);
|
|
65
72
|
const additionalItems = [];
|
|
@@ -120,6 +127,8 @@ function update(entity, typeInformation, callback) {
|
|
|
120
127
|
attributesCtxt.push(att);
|
|
121
128
|
});
|
|
122
129
|
}
|
|
130
|
+
let idTypeSSSList = utils.getIdTypeServSubServiceFromDevice(typeInformation);
|
|
131
|
+
attributesCtxt = attributesCtxt.concat(idTypeSSSList);
|
|
123
132
|
const ctx = parser.extractContext(attributesCtxt);
|
|
124
133
|
|
|
125
134
|
if (typeInformation.active) {
|
|
@@ -150,11 +159,13 @@ function update(entity, typeInformation, callback) {
|
|
|
150
159
|
|
|
151
160
|
callback(null, entity, typeInformation);
|
|
152
161
|
} catch (e) {
|
|
153
|
-
logger.
|
|
162
|
+
logger.info(context, 'expressionPlugin error %j procesing entity %j', e, entity);
|
|
154
163
|
callback(e);
|
|
164
|
+
return;
|
|
155
165
|
}
|
|
156
166
|
}
|
|
157
167
|
|
|
168
|
+
exports.parse = parse;
|
|
158
169
|
exports.update = update;
|
|
159
170
|
exports.setJEXLTransforms = setJEXLTransforms;
|
|
160
171
|
exports.applyExpression = applyExpression;
|
|
@@ -41,8 +41,8 @@ const utils = require('./pluginUtils');
|
|
|
41
41
|
/* eslint-disable-next-line no-unused-vars */
|
|
42
42
|
const aliasPlugin = require('./attributeAlias');
|
|
43
43
|
|
|
44
|
-
function
|
|
45
|
-
return item.entity_name;
|
|
44
|
+
function hasEntityNameOrEntityType(item) {
|
|
45
|
+
return item.entity_name || item.entity_type;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -206,37 +206,50 @@ function propagateTimestamp(entity, entities) {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
function updateAttribute(entity, typeInformation, callback) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
parser = jexlParser;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
209
|
+
try {
|
|
210
|
+
logger.debug(context, 'multiEntityPlugin entity %j', entity);
|
|
211
|
+
let parser = jexlParser;
|
|
212
|
+
if (!expressionPlugin.checkJexl(typeInformation)) {
|
|
213
|
+
parser = legacyParser;
|
|
214
|
+
}
|
|
215
|
+
// The context for the JEXL expression should be the ID, TYPE, S, SS
|
|
216
|
+
let attrList = utils.getIdTypeServSubServiceFromDevice(typeInformation);
|
|
217
|
+
const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
|
|
218
|
+
attrList = attrList.concat(attsArray);
|
|
219
|
+
const ctx = parser.extractContext(attrList);
|
|
220
|
+
let entities = [entity];
|
|
221
|
+
if (typeInformation.active) {
|
|
222
|
+
const multiEntityAttributes = typeInformation.active.filter(hasEntityNameOrEntityType);
|
|
223
|
+
for (let i in multiEntityAttributes) {
|
|
224
|
+
if (!multiEntityAttributes[i].entity_name) {
|
|
225
|
+
// Then hasEntityType and entity_name should be device.name
|
|
226
|
+
multiEntityAttributes[i].entity_name = typeInformation.name;
|
|
227
|
+
}
|
|
228
|
+
if (parser.contextAvailable(multiEntityAttributes[i].entity_name, ctx)) {
|
|
229
|
+
let entityName = parser.applyExpression(multiEntityAttributes[i].entity_name, ctx, typeInformation);
|
|
230
|
+
// An entity_name could not be null, but a result or expression could be null
|
|
231
|
+
if (entityName !== null) {
|
|
232
|
+
multiEntityAttributes[i].entity_name = entityName;
|
|
233
|
+
}
|
|
225
234
|
}
|
|
226
235
|
}
|
|
236
|
+
const newEntities = extractNewEntities(multiEntityAttributes, typeInformation.type);
|
|
237
|
+
|
|
238
|
+
if (multiEntityAttributes.length > 0) {
|
|
239
|
+
let resultAttributes = filterOutMultientitiesNgsi2(entity, multiEntityAttributes);
|
|
240
|
+
const newCes = generateNewCEsNgsi2(entity, newEntities, multiEntityAttributes);
|
|
241
|
+
entities = [resultAttributes].concat(newCes);
|
|
242
|
+
propagateTimestamp(entity, entities);
|
|
243
|
+
} else {
|
|
244
|
+
entities = entity;
|
|
245
|
+
}
|
|
227
246
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
entities = [resultAttributes].concat(newCes);
|
|
234
|
-
propagateTimestamp(entity, entities);
|
|
235
|
-
} else {
|
|
236
|
-
entities = entity;
|
|
237
|
-
}
|
|
247
|
+
callback(null, entities, typeInformation);
|
|
248
|
+
} catch (e) {
|
|
249
|
+
logger.info(context, 'multiEntityPlugin error %j procesing entity %j', e, entity);
|
|
250
|
+
callback(e);
|
|
251
|
+
return;
|
|
238
252
|
}
|
|
239
|
-
callback(null, entities, typeInformation);
|
|
240
253
|
}
|
|
241
254
|
|
|
242
255
|
exports.update = updateAttribute;
|
|
@@ -167,9 +167,26 @@ function identityFilter(entity, typeInformation, callback) {
|
|
|
167
167
|
callback(null, entity, typeInformation);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Creates an array of attributes from an device including id, type, service and subservice
|
|
172
|
+
* @param {Object} device
|
|
173
|
+
* @return {Object} Array of id, type, service and subservice extracted from device
|
|
174
|
+
*/
|
|
175
|
+
function getIdTypeServSubServiceFromDevice(typeInformation) {
|
|
176
|
+
let attrList = [
|
|
177
|
+
{ name: 'id', type: 'String', value: typeInformation.id },
|
|
178
|
+
{ name: 'type', type: 'String', value: typeInformation.type },
|
|
179
|
+
{ name: 'service', type: 'String', value: typeInformation.service },
|
|
180
|
+
{ name: 'subservice', type: 'String', value: typeInformation.subservice },
|
|
181
|
+
{ name: 'entity_name', type: 'String', value: typeInformation.name }
|
|
182
|
+
];
|
|
183
|
+
return attrList;
|
|
184
|
+
}
|
|
185
|
+
|
|
170
186
|
exports.createProcessAttribute = createProcessAttribute;
|
|
171
187
|
exports.createUpdateFilter = createUpdateFilter;
|
|
172
188
|
exports.createQueryFilter = createQueryFilter;
|
|
173
189
|
exports.identityFilter = identityFilter;
|
|
174
190
|
exports.createNgsi2Entity = createNgsi2Entity;
|
|
175
191
|
exports.extractAttributesArrayFromNgsi2Entity = extractAttributesArrayFromNgsi2Entity;
|
|
192
|
+
exports.getIdTypeServSubServiceFromDevice = getIdTypeServSubServiceFromDevice;
|
package/lib/request-shim.js
CHANGED
|
@@ -26,6 +26,7 @@ const logger = require('logops');
|
|
|
26
26
|
const context = {
|
|
27
27
|
op: 'IoTAgentNGSI.Request'
|
|
28
28
|
};
|
|
29
|
+
const util = require('util');
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Transform the "request" options into "got" options and add additional "got" defaults
|
|
@@ -103,7 +104,7 @@ function request(options, callback) {
|
|
|
103
104
|
return callback(null, response, response.body);
|
|
104
105
|
})
|
|
105
106
|
.catch((error) => {
|
|
106
|
-
logger.debug(context, 'Error: %s', JSON.stringify(error, null, 4));
|
|
107
|
+
logger.debug(context, 'Error: %s', JSON.stringify(util.inspect(error), null, 4));
|
|
107
108
|
return callback(error);
|
|
108
109
|
});
|
|
109
110
|
}
|
|
@@ -29,10 +29,12 @@ const config = require('../../commonConfig');
|
|
|
29
29
|
const constants = require('../../constants');
|
|
30
30
|
const ngsiService = require('../ngsi/ngsiService');
|
|
31
31
|
const deviceService = require('../devices/deviceService');
|
|
32
|
+
const pluginUtils = require('../../plugins/pluginUtils');
|
|
32
33
|
let daemonId;
|
|
33
34
|
const context = {
|
|
34
35
|
op: 'IoTAgentNGSI.CommandService'
|
|
35
36
|
};
|
|
37
|
+
const expressionPlugin = require('../../plugins/expressionPlugin');
|
|
36
38
|
|
|
37
39
|
function listCommands(service, subservice, deviceId, callback) {
|
|
38
40
|
logger.debug(context, 'Listing all commands for device [%s]', deviceId);
|
|
@@ -41,11 +43,35 @@ function listCommands(service, subservice, deviceId, callback) {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
function addCommand(service, subservice, deviceId, command, callback) {
|
|
44
|
-
logger.debug(context, 'Adding command [%
|
|
45
|
-
|
|
46
|
+
logger.debug(context, 'Adding command [%j] to the queue for device [%s]', command, deviceId);
|
|
46
47
|
config.getCommandRegistry().add(service, subservice, deviceId, command, callback);
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
function addCommandDevice(service, subservice, device, command, callback) {
|
|
51
|
+
logger.debug(context, 'Adding command [%j] to the queue for device [%j]', command, device);
|
|
52
|
+
let deviceCmd;
|
|
53
|
+
if (device && device.commands) {
|
|
54
|
+
deviceCmd = device.commands.find((c) => c.name === command.name);
|
|
55
|
+
}
|
|
56
|
+
if (deviceCmd && deviceCmd.expression) {
|
|
57
|
+
let parser = expressionPlugin;
|
|
58
|
+
// The context for the JEXL expression should be the ID, TYPE, S, SS
|
|
59
|
+
let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(device);
|
|
60
|
+
attrList = device.staticAttributes ? attrList.concat(device.staticAttributes) : attrList.concat([]);
|
|
61
|
+
let ctxt = parser.extractContext(attrList, device);
|
|
62
|
+
logger.debug(context, 'attrList [%j] for device %j', attrList, device);
|
|
63
|
+
// expression result will be the full command payload
|
|
64
|
+
let cmdValueRes = null;
|
|
65
|
+
try {
|
|
66
|
+
cmdValueRes = parser.applyExpression(deviceCmd.expression, ctxt, device);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// nothing to do
|
|
69
|
+
}
|
|
70
|
+
command.value = cmdValueRes ? cmdValueRes : deviceCmd.expression;
|
|
71
|
+
}
|
|
72
|
+
config.getCommandRegistry().add(service, subservice, device.id, command, callback);
|
|
73
|
+
}
|
|
74
|
+
|
|
49
75
|
function updateCommand(service, subservice, deviceId, name, value, callback) {
|
|
50
76
|
logger.debug(context, 'Updating command [%s] for device [%s] with value [%s]', name, deviceId, value);
|
|
51
77
|
|
|
@@ -147,6 +173,7 @@ function stopExpirationDaemon(callback) {
|
|
|
147
173
|
|
|
148
174
|
exports.list = intoTrans(context, listCommands);
|
|
149
175
|
exports.add = addCommand;
|
|
176
|
+
exports.addCmd = addCommandDevice;
|
|
150
177
|
exports.update = updateCommand;
|
|
151
178
|
exports.remove = removeCommand;
|
|
152
179
|
exports.start = startExpirationDaemon;
|
|
@@ -60,7 +60,8 @@ function register(callback) {
|
|
|
60
60
|
explicitAttrs: service.explicitAttrs,
|
|
61
61
|
expressionLanguage: service.expressionLanguage,
|
|
62
62
|
defaultEntityNameConjunction: service.defaultEntityNameConjunction,
|
|
63
|
-
ngsiVersion: service.ngsiVersion
|
|
63
|
+
ngsiVersion: service.ngsiVersion,
|
|
64
|
+
entityNameExp: service.entityNameExp
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -38,6 +38,8 @@ const logger = require('logops');
|
|
|
38
38
|
const config = require('../../commonConfig');
|
|
39
39
|
const registrationUtils = require('./registrationUtils');
|
|
40
40
|
const subscriptions = require('../ngsi/subscriptionService');
|
|
41
|
+
const expressionPlugin = require('./../../plugins/expressionPlugin');
|
|
42
|
+
const pluginUtils = require('./../../plugins/pluginUtils');
|
|
41
43
|
const _ = require('underscore');
|
|
42
44
|
const context = {
|
|
43
45
|
op: 'IoTAgentNGSI.DeviceService'
|
|
@@ -280,15 +282,45 @@ function registerDevice(deviceObj, callback) {
|
|
|
280
282
|
}
|
|
281
283
|
|
|
282
284
|
if (!deviceData.name) {
|
|
285
|
+
let entityName = null;
|
|
286
|
+
if (configuration && configuration.entityNameExp !== undefined && configuration.entityNameExp !== '') {
|
|
287
|
+
// Add device ID, TYPE, S, SS to the context for the JEXL expression
|
|
288
|
+
let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(deviceData);
|
|
289
|
+
attrList = deviceData.staticAttributes ? attrList.concat(deviceData.staticAttributes) : attrList;
|
|
290
|
+
let ctxt = expressionPlugin.extractContext(attrList, deviceData);
|
|
291
|
+
try {
|
|
292
|
+
entityName = expressionPlugin.applyExpression(configuration.entityNameExp, ctxt, deviceData);
|
|
293
|
+
} catch (e) {
|
|
294
|
+
logger.debug(
|
|
295
|
+
context,
|
|
296
|
+
'Error evaluating expression for entityName: %s with context: %s',
|
|
297
|
+
configuration.entityNameExp,
|
|
298
|
+
ctxt
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
deviceData.name = entityName ? entityName : defaultName();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!configuration && config.getConfig().types[deviceData.type]) {
|
|
306
|
+
selectedConfiguration = config.getConfig().types[deviceData.type];
|
|
307
|
+
} else {
|
|
308
|
+
selectedConfiguration = configuration;
|
|
309
|
+
}
|
|
310
|
+
callback(null, deviceData, selectedConfiguration);
|
|
311
|
+
|
|
312
|
+
function defaultName() {
|
|
283
313
|
let conjunction;
|
|
314
|
+
let name;
|
|
284
315
|
if (configuration && configuration.defaultEntityNameConjunction !== undefined) {
|
|
285
316
|
conjunction = configuration.defaultEntityNameConjunction;
|
|
286
317
|
} else {
|
|
287
318
|
conjunction = config.getConfig().defaultEntityNameConjunction;
|
|
288
319
|
}
|
|
289
|
-
|
|
320
|
+
name = deviceData.type + conjunction + deviceData.id;
|
|
321
|
+
|
|
290
322
|
if (config.checkNgsiLD(configuration)) {
|
|
291
|
-
|
|
323
|
+
name = 'urn:ngsi-ld:' + deviceData.type + conjunction + deviceData.id;
|
|
292
324
|
}
|
|
293
325
|
logger.debug(
|
|
294
326
|
context,
|
|
@@ -296,14 +328,8 @@ function registerDevice(deviceObj, callback) {
|
|
|
296
328
|
conjunction,
|
|
297
329
|
deviceData.name
|
|
298
330
|
);
|
|
331
|
+
return name;
|
|
299
332
|
}
|
|
300
|
-
|
|
301
|
-
if (!configuration && config.getConfig().types[deviceData.type]) {
|
|
302
|
-
selectedConfiguration = config.getConfig().types[deviceData.type];
|
|
303
|
-
} else {
|
|
304
|
-
selectedConfiguration = configuration;
|
|
305
|
-
}
|
|
306
|
-
callback(null, deviceData, selectedConfiguration);
|
|
307
333
|
}
|
|
308
334
|
|
|
309
335
|
function completeRegistrations(error, deviceData) {
|
|
@@ -57,7 +57,8 @@ const attributeList = [
|
|
|
57
57
|
'explicitAttrs',
|
|
58
58
|
'expressionLanguage',
|
|
59
59
|
'defaultEntityNameConjunction',
|
|
60
|
-
'ngsiVersion'
|
|
60
|
+
'ngsiVersion',
|
|
61
|
+
'entityNameExp'
|
|
61
62
|
];
|
|
62
63
|
|
|
63
64
|
/**
|
|
@@ -299,21 +300,21 @@ function clear(callback) {
|
|
|
299
300
|
dbService.db.db.dropDatabase(callback);
|
|
300
301
|
}
|
|
301
302
|
|
|
302
|
-
exports.create = alarmsInt(constants.MONGO_ALARM, intoTrans(context, createGroup));
|
|
303
|
-
exports.list = alarmsInt(constants.MONGO_ALARM, intoTrans(context, listGroups));
|
|
304
|
-
exports.init = alarmsInt(constants.MONGO_ALARM, intoTrans(context, init));
|
|
305
|
-
exports.find = alarmsInt(constants.MONGO_ALARM, intoTrans(context, find));
|
|
303
|
+
exports.create = alarmsInt(constants.MONGO_ALARM + '_01', intoTrans(context, createGroup));
|
|
304
|
+
exports.list = alarmsInt(constants.MONGO_ALARM + '_02', intoTrans(context, listGroups));
|
|
305
|
+
exports.init = alarmsInt(constants.MONGO_ALARM + '_03', intoTrans(context, init));
|
|
306
|
+
exports.find = alarmsInt(constants.MONGO_ALARM + '_04', intoTrans(context, find));
|
|
306
307
|
exports.findType = alarmsInt(
|
|
307
|
-
constants.MONGO_ALARM,
|
|
308
|
+
constants.MONGO_ALARM + '_05',
|
|
308
309
|
intoTrans(context, findBy(['service', 'subservice', 'type', 'apikey']))
|
|
309
310
|
);
|
|
310
311
|
exports.findTypeSilently = intoTrans(context, findBy(['service', 'subservice', 'type', 'apikey']));
|
|
311
312
|
exports.findSilently = intoTrans(context, findBy(['service', 'subservice', 'apikey']));
|
|
312
|
-
exports.findBy = alarmsInt(constants.MONGO_ALARM, intoTrans(context, findBy));
|
|
313
|
-
exports.get = alarmsInt(constants.MONGO_ALARM, intoTrans(context, findBy(['resource', 'apikey'])));
|
|
313
|
+
exports.findBy = alarmsInt(constants.MONGO_ALARM + '_06', intoTrans(context, findBy));
|
|
314
|
+
exports.get = alarmsInt(constants.MONGO_ALARM + '_07', intoTrans(context, findBy(['resource', 'apikey'])));
|
|
314
315
|
exports.getSilently = intoTrans(context, findBy(['resource', 'apikey']));
|
|
315
|
-
exports.getType = alarmsInt(constants.MONGO_ALARM, intoTrans(context, findBy(['type'])));
|
|
316
|
+
exports.getType = alarmsInt(constants.MONGO_ALARM + '_08', intoTrans(context, findBy(['type'])));
|
|
316
317
|
exports.getTypeSilently = intoTrans(context, findBy(['type']));
|
|
317
|
-
exports.update = alarmsInt(constants.MONGO_ALARM, intoTrans(context, update));
|
|
318
|
-
exports.remove = alarmsInt(constants.MONGO_ALARM, intoTrans(context, remove));
|
|
319
|
-
exports.clear = alarmsInt(constants.MONGO_ALARM, intoTrans(context, clear));
|
|
318
|
+
exports.update = alarmsInt(constants.MONGO_ALARM + '_09', intoTrans(context, update));
|
|
319
|
+
exports.remove = alarmsInt(constants.MONGO_ALARM + '_10', intoTrans(context, remove));
|
|
320
|
+
exports.clear = alarmsInt(constants.MONGO_ALARM + '_11', intoTrans(context, clear));
|
|
@@ -160,6 +160,13 @@ function convertAttrNGSILD(attr) {
|
|
|
160
160
|
delete obj.value;
|
|
161
161
|
break;
|
|
162
162
|
|
|
163
|
+
// LanguageProperties
|
|
164
|
+
case 'languageproperty':
|
|
165
|
+
obj.type = 'LanguageProperty';
|
|
166
|
+
obj.languageMap = attr.value;
|
|
167
|
+
delete obj.value;
|
|
168
|
+
break;
|
|
169
|
+
|
|
163
170
|
default:
|
|
164
171
|
obj.value = { '@type': attr.type, '@value': attr.value };
|
|
165
172
|
}
|