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.
- package/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
- package/.nyc_output/processinfo/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.readthedocs.yml +3 -1
- package/CHANGES_NEXT_RELEASE +1 -0
- package/README.md +5 -56
- package/doc/Contribution.md +3 -3
- package/doc/advanced-topics.md +8 -41
- package/doc/api.md +14 -3
- package/doc/expressionLanguage.md +17 -0
- package/doc/northboundinteractions.md +40 -33
- package/doc/operations.md +8 -5
- package/doc/requirements.txt +4 -0
- package/{docs → doc}/roadmap.md +21 -6
- package/doc/usermanual.md +4 -4
- package/docker/Mosquitto/Dockerfile +28 -12
- package/docker/Mosquitto/README.md +8 -7
- package/docker/Mosquitto/startMosquitto.sh +8 -0
- package/lib/fiware-iotagent-lib.js +1 -2
- package/lib/jexlTranformsMap.js +3 -1
- package/lib/model/Group.js +2 -1
- package/lib/model/dbConn.js +4 -0
- package/lib/plugins/expressionPlugin.js +56 -21
- package/lib/plugins/multiEntity.js +43 -49
- package/lib/plugins/pluginUtils.js +16 -0
- package/lib/services/commands/commandService.js +29 -2
- package/lib/services/common/domain.js +6 -2
- package/lib/services/common/iotManagerService.js +2 -1
- package/lib/services/devices/deviceRegistryMemory.js +13 -2
- package/lib/services/devices/deviceRegistryMongoDB.js +15 -7
- package/lib/services/devices/deviceService.js +52 -16
- package/lib/services/groups/groupRegistryMongoDB.js +13 -12
- package/lib/services/ngsi/entities-NGSI-LD.js +13 -4
- package/lib/services/ngsi/entities-NGSI-v2.js +8 -6
- package/lib/services/ngsi/ngsiService.js +3 -3
- package/lib/services/northBound/contextServer-NGSI-LD.js +20 -1
- package/lib/services/northBound/contextServer-NGSI-v2.js +39 -30
- package/lib/services/northBound/contextServerUtils.js +10 -10
- package/lib/services/northBound/deviceProvisioningServer.js +4 -1
- package/lib/templates/createDevice.json +13 -2
- package/lib/templates/createDeviceLax.json +2 -3
- package/lib/templates/updateDevice.json +13 -2
- package/package.json +27 -33
- package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
- package/test/unit/mongodb/mongoDBUtils.js +2 -2
- package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
- package/test/unit/mongodb/mongodb-registry-test.js +2 -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/lazyAndCommands/command-test.js +315 -1
- package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -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/updateContextExpressionPlugin35.json +2 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +63 -2
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -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/bin/agentConsole.js +0 -257
- package/bin/iotAgentTester.js +0 -44
- package/lib/command/commandLine.js +0 -918
- package/lib/command/migration.js +0 -176
- 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 `
|
|
213
|
-
with the `
|
|
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
|
|
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
|
|
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
|
-
|
|
1
|
+
ARG IMAGE_TAG=11.3-slim
|
|
2
|
+
FROM debian:${IMAGE_TAG}
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
-
|
|
6
|
-
- 1.
|
|
7
|
-
- 1.
|
|
8
|
-
- 1.
|
|
9
|
-
- 1.
|
|
10
|
-
- 1.
|
|
11
|
-
- 1.
|
|
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'),
|
package/lib/jexlTranformsMap.js
CHANGED
package/lib/model/Group.js
CHANGED
package/lib/model/dbConn.js
CHANGED
|
@@ -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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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
|
|
45
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
parser = jexlParser;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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 [%
|
|
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
|
|
143
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
246
|
+
function getByNameAndType(name, type, service, servicepath, callback) {
|
|
247
247
|
context = fillService(context, { service, subservice: servicepath });
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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);
|