• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

telefonicaid / iotagent-node-lib / 15190339532

22 May 2025 03:13PM UTC coverage: 79.263% (-0.7%) from 79.939%
15190339532

push

github

web-flow
Merge pull request #1560 from telefonicaid/task/notify_cmd

allow receive commands by CB notifications

1960 of 2644 branches covered (74.13%)

Branch coverage included in aggregate %.

7 of 16 new or added lines in 6 files covered. (43.75%)

25 existing lines in 1 file now uncovered.

3804 of 4628 relevant lines covered (82.2%)

275.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.37
/lib/services/commands/commandService.js
1
/*
2
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
3
 *
4
 * This file is part of fiware-iotagent-lib
5
 *
6
 * fiware-iotagent-lib is free software: you can redistribute it and/or
7
 * modify it under the terms of the GNU Affero General Public License as
8
 * published by the Free Software Foundation, either version 3 of the License,
9
 * or (at your option) any later version.
10
 *
11
 * fiware-iotagent-lib is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 * See the GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public
17
 * License along with fiware-iotagent-lib.
18
 * If not, see http://www.gnu.org/licenses/.
19
 *
20
 * For those usages not covered by the GNU Affero General Public License
21
 * please contact with::daniel.moranjimenez@telefonica.com
22
 */
23

24
const intoTrans = require('../common/domain').intoTrans;
1✔
25
const logger = require('logops');
1✔
26
const async = require('async');
1✔
27
const apply = async.apply;
1✔
28
const config = require('../../commonConfig');
1✔
29
const constants = require('../../constants');
1✔
30
const ngsiService = require('../ngsi/ngsiService');
1✔
31
const deviceService = require('../devices/deviceService');
1✔
32
const pluginUtils = require('../../plugins/pluginUtils');
1✔
33
let daemonId;
34
const context = {
1✔
35
    op: 'IoTAgentNGSI.CommandService'
36
};
37
const expressionPlugin = require('../../plugins/expressionPlugin');
1✔
38

39
function listCommands(service, subservice, deviceId, callback) {
40
    logger.debug(context, 'Listing all commands for device [%s]', deviceId);
17✔
41

42
    config.getCommandRegistry().list(service, subservice, deviceId, callback);
17✔
43
}
44

45
function addCommand(service, subservice, deviceId, command, callback) {
46
    logger.debug(context, 'Adding command [%j] to the queue for deviceId [%s]', command, deviceId);
84✔
47
    config.getCommandRegistry().add(service, subservice, deviceId, command, callback);
84✔
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);
9✔
52
    let deviceCmd;
53
    if (device && device.commands) {
9!
54
        deviceCmd = device.commands.find((c) => c.name === command.name);
9✔
55
    }
56
    if (deviceCmd && deviceCmd.expression) {
9✔
57
        let parser = expressionPlugin;
3✔
58
        // The context for the JEXL expression should be the ID, TYPE, S, SS
59
        let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(device);
3✔
60
        attrList = device.staticAttributes ? attrList.concat(device.staticAttributes) : attrList.concat([]);
3!
61
        let ctxt = parser.extractContext(attrList);
3✔
62
        logger.debug(context, 'attrList [%j] for device %j', attrList, device);
3✔
63
        // expression result will be the full command payload
64
        let cmdValueRes = null;
3✔
65
        try {
3✔
66
            cmdValueRes = parser.applyExpression(deviceCmd.expression, ctxt, device);
3✔
67
        } catch (e) {
68
            // nothing to do
69
        }
70
        command.value = cmdValueRes ? cmdValueRes : deviceCmd.expression;
3!
71
    }
72
    config.getCommandRegistry().add(service, subservice, device.id, command, callback);
9✔
73
}
74

75
function updateCommand(service, subservice, deviceId, name, value, callback) {
NEW
76
    logger.debug(context, 'Updating command [%s] for deviceId [%s] with value [%s]', name, deviceId, value);
×
77

78
    config.getCommandRegistry().update(service, subservice, deviceId, value, callback);
×
79
}
80

81
function removeCommand(service, subservice, deviceId, name, callback) {
82
    logger.debug(context, 'Removing command [%s] from deviceId [%s]', name, deviceId);
2✔
83

84
    config.getCommandRegistry().remove(service, subservice, deviceId, name, callback);
2✔
85
}
86

87
function markAsExpired(command) {
88
    logger.debug('Marking command as expired: %j', command);
4✔
89

90
    function getGroup(device, callback) {
91
        deviceService.findConfigurationGroup(device, function (error, group) {
2✔
92
            callback(error, device, group);
2✔
93
        });
94
    }
95

96
    function calculateTypeInformation(device, group, callback) {
97
        deviceService.mergeDeviceWithConfiguration(
2✔
98
            ['lazy', 'active', 'staticAttributes', 'commands', 'subscriptions'],
99
            [null, null, [], [], [], [], []],
100
            device,
101
            group,
102
            function (error, typeInformation) {
103
                callback(error, device, group, typeInformation);
2✔
104
            }
105
        );
106
    }
107

108
    function updateExpiredCommand(device, group, typeInformation, callback) {
109
        ngsiService.setCommandResult(
2✔
110
            device.name,
111
            group.resource,
112
            group.apikey,
113
            command.name,
114
            constants.COMMAND_EXPIRED_MESSAGE,
115
            constants.COMMAND_STATUS_ERROR,
116
            typeInformation,
117
            callback
118
        );
119
    }
120

121
    async.waterfall(
4✔
122
        [
123
            apply(deviceService.getDevice, command.deviceId, null, command.service, command.subservice),
124
            getGroup,
125
            calculateTypeInformation,
126
            updateExpiredCommand
127
        ],
128
        function (error) {
129
            if (error) {
4✔
130
                logger.error(context, 'Error updating polling command to expire: %j', error);
2✔
131
            } else {
132
                logger.debug(context, 'Command successfully expired');
2✔
133
            }
134
        }
135
    );
136
}
137

138
function expirationDaemon() {
139
    if (config.getConfig().pollingExpiration) {
157!
140
        config
157✔
141
            .getCommandRegistry()
142
            .removeFromDate(Date.now() - config.getConfig().pollingExpiration, function (error, results) {
143
                logger.debug(context, 'Executed expiration daemon');
44✔
144

145
                if (error) {
44!
146
                    logger.error(context, 'Error executing expiration daemon: %s', error);
×
147
                } else if (results && results.length) {
44✔
148
                    results.map(markAsExpired);
4✔
149
                }
150
            });
151
    }
152
}
153

154
function startExpirationDaemon(callback) {
155
    if (config.getConfig().pollingDaemonFrequency) {
794✔
156
        daemonId = setInterval(expirationDaemon, config.getConfig().pollingDaemonFrequency);
26✔
157

158
        if (callback) {
26!
159
            callback();
×
160
        }
161
    }
162
}
163

164
function stopExpirationDaemon(callback) {
165
    if (daemonId) {
794✔
166
        clearInterval(daemonId);
675✔
167
    }
168

169
    if (callback) {
794!
170
        callback();
×
171
    }
172
}
173

174
exports.list = intoTrans(context, listCommands);
1✔
175
exports.add = addCommand;
1✔
176
exports.addCmd = addCommandDevice;
1✔
177
exports.update = updateCommand;
1✔
178
exports.remove = removeCommand;
1✔
179
exports.start = startExpirationDaemon;
1✔
180
exports.stop = stopExpirationDaemon;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc