Skip to content

Commit

Permalink
Merge pull request #208 from telefonicaid/feature/205_time_conditions
Browse files Browse the repository at this point in the history
Feature/205 time conditions
  • Loading branch information
fgalan authored Oct 13, 2016
2 parents da563b7 + e9ea16f commit cd7f52e
Show file tree
Hide file tree
Showing 6 changed files with 491 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Add: Processing location attributes for EPL (#198)
- Add: Processing time attributes for EPL (#205)

175 changes: 174 additions & 1 deletion documentation/plain_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,4 +482,177 @@ circle equation, `(x - a)^2 + (y - b)^2 = d^2`, being `(a, b)` 618618.8286057833
of Cuenca and `d` the distance of 5 000 m.

Note: for long distances the precision of the computations and the distortion of the projection can introduce some degree
of inaccuracy.
of inaccuracy.

## Time fields
Some attributes and metadata, supposed to contain a time in ISO8601 format, will generate a pseudo-attribute with the
same name as the attribute (or metadata field) and a suffix "__ts", with the parsed value as milliseconds for Unix epoch.
This value makes easier to write the EPL text which involves time comparisons. The fields (attribute or metadata) supposed
to convey time information are

* Fields named `TimeInstant`
* Fields of type `DateTime`
* Fields of type `urn:x-ogc:def:trs:IDAS:1.0:ISO8601`

Additionally, some derived pseudo-attributes are included also

* `x__day`: the day of the month (1-31) for the specified date according to local time.
* `x__month`: the month (1-12) in the specified date according to local time.
* `x__year`: the year (4 digits) of the specified date according to local time.
* `x__hour`: the hour (0-23) in the specified date according to local time.
* `x__minute`: the minutes (0-59) in the specified date according to local time.
* `x__second`: the seconds (0-59) in the specified date according to local time.
* `x__millisecond`: the milliseconds (0-999) in the specified date according to local time.
* `x__dayUTC`: the day of the month (1-31) in the specified date according to universal time.
* `x__monthUTC`: the month (1-12) in the specified date according to universal time.
* `x__yearUTC`: the year (4 digits) of the specified date according to universal time.
* `x__hourUTC`: the hour (0-23) in the specified date according to universal time.
* `x__minuteUTC`: the minutes (0-59) in the specified date according to universal time.
* `x__secondUTC`: the seconds (0-59) in the specified date according to universal time.
* `x__millisecondUTC`: the milliseconds (0-999) in the specified date according to universal time.

So, an incoming notification like

```json
{
"subscriptionId": "51c04a21d714fb3b37d7d5a7",
"originator": "localhost",
"contextResponses": [
{
"contextElement": {
"attributes": [
{
"name": "TimeInstant",
"value": "2014-04-29T13:18:05Z"
},
{
"name": "birthdate",
"type": "urn:x-ogc:def:trs:IDAS:1.0:ISO8601",
"value": "2014-04-29T13:18:05Z"
},
{
"name": "hire",
"type": "DateTime",
"value": "2016-10-13T12:10:44.149Z"
},
{
"name": "role",
"value": "benevolent dictator for life",
"metadatas": [{
"name": "when",
"value": "2014-04-29T13:18:05Z",
"type": "DateTime"
}]
}
],
"type": "employee",
"isPattern": "false",
"id": "John Doe"
},
"statusCode": {
"code": "200",
"reasonPhrase": "OK"
}
}
]
}
```

will send to core the "event"
```json
{
"noticeId":"799635b0-914f-11e6-836b-bf1691c99768",
"noticeTS":1476368120971,
"id":"John Doe",
"type":"employee",
"isPattern":"false",
"subservice":"/",
"service":"unknownt",
"TimeInstant":"2014-04-29T13:18:05Z",
"TimeInstant__ts":1398777485000,
"TimeInstant__day":29,
"TimeInstant__month":4,
"TimeInstant__year":2014,
"TimeInstant__hour":15,
"TimeInstant__minute":18,
"TimeInstant__second":5,
"TimeInstant__millisecond":0,
"TimeInstant__dayUTC":29,
"TimeInstant__monthUTC":4,
"TimeInstant__yearUTC":2014,
"TimeInstant__hourUTC":13,
"TimeInstant__minuteUTC":18,
"TimeInstant__secondUTC":5,
"TimeInstant__millisecondUTC":0,
"birthdate":"2014-04-29T13:18:05Z",
"birthdate__type":"urn:x-ogc:def:trs:IDAS:1.0:ISO8601",
"birthdate__ts":1398777485000,
"birthdate__day":29,
"birthdate__month":4,
"birthdate__year":2014,
"birthdate__hour":15,
"birthdate__minute":18,
"birthdate__second":5,
"birthdate__millisecond":0,
"birthdate__dayUTC":29,
"birthdate__monthUTC":4,
"birthdate__yearUTC":2014,
"birthdate__hourUTC":13,
"birthdate__minuteUTC":18,
"birthdate__secondUTC":5,
"birthdate__millisecondUTC":0,
"hire":"2014-04-29T13:18:05Z",
"hire__type":"DateTime",
"hire__ts":1398777485000,
"hire__day":29,
"hire__month":4,
"hire__year":2014,
"hire__hour":15,
"hire__minute":18,
"hire__second":5,
"hire__millisecond":0,
"hire__dayUTC":29,
"hire__monthUTC":4,
"hire__yearUTC":2014,
"hire__hourUTC":13,
"hire__minuteUTC":18,
"hire__secondUTC":5,
"hire__millisecondUTC":0,
"role":"benevolent dictator for life",
"role__metadata__when":"2014-04-29T13:18:05Z",
"role__metadata__when__type":"DateTime",
"role__metadata__when__ts":1398777485000,
"role__metadata__when__day":29,
"role__metadata__when__month":4,
"role__metadata__when__year":2014,
"role__metadata__when__hour":15,
"role__metadata__when__minute":18,
"role__metadata__when__second":5,
"role__metadata__when__millisecond":0,
"role__metadata__when__dayUTC":29,
"role__metadata__when__monthUTC":4,
"role__metadata__when__yearUTC":2014,
"role__metadata__when__hourUTC":13,
"role__metadata__when__minuteUTC":18,
"role__metadata__when__secondUTC":5,
"role__metadata__when__millisecondUTC":0
}
```

A rule that will check if the employee has been hired in the last half hour, could be

```json
{
"name": "rule_time",
"text": "select *, \"rule_time\" as ruleName from pattern [every ev=iotEvent(cast(cast(hire__ts?,String),float) > current_timestamp - 30*60*1000)]",
"action": {
"type": "email",
"template": "So glad with our new ${role}, ${id}!",
"parameters": {
"to": "[email protected]",
"from": "[email protected]",
"subject": "Welcome ${id}!"
}
}
}
```
13 changes: 13 additions & 0 deletions examples/rule_time.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "rule_time",
"text": "select *, \"rule_time\" as ruleName from pattern [every ev=iotEvent(cast(cast(hire__ts?,String),float) > current_timestamp - 30*60*1000)]",
"action": {
"type": "email",
"template": "So glad with our new ${role}, ${id}!",
"parameters": {
"to": "[email protected]",
"from": "[email protected]",
"subject": "Welcome ${id}!"
}
}
}
29 changes: 29 additions & 0 deletions examples/time_notify.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"subscriptionId": "51c04a21d714fb3b37d7d5a7",
"originator": "localhost",
"contextResponses": [
{
"contextElement": {
"attributes": [
{
"name": "hire",
"type": "DateTime",
"value": "2016-10-13T12:10:44.149Z"
},
{
"name": "role",
"value": "benevolent dictator for life"
}
],
"type": "employee",
"isPattern": "false",
"id": "John Doe"
},
"statusCode": {
"code": "200",
"reasonPhrase": "OK"
}
}
]
}

67 changes: 66 additions & 1 deletion lib/models/notices.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,60 @@ function parseLocation(locStr) {
y: utmResult.coord.y
};
}

// http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
function parseDate(isoStr) {
var date,
ts,
error;

ts = Date.parse(isoStr);
if (isNaN(ts)) {
error = new errors.InvalidDateTime(isoStr);
myutils.logErrorIf(error);
return error;
} else {
date = new Date(isoStr);
return {
ts: ts,
day: date.getDate(),
month: date.getMonth() + 1,
year: date.getFullYear(),
hour: date.getHours(),
minute: date.getMinutes(),
second: date.getSeconds(),
millisecond: date.getMilliseconds(),
dayUTC: date.getUTCDate(),
monthUTC: date.getUTCMonth() + 1,
yearUTC: date.getUTCFullYear(),
hourUTC: date.getUTCHours(),
minuteUTC: date.getUTCMinutes(),
secondUTC: date.getUTCSeconds(),
millisecondUTC: date.getUTCMilliseconds()
};
}
}

function addTimeInfo(object, key, timeInfo) {
if (timeInfo && !(timeInfo instanceof Error)) {
for (var p in timeInfo) {
if (timeInfo.hasOwnProperty(p)) {
object[key + '__' + p] = timeInfo[p];
}
}
}
}

function processCBNotice(ncr) {
var n = {},
pp,
temp,
location = null,
timeInfo = null,
localError = null;

n.noticeId = uuid.v1();
n.received = new Date();
n.noticeTS = Date.now();

try {
n.id = ncr.contextResponses[0].contextElement.id;
Expand All @@ -92,11 +137,25 @@ function processCBNotice(ncr) {
location = parseLocation(attr.value);
}

if (attr.name === 'TimeInstant' ||
attr.type === 'urn:x-ogc:def:trs:IDAS:1.0:ISO8601' ||
attr.type === 'DateTime') {
timeInfo = parseDate(attr.value);
addTimeInfo(n, attr.name, timeInfo);
}

if (attr.metadatas) {
for (var i = 0; i < attr.metadatas.length; i++) {
n[attr.name + '__metadata__' + attr.metadatas[i].name] = attr.metadatas[i].value;
n[attr.name + '__metadata__' + attr.metadatas[i].name + '__type'] = attr.metadatas[i].type;

if (attr.metadatas[i].name === 'TimeInstant' ||
attr.metadatas[i].type === 'urn:x-ogc:def:trs:IDAS:1.0:ISO8601' ||
attr.metadatas[i].type === 'DateTime') {
timeInfo = parseDate(attr.metadatas[i].value);
addTimeInfo(n, attr.name + '__metadata__' + attr.metadatas[i].name, timeInfo);
}

// Deprecated location in NGSV1
// see links in issues/198
if (attr.metadatas[i].name === 'location') {
Expand Down Expand Up @@ -158,6 +217,7 @@ function DoNotice(orionN, callback) {

module.exports.Do = DoNotice;
module.exports.ParseLocation = parseLocation;
module.exports.ProcessCBNotice = processCBNotice;
/**
* Constructors for possible errors from this module
*
Expand Down Expand Up @@ -196,6 +256,11 @@ module.exports.errors = errors;
this.message = 'latitude is not valid ' + msg;
this.httpCode = 400;
};
errors.InvalidDateTime = function InvalidDateTime(msg) {
this.name = 'INVALID_DATETIME';
this.message = 'datetime is not valid ' + msg;
this.httpCode = 400;
};
Object.keys(errors).forEach(function(element) {
util.inherits(errors[element], Error);
});
Expand Down
Loading

0 comments on commit cd7f52e

Please sign in to comment.