iotagent-node-lib 2.19.0 → 2.22.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 (67) hide show
  1. package/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
  2. package/.nyc_output/processinfo/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
  3. package/.nyc_output/processinfo/index.json +1 -0
  4. package/.readthedocs.yml +3 -1
  5. package/CHANGES_NEXT_RELEASE +1 -0
  6. package/README.md +5 -56
  7. package/doc/Contribution.md +3 -3
  8. package/doc/advanced-topics.md +8 -41
  9. package/doc/api.md +14 -3
  10. package/doc/expressionLanguage.md +17 -0
  11. package/doc/northboundinteractions.md +40 -33
  12. package/doc/operations.md +8 -5
  13. package/doc/requirements.txt +4 -0
  14. package/{docs → doc}/roadmap.md +21 -6
  15. package/doc/usermanual.md +4 -4
  16. package/docker/Mosquitto/Dockerfile +28 -12
  17. package/docker/Mosquitto/README.md +8 -7
  18. package/docker/Mosquitto/startMosquitto.sh +8 -0
  19. package/lib/fiware-iotagent-lib.js +1 -2
  20. package/lib/jexlTranformsMap.js +3 -1
  21. package/lib/model/Group.js +2 -1
  22. package/lib/model/dbConn.js +4 -0
  23. package/lib/plugins/expressionPlugin.js +56 -21
  24. package/lib/plugins/multiEntity.js +43 -49
  25. package/lib/plugins/pluginUtils.js +16 -0
  26. package/lib/services/commands/commandService.js +29 -2
  27. package/lib/services/common/domain.js +6 -2
  28. package/lib/services/common/iotManagerService.js +2 -1
  29. package/lib/services/devices/deviceRegistryMemory.js +13 -2
  30. package/lib/services/devices/deviceRegistryMongoDB.js +15 -7
  31. package/lib/services/devices/deviceService.js +52 -16
  32. package/lib/services/groups/groupRegistryMongoDB.js +13 -12
  33. package/lib/services/ngsi/entities-NGSI-LD.js +13 -4
  34. package/lib/services/ngsi/entities-NGSI-v2.js +8 -6
  35. package/lib/services/ngsi/ngsiService.js +3 -3
  36. package/lib/services/northBound/contextServer-NGSI-LD.js +20 -1
  37. package/lib/services/northBound/contextServer-NGSI-v2.js +39 -30
  38. package/lib/services/northBound/contextServerUtils.js +10 -10
  39. package/lib/services/northBound/deviceProvisioningServer.js +4 -1
  40. package/lib/templates/createDevice.json +13 -2
  41. package/lib/templates/createDeviceLax.json +2 -3
  42. package/lib/templates/updateDevice.json +13 -2
  43. package/package.json +27 -33
  44. package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
  45. package/test/unit/mongodb/mongoDBUtils.js +2 -2
  46. package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
  47. package/test/unit/mongodb/mongodb-registry-test.js +2 -3
  48. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +2 -1
  49. package/test/unit/ngsi-ld/examples/contextRequests/updateContextLanguageProperties1.json +15 -0
  50. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +315 -1
  51. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -0
  52. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +1 -1
  53. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +8 -0
  54. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +6 -0
  55. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
  56. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
  57. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
  58. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +63 -2
  59. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -0
  60. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +63 -0
  61. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +60 -0
  62. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +2 -1
  63. package/bin/agentConsole.js +0 -257
  64. package/bin/iotAgentTester.js +0 -44
  65. package/lib/command/commandLine.js +0 -918
  66. package/lib/command/migration.js +0 -176
  67. package/test/unit/general/migration-test.js +0 -257
package/doc/usermanual.md CHANGED
@@ -209,8 +209,8 @@ function setCommandResult(entityName, resource, apikey, commandName, commandResu
209
209
  ###### Description
210
210
 
211
211
  Update the result of a command in the Context Broker. The result of the command has two components: the result of the
212
- command itself will be represented with the suffix `_result` in the entity while the status is updated in the attribute
213
- with the `_info` suffix.
212
+ command itself will be represented with the suffix `_info` in the entity while the status is updated in the attribute
213
+ with the `_status` suffix.
214
214
 
215
215
  ###### Params
216
216
 
@@ -258,7 +258,7 @@ function setDataUpdateHandler(newHandler)
258
258
  ###### Description
259
259
 
260
260
  Sets the new user handler for Entity update requests. This handler will be called whenever an update request arrives
261
- with the following parameters: (id, type, service, subservice, attributes, callback). The handler is in charge of
261
+ with the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler is in charge of
262
262
  updating the corresponding values in the devices with the appropriate protocol.
263
263
 
264
264
  Once all the updates have taken place, the callback must be invoked with the updated Context Element. E.g.:
@@ -296,7 +296,7 @@ function setDataQueryHandler(newHandler)
296
296
  ###### Description
297
297
 
298
298
  Sets the new user handler for Entity query requests. This handler will be called whenever a query request arrives, with
299
- the following parameters: (id, type, service, subservice, attributes, callback). The handler must retrieve all the
299
+ the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve all the
300
300
  corresponding information from the devices and return a NGSI entity with the requested values.
301
301
 
302
302
  The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:
@@ -1,19 +1,35 @@
1
- FROM centos:7.9.2009
1
+ ARG IMAGE_TAG=11.3-slim
2
+ FROM debian:${IMAGE_TAG}
2
3
 
3
- COPY aclfile /root/
4
- COPY startMosquitto.sh /bin
4
+ ARG CLEAN_DEV_TOOLS
5
+ ENV CLEAN_DEV_TOOLS ${CLEAN_DEV_TOOLS:-1}
5
6
 
6
7
  ENV CONGIF_FROM_ENV true
7
8
 
8
- RUN yum update -y && yum install -y wget \
9
- && yum install -y epel-release \
10
- && yum update -y epel-release \
11
- && yum install -y mosquitto \
12
- && cp /etc/mosquitto/mosquitto.conf /etc/mosquitto/mosquitto.conf.orig \
13
- && chmod 755 /bin/startMosquitto.sh \
14
- && mkdir /var/log/mosquitto \
15
- && chown mosquitto:mosquitto /var/log/mosquitto \
16
- && yum clean all
9
+ COPY aclfile /root/
10
+ COPY startMosquitto.sh /bin
11
+
12
+ RUN \
13
+ # Install security updates
14
+ apt-get -y update && \
15
+ apt-get -y upgrade && \
16
+ # Install dependencies
17
+ apt-get -y install \
18
+ wget \
19
+ mosquitto && \
20
+ cp /etc/mosquitto/mosquitto.conf /etc/mosquitto/mosquitto.conf.orig && \
21
+ chmod 755 /bin/startMosquitto.sh && \
22
+ mkdir -p /var/log/mosquitto && \
23
+ chown mosquitto:mosquitto /var/log/mosquitto && \
24
+ mkdir -p /var/run/mosquitto/ && \
25
+ chown mosquitto:mosquitto /var/run/mosquitto && \
26
+ echo "INFO: Cleaning unused software..." && \
27
+ apt-get clean && \
28
+ apt-get -y autoremove --purge && \
29
+ if [ ${CLEAN_DEV_TOOLS} -eq 0 ] ; then exit 0 ; fi && \
30
+ # remove the same packages we installed at the beginning to build Orch
31
+ apt-get -y autoremove --purge \
32
+ wget
17
33
 
18
34
 
19
35
  EXPOSE 1883
@@ -2,10 +2,11 @@ Thi directory containts the Dockerfile (and associated files) for a container of
2
2
  This container is provide as a help for users to test with MQTT, but it is just an auxiliary material in this repository.
3
3
 
4
4
  The following releases matches with eclipse-mosquitto version:
5
- - 1.6.0 uses mosquitto-1.6.10-1.el7.x86_64
6
- - 1.5.0 uses mosquitto-1.6.10-1.el7.x86_64
7
- - 1.4.0 uses mosquitto-1.6.10-1.el7.x86_64
8
- - 1.3.0 uses mosquitto-1.6.8-1.el7.x86_64
9
- - 1.2.0 uses mosquitto-1.6.7-1.el7.x86_64
10
- - 1.1.0 uses mosquitto-1.5.8-1.el7.x86_64
11
- - 1.0.0 uses mosquitto-1.4.8-1.el7.x86_64
5
+ - 2.0.0 uses mosquitto-2.0.11 from Debian 11
6
+ - 1.6.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
7
+ - 1.5.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
8
+ - 1.4.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
9
+ - 1.3.0 uses mosquitto-1.6.8-1.el7.x86_64 (from Centos7)
10
+ - 1.2.0 uses mosquitto-1.6.7-1.el7.x86_64 (from Centos7)
11
+ - 1.1.0 uses mosquitto-1.5.8-1.el7.x86_64 (from Centos7)
12
+ - 1.0.0 uses mosquitto-1.4.8-1.el7.x86_64 (from Centos7)
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ echo "INFO: startMosquitto..."
4
+
3
5
  if [ "${CONGIF_FROM_ENV}" = true ] ; then
4
6
  cp /etc/mosquitto/mosquitto.conf.orig /etc/mosquitto/mosquitto.conf
7
+ sed -i 's/log_dest file \/var\/log\/mosquitto\/mosquitto.log/log_dest stderr/g' /etc/mosquitto/mosquitto.conf
5
8
  echo "log_timestamp true" >> /etc/mosquitto/mosquitto.conf
6
9
  echo "log_timestamp_format %Y-%m-%dT%H:%M:%S" >> /etc/mosquitto/mosquitto.conf
7
10
  echo 'listener 9001' >> /etc/mosquitto/mosquitto.conf
@@ -18,4 +21,9 @@ if [ "${CONGIF_FROM_ENV}" = true ] ; then
18
21
  fi
19
22
  fi
20
23
 
24
+ echo "INFO: content /etc/mosquitto/mosquitto.conf: "
25
+ cat /etc/mosquitto/mosquitto.conf
26
+
27
+ echo "INFO: start: startMosquitto -c /etc/mosquitto/mosquitto.conf"
28
+
21
29
  /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
@@ -314,6 +314,7 @@ exports.listDevices = deviceService.listDevices;
314
314
  exports.getDevice = deviceService.getDevice;
315
315
  exports.getDeviceSilently = deviceService.getDeviceSilently;
316
316
  exports.getDeviceByName = deviceService.getDeviceByName;
317
+ exports.getDeviceByNameAndType = deviceService.getDeviceByNameAndType;
317
318
  exports.getDevicesByAttribute = deviceService.getDevicesByAttribute;
318
319
  exports.mergeDeviceWithConfiguration = deviceService.mergeDeviceWithConfiguration;
319
320
  exports.retrieveDevice = deviceService.retrieveDevice;
@@ -355,8 +356,6 @@ exports.fillService = domainUtils.fillService;
355
356
 
356
357
  exports.middlewares = middlewares;
357
358
 
358
- exports.commandLine = require('./command/commandLine');
359
-
360
359
  exports.dataPlugins = {
361
360
  compressTimestamp: require('./plugins/compressTimestamp'),
362
361
  attributeAlias: require('./plugins/attributeAlias'),
@@ -65,7 +65,9 @@ const map = {
65
65
  let s = new Set(arr);
66
66
  s.delete(x);
67
67
  return Array.from(s);
68
- }
68
+ },
69
+ touppercase: (val) => String(val).toUpperCase(),
70
+ tolowercase: (val) => String(val).toLowerCase()
69
71
  };
70
72
 
71
73
  exports.map = map;
@@ -62,7 +62,8 @@ const Group = new Schema({
62
62
  expressionLanguage: String,
63
63
  explicitAttrs: ExplicitAttrsType,
64
64
  defaultEntityNameConjunction: String,
65
- ngsiVersion: String
65
+ ngsiVersion: String,
66
+ entityNameExp: String
66
67
  });
67
68
 
68
69
  function load(db) {
@@ -113,6 +113,10 @@ function init(host, db, port, options, callback) {
113
113
  options,
114
114
  retries
115
115
  );
116
+ // FIXME: useNewUrlParser is no longer used in underlying mongodb driver 4.x
117
+ // (see https://github.com/mongodb/node-mongodb-native/blob/HEAD/etc/notes/CHANGES_4.0.0.md)
118
+ // but not sure if current mongoose version is still using mongodb 3.x internally
119
+ // probably mongodb-connectionoptions-test.js needs to be fixed if useNewUrlParser is removed at the end
116
120
  options.useNewUrlParser = true;
117
121
  mongoose.set('useCreateIndex', true);
118
122
  /* eslint-disable-next-line no-unused-vars */
@@ -30,6 +30,8 @@ const jexlParser = require('./jexlParser');
30
30
  const config = require('../commonConfig');
31
31
  /* eslint-disable no-unused-vars */
32
32
  const logger = require('logops');
33
+ const errors = require('../errors');
34
+ const constants = require('../constants');
33
35
  const context = {
34
36
  op: 'IoTAgentNGSI.expressionPlugin'
35
37
  };
@@ -42,6 +44,22 @@ function setJEXLTransforms(transformationMap) {
42
44
  jexlParser.setTransforms(transformationMap);
43
45
  }
44
46
 
47
+ function applyExpression(expression, context, typeInformation) {
48
+ let parser = jexlParser;
49
+ if (!checkJexl(typeInformation)) {
50
+ parser = legacyParser;
51
+ }
52
+ return parser.applyExpression(expression, context, typeInformation);
53
+ }
54
+
55
+ function extractContext(attributeList, typeInformation) {
56
+ let parser = jexlParser;
57
+ if (!checkJexl(typeInformation)) {
58
+ parser = legacyParser;
59
+ }
60
+ return parser.extractContext(attributeList);
61
+ }
62
+
45
63
  function mergeAttributes(attrList1, attrList2) {
46
64
  const finalCollection = _.clone(attrList1);
47
65
  const additionalItems = [];
@@ -70,26 +88,26 @@ function mergeAttributes(attrList1, attrList2) {
70
88
  return finalCollection.concat(additionalItems);
71
89
  }
72
90
 
73
- function update(entity, typeInformation, callback) {
74
- function checkJexl(typeInformation) {
75
- if (
76
- config.getConfig().defaultExpressionLanguage === 'jexl' &&
77
- typeInformation.expressionLanguage &&
78
- typeInformation.expressionLanguage !== 'legacy'
79
- ) {
80
- return true;
81
- } else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
82
- return true;
83
- } else if (
84
- config.getConfig().defaultExpressionLanguage === 'legacy' &&
85
- typeInformation.expressionLanguage &&
86
- typeInformation.expressionLanguage === 'jexl'
87
- ) {
88
- return true;
89
- }
90
- return false;
91
+ function checkJexl(typeInformation) {
92
+ if (
93
+ config.getConfig().defaultExpressionLanguage === 'jexl' &&
94
+ typeInformation.expressionLanguage &&
95
+ typeInformation.expressionLanguage !== 'legacy'
96
+ ) {
97
+ return true;
98
+ } else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
99
+ return true;
100
+ } else if (
101
+ config.getConfig().defaultExpressionLanguage === 'legacy' &&
102
+ typeInformation.expressionLanguage &&
103
+ typeInformation.expressionLanguage === 'jexl'
104
+ ) {
105
+ return true;
91
106
  }
107
+ return false;
108
+ }
92
109
 
110
+ function update(entity, typeInformation, callback) {
93
111
  function processEntityUpdateNgsi2(attributes) {
94
112
  let parser = legacyParser;
95
113
  if (checkJexl(typeInformation)) {
@@ -114,15 +132,32 @@ function update(entity, typeInformation, callback) {
114
132
 
115
133
  try {
116
134
  logger.debug(context, 'expressionPlugin entity %j', entity);
117
- let attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
118
- attsArray = processEntityUpdateNgsi2(attsArray);
119
- entity = utils.createNgsi2Entity(entity.id, entity.type, attsArray, true);
135
+ const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
136
+ // Exclude processing all attr expressions when current attr is of type 'commandStatus' or 'commandResult'
137
+ const attsArrayFiltered = attsArray.filter((obj) => {
138
+ return ![constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
139
+ });
140
+ const attsArrayCmd = attsArray.filter((obj) => {
141
+ // just attr of type 'commandStatus' or 'commandResult'
142
+ return [constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
143
+ });
144
+ let attsArrayFinal = [];
145
+ if (attsArrayFiltered.length > 0) {
146
+ attsArrayFinal = processEntityUpdateNgsi2(attsArrayFiltered);
147
+ }
148
+ attsArrayFinal = attsArrayFinal.concat(attsArrayCmd);
149
+ entity = utils.createNgsi2Entity(entity.id, entity.type, attsArrayFinal, true);
120
150
 
121
151
  callback(null, entity, typeInformation);
122
152
  } catch (e) {
153
+ logger.info(context, 'expressionPlugin error %j procesing entity %j', e, entity);
123
154
  callback(e);
155
+ return;
124
156
  }
125
157
  }
126
158
 
127
159
  exports.update = update;
128
160
  exports.setJEXLTransforms = setJEXLTransforms;
161
+ exports.applyExpression = applyExpression;
162
+ exports.extractContext = extractContext;
163
+ exports.checkJexl = checkJexl;
@@ -30,7 +30,7 @@ const _ = require('underscore');
30
30
  const constants = require('../constants');
31
31
  const legacyParser = require('./expressionParser');
32
32
  const jexlParser = require('./jexlParser');
33
- const config = require('../commonConfig');
33
+ const expressionPlugin = require('./expressionPlugin');
34
34
  /* eslint-disable-next-line no-unused-vars */
35
35
  const logger = require('logops');
36
36
  /* eslint-disable-next-line no-unused-vars */
@@ -41,27 +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 checkJexl(typeInformation) {
45
- if (
46
- config.getConfig().defaultExpressionLanguage === 'jexl' &&
47
- typeInformation.expressionLanguage &&
48
- typeInformation.expressionLanguage !== 'legacy'
49
- ) {
50
- return true;
51
- } else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
52
- return true;
53
- } else if (
54
- config.getConfig().defaultExpressionLanguage === 'legacy' &&
55
- typeInformation.expressionLanguage &&
56
- typeInformation.expressionLanguage === 'jexl'
57
- ) {
58
- return true;
59
- }
60
- return false;
61
- }
62
-
63
- function hasEntityName(item) {
64
- return item.entity_name;
44
+ function hasEntityNameOrEntityType(item) {
45
+ return item.entity_name || item.entity_type;
65
46
  }
66
47
 
67
48
  /**
@@ -225,37 +206,50 @@ function propagateTimestamp(entity, entities) {
225
206
  }
226
207
 
227
208
  function updateAttribute(entity, typeInformation, callback) {
228
- let parser = legacyParser;
229
- if (checkJexl(typeInformation)) {
230
- parser = jexlParser;
231
- }
232
- const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
233
- const ctx = parser.extractContext(attsArray);
234
-
235
- let entities = [entity];
236
- if (typeInformation.active) {
237
- const multiEntityAttributes = typeInformation.active.filter(hasEntityName);
238
- for (let i in multiEntityAttributes) {
239
- if (parser.contextAvailable(multiEntityAttributes[i].entity_name, ctx)) {
240
- let entityName = parser.applyExpression(multiEntityAttributes[i].entity_name, ctx, typeInformation);
241
- // An entity_name could not be null, but a result or expression could be null
242
- if (entityName !== null) {
243
- multiEntityAttributes[i].entity_name = entityName;
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
+ }
244
234
  }
245
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
+ }
246
246
  }
247
- const newEntities = extractNewEntities(multiEntityAttributes, typeInformation.type);
248
-
249
- if (multiEntityAttributes.length > 0) {
250
- let resultAttributes = filterOutMultientitiesNgsi2(entity, multiEntityAttributes);
251
- const newCes = generateNewCEsNgsi2(entity, newEntities, multiEntityAttributes);
252
- entities = [resultAttributes].concat(newCes);
253
- propagateTimestamp(entity, entities);
254
- } else {
255
- entities = entity;
256
- }
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;
257
252
  }
258
- callback(null, entities, typeInformation);
259
253
  }
260
254
 
261
255
  exports.update = updateAttribute;
@@ -167,9 +167,25 @@ 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
+ ];
182
+ return attrList;
183
+ }
184
+
170
185
  exports.createProcessAttribute = createProcessAttribute;
171
186
  exports.createUpdateFilter = createUpdateFilter;
172
187
  exports.createQueryFilter = createQueryFilter;
173
188
  exports.identityFilter = identityFilter;
174
189
  exports.createNgsi2Entity = createNgsi2Entity;
175
190
  exports.extractAttributesArrayFromNgsi2Entity = extractAttributesArrayFromNgsi2Entity;
191
+ exports.getIdTypeServSubServiceFromDevice = getIdTypeServSubServiceFromDevice;
@@ -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 [%s] to the queue for device [%s]', command.name, deviceId);
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;
@@ -47,6 +47,8 @@ function cleanDomain(domainToClean) {
47
47
  delete domainToClean.from;
48
48
  delete domainToClean.op;
49
49
  delete domainToClean.path;
50
+ delete domainToClean.service;
51
+ delete domainToClean.subservice;
50
52
  domainToClean.exit();
51
53
  }
52
54
 
@@ -139,8 +141,10 @@ function ensureSouthboundTransaction(context, callback) {
139
141
  reqDomain.corr = reqDomain.trans;
140
142
  }
141
143
 
142
- if (context && context.op) {
143
- reqDomain.op = context.op;
144
+ if (context) {
145
+ if (context.op) {
146
+ reqDomain.op = context.op;
147
+ }
144
148
 
145
149
  if (context.srv) {
146
150
  reqDomain.service = context.srv;
@@ -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
 
@@ -149,13 +149,19 @@ function getDevice(id, service, subservice, callback) {
149
149
  }
150
150
  }
151
151
 
152
- function getByName(name, service, subservice, callback) {
152
+ function getByNameAndType(name, type, service, subservice, callback) {
153
153
  const devices = _.values(registeredDevices[service]);
154
154
  let device;
155
155
 
156
156
  for (let i = 0; i < devices.length; i++) {
157
157
  if (devices[i].name === name) {
158
- device = devices[i];
158
+ if (type) {
159
+ if (devices[i].type === type) {
160
+ device = devices[i];
161
+ }
162
+ } else {
163
+ device = devices[i];
164
+ }
159
165
  }
160
166
  }
161
167
 
@@ -166,6 +172,10 @@ function getByName(name, service, subservice, callback) {
166
172
  }
167
173
  }
168
174
 
175
+ function getByName(name, service, subservice, callback) {
176
+ getByNameAndType(name, null, service, subservice, callback);
177
+ }
178
+
169
179
  function update(device, callback) {
170
180
  registeredDevices[device.service][device.id] = deepClone(device);
171
181
  callback(null, device);
@@ -208,4 +218,5 @@ exports.list = listDevices;
208
218
  exports.get = getDevice;
209
219
  exports.getSilently = getDevice;
210
220
  exports.getByName = getByName;
221
+ exports.getByNameAndType = getByNameAndType;
211
222
  exports.clear = clear;
@@ -243,15 +243,18 @@ function getDevice(id, service, subservice, callback) {
243
243
  });
244
244
  }
245
245
 
246
- function getByName(name, service, servicepath, callback) {
246
+ function getByNameAndType(name, type, service, servicepath, callback) {
247
247
  context = fillService(context, { service, subservice: servicepath });
248
- logger.debug(context, 'Looking for device with name [%s].', name);
249
-
250
- const query = Device.model.findOne({
251
- name,
252
- service,
248
+ let optionsQuery = {
249
+ name: name,
250
+ service: service,
253
251
  subservice: servicepath
254
- });
252
+ };
253
+ if (type) {
254
+ optionsQuery.type = type;
255
+ }
256
+ logger.debug(context, 'Looking for device with [%j].', optionsQuery);
257
+ const query = Device.model.findOne(optionsQuery);
255
258
 
256
259
  query.select({ __v: 0 });
257
260
 
@@ -270,6 +273,10 @@ function getByName(name, service, servicepath, callback) {
270
273
  });
271
274
  }
272
275
 
276
+ function getByName(name, service, servicepath, callback) {
277
+ getByNameAndType(name, null, service, servicepath, callback);
278
+ }
279
+
273
280
  /**
274
281
  * Updates the given device into the database.
275
282
  * updated.
@@ -361,4 +368,5 @@ exports.list = alarmsInt(constants.MONGO_ALARM, listDevices);
361
368
  exports.get = alarmsInt(constants.MONGO_ALARM, getDevice);
362
369
  exports.getSilently = getDevice;
363
370
  exports.getByName = alarmsInt(constants.MONGO_ALARM, getByName);
371
+ exports.getByNameAndType = alarmsInt(constants.MONGO_ALARM, getByNameAndType);
364
372
  exports.clear = alarmsInt(constants.MONGO_ALARM, clear);