Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/store last measure #1670

Merged
merged 28 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eeb7308
store last measure
AlvaroVega Nov 19, 2024
aaffe7b
fix storeLastMeasure
AlvaroVega Nov 19, 2024
28f29f0
move measure to lastMeasure
AlvaroVega Nov 19, 2024
ba0ccab
fix linter
AlvaroVega Nov 19, 2024
e1ab070
fix registy callbacks
AlvaroVega Nov 20, 2024
aced669
add timestamp to lastMeasure
AlvaroVega Nov 20, 2024
16860ce
update CNR
AlvaroVega Nov 20, 2024
ea16a2c
add and check store last measure config
AlvaroVega Nov 21, 2024
85c428f
update doc
AlvaroVega Nov 25, 2024
e554ed0
Merge branch 'master' into task/store_last_measure
AlvaroVega Dec 5, 2024
302c0e2
Merge branch 'master' into task/store_last_measure
AlvaroVega Dec 17, 2024
60bad44
Update CHANGES_NEXT_RELEASE
AlvaroVega Dec 17, 2024
564b40a
fix save device with last measure with mongodb 8
AlvaroVega Jan 8, 2025
c190fad
Update CHANGES_NEXT_RELEASE
fgalan Jan 8, 2025
ba058b1
Update CHANGES_NEXT_RELEASE
AlvaroVega Jan 8, 2025
df26df1
make storeLastMeasure configurable at device and group level
AlvaroVega Jan 9, 2025
a8bf666
update doc
AlvaroVega Jan 9, 2025
6d77aee
add storeLastMeasure to iotagent-manager model
AlvaroVega Jan 9, 2025
1f6f3f4
add example
AlvaroVega Jan 9, 2025
c08b427
add log
AlvaroVega Jan 10, 2025
65f5d3f
add lastMeasure to device api
AlvaroVega Jan 10, 2025
ecc6df1
Update admin.md
AlvaroVega Jan 10, 2025
38743b5
Update doc/admin.md
AlvaroVega Jan 13, 2025
10f0a94
Update doc/admin.md
AlvaroVega Jan 13, 2025
8494743
update api
AlvaroVega Jan 13, 2025
5ef6d15
Update doc/api.md
AlvaroVega Jan 13, 2025
1cab967
Update doc/api.md
AlvaroVega Jan 13, 2025
b8c5b6e
Update CHANGES_NEXT_RELEASE
AlvaroVega Jan 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- Add: store last measure in device (by id, apikey, service and subservice) (#1669)
AlvaroVega marked this conversation as resolved.
Show resolved Hide resolved
- Add: IOTA_STORE_LAST_MEASURE env var to set default store last measure behaviour at instance level (#1669)
- Upgrade express dep from 4.19.2 to 4.20.0
- Upgrade mongodb devdep from 4.17.1 to 4.17.2
- Upgrade mongoose dep from 5.13.20 to 8.4.4 (solving vulnerability CVE-2024-53900) (#1674)
3 changes: 2 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ var config = {
providerUrl: 'http://192.168.56.1:4041',
deviceRegistrationDuration: 'P1M',
defaultType: 'Thing',
expressLimit: '1Mb'
expressLimit: '1Mb',
storeLastMeasure: false
};

module.exports = config;
38 changes: 33 additions & 5 deletions doc/admin.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ allowing the computer to interpret the rest of the data with more clarity and de
```

Under mixed mode, **NGSI v2** payloads are used for context broker communications by default, but this payload may also
be switched to **NGSI LD** at group or device provisioning time using the `ngsiVersion` field in the
provisioning API. The `ngsiVersion` field switch may be added at either group or device level, with the device level
overriding the group setting.
be switched to **NGSI LD** at group or device provisioning time using the `ngsiVersion` field in the provisioning API.
The `ngsiVersion` field switch may be added at either group or device level, with the device level overriding the group
setting.

#### `server`

Expand Down Expand Up @@ -306,7 +306,8 @@ added `agentPath`:

#### `types`

This parameter includes additional groups configuration as described into the [Config group API](api.md#config-group-api) section.
This parameter includes additional groups configuration as described into the
[Config group API](api.md#config-group-api) section.

#### `service`

Expand Down Expand Up @@ -415,7 +416,33 @@ IotAgents, as all Express applications that use the body-parser middleware, have
size that the application will handle. This default limit for ioiotagnets are 1Mb. So, if your IotAgent receives a
request with a body that exceeds this limit, the application will throw a “Error: Request entity too large”.

The 1Mb default can be changed setting the `expressLimit` configuration parameter (or equivalente `IOTA_EXPRESS_LIMIT` environment variable).
The 1Mb default can be changed setting the `expressLimit` configuration parameter (or equivalente `IOTA_EXPRESS_LIMIT`
environment variable).

#### `storeLastMeasure`

If this flag is activated, last measure arrived to Device IoTAgent without be processed will be stored in Device under
lastMeasure field with in timestamp. This flag is overwritten by `storeLastMeasure` flag in group or device. This flag
is disabled by default.
AlvaroVega marked this conversation as resolved.
Show resolved Hide resolved

For example in a device document stored in MongoDB will be extended with a subdocument named lastMeasure like this:

```json
{
"lastMeasure": {
"timestamp": "2025-01-09T10:35:33.079Z",
"measure": [
[
{
"name": "level",
"type": "Text",
"value": "33"
AlvaroVega marked this conversation as resolved.
Show resolved Hide resolved
}
]
]
}
}
```

### Configuration using environment variables

Expand Down Expand Up @@ -479,6 +506,7 @@ overrides.
| IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION | `defaultEntityNameConjunction` |
| IOTA_RELAX_TEMPLATE_VALIDATION | `relaxTemplateValidation` |
| IOTA_EXPRESS_LIMIT | `expressLimit` |
| IOTA_STORE_LAST_MEASURE | `storeLastMeasure` |

Note:

Expand Down
11 changes: 9 additions & 2 deletions lib/commonConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ function processEnvironmentVariables() {
'IOTA_FALLBACK_PATH',
'IOTA_LD_SUPPORT_NULL',
'IOTA_LD_SUPPORT_DATASET_ID',
'IOTA_EXPRESS_LIMIT'
'IOTA_EXPRESS_LIMIT',
'IOTA_STORE_LAST_MEASURE'
];
const iotamVariables = [
'IOTA_IOTAM_URL',
Expand Down Expand Up @@ -474,6 +475,11 @@ function processEnvironmentVariables() {
} else {
config.expressLimit = config.expressLimit ? config.expressLimit : '1mb';
}
if (process.env.IOTA_STORE_LAST_MEASURE) {
config.storeLastMeasure = process.env.IOTA_STORE_LAST_MEASURE === 'true';
} else {
config.storeLastMeasure = config.storeLastMeasure === true;
}
}

function setConfig(newConfig) {
Expand Down Expand Up @@ -503,7 +509,8 @@ function getConfigForTypeInformation() {
multiCore: config.multiCore,
relaxTemplateValidation: config.relaxTemplateValidation,
defaultEntityNameConjunction: config.defaultEntityNameConjunction,
defaultType: config.defaultType
defaultType: config.defaultType,
storeLastMeasure: config.storeLastMeasure
};
return conf;
}
Expand Down
4 changes: 3 additions & 1 deletion lib/model/Device.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ const Device = new Schema({
autoprovision: Boolean,
explicitAttrs: Group.ExplicitAttrsType,
ngsiVersion: String,
payloadType: String
payloadType: String,
storeLastMeasure: Boolean,
lastMeasure: Object
});

function load() {
Expand Down
3 changes: 2 additions & 1 deletion lib/model/Group.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ const Group = new Schema({
defaultEntityNameConjunction: String,
ngsiVersion: String,
entityNameExp: String,
payloadType: String
payloadType: String,
storeLastMeasure: Boolean
});

function load() {
Expand Down
3 changes: 2 additions & 1 deletion lib/services/common/iotManagerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ function register(callback) {
entityNameExp: service.entityNameExp,
payloadType: service.payloadType,
endpoint: service.endpoint,
transport: service.transport
transport: service.transport,
storeLastMeasure: service.storeLastMeasure
};
}

Expand Down
32 changes: 32 additions & 0 deletions lib/services/devices/deviceRegistryMemory.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,40 @@ function getDevicesByAttribute(name, value, service, subservice, callback) {
}
}

function storeLastMeasure(measure, typeInformation, callback) {
if (
typeInformation &&
typeInformation.id &&
typeInformation.apikey &&
typeInformation.service &&
typeInformation.subservice
) {
getDevice(
typeInformation.id,
typeInformation.apikey,
typeInformation.service,
typeInformation.subservice,
function (error, data) {
if (!error) {
if (data) {
data.lastMeasure = measure;
}
if (callback) {
callback(null, data);
}
} else {
callback(error, null);
}
}
);
} else {
callback(null, null);
}
}

exports.getDevicesByAttribute = getDevicesByAttribute;
exports.store = storeDevice;
exports.storeLastMeasure = storeLastMeasure;
exports.update = update;
exports.remove = removeDevice;
exports.list = listDevices;
Expand Down
55 changes: 50 additions & 5 deletions lib/services/devices/deviceRegistryMongoDB.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ const attributeList = [
'explicitAttrs',
'ngsiVersion',
'subscriptions',
'payloadType'
'payloadType',
'storeLastMeasure'
];

/**
Expand Down Expand Up @@ -222,7 +223,7 @@ function findOneInMongoDB(queryParams, id, callback) {
* @param {String} subservice Division inside the service (optional).
*/
function getDeviceById(id, apikey, service, subservice, callback) {
let queryParams = {
const queryParams = {
id,
service,
subservice
Expand Down Expand Up @@ -254,9 +255,9 @@ function getDevice(id, apikey, service, subservice, callback) {

function getByNameAndType(name, type, service, servicepath, callback) {
context = fillService(context, { service, subservice: servicepath });
let optionsQuery = {
name: name,
service: service,
const optionsQuery = {
name,
service,
subservice: servicepath
};
if (type) {
Expand Down Expand Up @@ -319,6 +320,7 @@ function update(previousDevice, device, callback) {
data.timestamp = device.timestamp;
data.subscriptions = device.subscriptions;
data.payloadType = device.payloadType;
data.storeLastMeasure = device.storeLastMeasure;

/* eslint-disable-next-line new-cap */
const deviceObj = new Device.model(data);
Expand Down Expand Up @@ -392,8 +394,51 @@ function getDevicesByAttribute(name, value, service, subservice, callback) {
});
}

function storeLastMeasure(measure, typeInformation, callback) {
if (
typeInformation &&
typeInformation.id &&
typeInformation.apikey &&
typeInformation.service &&
typeInformation.subservice
) {
getDevice(
typeInformation.id,
typeInformation.apikey,
typeInformation.service,
typeInformation.subservice,
function (error, data) {
if (error) {
callback(error);
} else {
data.lastMeasure = { timestamp: new Date().toISOString(), measure };
/* eslint-disable-next-line new-cap */
const deviceObj = new Device.model(data);
deviceObj.isNew = false;
deviceObj
.save({})
.then((deviceDao) => {
callback(null, deviceDao.toObject());
})
.catch((error) => {
logger.debug(
fillService(context, deviceObj),
'Error storing device information: %s',
error
);
callback(new errors.InternalDbError(error));
});
}
}
);
} else {
callback(null, null);
}
}

exports.getDevicesByAttribute = alarmsInt(constants.MONGO_ALARM, getDevicesByAttribute);
exports.store = alarmsInt(constants.MONGO_ALARM, storeDevice);
exports.storeLastMeasure = alarmsInt(constants.MONGO_ALARM, storeLastMeasure);
exports.update = alarmsInt(constants.MONGO_ALARM, update);
exports.remove = alarmsInt(constants.MONGO_ALARM, removeDevice);
exports.list = alarmsInt(constants.MONGO_ALARM, listDevices);
Expand Down
12 changes: 10 additions & 2 deletions lib/services/devices/deviceService.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuratio
if (configuration && configuration.payloadType !== undefined && deviceData.payloadType === undefined) {
deviceData.payloadType = configuration.payloadType;
}
if (configuration && configuration.storeLastMeasure !== undefined && deviceData.storeLastMeasure === undefined) {
deviceData.storeLastMeasure = configuration.storeLastMeasure;
}
logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
callback(null, deviceData);
}
Expand Down Expand Up @@ -285,7 +288,7 @@ function registerDevice(deviceObj, callback) {
let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(deviceData);
attrList = deviceData.staticAttributes ? attrList.concat(deviceData.staticAttributes) : attrList;
attrList = configuration.staticAttributes ? attrList.concat(configuration.staticAttributes) : attrList;
let ctxt = expressionPlugin.extractContext(attrList);
const ctxt = expressionPlugin.extractContext(attrList);
try {
entityName = expressionPlugin.applyExpression(configuration.entityNameExp, ctxt, deviceData);
} catch (e) {
Expand Down Expand Up @@ -616,7 +619,7 @@ function findOrCreate(deviceId, apikey, group, callback) {
} else if (error.name === 'DEVICE_NOT_FOUND') {
const newDevice = {
id: deviceId,
apikey: apikey,
apikey,
service: group.service,
subservice: group.subservice,
type: group.type
Expand Down Expand Up @@ -692,6 +695,10 @@ function retrieveDevice(deviceId, apiKey, callback) {
}
}

function storeLastMeasure(measure, typeInformation, callback) {
config.getRegistry().storeLastMeasure(measure, typeInformation, callback);
}

exports.listDevices = intoTrans(context, checkRegistry)(listDevices);
exports.listDevicesWithType = intoTrans(context, checkRegistry)(listDevicesWithType);
exports.getDevice = intoTrans(context, checkRegistry)(getDevice);
Expand All @@ -708,4 +715,5 @@ exports.retrieveDevice = intoTrans(context, checkRegistry)(retrieveDevice);
exports.mergeDeviceWithConfiguration = mergeDeviceWithConfiguration;
exports.findOrCreate = findOrCreate;
exports.findConfigurationGroup = findConfigurationGroup;
exports.storeLastMeasure = storeLastMeasure;
exports.init = init;
4 changes: 3 additions & 1 deletion lib/services/devices/devices-NGSI-v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,9 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
if ('transport' in newDevice && newDevice.transport !== undefined) {
oldDevice.transport = newDevice.transport;
}

if ('storeLastMeasure' in newDevice && newDevice.storeLastMeasure !== undefined) {
oldDevice.storeLastMeasure = newDevice.storeLastMeasure;
}
callback(null, oldDevice);
} else {
callback(new errors.DeviceNotFound(newDevice.id, newDevice));
Expand Down
3 changes: 2 additions & 1 deletion lib/services/groups/groupRegistryMongoDB.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ const attributeList = [
'defaultEntityNameConjunction',
'ngsiVersion',
'entityNameExp',
'payloadType'
'payloadType',
'storeLastMeasure'
];

function createGroup(group, callback) {
Expand Down
11 changes: 10 additions & 1 deletion lib/services/ngsi/ngsiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
const async = require('async');
const apply = async.apply;
const statsRegistry = require('../stats/statsRegistry');
const deviceService = require('../devices/deviceService');
const intoTrans = require('../common/domain').intoTrans;
const fillService = require('./../common/domain').fillService;
const errors = require('../../errors');
Expand Down Expand Up @@ -68,8 +69,16 @@ function init() {
* @param {String} token User token to identify against the PEP Proxies (optional).
*/
function sendUpdateValue(entityName, attributes, typeInformation, token, callback) {
// check config about store last measure
const newCallback = statsRegistry.withStats('updateEntityRequestsOk', 'updateEntityRequestsError', callback);
entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, newCallback);
if (typeInformation.storeLastMeasure) {
logger.debug(context, 'StoreLastMeasure for %j', typeInformation);
deviceService.storeLastMeasure(attributes, typeInformation, function () {
return entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, newCallback);
});
} else {
entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, newCallback);
}
}

/**
Expand Down
Loading
Loading