@@ -14,6 +14,15 @@ const numberOfPigs = process.env.PIG_COUNT || 5;
1414const numberOfCows = process . env . COW_COUNT || 5 ;
1515const 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+
1726const 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+
27129const 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
55154myCache . init ( ) . then ( ( ) => {
56155 for ( let i = 1 ; i <= numberOfPigs ; i ++ ) {
@@ -159,39 +258,21 @@ function setDeviceState(deviceId, state, isSensor = true, force = false) {
159258}
160259
161260function 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
197278async 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
235319function 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+
283394async 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