Skip to content

Commit 7582e5e

Browse files
committed
Set statistical heartRate
1 parent 4213100 commit 7582e5e

File tree

2 files changed

+165
-45
lines changed

2 files changed

+165
-45
lines changed

iot-devices/lib/writer.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ exports.write = function (deviceId, state) {
1616
}
1717

1818
const data = JSON.parse(json.format(state, false));
19-
const animal = `urn:ngsi-ld:Animal:${deviceId}`;
20-
const device = `urn:ngsi-ld:Device:${deviceId}`;
21-
const line = `${animal},${data.bpm},${data.gps},${device},${data.d},${data.o}`;
22-
19+
let line = `${data.o},${deviceId},${data.bpm},${data.gps},${data.d},`;
20+
line = line + `${data.accel_x},${data.accel_y},${data.bmp},${data.body_temp},${data.step_count},${data.by}`;
2321
stream.write(line + '\n');
2422
};

iot-devices/models/animals.js

Lines changed: 163 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ const numberOfPigs = process.env.PIG_COUNT || 5;
1414
const numberOfCows = process.env.COW_COUNT || 5;
1515
const numberOfFields = process.env.FIELD_COUNT || 8;
1616

17+
const COW_HEART_RATE = 50;
18+
const COW_BODY_TEMPERATURE = 38.6;
19+
const COW_AVERAGE_STEPS = 10;
20+
const COW_ACCEL_X = 0.133;
21+
const COW_ACCEL_Y = 0.4;
22+
23+
const ABNORMAL_COW_HEART_RATE = 68;
24+
const PIG_HEART_RATE = 60;
25+
1726
const ANIMAL_STATUS = Object.freeze({
1827
ILL: 'ill',
1928
IN_CALF: 'calf',
@@ -24,6 +33,99 @@ const ANIMAL_STATUS = Object.freeze({
2433
LONELY: 'lonely',
2534
});
2635

36+
37+
function generateRange(mean, sd) {
38+
function createListOfNNumbersBetweenAAndB(n, a, b) {
39+
const listOfN = Array(...new Array(n));
40+
return listOfN.map(() => Math.random() * (b - a) + a);
41+
}
42+
43+
function computeMeanSdAndItervalRangeMinMax(list) {
44+
const sum = list.reduce((a, b) => a + b, 0);
45+
const mean = sum / list.length;
46+
const sumMinusMean = list.reduce((a, b) => a + (b - mean) * (b - mean), 0);
47+
48+
return {
49+
mean: mean,
50+
sd: Math.sqrt(sumMinusMean / (list.length - 1)),
51+
range: [Math.min(...list), Math.max(...list)],
52+
};
53+
}
54+
55+
function transfomListToExactMeanAndSd(list, mean, sd) {
56+
const current = computeMeanSdAndItervalRangeMinMax(list);
57+
return list.map((n) => (sd * (n - current.mean)) / current.sd + mean);
58+
}
59+
60+
const ARRAY_SIZE = 100;
61+
const MIN = 1;
62+
const MAX = 10;
63+
let list = createListOfNNumbersBetweenAAndB(ARRAY_SIZE, MIN, MAX);
64+
let newList = transfomListToExactMeanAndSd(list, mean, sd);
65+
66+
return newList;
67+
}
68+
69+
const STATUS = {
70+
AT_REST: {
71+
code: 0,
72+
heartRates: generateRange(COW_HEART_RATE, 2).map((n) => (parseFloat((n).toFixed(2)))),
73+
temperatures: generateRange(COW_BODY_TEMPERATURE, 1).map((n) => (parseFloat((n).toFixed(2)))),
74+
steps: generateRange(COW_AVERAGE_STEPS, 4).map((n) => (Math.floor(n * 1000))),
75+
x: generateRange(COW_ACCEL_X, 1).map((n) => (parseFloat((n).toFixed(4)))),
76+
y: generateRange(COW_ACCEL_Y, 1).map((n) => (parseFloat((n).toFixed(4))))
77+
},
78+
FORAGING: {
79+
code: 3,
80+
heartRates: [],
81+
temperatures: [],
82+
steps: [],
83+
x: [],
84+
y: []
85+
},
86+
DRINKING: {
87+
code: 5,
88+
heartRates: generateRange(COW_HEART_RATE + 0.5, 0.4).map((n) => (parseFloat((n).toFixed(2)))),
89+
temperatures: generateRange(COW_BODY_TEMPERATURE + 0.2, 0.2).map((n) => (parseFloat((n).toFixed(2)))),
90+
steps: generateRange(COW_AVERAGE_STEPS - 2, 1).map((n) => (Math.floor(n * 1000))),
91+
x: generateRange(COW_ACCEL_X, 0.8),
92+
y: generateRange(COW_ACCEL_Y, 1.2)
93+
},
94+
WALLOWING: {
95+
code: 5,
96+
heartRates: [],
97+
temperatures: [],
98+
steps: [],
99+
x: [],
100+
y: []
101+
},
102+
GRAZING: {
103+
code: 6,
104+
heartRates: generateRange(COW_HEART_RATE - 1, 3).map((n) => (parseFloat((n).toFixed(2)))),
105+
temperatures: generateRange(COW_BODY_TEMPERATURE -0.1, 1).map((n) => (parseFloat((n).toFixed(2)))),
106+
steps: generateRange(COW_AVERAGE_STEPS, 3).map((n) => (Math.floor(n * 1000))),
107+
x: generateRange(COW_ACCEL_X, .1),
108+
y: generateRange(COW_ACCEL_Y, 2)
109+
},
110+
MOVING: {
111+
code: 7,
112+
heartRates: generateRange(COW_HEART_RATE + 10, 2).map((n) => (parseFloat((n).toFixed(2)))),
113+
temperatures: generateRange(COW_BODY_TEMPERATURE + 0.2, 2).map((n) => (parseFloat((n).toFixed(2)))),
114+
steps: generateRange(COW_AVERAGE_STEPS + 2, 4).map((n) => (Math.floor(n * 1000))),
115+
x: generateRange(COW_ACCEL_X, 2),
116+
y: generateRange(COW_ACCEL_Y, 2)
117+
},
118+
MOUNTING: {
119+
code: 9,
120+
heartRates: generateRange(COW_HEART_RATE + 3, 0.5).map((n) => (parseFloat((n).toFixed(2)))),
121+
temperatures: generateRange(COW_BODY_TEMPERATURE, 1).map((n) => (parseFloat((n).toFixed(2)))),
122+
steps: generateRange(COW_AVERAGE_STEPS, 4).map((n) => (Math.floor(n * 1000))),
123+
x: generateRange(COW_ACCEL_X, 1.1),
124+
y: generateRange(COW_ACCEL_Y, 0.6)
125+
},
126+
};
127+
128+
27129
const PIG_ACTIVITY = [
28130
'AT_REST',
29131
'FORAGING',
@@ -48,9 +150,6 @@ const OFFSET_RATE = {
48150
WALLOWING: 5,
49151
};
50152

51-
const COW_HEART_RATE = 50;
52-
const ABNORMAL_COW_HEART_RATE = 68;
53-
const PIG_HEART_RATE = 60;
54153

55154
myCache.init().then(() => {
56155
for (let i = 1; i <= numberOfPigs; i++) {
@@ -159,39 +258,21 @@ function setDeviceState(deviceId, state, isSensor = true, force = false) {
159258
}
160259

161260
function getStatusCode(status) {
162-
let code = 0;
163-
switch (status) {
164-
case 'AT_REST':
165-
case 'IDLE':
166-
case 'OFF':
167-
code = 0;
168-
break;
169-
case 'ON':
170-
code = 1;
171-
break;
172-
case 'FORAGING':
173-
code = 3;
174-
break;
175-
case 'DRINKING':
176-
code = 5;
177-
break;
178-
case 'WALLOWING':
179-
code = 5;
180-
break;
181-
case 'GRAZING':
182-
code = 6;
183-
break;
184-
case 'MOVING':
185-
code = 7;
186-
break;
187-
case 'SOWING':
188-
code = 8;
189-
break;
190-
case 'MOUNTING':
191-
code = 9;
192-
break;
193-
}
194-
return code;
261+
return STATUS[status].code;
262+
}
263+
264+
function getRandomFromArray(array){
265+
console.log(array)
266+
const randomElement = array[Math.floor(Math.random() * array.length)];
267+
return randomElement;
268+
}
269+
270+
function setRawReadings(state, desc) {
271+
state.accel_x = getRandomFromArray(STATUS[desc].x).toFixed(4);
272+
state.accel_y = getRandomFromArray(STATUS[desc].y).toFixed(4);
273+
state.bmp = getRandomFromArray(STATUS[desc].heartRates);
274+
state.body_temp = getRandomFromArray(STATUS[desc].temperatures);
275+
state.step_count = getRandomFromArray(STATUS[desc].steps);
195276
}
196277

197278
async function directedWalk(state, deviceId, goal) {
@@ -211,8 +292,9 @@ async function directedWalk(state, deviceId, goal) {
211292
const ty = parseFloat(targetLocation[0]);
212293
const tx = parseFloat(targetLocation[1]);
213294

214-
const offset1 = Math.abs(
215-
0 + parseFloat(location[1]) - tx + parseFloat(location[0]) - ty
295+
const offset1 = (
296+
Math.abs(0 + parseFloat(location[1]) - tx) +
297+
Math.abs(0 + parseFloat(location[0]) - ty)
216298
).toFixed(4);
217299

218300
if (tx > x) {
@@ -228,8 +310,10 @@ async function directedWalk(state, deviceId, goal) {
228310
y = addAndTrim(y, false, weather);
229311
}
230312

231-
const offset2 = Math.abs(0 + x - tx + y - ty).toFixed(4);
232-
return { gps: y + ',' + x, complete: offset2 >= offset1 };
313+
const offset2 = (Math.abs(0 + x - tx) + Math.abs(0 + y - ty)).toFixed(4);
314+
315+
const onHeat = target.st && target.st.includes(ANIMAL_STATUS.HEAT);
316+
return { gps: y + ',' + x, complete: offset2 >= offset1, onHeat };
233317
}
234318

235319
function randomWalk(state, deviceId, lng, lat) {
@@ -280,6 +364,33 @@ function selectTarget(id, type, animals) {
280364
return target;
281365
}
282366

367+
function findNeighbour(id, state, animals) {
368+
const location = state.gps.split(',');
369+
const y = parseFloat(location[0]);
370+
const x = parseFloat(location[1]);
371+
372+
let nearest = undefined;
373+
let distance = Infinity;
374+
_.forEach(animals, function (animal) {
375+
if (animal.id !== id) {
376+
const animalLocation = animal.state.gps.split(',');
377+
const ty = parseFloat(animalLocation[0]);
378+
const tx = parseFloat(animalLocation[1]);
379+
380+
const animalDistance = (
381+
Math.abs(0 + x - tx) + Math.abs(0 + y - ty)
382+
).toFixed(4);
383+
384+
if (distance > animalDistance) {
385+
nearest = animal.id;
386+
distance = animalDistance;
387+
}
388+
}
389+
});
390+
391+
return nearest;
392+
}
393+
283394
async function getAllAnimalData() {
284395
const deviceIds = myCache.keys();
285396
const animals = {
@@ -398,8 +509,11 @@ function sendAnimalCollarReadings(animals) {
398509

399510
state.s = getStatusCode(state.d);
400511
if (state.o) {
512+
setRawReadings(state, state.d);
401513
state.o++;
402514
}
515+
516+
state.by = findNeighbour(cow.id, state, animals.cow);
403517
setDeviceState(cow.id, toUltraLight(cow.state), true);
404518
} else if (isLonely) {
405519
directedWalk(state, cow.id, 'lonely').then((result) => {
@@ -411,13 +525,20 @@ function sendAnimalCollarReadings(animals) {
411525
animalStatus = animalStatus.filter((e) => e !== ANIMAL_STATUS.LONELY);
412526
animalStatus.push(ANIMAL_STATUS.HUNGRY);
413527
state.st = animalStatus.join(',');
414-
state.d = 'MOUNTING';
528+
529+
state.d = 'AT_REST';
530+
if (result.onHeat || getRandom() > 3) {
531+
state.d = 'MOUNTING';
532+
}
533+
415534
state.s = getStatusCode(state.d);
416535
}
417536
if (state.o) {
537+
setRawReadings(state, state.d);
418538
state.o++;
419539
}
420540

541+
state.by = findNeighbour(cow.id, state, animals.cow);
421542
setDeviceState(cow.id, toUltraLight(cow.state), true);
422543
});
423544
} else if (isThirsty) {
@@ -437,6 +558,7 @@ function sendAnimalCollarReadings(animals) {
437558
}
438559

439560
if (state.o) {
561+
setRawReadings(state, state.d);
440562
state.o++;
441563
}
442564
setDeviceState(cow.id, toUltraLight(cow.state), true);

0 commit comments

Comments
 (0)