##// END OF EJS Templates
.
Armando123x -
r225:3f6ada0723f2
parent child
Show More
@@ -1,1881 +1,1876
1 1 import gc
2 2 import os
3 3 import io
4 4 import cv2
5 5 import json
6 6 import pytz
7 7 import busio
8 8 import board
9 9 import gzip
10 10 import random
11 11 import numpy
12 12 import base64
13 13 import requests
14 14 import traceback
15 15 import adafruit_ina219
16 16 import RPi.GPIO as GPIO
17 17 import adafruit_lidarlite
18 18 import urllib.request
19 19
20 20 from time import sleep
21 21 from copy import deepcopy
22 22 from PIL import Image, ImageOps
23 23 from datetime import datetime,time
24 24 from contextlib import contextmanager
25 25 from requests.auth import HTTPDigestAuth
26 26 from adafruit_ina219 import ADCResolution, BusVoltageRange
27 27
28 28
29 29
30 30
31 31 #---------------------------------------#
32 32 def load_version():
33 33 try:
34 34 model = None
35 35 with open('/proc/cpuinfo', 'r') as cpuinfo:
36 36 for line in cpuinfo:
37 37 if line.startswith('Hardware'):
38 38 hardware = line.split(':')[1].strip()
39 39 elif line.startswith('Revision'):
40 40 revision = line.split(':')[1].strip()
41 41 elif line.startswith('Model'):
42 42 model = line.split(':')[1].strip()
43 43
44 44 return model
45 45 except:
46 46 return False
47 47
48 48
49 49 model = load_version()
50 50
51 51
52 52
53 53 if 'Raspberry Pi Zero' in model :
54 54
55 55 '''
56 56 Importamos la versión simple
57 57 '''
58 58
59 59 TOTAL_BUFFER_VIDEO = 10
60 60 import tflite_runtime.interpreter as lite
61 61
62 62
63 63 elif 'Raspberry Pi 4' in model :
64 64 '''
65 65
66 66 Agregar más dispositivos si es necesario
67 67
68 68 '''
69 69 TOTAL_BUFFER_VIDEO = 150
70 70 ######################################################################
71 71 ######################################################################
72 72 ######################################################################
73 73
74 74 # import tqdm
75 75 # import keras
76 76 # import random
77 77 # import einops
78 78 # import pathlib
79 79 # import itertools
80 80 # import collections
81 81 # import tensorflow as tf
82 82
83 83 TOTAL_BUFFER_VIDEO = 10
84 84 import tflite_runtime.interpreter as lite
85 85
86 86
87 87 ##################################################################################################################################################################################################################
88 88 ##################################################################################################################################################################################################################
89 89 ##################################################################################################################################################################################################################
90 90 ##################################################################################################################################################################################################################
91 91 ##################################################################################################################################################################################################################
92 92 ##################################################################################################################################################################################################################
93 93 #---------------------------------------#
94 94
95 95
96 96 #------------------------------------#
97 97 i2c = busio.I2C(board.SCL, board.SDA)
98 98 #------------------------------------#
99 99
100 100
101 101 @contextmanager
102 102 def locked(lock):
103 103 lock.acquire()
104 104 try:
105 105 yield
106 106 finally:
107 107 lock.release()
108 108
109 109
110 110
111 111 def format_frames(frame, output_size):
112 112 """
113 113 Pad and resize an image from a video.
114 114
115 115 Args:
116 116 frame: Image that needs to resized and padded.
117 117 output_size: Pixel size of the output frame image.
118 118
119 119 Return:
120 120 Formatted frame with padding of specified output size.
121 121 """
122 122 ########frame = tf.image.convert_image_dtype(frame, tf.float32)
123 123 ########frame = tf.image.resize_with_pad(frame, *output_size)
124 124
125 125
126 126 frame = Image.fromarray(frame)
127 127 frame = ImageOps.pad(frame,output_size,method=Image.Resampling.BILINEAR)
128 128
129 129 frame = numpy.array(frame)/255.0
130 130
131 131 return frame
132 132
133 133
134 134 def frames_from_video_file(video, n_frames, output_size = (224,224), frame_step = 15):
135 135 """
136 136 Creates frames from each video file present for each category.
137 137
138 138 Args:
139 139 video_path: File path to the video.
140 140 n_frames: Number of frames to be created per video file.
141 141 output_size: Pixel size of the output frame image.
142 142
143 143 Return:
144 144 An NumPy array of frames in the shape of (n_frames, height, width, channels).
145 145 """
146 146 # Read each video frame by frame
147 147 result = []
148 148 #src = cv2.VideoCapture(str(video_path))
149 149
150 150 src = video
151 151
152 152 video_length = len(src)
153 153
154 154 need_length = 1 + (n_frames - 1) * frame_step
155 155
156 156 if need_length > video_length:
157 157 start = 0
158 158 else:
159 159 max_start = video_length - need_length
160 160 start = random.randint(0, max_start + 1)
161 161
162 162 # ret is a boolean indicating whether read was successful, frame is the image itself
163 163
164 164
165 165 for _ in range(n_frames):
166 166 frame = video[start]
167 167 frame = format_frames(frame, output_size)
168 168 result.append(frame)
169 169 start += frame_step
170 170 if start >= video_length:
171 171 break
172 172
173 173 result = numpy.array(result)[..., [2, 1, 0]]
174 174
175 175 result = result.reshape((1,result.shape[0],result.shape[1],result.shape[2],result.shape[3]))
176 176
177 177 return result
178 178
179 179
180 180
181 181
182 182 class MyErrorForManage(Exception):
183 183 def __init__(self, mensaje):
184 184 super().__init__(mensaje)
185 185 self.mensaje = mensaje
186 186
187 187
188 188 class BytesEncoder(json.JSONEncoder):
189 189 def default(self, obj):
190 190 if isinstance(obj, bytes):
191 191 return obj.decode('utf-8')
192 192 return json.JSONEncoder.default(self, obj)
193 193
194 194
195 195
196 196 def on_connect(client, userdata, flags, rc):
197 197 print("Connected with result code " + str(rc))
198 198 print("UserData= " + str(userdata))
199 199 print("flags= " + str(flags))
200 200 print("")
201 201
202 202
203 203 class VarsJons(object):
204 204
205 205 id = None
206 206 location = None
207 207 data = None
208 208 debug = False
209 209 type_weights = None
210 210 store_data = False
211 211 latitude = None
212 212 longitude = None
213 213
214 214 vars_mqtt = None
215 215 vars_gpio = None
216 216 vars = None
217 217 weights = None
218 218
219 219 def __init__(self):
220 220
221 221 self.path_file = '/others/vars.json'
222 222
223 223 self.load_auth_data()
224 224
225 225 def load_auth_data(self):
226 226 try:
227 227 with open(self.path_file, 'r') as file:
228 228 self.data = json.load(file)
229 229
230 230 except FileNotFoundError:
231 231
232 232 raise FileNotFoundError("Archivo auth.json no encontrado en el directorio.")
233 233
234 234 else:
235 235
236 236 self.vars = self.data["vars"]
237 237 self.vars_mqtt = self.data['mqtt']
238 238 self.vars_gpio = self.data['gpio']
239 239 self.debug = self.data['debug']
240 240 self.latitude = self.data["latitude"]
241 241 self.longitude = self.data["longitude"]
242 242 self.weights = self.data['weights']
243 243 self.id = self.data['id_device']
244 244 self.location = self.data["location"]
245 245 self.inference_mode = self.data['inference_mode']
246 246 self.type_weights = self.data['type_weights']
247 247 self.store_data = self.data['store_data']
248 248 self.camera = self.data['camera']
249 249
250 250 def save_json(self):
251 251
252 252 try:
253 253 self.data["vars"] = self.vars
254 254 self.data["debug"] = self.debug
255 255 self.data["location"] = self.location
256 256 self.data['mqtt'] = self.vars_mqtt
257 257 self.data["gpio"] = self.vars_gpio
258 258 self.data['debug'] = self.debug
259 259 self.data["weights"] = self.weights
260 260 self.data["id_device"] = self.id
261 261 self.data["type_weights"] = self.type_weights
262 262 self.data['camera'] = self.camera
263 263
264 264 except:
265 265
266 266 pass
267 267
268 268 else:
269 269
270 270 try:
271 271 with open(self.path_file,'w') as file:
272 272 json.dump(self.data,file,indent=7)
273 273
274 274 except:
275 275 pass
276 276
277 277 def on_disconnect(client,userdata,rc):
278 278 def write_status(chain):
279 279 now = datetime.now()
280 280
281 281 formatted_date_time = now.strftime("%d/%m/%Y %H:%M:%S")
282 282
283 283 filename = '/logs/log.txt'
284 284
285 285 if not os.path.isdir(os.path.dirname(filename)):
286 286 os.makedirs(os.path.dirname(filename))
287 287
288 288 chain = formatted_date_time + " " + chain
289 289
290 290 try:
291 291 with open(filename,'a') as file:
292 292
293 293 file.write(chain + '\n')
294 294 except:
295 295 pass
296 296
297 297 return
298 298
299 299 write_status("Se ha desconectado el MQTT, recuperando conexión.")
300 300 sleep(0.5)
301 301
302 302 if rc != 0:
303 303 count_attempts = 0
304 304 while 1:
305 305
306 306 try:
307 307 client.reconnect()
308 308 except:
309 309
310 310 error = traceback.format_exc()
311 311
312 312 write_status(f"Error al reconectar MQTT broker. Intento {count_attempts+1}/5. Copia del error: {error}")
313 313
314 314 count_attempts +=1
315 315
316 316 time.sleep(0.5)
317 317
318 318 if count_attempts == 5:
319 319
320 320 write_status(f"Error al reconectar MQTT broker. Nos desconectamos del servidor.")
321 321
322 322 return
323 323 else:
324 324 return
325 325
326 326 MAX_NUMBER_SENSORS = 4
327 327
328 328 class estimator(object):
329 329 '''
330 330 Clase que permite estimar si hay un evento de huayco o lahar
331 331 Solo conserva los ultimos valores
332 332 '''
333 333
334 334 flag_load_weights = False
335 335
336 336 _dataOut = None
337 337 _image = None
338 338 _share = 10
339 339 _video = None
340 340
341 341 _string_status = None
342 342
343 343 activate = False
344 344 activate_count = 0
345 345
346 346 count_hb = 0
347 347 count_HFS = 0
348 348 count_RCWL = 0
349 349 status_lidar = 0
350 350
351 351 flag_internet = False
352 352
353 353 inference_value = None
354 354
355 355 list_HB = numpy.empty(MAX_NUMBER_SENSORS,dtype=float)
356 356 list_HB.fill(numpy.nan)
357 357
358 358 list_HFS = numpy.empty(MAX_NUMBER_SENSORS,dtype=float)
359 359 list_HFS.fill(numpy.nan)
360 360
361 361 list_RCWL = numpy.empty(MAX_NUMBER_SENSORS,dtype=float)
362 362 list_RCWL.fill(numpy.nan)
363 363 timestamp = None
364 364
365 365 #Para inferencia por imagen
366 366 TH_UMBRAL = 0.8
367 367
368 368 values_dict = {'photo':{'0':'no_huayco','1':'huayco','10':'Camera Not Working'},
369 369 'video':{'1':'huayco','0':'no_huayco','10':'Camera Not Working'},
370 370 'server':{'1':'huayco','0':'no_huayco','10':'Camera Not Working'}}
371 371
372 372 def __init__(self,obj):
373 373
374 374 self.obj_vars = obj
375 375 self.id = self.obj_vars.id
376 376 self.path_save_json = obj.vars.get("path_save",os.path.join(os.getcwd(),'data'))
377 377 self.weights = obj.weights.get(obj.type_weights,None)
378 378
379 379 self.inference_mode = self.obj_vars.inference_mode
380 380
381 381
382 382 if self.weights is None:
383 383 self.write_status("[ERROR] El atributo weights no puede ser None en el objeto Estimator. Porfavor, asegure de configurar correctamente la variable.")
384 384 raise AttributeError("El atributo weights no puede ser None en el objeto Estimator. Porfavor, asegure de configurar correctamente la variable.")
385 385
386 386
387 387 def reset_values(self):
388 388
389 389
390 390 self.list_HFS.fill(numpy.nan)
391 391 self.list_RCWL.fill(numpy.nan)
392 392 self.list_HB.fill(numpy.nan)
393 393
394 394 self.count_hb = 0
395 395 self.count_HFS = 0
396 396 self.count_RCWL = 0
397 397
398 398 gc.collect()
399 399
400 400 @property
401 401 def video(self):
402 402 return self._video
403 403
404 404 @property
405 405 def share(self):
406 406 return self._share
407 407
408 408 @property
409 409 def dataOut(self):
410 410 return self._dataOut
411 411
412 412 @property
413 413 def image(self):
414 414 return self._image
415 415
416 416 @property
417 417 def string_status(self):
418 418 tmp = self.values_dict[self.inference_mode]
419 419
420 420 if self._share > 0.5 and self._share<= 2:
421 421
422 422 self._string_status = tmp['1']
423 423 elif self._share <= 0.5:
424 424
425 425 self._string_status = tmp['0']
426 426 else:
427 427
428 428 self._string_status = tmp['10']
429 429
430 430
431 431 return self._string_status
432 432
433 433 @image.setter
434 434 def image(self,value):
435 435
436 436 self._image = deepcopy(value['image'])
437 437 self.timestamp = deepcopy(value['timestamp'])
438 438
439 439 @video.setter
440 440 def video(self,value):
441 441 self._video = deepcopy(value['video'])
442 442 self.timestamp = deepcopy(value['timestamp'])
443 443
444 444
445 445
446 446 @share.setter
447 447 def share(self,value):
448 448
449 449 self._share = value
450 450
451 451 if self._share>=0.1 and self._share <5 :
452 452 self._share = value
453 453 else:
454 454 self._share = 0
455 455
456 456
457 457 #------------- Realizamos la ponderación -----------------
458 458 count = 0
459 459
460 460 tmp = round(self.weights['camara']*self._share,2)
461 461
462 462 if tmp>1.1:
463 463 tmp = 0
464 464
465 465 count = tmp + count
466 466
467 467 tmp = numpy.nanmean(self.list_HFS)
468 468
469 469
470 470
471 471
472 472 if numpy.isnan(tmp):
473 473 tmp = 0
474 474 if tmp>1:
475 475 tmp = 0
476 476
477 477 count += self.weights['HFS']*tmp
478 478
479 479 if numpy.isnan(self.status_lidar):
480 480 self.status_lidar = 0
481 481
482 482 if self.status_lidar>1:
483 483 self.status_lidar = 1
484 484
485 485 count += self.weights['LIDAR']*(self.status_lidar)
486 486
487 487 tmp = numpy.nanmean(self.list_HB)
488 488
489 489 if numpy.isnan(tmp):
490 490 tmp = 0
491 491
492 492 if tmp>1:
493 493 tmp = 0
494 494 count += self.weights['HB100']*tmp
495 495
496 496 if count>0.7:
497 497
498 498 self.activate = True
499 499 self.activate_count = count
500 500
501 501 else:
502 502
503 503 self.activate = False
504 504 self.activate_count = count
505 505
506 506
507 507
508 508
509 509 @dataOut.setter
510 510 def dataOut(self,value):
511 511
512 512 self._dataOut = value
513 513
514 514 list_keys = self._dataOut.keys()
515 515
516 516 self.reset_values()
517 517
518 518 for key in list_keys:
519 519
520 520 obj = self._dataOut[key]
521 521
522 522 y = obj.get_latest()[1]
523 523
524 524 if 'sensor_HFS' in key:
525 525 self.list_HFS[self.count_HFS%MAX_NUMBER_SENSORS] = y
526 526
527 527 self.count_HFS +=1
528 528
529 529 if 'sensor_HB' in key:
530 530 self.list_HB[self.count_hb%MAX_NUMBER_SENSORS] = y
531 531 self.count_hb +=1
532 532
533 533 if 'sensor_RCWL' in key:
534 534 self.list_RCWL[self.count_RCWL%MAX_NUMBER_SENSORS] = y
535 535
536 536 self.count_RCWL +=1
537 537
538 538 if 'lidar' in key:
539 539
540 540 #Solo contamos con un lidar
541 541 #Asi que nos es suficiente manejarlo como una variable
542 542
543 543 self.status_lidar = obj.activate
544 544
545 545 if self.status_lidar:
546 546 self.status_lidar = 1
547 547 else:
548 548 self.status_lidar = 0
549 549
550 550 def __load_model_photo(self,path_model):
551 551
552 552 try:
553 553 self.model_IA = lite.Interpreter(model_path=path_model)
554 554 self.model_IA.allocate_tensors()
555 555
556 556 except Exception as e:
557 557 #Modelo IA no se pudo cargar
558 558 self.write_status(f"No se pudo cargar el modelo IA de foto. Error: {e}")
559 559 self.flag_load_weights = False
560 560
561 561 else:
562 562 self.write_status("Modelo IA de photo cargado con éxito.")
563 563 self.flag_load_weights = True
564 564
565 565 def __load_model_video(self,path):
566 566
567 567 try:
568 568
569 569
570 570 HEIGHT = 224
571 571 WIDTH = 224
572 572 input_shape = (None, 10, HEIGHT, WIDTH, 3)
573 573 input = layers.Input(shape=(input_shape[1:]))
574 574 x = input
575 575
576 576 x = Conv2Plus1D(filters=16, kernel_size=(3, 7, 7), padding='same')(x)
577 577 x = layers.BatchNormalization()(x)
578 578 x = layers.ReLU()(x)
579 579 x = Dropout(0.1)(x)
580 580 x = ResizeVideo(HEIGHT // 2, WIDTH // 2)(x)
581 581
582 582 # Block 1
583 583 x = add_residual_block(x, 16, (3, 3, 3))
584 584 x = Dropout(0.1)(x)
585 585 x = ResizeVideo(HEIGHT // 4, WIDTH // 4)(x)
586 586
587 587 # Block 2
588 588 x = add_residual_block(x, 32, (3, 3, 3))
589 589 x = Dropout(0.1)(x)
590 590 x = ResizeVideo(HEIGHT // 8, WIDTH // 8)(x)
591 591
592 592 # Block 3
593 593 x = add_residual_block(x, 64, (3, 3, 3))
594 594 x = Dropout(0.1)(x)
595 595 x = ResizeVideo(HEIGHT // 16, WIDTH // 16)(x)
596 596
597 597 # Block 4
598 598 x = add_residual_block(x, 128, (3, 3, 3))
599 599 x = Dropout(0.1)(x)
600 600 x = ResizeVideo(HEIGHT // 32, WIDTH // 32)(x)
601 601
602 602
603 603 x = layers.AveragePooling3D((10,1,1))(x)
604 604 x = layers.Reshape((x.shape[1]*x.shape[2]*x.shape[3],-1))(x)
605 605 x = layers.LSTM(128,return_sequences=True)(x)
606 606 x = layers.Flatten()(x)
607 607 x = layers.Dense(512)(x)
608 608 x = Dropout(0.1)(x)
609 609 x = layers.Dense(256)(x)
610 610
611 611 x = layers.Dense(1, activation='sigmoid')(x)
612 612
613 613
614 614 self.model_IA = keras.Model(input, x)
615 615
616 616 self.model_IA.load_weights(path)
617 617
618 618 except:
619 619
620 620 self.write_status(f"[ERROR] No se pudo cargar el modelo IA de video. Error: {traceback.format_exc()}")
621 621 self.flag_load_weights = False
622 622
623 623 else:
624 624
625 625 self.write_status("Modelo IA de video cargado con exito.")
626 626 self.flag_load_weights = True
627 627
628 628
629 629
630 630 def load_weights(self):
631 631 #Usar mobilnet debido a su reducido tamaño
632 632 if self.inference_mode == 'photo':
633 633 path_model = "/tools/models/mobilnet.tflite"
634 634
635 635 self.__load_model_photo(path_model)
636 636
637 637 elif self.inference_mode == 'video':
638 638 #Peso de videos
639 639 path_model = "/tools/models/weights_video.h5"
640 640
641 641 self.__load_model_video(path_model)
642 642
643 643 elif self.inference_mode == 'server':
644 644 '''
645 645 Aqui se realizará inferencias a la IP publica del OVS
646 646 - La inferencia al OVS se realizará mientras se cuente con internet.
647 647 - Si no se cuenta con internet, se realizará inferencias con el pequeño modelo siempre y
648 648 cuando sea una RPI 4 o superior.
649 649 '''
650 650
651 651 self.__model_less_complexity()
652 652
653 653
654 654
655 655 def __model_less_complexity(self,):
656 656
657 657 self.model_IA = True
658 658
659 659 return
660 660
661 661
662 662
663 663 def check_internet(self,verbose=True):
664 664
665 665 count = 0
666 666
667 667 while 1:
668 668
669 669 try:
670 670 urllib.request.urlopen('http://www.google.com', timeout=1)
671 671
672 672 except:
673 673 count +=1
674 674 if (count ==3):
675 675 self.flag_internet = False
676 676 break
677 677
678 678 sleep(0.5)
679 679 else:
680 680 if verbose:
681 681 self.write_status("Se cuenta con conexión a internet.")
682 682
683 683 if self.debug:
684 684 print("Se cuenta con conexión a internet")
685 685
686 686 self.flag_internet = True
687 687
688 688 return
689 689
690 690 return
691 691
692 692
693 693 def get_inference(self,):
694 694
695 695
696 696 self.check_internet(False)
697 697
698 698 if self.inference_mode == 'video':
699 699
700 700 '''
701 701 Se realiza predicción con el modelo de baja complejidad.
702 702 '''
703 703
704 704 n_frames = 10
705 705
706 706 if self._video != None:
707 707
708 708 self.write_status("Realizando inferencia del modelo IA con video.")
709 709
710 710 try:
711 711
712 712 self._video = frames_from_video_file(self._video,n_frames)#result.reshape((1,result.shape[0],result.shape[1],result.shape[2],result.shape[3]))
713 713 result = 1- self.model_IA.predict(self._video)[0][0]
714 714
715 715 except:
716 716
717 717 self.write_status(f"[ERROR] Error en la estimación del video. {traceback.format_exc()} ")
718 718 self._video = None
719 719
720 720 return None
721 721 else:
722 722
723 723 #------------------ Guardamos las inferencias en video ---------------------#
724 724 #############################################################################
725 725
726 726 self.__save_inferences(result)
727 727 self._video = None
728 728 return result
729 729
730 730 else:
731 731
732 732 self.write_status("No se puede realizar la inferencia porque el batch es None.")
733 733
734 734 elif self.inference_mode == 'photo':
735 735
736 736 '''
737 737 Se realiza inferencias mediante el modelo ML mediante foto.
738 738 Se va a deprecar este modo debido a que no es suficiente una foto para la estimación
739 739 de huaycos.
740 740 '''
741 741
742 742 if self._image is not None:
743 743 self.write_status("Realizando inferencia del modelo IA con photo.")
744 744
745 745 try:
746 746 input_details = self.model_IA.get_input_details()
747 747 output_details = self.model_IA.get_output_details()
748 748
749 749 input_data = deepcopy(self._image)
750 750
751 751 input_data = Image.fromarray(input_data)
752 752
753 753 resize = input_data.resize((256,256))
754 754
755 755 resize = numpy.array(resize)
756 756
757 757 resize = numpy.expand_dims(resize,axis=0)
758 758
759 759 resize = resize.astype(numpy.float32)
760 760
761 761 self.model_IA.set_tensor(input_details[0]['index'], resize)
762 762 self.model_IA.invoke()
763 763
764 764 output_data = self.model_IA.get_tensor(output_details[0]['index'])[0][0]
765 765
766 766 self.write_status(f"Inferencia realizado con exito. Valor de inferencia: {output_data}.")
767 767
768 768 if output_data>=0.6:
769 769 fpath = r'/data/inferences/img/01'
770 770 else:
771 771 fpath = r'/data/inferences/img/00'
772 772
773 773 if not os.path.isdir:
774 774 os.makedirs(fpath)
775 775
776 776 name = f'{self.timestamp}.png'
777 777
778 778 fpath = os.path.join(fpath,name)
779 779 original_image = Image.fromarray(self._image)
780 780 original_image.save(fpath)
781 781
782 782
783 783
784 784 return output_data
785 785
786 786 except:
787 787 exc = traceback.format_exc()
788 788
789 789 self.write_status(f"[ERROR] Error al realizar inferencia. Copia del error {exc}")
790 790
791 791 return None
792 792
793 793 else:
794 794 self.write_status("No se puede realizar la inferencia porque la imagen es None.")
795 795
796 796
797 797 elif self.inference_mode == 'server':
798 798
799 799 '''
800 800 Se realizará la inferencia al servidor.
801 801 Solo se envía los datos comprimidos en formato json. El servidor se encargará
802 802 de darle formato a la imagen.
803 803 '''
804 804
805 805
806 806
807 807 url = "http://38.10.105.243:7777/predict"
808 808
809 809
810 810 input_data = {'instances':base64.b64encode(self._video.getvalue()).decode('utf-8'),
811 811 'id_user':str(self.id),
812 812 'request_format':True,
813 813 'shape':(360,640)}
814 814
815 815 headers = {
816 816 'Content-Type': 'application/json',
817 817 'Content-Encoding': 'gzip-B64',
818 818 }
819 819 input_data = json.dumps(input_data)
820 820
821 821 compress = io.BytesIO()
822 822
823 823 with gzip.GzipFile(fileobj=compress, mode='wb', compresslevel=9) as gz2:
824 824 gz2.write(input_data.encode('utf-8'))
825 825
826 826
827 827 if self.flag_internet:
828 828
829 829 '''
830 830 Se cuenta con internet para enviar el video al servidor a fin de realizar la inferencia.
831 831 '''
832 832
833 833 try:
834 834 resp = requests.post(url,data=compress.getvalue(),headers=headers)
835 835 except:
836 836 self._video = None
837 837 compress = None
838 838 gc.collect()
839 839 self.write_status(f"Error ocurrido al realizar la inferencia al servidor. {traceback.format_exc()}")
840 840
841 841 else:
842 842
843 843 if resp.status_code == 200:
844 844 time1= datetime.now().timestamp()
845 845
846 846 value_inference = round(1-resp.json()['predictions'][0][0],4) # El modelo actual requiere una resta de 1. Debido a que 0 es evento y 1 es no evento.
847 847
848 848 self.write_status(f"Inferencia al servidor realizado con exito. Valor de inferencia: {value_inference}.")
849 849 self.__save_inferences(value_inference)
850 850 self._video = None
851 851 compress = None
852 852 gc.collect()
853 853
854 854 return value_inference
855 855 else:
856 856
857 857
858 858 self.write_status(f"Se obtuvo otro codigo de respuesta al realizar inferencia al servidor. {resp.status_code}")
859 859 self._video = None
860 860 compress = None
861 861 gc.collect()
862 862
863 863 return None
864 864
865 865 else:
866 866
867 867 '''
868 868 Probamos con el modelo de menor complejidad.
869 869 - No se encuentra desarrollado por el momento.
870 870 '''
871 871
872 872 self.write_status("[IA] Metodo IA de menor complejidad no ha sido implementado para este modo.")
873 873
874 874 return None
875 875
876 876 def __save_inferences(self,result):
877 877
878 878 return
879 879 frame_width, frame_height = self._video.shape[1],self._video.shape[2]
880 880
881 881 if result>=0.6:
882 882 fpath = r'/data/inferences/video/01'
883 883 else:
884 884 fpath = r'/data/inferences/video/00'
885 885
886 886 if not os.path.isdir(fpath):
887 887 os.makedirs(fpath)
888 888 try:
889 889 name = f'{self.timestamp}.mp4'
890 890 fpath = os.path.join(fpath,name)
891 891 out = cv2.VideoWriter(fpath, cv2.VideoWriter_fourcc(*'mp4v'), 10, (frame_width, frame_height))
892 892
893 893 batch = self._video[0]
894 894
895 895 for frame in batch:
896 896 if frame.dtype != numpy.uint8:
897 897 frame = frame.astype(numpy.uint8)
898 898 out.write(frame)
899 899
900 900 out.release()
901 901 except:
902 902
903 903 self.write_status(traceback.format_exc())
904 904
905 905
906 906 def write_status(self,chain):
907 907
908 908 now = datetime.now()
909 909
910 910 formatted_date_time = now.strftime("%d/%m/%Y %H:%M:%S")
911 911
912 912 filename = '/logs/log.txt'
913 913
914 914 if not os.path.isdir(os.path.dirname(filename)):
915 915 os.makedirs(os.path.dirname(filename))
916 916
917 917 chain = formatted_date_time + " " + chain
918 918
919 919 try:
920 920 with open(filename,'a') as file:
921 921
922 922 file.write(chain + '\n')
923 923 except:
924 924
925 925 if self.debug:
926 926 print("Ocurrió un error al guardar datos logs.")
927 927
928 928 return
929 929
930 930
931 931 def write_data(self,data):
932 932
933 933 now = datetime.now()
934 934 formatted_date_time = now.strftime("%d/%m/%Y %H:%M:%S") + " |"
935 935
936 936 try:
937 937 name = 'data.txt'
938 938 filename = os.path.join(self.path_save_json,name)
939 939
940 940 with open(filename,'a') as file:
941 941
942 942 file.write(formatted_date_time + str(json.dumps(data)) + '\n')
943 943
944 944 except:
945 945
946 946 if self.debug:
947 947
948 948 print(f"Ocurrió un error al guardar los datos en el archivo {name}")
949 949
950 950 self.write_status(f"[ERROR] Ocurrió un error al guardar los datos en el archivo {name}.")
951 951
952 952
953 953 def run(self,):
954 954
955 955 '''
956 956 -----------------------------------------------------------------------------------
957 957 Se ha obtenido un promedio de 0.303 segundos por inferencia para foto con RPI Zero.
958 958 Se ha obtenido un promedio de 2.4 segundos de inferencia para videos con RPI 4
959 959 -----------------------------------------------------------------------------------
960 960 '''
961 961 value = None
962 962
963 963 if self.flag_load_weights or self.inference_mode == 'server':
964 964
965 965 #--------------- Realizamos la inferencia ---------------#
966 966
967 967 self.inference_value = self.get_inference()
968 968
969 969 #Por ahora solo copiamos los datos
970 970
971 971
972 972 self.write_data("Inferencia:" + str(self.inference_value) + " Timestamp: " + str(self.timestamp) )
973 973 self.write_status("Inferencia:" + str(self.inference_value) + " Timestamp: " + str(self.timestamp) )
974 974
975 975
976 976
977 977
978 978
979 979 class camera(object):
980 980
981 981 data = None
982 982
983 983 flag = False
984 984 activity = False
985 985 _status = False
986 986 url_rstp = None
987 987
988 988 brightness = False
989 989
990 990 flag_brightness = False
991 991
992 992 def write_status(self,chain):
993 993
994 994 now = datetime.now()
995 995
996 996 formatted_date_time = now.strftime("%d/%m/%Y %H:%M:%S")
997 997
998 998 filename = '/logs/log.txt'
999 999
1000 1000 chain = formatted_date_time + " |" + chain
1001 1001
1002 1002 if not os.path.isdir(os.path.dirname(filename)):
1003 1003 os.makedirs(os.path.dirname(filename))
1004 1004
1005 1005 try:
1006 1006 with open(filename,'a') as file:
1007 1007
1008 1008 file.write(chain + '\n')
1009 1009 except:
1010 1010
1011 1011 if self.debug:
1012 1012 print("Ocurrió un error al guardar datos logs.")
1013 1013
1014 1014 return
1015 1015
1016 1016 def __init__(self,flag=False, pin= 26,obj=None):
1017 1017
1018 1018 '''
1019 1019 Definiciones
1020 1020 ------------
1021 1021 flag -> Bandera que condiciona la adquisición de fotografías por la cámara. Permite decidir si se usa o no la cámara.
1022 1022 pin -> Pin que controla el relé de alimentación hacia la cámara.
1023 1023 camara always on -> Modo que permite capturar en cualquier estación del año, pero es condicionado por el flag.
1024 1024
1025 1025 08-08-24
1026 1026 --------
1027 1027 Se agregan nuevas funciones para la camara HIKVISION usando ISAPI
1028 1028 '''
1029 1029
1030 1030
1031 1031
1032 1032 self.flag = flag
1033 1033 self.pin = pin
1034 1034
1035 1035
1036 1036 self.debug = obj.debug
1037 1037 self.vars = obj.vars
1038 1038 self.vars_mqtt = obj.vars_mqtt
1039 1039 self.store_data = obj.store_data
1040 1040 self.vars_gpio = obj.vars_gpio
1041 1041 self.camera_keys = obj.camera
1042 1042
1043 1043
1044 1044 self.camera_always_on = self.vars_gpio.get("camera_always_on",False)
1045 1045
1046 1046 self.camera_ip = self.camera_keys.get("ip")
1047 1047 self.username_camera = self.camera_keys.get("username")
1048 1048 self.password_camera = self.camera_keys.get("password")
1049 1049 self.port_camera = self.camera_keys.get("port")
1050 1050
1051 1051 self.__config()
1052 1052
1053 1053
1054 1054 def __config(self,):
1055 1055
1056 1056 self.__gen__rstp()
1057 1057 self.__update_time()
1058 1058 self.__switch_mode_to_night()
1059 1059 self.__update_brightness(brightness=0) #Apagamos la luz de la cámara
1060 1060
1061 1061 def __update_time(self,):
1062 1062
1063 1063 if self.camera_ip != None:
1064 1064 url_supplement_light = f'http://{self.camera_ip}/ISAPI/System/time'
1065 1065
1066 1066
1067 1067 now = datetime.now(pytz.utc).astimezone(pytz.timezone('Etc/GMT+5')).time()
1068 1068 hora_actual = now.strftime('%Y-%m-%dT%H:%M:%S')
1069 1069 zona_horaria = "EST5"
1070 1070
1071 1071 xml_data = f"""<Time version="2.0" xmlns="http://www.isapi.org/ver20/XMLSchema">
1072 1072 <timeMode>manual</timeMode>
1073 1073 <localTime>{hora_actual}</localTime>
1074 1074 <timeZone>{zona_horaria}</timeZone>
1075 1075 </Time>"""
1076 1076 try:
1077 1077 response = requests.put(
1078 1078 url_supplement_light,
1079 1079 data=xml_data,
1080 1080 headers={'Content-Type': 'application/xml'},
1081 1081 auth=HTTPDigestAuth(self.username_camera, self.password_camera)
1082 1082 )
1083 1083
1084 1084 if response.status_code == 200:
1085 1085 print(f"Hora actualizada.")
1086 1086 else:
1087 1087 raise RuntimeError(f"Error {response.status_code}: {response.text}")
1088 1088
1089 1089 except:
1090 1090
1091 1091 self.write_status(f"[Camera] Error producido al actualizar la fecha. Error: {traceback.format_exc()}.")
1092 1092
1093 1093 else:
1094 1094
1095 1095 self.write_status(f"[Camera] Fecha actualizada.")
1096 1096
1097 1097
1098 1098
1099 1099 def __gen__rstp(self):
1100 1100 self.url_rstp = f'rtsp://{self.username_camera}:{self.password_camera}@{self.camera_ip}:{self.port_camera}/streaming/channels/1'
1101 1101
1102 1102 def __update_brightness(self,brightness=100):
1103 1103
1104 1104
1105 1105 if self.camera_ip != None:
1106 1106 url_supplement_light = f'http://{self.camera_ip}/ISAPI/Image/channels/1/supplementLight'
1107 1107
1108 1108 xml_data = f'''
1109 1109 <SupplementLight>
1110 1110 <supplementLightMode>colorVuWhiteLight</supplementLightMode>
1111 1111 <mixedLightBrightnessRegulatMode>manual</mixedLightBrightnessRegulatMode>
1112 1112 <whiteLightBrightness>{brightness}</whiteLightBrightness>
1113 1113 </SupplementLight>
1114 1114 '''
1115 1115
1116 1116 try:
1117 1117 response = requests.put(
1118 1118 url_supplement_light,
1119 1119 data=xml_data,
1120 1120 headers={'Content-Type': 'application/xml'},
1121 1121 auth=HTTPDigestAuth(self.username_camera, self.password_camera)
1122 1122 )
1123 1123
1124 1124 if response.status_code == 200:
1125 1125 print(f"Brillo ajustado a {brightness}%.")
1126 1126 else:
1127 1127 raise RuntimeError(f"Error {response.status_code}: {response.text}")
1128 1128
1129 1129 except:
1130 1130
1131 1131 self.write_status(f"[Camera] Error producido al actualizar el brillo. Error {traceback.format_exc()}.")
1132 1132
1133 1133 else:
1134 1134
1135 1135 self.write_status(f"[Camera] Brillo de luz actualizado a {brightness}.")
1136 1136 if brightness >50:
1137 1137 sleep(3)
1138 1138
1139 1139
1140 1140
1141 1141
1142 1142
1143 1143
1144 1144 def __switch_mode_to_night(self,):
1145 1145
1146 1146 '''
1147 1147 En este modulo, se configura a la cámara para que pueda cambiar el modo switch a modo noche.
1148 1148 '''
1149 1149 xml_data = """
1150 1150 <?xml version:"1.0" encoding="UTF-8"?>
1151 1151 <IrcutFilter>
1152 1152 <IrcutFilterType>night</IrcutFilterType>
1153 1153 </IrcutFilter>
1154 1154 """
1155 1155
1156 1156 headers ={'Content-Type': 'application/xml'}
1157 1157 url = f'http://{self.camera_ip}/ISAPI/Image/channels/1/ircutFilter'
1158 1158
1159 1159 username = self.username_camera
1160 1160 password = self.password_camera
1161 1161
1162 1162
1163 1163 if self.camera_ip != None:
1164 1164
1165 1165 try:
1166 1166 response = requests.put(url, data=xml_data, auth=HTTPDigestAuth(username, password), headers=headers)
1167 1167 except:
1168 1168
1169 1169 self.write_status(f"[ERROR] Error al cambiar modo de la camara a noche. Error: {traceback.format_exc()}")
1170 1170 else:
1171 1171 if response.status_code == 200:
1172 1172 self.write_status("[Camera] Modo ha sido cambiado a Noche.")
1173 1173
1174 1174 else:
1175 1175 self.write_status("[Camera] Error al cambiar a modo noche.")
1176 1176
1177 1177
1178 1178
1179 1179 def __on_camera(self,):
1180 1180
1181 1181 GPIO.setmode(GPIO.BCM)
1182 1182 GPIO.setwarnings(False)
1183 1183 GPIO.setup(self.pin,GPIO.OUT)
1184 1184 GPIO.output(self.pin,GPIO.LOW)
1185 1185
1186 1186 self.activity = True
1187 1187
1188 1188 def __off_camera(self,):
1189 1189
1190 1190 GPIO.setmode(GPIO.BCM)
1191 1191 GPIO.setwarnings(False)
1192 1192 GPIO.setup(self.pin,GPIO.OUT)
1193 1193 GPIO.output(self.pin,GPIO.HIGH)
1194 1194
1195 1195 self.activity = False
1196 1196
1197 1197
1198 1198 def __is_night(self):
1199 1199
1200 1200 '''
1201 1201 Se establecen las condiciones para que tiempos sea declarado noche
1202 1202 '''
1203 1203
1204 1204 now = datetime.now(pytz.utc).astimezone(pytz.timezone('Etc/GMT+5')).time()
1205 1205
1206 1206 flag = False
1207 1207 flag = (time(17,0)<=now<=time(23,59)) or (time(0,0)<=now<=time(6,50))
1208 1208
1209 1209 return flag
1210 1210
1211 1211 @property
1212 1212 def status(self):
1213 1213
1214 1214 if self.flag == False:
1215 1215
1216 1216 self.__off_camera()
1217 1217 return False
1218 1218
1219 1219 if self.camera_always_on:
1220 1220
1221 1221 '''La cámara siempre estará prendida'''
1222 1222
1223 1223 self.__on_camera()
1224 1224 self.__process_on()
1225 1225
1226 1226 return True
1227 1227
1228 1228 #------ Establecemos UTC -5 ---------#
1229 1229 utc_minus_5 = pytz.timezone('Etc/GMT+5')
1230 1230
1231 1231 now = datetime.now(pytz.utc).astimezone(utc_minus_5).time()
1232 1232
1233 1233 '''
1234 1234 Aqui establecemos criterío de activación de la cámara
1235 1235 -----------------------------------------------------
1236 1236 - Por ejemplo, aqui se define que la cámara funciona entre las 6 am y 18pm
1237 1237 de cada día. La activación y desactivación de la cámara será controlada mediante un relé.
1238 1238
1239 1239 - Se puede establecer otros criterios como activar la cámara durante ciertos
1240 1240 periodos de meses por inactividad o menor radiación.
1241 1241 '''
1242 1242
1243 1243 #------------------- Criterio ----------------------------#
1244 1244 # self._status = False
1245 1245 # self._status = time(6, 00) <= now <= time(7, 10)
1246 1246 # self._status = time(10, 00) <= now <= time(10, 10)
1247 1247 # self._status = time(12, 00) <= now <= time(12, 10)
1248 1248 # self._status = time(16, 00) <= now <= time(16, 10)
1249 1249 # self._status = time(17, 30) <= now <= time(18,30)
1250 1250 # self._status = time(21, 00) <= now <= time(21,10)
1251 1251 # self._status = time(1, 00) <= now <= time(1,10)
1252 1252 # self._status = time(4, 00) <= now <= time(4,10)
1253 1253
1254 1254 self._status = True
1255 1255
1256 1256 #-------------------- Condiciones ------------------------#
1257 1257
1258 1258
1259 1259
1260 1260 if self._status ==True and self.pin != None and self.activity == False:
1261 1261
1262 1262 #Prendemos la cámara del relé.
1263 1263
1264 1264 self._status = True
1265 1265
1266 1266 self.__on_camera()
1267 1267
1268 1268 self.write_status("[CAMERA] Se prendió la cámara.")
1269 1269
1270 1270 elif self._status == False and self.pin != None and self.activity == True:
1271 1271
1272 1272 # Operaciones para desactivar la salida del relé
1273 1273
1274 1274 self.__off_camera()
1275 1275
1276 1276 self.write_status("[CAMERA] Se apagó la cámara.")
1277 1277
1278 1278 ###############################################################
1279 1279
1280 1280 # if self._status:
1281 1281
1282 1282 # self.__process_on() # Procesos de validación cuando la cámara esté prendida o en funcionamiento.
1283 1283
1284 1284
1285 1285 return self._status
1286 1286
1287 1287 def control_brightness(self,brightness=100):
1288 1288
1289 1289 try:
1290 1290 flag_night = self.__is_night()
1291 1291
1292 1292 if flag_night == True and brightness>0:
1293 1293
1294 1294
1295 1295 #Realizamos el cambio de brillo
1296 1296 self.brightness = True
1297 1297 self.__update_brightness(brightness=brightness)
1298 1298
1299 elif flag_night == False and brightness == 0:
1300
1301 #Como no es de noche, mantenemos el brillo seteado a 0.
1302 self.brightness = False
1303 self.__update_brightness(brightness=0)
1304 elif flag_night == True and brightness == 0:
1299 elif brightness == 0:
1305 1300 self.brightness = False
1306 1301 self.__update_brightness(brightness=0)
1307 1302 except:
1308 1303
1309 1304 self.write_status("[CAMERA_ERROR] Ocurrió un error al controlar el brillo de la camara.")
1310 1305
1311 1306 return
1312 1307
1313 1308
1314 1309 def __process_on(self,):
1315 1310
1316 1311 '''
1317 1312 Procesos que se ejecutan o validan cuando la cámara está prendida.
1318 1313 '''
1319 1314
1320 1315 flag_night = self.__is_night()
1321 1316
1322 1317 if flag_night == True and self.flag_brightness == False:
1323 1318
1324 1319
1325 1320 self.flag_brightness = True
1326 1321 self.__update_brightness(brightness=100)
1327 1322
1328 1323 if flag_night==False and self.flag_brightness == True:
1329 1324
1330 1325 self.flag_brightness = False
1331 1326 self.__update_brightness(brightness=0)
1332 1327
1333 1328 class sensor(object):
1334 1329
1335 1330 max_size = 300
1336 1331 name = ""
1337 1332 key = None
1338 1333
1339 1334 H0 = None
1340 1335
1341 1336 y_value = list()
1342 1337 x_value = list()
1343 1338
1344 1339 FLAG_CALIBRATION_LIDAR = False
1345 1340
1346 1341 array_calibration = list()
1347 1342
1348 1343 def write_status(self,chain):
1349 1344
1350 1345 now = datetime.now()
1351 1346
1352 1347 formatted_date_time = now.strftime("%d/%m/%Y %H:%M:%S")
1353 1348
1354 1349 filename = '/logs/log.txt'
1355 1350
1356 1351 chain = formatted_date_time + " |" + chain
1357 1352
1358 1353 if not os.path.isdir(os.path.dirname(filename)):
1359 1354 os.makedirs(os.path.dirname(filename))
1360 1355
1361 1356 try:
1362 1357 with open(filename,'a') as file:
1363 1358
1364 1359 file.write(chain + '\n')
1365 1360 except:
1366 1361
1367 1362 if self.debug:
1368 1363 print("Ocurrió un error al guardar datos logs.")
1369 1364
1370 1365 return
1371 1366
1372 1367
1373 1368 def __init__(self,name,key):
1374 1369
1375 1370 self.name = name
1376 1371 self.key = key
1377 1372
1378 1373
1379 1374
1380 1375 def __logic(self,value):
1381 1376
1382 1377 '''
1383 1378 Se implementará en las clases hijas.
1384 1379 '''
1385 1380 self.write_status("[ERROR SENSOR] No se encuentra implementado el método lógic.")
1386 1381
1387 1382 raise NotImplementedError("No se encuentra implementado el método logic.")
1388 1383
1389 1384
1390 1385 def insert_value(self,value):
1391 1386
1392 1387
1393 1388 if 'lidar' in self.name:
1394 1389
1395 1390 self.__load_H0()
1396 1391
1397 1392 if self.FLAG_CALIBRATION_LIDAR == False:
1398 1393 self.__calibration(value)
1399 1394
1400 1395
1401 1396 timestamp = datetime.now().timestamp()
1402 1397
1403 1398 size = len(self.x_value)
1404 1399
1405 1400 if(size>self.max_size):
1406 1401
1407 1402 self.x_value = self.x_value[1:]
1408 1403 self.y_value = self.y_value[1:]
1409 1404
1410 1405 self.x_value.append(timestamp)
1411 1406 self.y_value.append(value)
1412 1407
1413 1408 gc.collect()
1414 1409
1415 1410
1416 1411 def get_values(self,):
1417 1412
1418 1413 return numpy.array(self.x_value,dtype=float), numpy.array(self.y_value,dtype=float)
1419 1414
1420 1415 def get_latest(self,):
1421 1416
1422 1417 if len(self.x_value)>0:
1423 1418 return self.x_value[-1], self.y_value[-1]
1424 1419 else:
1425 1420 return None,None
1426 1421
1427 1422 class sensor_HFS(sensor):
1428 1423
1429 1424
1430 1425 activate = False
1431 1426 timestamp_init = 0
1432 1427 timestamp_fin = 0
1433 1428 timestamp = 0
1434 1429
1435 1430 timestamp_off = 0
1436 1431 status = False
1437 1432 prev_status = False
1438 1433 THRESHOLD_BETWEEN_OFF_SENSOR = 3
1439 1434 THRESHOLD_BETWEEN_ON_SENSOR = 5
1440 1435
1441 1436
1442 1437 pull_down = True
1443 1438
1444 1439 def __init__(self,name,key,pin,**kwargs):
1445 1440
1446 1441 self.name = name
1447 1442 self.key = key
1448 1443 self.pin = pin
1449 1444
1450 1445 if pin == None:
1451 1446 self.write_status("[ERROR] Pin no debe de ser None para el sensor HFS.")
1452 1447 raise AttributeError("Valor de Pin es None.")
1453 1448
1454 1449 self.pull_down = False
1455 1450
1456 1451
1457 1452 self.__config()
1458 1453
1459 1454 def __config(self,):
1460 1455
1461 1456 GPIO.setwarnings(False)
1462 1457 GPIO.setmode(GPIO.BCM)
1463 1458
1464 1459 if self.pull_down:
1465 1460 GPIO.setup(self.pin,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
1466 1461 else:
1467 1462 GPIO.setup(self.pin,GPIO.IN)
1468 1463
1469 1464 chain = f"[Settings] Sensor HFS configurado con valores key:{self.key} name:{self.name} pin{self.pin}"
1470 1465 self.write_status(chain)
1471 1466
1472 1467 def run(self,):
1473 1468
1474 1469 value = GPIO.input(self.pin)
1475 1470
1476 1471 self.__logic(value)
1477 1472
1478 1473 def current_sensor(self,):
1479 1474
1480 1475 return GPIO.input(self.pin)
1481 1476
1482 1477 def __logic(self,status):
1483 1478
1484 1479 timestamp = datetime.now().timestamp()
1485 1480
1486 1481 self.prev_status = self.status
1487 1482
1488 1483 self.status = status
1489 1484 self.timestamp = timestamp
1490 1485
1491 1486 if(self.prev_status == False and self.status == True):
1492 1487
1493 1488 self.timestamp_init = timestamp
1494 1489
1495 1490 elif(self.prev_status == False and self.status == False):
1496 1491 #sensor desactivado
1497 1492 if self.activate:
1498 1493 if ((timestamp - self.timestamp_off)>=self.THRESHOLD_BETWEEN_OFF_SENSOR):
1499 1494 self.activate = False
1500 1495
1501 1496
1502 1497 elif(self.prev_status == True and self.status == False):
1503 1498 #se desactivó el sensor
1504 1499 if self.activate:
1505 1500 self.timestamp_off = timestamp
1506 1501
1507 1502 elif(self.prev_status == True and self.status == True):
1508 1503 if (timestamp - self.timestamp_init>=self.THRESHOLD_BETWEEN_ON_SENSOR):
1509 1504 #sensor activado
1510 1505 self.activate = True
1511 1506
1512 1507
1513 1508
1514 1509 class ina(sensor):
1515 1510
1516 1511
1517 1512 bus_voltage = 0
1518 1513 shunt_voltage = 0
1519 1514 current = 0
1520 1515
1521 1516 def __init__(self,name,key,address):
1522 1517
1523 1518 if address == None:
1524 1519
1525 1520 self.write_status("[ERROR] Se debe de asignar la dirección al atributo INA.")
1526 1521 raise AttributeError("Se debe de asignar la dirección al atributo INA.")
1527 1522
1528 1523 self.address = address
1529 1524 self.name = name
1530 1525 self.key = key
1531 1526
1532 1527 self.__config()
1533 1528
1534 1529 def __config(self,):
1535 1530
1536 1531
1537 1532 self.sensor = adafruit_ina219.INA219(i2c,self.address)
1538 1533 #Aumentamos resolución del ina219
1539 1534 self.sensor.bus_adc_resolution = ADCResolution.ADCRES_12BIT_32S
1540 1535 self.sensor.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_32S
1541 1536 #self.sensor.bus_voltage_range = BusVoltageRange.RANGE_16V
1542 1537
1543 1538
1544 1539 def run(self,):
1545 1540
1546 1541 self.bus_voltage = self.sensor.bus_voltage #V
1547 1542 self.shunt_voltage = self.sensor.shunt_voltage / 1000 #mV
1548 1543 self.current = self.sensor.current #mA
1549 1544
1550 1545
1551 1546
1552 1547 class lidar(sensor):
1553 1548
1554 1549
1555 1550
1556 1551 H0 = 0
1557 1552 dH_ = None
1558 1553
1559 1554 minus_H = -6 #Desnivel para realizar una nueva calibración.
1560 1555
1561 1556 TIME_LIDAR_ON = 30 #Segundos para considerar activado el sensor.
1562 1557 TIME_LIDAR_OFF = 60 #Segundos para considerar desactivado el sensor
1563 1558
1564 1559 TIME_RARE_EVENT = 60 #Silenciamos la activación por 1 minutos
1565 1560 NUM_SAMPLES_CALIBRATION = 15
1566 1561 NUM_SAMPLES_MEAN = 10
1567 1562
1568 1563 TIMEOUT_CALIBRATION = 60*60*24*1 #Se calibrará automaticamente cada 1 dia.
1569 1564 rare_height = 60
1570 1565 min_H = 10 # Centimetros como minimo de columna de agua
1571 1566
1572 1567
1573 1568 #---------------------------------------------------------------------------------#
1574 1569 mode_calibration = False
1575 1570 ERROR_WIRE = False
1576 1571 activate = False
1577 1572 FLAG_RARE_EVENT = False
1578 1573
1579 1574 timestamp_init = None
1580 1575 timestamp_fin = None
1581 1576 timestamp_calibrate = None
1582 1577 timestamp_rare_event = None
1583 1578 timestamp_init_calibration = 0 #Aseguramos de calibrar el sensor en cada encendido.
1584 1579 #---------------------------------------------------------------------------------#
1585 1580
1586 1581 array_samples = list()
1587 1582
1588 1583 def __load_H0(self,):
1589 1584
1590 1585 try:
1591 1586 timestamp = datetime.now().timestamp()
1592 1587 path = "/others/h0.txt"
1593 1588
1594 1589 flag = os.path.exists(path)
1595 1590
1596 1591 if (flag):
1597 1592
1598 1593 with open(path,'r') as file:
1599 1594
1600 1595 string_ = (file.readline())
1601 1596 values = string_.split("|")
1602 1597
1603 1598 self.timestamp_init_calibration = float(values[0])
1604 1599 self.H0 = float(values[1].strip())
1605 1600
1606 1601 if(timestamp - self.timestamp_init_calibration>self.TIMEOUT_CALIBRATION):
1607 1602
1608 1603 self.FLAG_CALIBRATION_LIDAR = False
1609 1604
1610 1605 elif self.H0 > 10000:
1611 1606 self.FLAG_CALIBRATION_LIDAR = False
1612 1607 else:
1613 1608
1614 1609 self.FLAG_CALIBRATION_LIDAR = True
1615 1610
1616 1611 else:
1617 1612 # Se debe de realizar su calibración de H0
1618 1613 self.FLAG_CALIBRATION_LIDAR = False
1619 1614
1620 1615 except Exception as e:
1621 1616
1622 1617 print(f"Error producido al leer H0 del lidar. Copia del error {e}.")
1623 1618
1624 1619 def __calibration(self,value):
1625 1620
1626 1621 self.array_calibration.append(value)
1627 1622
1628 1623 if(len(self.array_calibration)==self.NUM_SAMPLES_CALIBRATION):
1629 1624
1630 1625 self.H0 = numpy.nanmedian(numpy.array(self.array_calibration))
1631 1626 self.FLAG_CALIBRATION_LIDAR = True
1632 1627 self.array_calibration = list()
1633 1628
1634 1629
1635 1630 #Guardamos el archivo de calibración
1636 1631
1637 1632 try:
1638 1633 path = "/others/h0.txt"
1639 1634
1640 1635 flag = os.path.exists(path)
1641 1636
1642 1637 if flag:
1643 1638 try:
1644 1639 os.remove(path)
1645 1640 except:
1646 1641 pass
1647 1642
1648 1643 with open(path,"w") as file:
1649 1644
1650 1645 timestamp = datetime.now().timestamp()
1651 1646 file.write(str(timestamp)+"|"+str(self.H0))
1652 1647
1653 1648
1654 1649 self.timestamp_init_calibration = timestamp
1655 1650
1656 1651 self.timestamp_calibrate = None
1657 1652 self.timestamp_init = None
1658 1653 self.timestamp_fin = None
1659 1654 self.timestamp_rare_event= None
1660 1655
1661 1656 self.FLAG_RARE_EVENT = False
1662 1657 self.activate = False
1663 1658 self.mode_calibration = False
1664 1659
1665 1660 except:
1666 1661 pass
1667 1662
1668 1663 finally:
1669 1664 gc.collect()
1670 1665
1671 1666
1672 1667 def __config(self,):
1673 1668
1674 1669
1675 1670 self.sensor_lidar = adafruit_lidarlite.LIDARLite(i2c, sensor_type=adafruit_lidarlite.TYPE_V3HP)
1676 1671
1677 1672
1678 1673
1679 1674 def __init__(self,name,key):
1680 1675
1681 1676 super().__init__(name,key)
1682 1677
1683 1678 self.__config()
1684 1679 self.__load_H0()
1685 1680
1686 1681 if self.FLAG_CALIBRATION_LIDAR == False:
1687 1682
1688 1683 self.mode_calibration = True
1689 1684
1690 1685 else:
1691 1686 self.mode_calibration = False
1692 1687
1693 1688 def run(self,):
1694 1689 timestamp = datetime.now().timestamp()
1695 1690
1696 1691 diff = timestamp- self.timestamp_init_calibration
1697 1692
1698 1693 value = self.sensor_lidar.distance
1699 1694
1700 1695 if self.mode_calibration:
1701 1696 self.__calibration(value)
1702 1697
1703 1698 elif (diff> self.TIMEOUT_CALIBRATION):
1704 1699 #Es necesario realizar una calibración
1705 1700 self.mode_calibration = True
1706 1701
1707 1702 else:
1708 1703
1709 1704 if (value > 10000 and self.ERROR_WIRE == False):
1710 1705
1711 1706 '''
1712 1707 Error de sensor lidar en la obtención de datos. Puede ser problema de cables
1713 1708 Se desabilita hasta que sea corregido manualmente.
1714 1709 '''
1715 1710
1716 1711 self.ERROR_WIRE = True
1717 1712 self.dH_ = None
1718 1713
1719 1714
1720 1715 path = "/others/h0.txt"
1721 1716
1722 1717 with open(path,"w") as file:
1723 1718
1724 1719 timestamp = datetime.now().timestamp()
1725 1720 file.write(str(0)+"|"+str(0))
1726 1721
1727 1722 elif (value < 10000 ):
1728 1723 if(self.ERROR_WIRE):
1729 1724 self.ERROR_WIRE = False
1730 1725 self.mode_calibration = True
1731 1726 else:
1732 1727
1733 1728 self.array_samples.append(value)
1734 1729 self.array_samples.append(self.sensor_lidar.distance)
1735 1730
1736 1731 if(len(self.array_samples)>=self.NUM_SAMPLES_MEAN):
1737 1732
1738 1733 value = numpy.nanmedian(numpy.array(self.array_samples))
1739 1734
1740 1735 self.array_samples = list()
1741 1736 size = len(self.x_value)
1742 1737
1743 1738 if(size>self.max_size):
1744 1739
1745 1740 self.x_value = self.x_value[1:]
1746 1741 self.y_value = self.y_value[1:]
1747 1742
1748 1743 self.x_value.append(timestamp)
1749 1744 self.y_value.append(value)
1750 1745
1751 1746 #---------------------- logica -------------------------#
1752 1747
1753 1748 self.__logic(value)
1754 1749
1755 1750 def __logic(self,value):
1756 1751
1757 1752
1758 1753 timestamp = datetime.now().timestamp()
1759 1754
1760 1755 dH = self.H0 - value
1761 1756
1762 1757 self.dH_ = dH
1763 1758
1764 1759 '''
1765 1760 Si dH>0, entonces el sistema ha detectado evento
1766 1761 Si dH<0, hay un desnivel en el suelo o referencia por lo que es necesario volver a calibrar
1767 1762 '''
1768 1763
1769 1764 if dH>= self.min_H and self.timestamp_init == None:
1770 1765
1771 1766 '''
1772 1767 Comienza el evento
1773 1768 '''
1774 1769 self.timestamp_init = timestamp
1775 1770
1776 1771 elif dH<self.min_H and dH>=0 :
1777 1772
1778 1773 if self.timestamp_init != None:
1779 1774
1780 1775 diff_timestamp = datetime.now().timestamp() - self.timestamp_init # Calculamos cuanto tiempo va activado la señal.
1781 1776
1782 1777 if self.timestamp_init != None and dH>1 and diff_timestamp<15:
1783 1778 '''
1784 1779 Al inicio del evento se puede considerar pequeñas variaciones
1785 1780 '''
1786 1781 pass
1787 1782
1788 1783 elif self.timestamp_init !=None and self.timestamp_fin == None:
1789 1784
1790 1785 '''
1791 1786 El tiempo que lleva activado la señal de alerta es más de 15 segundos.
1792 1787 '''
1793 1788
1794 1789 self.timestamp_fin = timestamp
1795 1790
1796 1791 elif self.timestamp_init!=None and self.timestamp_fin !=None:
1797 1792
1798 1793 '''
1799 1794 En caso la diferencia de altura es menor a lo establecido como emergencia,
1800 1795 entonces se desactiva la señal de alerta pero ya pasado un tiempo.
1801 1796
1802 1797 '''
1803 1798
1804 1799 if (timestamp - self.timestamp_fin>=self.TIME_LIDAR_OFF):
1805 1800
1806 1801 self.timestamp_fin = None
1807 1802 self.timestamp_init = None
1808 1803 self.timestamp_rare_event = None
1809 1804
1810 1805 self.activate = False
1811 1806
1812 1807
1813 1808 elif dH>= self.min_H and self.timestamp_init != None:
1814 1809
1815 1810 #Consideramos que debe de ser constante tal cambio al menos 30 segundos
1816 1811 #Se puede configurar en el archivo vars.json
1817 1812 timestamp = datetime.now().timestamp()
1818 1813
1819 1814 diff_timestamp = timestamp - self.timestamp_init
1820 1815
1821 1816 if dH>= self.rare_height and diff_timestamp<=10:
1822 1817
1823 1818 '''
1824 1819 En este caso consideramos que la altura aumentó rapidamente en menos de 10 segundos.
1825 1820 Este suceso puede ser producido por un mantenimiento o por seres vivos en el cauce.
1826 1821 '''
1827 1822
1828 1823 self.FLAG_RARE_EVENT = True
1829 1824 self.timestamp_rare_event = timestamp
1830 1825 self.timestamp_init = None
1831 1826 self.timestamp_fin = None
1832 1827 self.activate = False
1833 1828
1834 1829
1835 1830 elif diff_timestamp >= self.TIME_LIDAR_ON:
1836 1831
1837 1832 '''
1838 1833 El tiempo de activación fue superado, por lo que se validará la activación.
1839 1834 '''
1840 1835
1841 1836 if(self.timestamp_rare_event == None):
1842 1837 self.timestamp_rare_event = 0
1843 1838
1844 1839 diff = timestamp - self.timestamp_rare_event
1845 1840
1846 1841 if (diff>self.TIME_RARE_EVENT):
1847 1842 self.timestamp_rare_event = 0
1848 1843 self.FLAG_RARE_EVENT = False
1849 1844
1850 1845 if not self.FLAG_RARE_EVENT:
1851 1846 self.activate = True
1852 1847 self.timestamp_rare_event = None
1853 1848
1854 1849 elif dH <=self.minus_H and self.timestamp_calibrate == None:
1855 1850 '''
1856 1851 Verificamos si es necesario realizar una calibración.
1857 1852 '''
1858 1853 self.timestamp_calibrate = timestamp
1859 1854
1860 1855 elif dH <=self.minus_H and self.timestamp_calibrate != None:
1861 1856
1862 1857 '''
1863 1858 Si se sobrepasa el tiempo necesario para una nueva calibración,
1864 1859 entonces se seleccion
1865 1860 '''
1866 1861 diff = timestamp - self.timestamp_calibrate
1867 1862 if diff >= 30:
1868 1863 self.mode_calibration = True
1869 1864
1870 1865
1871 1866 else:
1872 1867 '''
1873 1868 En el unico caso que el lidar marque dH=0.
1874 1869 '''
1875 1870 self.timestamp_calibrate = None
1876 1871 self.timestamp_init = None
1877 1872 self.timestamp_fin = None
1878 1873 self.timestamp_rare_event= None
1879 1874
1880 1875 gc.collect()
1881 1876 No newline at end of file
@@ -1,1561 +1,1561
1 1 import os
2 2 import io
3 3 import gc
4 4 import cv2
5 5 import json
6 6 import time
7 7 import gzip
8 8 import numpy
9 9 import busio
10 10 import board
11 11 import ntplib
12 12 import psutil
13 13 import base64
14 14 import requests
15 15 import datetime
16 16 import traceback
17 17 import threading
18 18 import urllib.request
19 19 import multiprocessing
20 20 import RPi.GPIO as GPIO
21 21 import adafruit_lidarlite
22 22 import paho.mqtt.client as mqtt
23 23 import paho.mqtt.publish as publish
24 24
25 25 from others import *
26 26 from copy import deepcopy
27 27 from multiprocessing import Manager, Pipe,Queue,Lock
28 28 from flask import Flask, render_template, jsonify
29 29 from datetime import datetime, timedelta
30 30 from time import sleep
31 31
32 32
33 33 class manage_lahares(object):
34 34
35 35 timestamp_init_system = None
36 36 max_retries = 5
37 37 capture_count = 0
38 38
39 39 flag_date_update = False
40 40 flag_internet = False
41 41
42 42 bytes_send = 0
43 43 bytes_recv = 0
44 44
45 45 bytes_recv_total = 0
46 46 bytes_send_total = 0
47 47
48 48 count_status = 0
49 49 count_write_data = 0
50 50
51 51 __version__ = 0
52 52
53 53
54 54 dataOut = dict()
55 55
56 56 def __get_temperature(self,):
57 57
58 58 '''
59 59 Metodo para leer la temperatura del sistema.
60 60 '''
61 61
62 62 try:
63 63 with open("/sys/class/thermal/thermal_zone0/temp", "r") as archivo:
64 64 temperature = int(archivo.read())
65 65 temperature = temperature / 1000.0 # Convertir de miligrados a grados Celsius
66 66
67 67 return temperature
68 68
69 69 except FileNotFoundError:
70 70 return -1
71 71
72 72
73 73 def create_path(self,path):
74 74 if path is None:
75 75 return
76 76
77 77 if not os.path.isdir(path):
78 78 os.makedirs(path)
79 79
80 80 return
81 81
82 82 def read_metadata(self):
83 83
84 84 #Cargamos la versión de los dispositivos
85 85
86 86 file_version_rpi = "/others/version_rpi.txt"
87 87
88 88 file_bytes_internet = "/others/b4g.txt"
89 89
90 90
91 91 if os.path.exists(file_version_rpi):
92 92
93 93 with open(file_version_rpi,"r") as f:
94 94
95 95 value = float(f.readline())
96 96
97 97 self.__version__ = value
98 98
99 99 else:
100 100
101 101 self.__version__ = 0
102 102
103 103 try:
104 104
105 105 if os.path.exists(file_bytes_internet):
106 106 '''
107 107 Con este archivo podemos realizar el control de consumo de internet.
108 108 El archivo existe, entonces leemos los valores
109 109 Estructura (2 lineas):
110 110 ----------
111 111 - fecha de creacion
112 112 - {bytes_send}#{bytes_recv}
113 113
114 114 '''
115 115
116 116 with open(file_bytes_internet,"r") as f:
117 117
118 118 lines = numpy.array(f.read().splitlines(),dtype=object)
119 119
120 120 if lines.shape[0] == 0:
121 121 self.__create_file_bytes()
122 122
123 123 if lines.shape[0]>1:
124 124 date = lines[0]
125 125 #------------- Realizamos control de fecha ------------------#
126 126
127 127 date = datetime.strptime(date, "%d/%m/%Y")
128 128 now = datetime.now()
129 129
130 130 if (datetime(date.year,date.month,1) == datetime(now.year,now.month,1)):
131 131 #Estamos en el mismo mes, cargamos los datos
132 132
133 133 tmp = lines[1].split("#")
134 134
135 135 self.bytes_send_total = float(tmp[0])
136 136 self.bytes_recv_total = float(tmp[1])
137 137
138 138 self.bytes_recv = psutil.net_io_counters().bytes_recv
139 139 self.bytes_send = psutil.net_io_counters().bytes_sent
140 140
141 141 else:
142 142 #Creamos nuevo archivo con datos
143 143 formatted_date_time = now.strftime("%d/%m/%Y")
144 144
145 145 with open(file_bytes_internet,"w") as f:
146 146
147 147 f.write(formatted_date_time)
148 148
149 149 self.bytes_recv = psutil.net_io_counters().bytes_recv
150 150 self.bytes_send = psutil.net_io_counters().bytes_sent
151 151
152 152 self.bytes_send_total = 0
153 153 self.bytes_recv_total = 0
154 154
155 155 else:
156 156
157 157 self.__create_file_bytes()
158 158
159 159
160 160 except:
161 161
162 162 self.write_status(f"[ERROR] Error: {traceback.format_exc()}")
163 163
164 164
165 165
166 166 def __create_file_bytes(self,):
167 167
168 168 file_bytes_internet = "/others/b4g.txt"
169 169 #preparamos los datos
170 170 now = datetime.now()
171 171
172 172 formatted_date_time = now.strftime("%d/%m/%Y")
173 173 self.bytes_recv = psutil.net_io_counters().bytes_recv
174 174 self.bytes_send = psutil.net_io_counters().bytes_sent
175 175
176 176 with open(file_bytes_internet,"w") as f:
177 177
178 178 formatted_date_time = now.strftime("%d/%m/%Y")
179 179 f.write(formatted_date_time)
180 180
181 181 self.bytes_send_total = 0
182 182 self.bytes_recv_total = 0
183 183
184 184 def __handle_size(self, path):
185 185
186 186 try:
187 187 #en mb
188 188 limite = 2048
189 189
190 190 limite *=1024
191 191 size = os.path.getsize(path)
192 192
193 193 if (size>0.95*limite):
194 194
195 195 #---------------------- Guardamos la mitad final del archivo ---------------------------------------#
196 196
197 197 get_position = size // 2
198 198 lectura = None
199 199
200 200
201 201 with open(path,'rb') as file:
202 202
203 203 file.seek(get_position)
204 204 lectura = file.read()
205 205
206 206 with open(path,'wb') as file:
207 207 file.write(lectura)
208 208
209 209
210 210 except:
211 211
212 212 if self.debug:
213 213 print("Ocurrió un error al analizar el tamaño del archivo {}".format(os.path.basename(path)))
214 214
215 215 error = traceback.format_exc()
216 216 self.write_status("Ocurrió un error al analizar el tamaño del archivo {} Copia del error: {}.".format(os.path.basename(path),error))
217 217
218 218 def write_status(self,chain):
219 219
220 220 now = datetime.now()
221 221
222 222 formatted_date_time = now.strftime("%d/%m/%Y %H:%M:%S")
223 223
224 224 filename = '/logs/log.txt'
225 225
226 226 chain = formatted_date_time + " |" + chain
227 227
228 228 if not os.path.isdir(os.path.dirname(filename)):
229 229 os.makedirs(os.path.dirname(filename))
230 230
231 231 try:
232 232 with open(filename,'a') as file:
233 233
234 234 file.write(chain + '\n')
235 235 except:
236 236
237 237 if self.debug:
238 238 print("Ocurrió un error al guardar datos logs.")
239 239
240 240 else:
241 241 if(self.count_status == 150):
242 242 self.count_status = 0
243 243 #--------------------- Revisamos el tamaño de los archivos-------------------#
244 244 # Debe de ser menor a 1 GB.
245 245
246 246 self.__handle_size(filename)
247 247 else:
248 248 self.count_status +=1
249 249 return
250 250
251 251 def check_internet(self,verbose=True):
252 252
253 253 count = 0
254 254
255 255 while 1:
256 256
257 257 try:
258 258 urllib.request.urlopen('http://www.google.com', timeout=1)
259 259
260 260 except:
261 261 count +=1
262 262 if (count ==3):
263 263 self.flag_internet = False
264 264 break
265 265
266 266 sleep(0.5)
267 267 else:
268 268 if verbose:
269 269 self.write_status("Se cuenta con conexión a internet.")
270 270
271 271 if self.debug:
272 272 print("Se cuenta con conexión a internet")
273 273
274 274 self.flag_internet = True
275 275
276 276 return
277 277
278 278 return
279 279
280 280
281 281
282 282
283 283 def update_time(self,):
284 284
285 285 ntp_server = 'pool.ntp.org'
286 286
287 287 count = 0
288 288
289 289 while 1:
290 290
291 291 try:
292 292
293 293 client = ntplib.NTPClient()
294 294
295 295 response = client.request(ntp_server)
296 296
297 297 ntp_time = datetime.utcfromtimestamp(response.tx_time) - timedelta(hours=+5)
298 298
299 299 os.system('sudo date {} --utc'.format(ntp_time.strftime('%Y%m%d%H%M.%S')))
300 300
301 301 if self.debug:
302 302 print(f'Hora actualizada: {ntp_time} UTC')
303 303
304 304 self.write_status(f"La hora ha sido actualizado a {ntp_time}")
305 305
306 306
307 307 except Exception as e:
308 308
309 309 if self.debug:
310 310 print(f'Error al actualizar la hora: {e}')
311 311 print(f"Volviendo a intentar en unos instantes...")
312 312
313 313 count +=1
314 314
315 315 if(count == 3):
316 316
317 317 if self.debug:
318 318
319 319 print("Se alcanzó el maximo de intentos. Se actualizará después")
320 320
321 321 self.write_status("No se pudo actualizar la hora.")
322 322
323 323 break
324 324
325 325 sleep(3)
326 326 else:
327 327
328 328 self.flag_date_update = True
329 329
330 330 break
331 331
332 332
333 333 def make_setup(self,):
334 334
335 335 #----------------------- extract lists --------------------------#
336 336
337 337 try:
338 338
339 339 list_HFS = self.vars_gpio.get("gpio_HFS",None)
340 340 list_HB = self.vars_gpio.get("gpio_HB100",None)
341 341 list_RCWL = self.vars_gpio.get("gpio_RCWL",None)
342 342 list_ina = self.vars_gpio.get("address_ina",None)
343 343
344 344 if list_HFS is not None:
345 345 for n,key in enumerate(list_HFS.keys()):
346 346 if(n>=self.vars.get("MAX_NUMBER_SENSORS")):
347 347 if self.debug:
348 348 print("Se ha proporcionado una cantidad mayor de sensores para el tipo HFS. Máximo definido son 4 sensores.")
349 349
350 350 self.write_status("[WARNING] Se asignaron más de 4 sensores para el tipo HFS.")
351 351 else:
352 352 name = 'sensor_HFS{:02d}'.format(n+1)
353 353 pin = int(list_HFS[key])
354 354
355 355 try:
356 356 self.dataOut[name] = sensor_HFS(name,key,pin)
357 357 self.dataOut[name].THRESHOLD_BETWEEN_ON_SENSOR = self.vars.get("MIN_ON_SENSOR",30)
358 358 self.dataOut[name].THRESHOLD_BETWEEN_OFF_SENSOR = self.vars.get("MIN_OFF_SENSOR",5)
359 359 except:
360 360
361 361 if self.debug:
362 362 print(f"[ERROR] Al declarando sensor HFS. Error: {traceback.format_exc()}")
363 363 self.write_status(f"[ERROR] Al declarando sensor HFS. Error: {traceback.format_exc()}")
364 364
365 365 if list_HB is not None:
366 366 for n,key in enumerate(list_HB.keys()):
367 367 if(n>=self.vars.get("MAX_NUMBER_SENSORS")):
368 368 if self.debug:
369 369 print("Se ha proporcionado una cantidad mayor de sensores para el tipo HB100. Máximo definido son 4 sensores.")
370 370
371 371 self.write_status("[WARNING] Se asignaron más de 4 sensores para el tipo HB100.")
372 372
373 373 else:
374 374 name = 'sensor_HB{:02d}'.format(n+1)
375 375 self.dataOut[name] = sensor(name,key)
376 376
377 377 if list_RCWL is not None:
378 378 for n,key in enumerate(list_RCWL.keys()):
379 379 if(n>=self.vars.get("MAX_NUMBER_SENSORS")):
380 380 if self.debug:
381 381 print("Se ha proporcionado una cantidad mayor de sensores para el tipo RCWL. Máximo definido son 4 sensores.")
382 382
383 383 self.write_status("[WARNING] Se asignaron más de 4 sensores para el tipo RCWL.")
384 384
385 385 else:
386 386 name = 'sensor_RCWL{:02d}'.format(n+1)
387 387
388 388 self.dataOut[name] = sensor(name,key)
389 389
390 390
391 391 if list_ina is not None and self.flag_ina:
392 392
393 393 for n, key in enumerate(list_ina.keys()):
394 394
395 395 name = 'ina_{:02d}'.format(n+1)
396 396 address = int(list_ina[key])
397 397 try:
398 398 self.dataOut[name] = ina(name,key,address)
399 399 except:
400 400 if self.debug:
401 401 print(f"[ERROR] Error iniciando sensor ina: {traceback.format_exc()}")
402 402 self.write_status(f"[ERROR] Error iniciando sensor ina: {traceback.format_exc()}")
403 403
404 404
405 405 #--------------------- others sensors and modules -----------------#
406 406 if self.flag_lidar:
407 407 try:
408 408 self.dataOut['lidar'] = lidar(name='lidar',key='lidar')
409 409
410 410 self.dataOut['lidar'].TIME_LIDAR_ON = self.vars.get("TIME_LIDAR_ON",30)
411 411 self.dataOut['lidar'].TIME_LIDAR_OFF = self.vars.get("TIME_LIDAR_OFF",60)
412 412 self.dataOut['lidar'].min_H = self.vars.get("MIN_HEIGHT_WATER_FOR_LIDAR",5)
413 413 self.dataOut['lidar'].minus_H = self.vars.get("MIN_MINUS_HEIGHT_FOR_LIDAR",4)*-1
414 414 self.dataOut['lidar'].rare_height = self.vars.get("RARE_HEIGHT",70)
415 415 except:
416 416 if self.debug:
417 417 print(f"[ERROR] Error iniciando sensor: {traceback.format_exc()}")
418 418 self.write_status(f"[ERROR] Error iniciando sensor: {traceback.format_exc()}")
419 419 except:
420 420
421 421 if self.debug:
422 422 print("Ocurrió un error en la preparación de la trama de configuración.")
423 423
424 424 error = traceback.format_exc()
425 425
426 426 self.write_status(f"Ocurrió un error en la preparación de la trama de configuración. Copia del error: {error}")
427 427
428 428 else:
429 429 return
430 430
431 431 def __config(self,):
432 432
433 433 #------------------------ revisamos algunas configuraciones ----------------#
434 434
435 435 if self.flag_camera:
436 436
437 437 pin = self.camera_keys.get("pin_camera",None)
438 438
439 439 if pin == None:
440 440
441 441 self.write_status("[ERROR] Pin del control de camara no ha sido definido. Se controlará el tomado de fotografías por Software.")
442 442
443 443 else:
444 444 # Configuramos el pin como salida
445 445 GPIO.setup(pin,GPIO.OUT)
446 446
447 447 self.camera = camera(flag=self.flag_camera,pin=pin,obj=self.obj_vars)
448 448 else:
449 449
450 450 self.camera = camera(flag=False,pin=None,obj=self.obj_vars)
451 451
452 452
453 453 #-------------------------- Cargamos el modo ---------------------------------#
454 454
455 455 self.string_model = load_version()
456 456
457 457
458 458 if (self.inference_mode == 'video' and 'Raspberry Pi Zero' in model) or self.inference_mode == 'photo':
459 459
460 460 if self.debug:
461 461
462 462 print("Se ha cambiado el modo de inferencia automaticamente. Debido a que los requisitos del sistema no son soportados. Actual inferencia de ML a Server.")
463 463
464 464 self.write_status("Se ha cambiado el modo de inferencia automaticamente. Debido a que los requisitos del sistema no son soportados. Actual inferencia de ML a Server.")
465 465
466 466 self.inference_mode = 'server'
467 467
468 468
469 469 def __load_vars(self):
470 470
471 471 self.path_save = "/data"
472 472
473 473 self.path_save_img = os.path.join(self.path_save,'img')
474 474 self.path_save_json = os.path.join(self.path_save,'json')
475 475
476 476
477 self.semaphore = threading.Semaphore(1)
478 self.semaphore_estimator = threading.Semaphore(1)
479 self.semaphore_get_video = threading.Semaphore(1)
477 self.semaphore = threading.Semaphore()
478 self.semaphore_estimator = threading.Semaphore()
479 self.semaphore_get_video = threading.Semaphore()
480 480
481 481 self.manager = Manager()
482 482 self.queue = self.manager.Queue()
483 483 self.queue_estimator = self.manager.Queue()
484 484
485 485 self.obj_vars = VarsJons()
486 486 self.estimator = estimator(self.obj_vars,)
487 487
488 488 self.debug = self.obj_vars.debug
489 489 self.vars = self.obj_vars.vars
490 490 self.vars_mqtt = self.obj_vars.vars_mqtt
491 491 self.store_data = self.obj_vars.store_data
492 492 self.vars_gpio = self.obj_vars.vars_gpio
493 493 self.inference_mode = self.obj_vars.inference_mode
494 494 self.camera_keys = self.obj_vars.camera
495 495
496 496 self.flag_camera = self.vars_gpio.get("camera", True)
497 497 self.flag_lidar = self.vars_gpio.get("lidar",True)
498 498 self.flag_ina = self.vars_gpio.get("ina",True)
499 499
500 500 self.id = str(self.obj_vars.id)
501 501 #----------------- share variables --------------------#
502 502
503 503 self.share_estimator = multiprocessing.Value('d', 0)
504 504
505 505
506 506 def __init__(self):
507 507
508 508 #---------- Esperamos el inicio del sistema ---------#
509 509
510 510
511 511
512 512 sleep(4)
513 513
514 514 #Guardamos el timestamp de inicio del sistema
515 515 self.timestamp_init_system = datetime.now().timestamp()
516 516 #------------------ get parameters ------------------#
517 517
518 518 self.__load_vars()
519 519
520 520 #------------------- Prints -------------------------#
521 521 if self.debug:
522 522 print(10*"-")
523 523 print("Iniciando sistema")
524 524 print(r"Ruta de guardado {}".format(self.store_data))
525 525 print(r"Ruta de guardado de imagenes {}".format(self.path_save_img))
526 526 print(r"Ruta de guardado de archivos .json: {}".format(self.path_save_json))
527 527 print(10*"-")
528 528
529 529 #----------------- Creamos paths ----------------------#
530 530
531 531 self.create_path(self.path_save_img)
532 532 self.create_path(self.path_save_json)
533 533 self.create_path(self.path_save)
534 534
535 535 #------------------ Read metadatos --------------------#
536 536
537 537 self.read_metadata()
538 538
539 539 #-------------------- Escribiendo logs -----------------#
540 540 self.write_status(30*"-")
541 541 self.write_status("Sistema iniciado")
542 542 self.write_status(r"Ruta de guardado {}".format(self.store_data))
543 543 self.write_status(r"Ruta de guardado de imagenes {}".format(self.path_save_img))
544 544 self.write_status(r"Ruta de guardado de archivos .json: {}".format(self.path_save_json))
545 545 self.write_status(30*"-")
546 546
547 547
548 548 #-------------------- Leyendo setup --------------------#
549 549
550 550 self.make_setup()
551 551
552 552 #--------------------- Others config -------------------#
553 553
554 554 self.__config()
555 555 #------------------- Run process -----------------------#
556 556
557 557 self.run_process()
558 558
559 559 #------------------ Fin script -------------------------#
560 560
561 561 def run_process(self,):
562 562
563 563 if self.debug:
564 564 print("Realizando la derivación de procesos")
565 565
566 566 self.write_status("Realizando la derivación de procesos.")
567 567
568 568 #--------------------- Creamos la variable compartida ----------------------#
569 569 #Esta variable compartida nos va a permitir compartir datos para mostrarlo en Flask
570 570
571 571 json_dict = self.manager.dict()
572 572
573 573 lock = Lock()
574 574 #-------------------------- init process --------------------#
575 575 try:
576 576 self.process_sensores = multiprocessing.Process(target = self.method_sensores, args=(self.share_estimator,json_dict,lock,))
577 577 self.process_mqtt = multiprocessing.Process(target = self.method_mqtt,args=(json_dict,lock,))
578 578 self.process_estimator= multiprocessing.Process(target= self.method_estimator,args=(self.share_estimator,lock,))
579 579 self.process_flask = multiprocessing.Process(target=self.method_flask,args=(json_dict,lock,))
580 580
581 581
582 582 self.process_sensores.start()
583 583 self.process_mqtt.start()
584 584 self.process_estimator.start()
585 585 self.process_flask.start()
586 586
587 587
588 588 if self.inference_mode == 'video' or self.inference_mode == 'server':
589 589
590 590 self.process_video = multiprocessing.Process(target=self.method_video,args=())
591 591 self.process_video.start()
592 592 self.process_video.join()
593 593
594 594
595 595 self.process_mqtt.join()
596 596 self.process_sensores.join()
597 597 self.process_estimator.join()
598 598 self.process_flask.join()
599 599
600 600
601 601 except:
602 602
603 603 error = traceback.format_exc()
604 604
605 605 if self.debug:
606 606 print("Error en la inialización de procesos.")
607 607
608 608 error = traceback.format_exc()
609 609 self.write_status(f"Error en la inialización de procesos. Copia del error {error}")
610 610
611 611 def method_flask(self,json_dict,lock):
612 612
613 613 sleep(2)
614 614
615 615 if self.debug:
616 616
617 617 print("Metodo Flask Lanzado")
618 618
619 619 app = Flask(__name__)
620 620 self.write_status(f"[Flask] Metodo lanzado")
621 621
622 622
623 623 @app.route('/')
624 624 def home():
625 625 return render_template("main.html")
626 626
627 627 def get_image():
628 628
629 629 latest_name = sorted(os.listdir("/data/img"))[-1]
630 630
631 631 return os.path.join("data/img",latest_name)
632 632
633 633
634 634 @app.get("/update")
635 635 def update():
636 636
637 637 try:
638 638 data = dict()
639 639 with locked(lock):
640 640
641 641 data = dict(json_dict)
642 642
643 643 x = jsonify(data)
644 644
645 645 except:
646 646 if self.debug:
647 647 print(f"[FlaskUPDTErr] Error: {traceback.format_exc()}")
648 648 self.write_status(f"[FlaskUPDTErr] Error: {traceback.format_exc()}")
649 649
650 650 else:
651 651
652 652 return x
653 653
654 654
655 655 try:
656 656
657 657 app.run(debug=False,port=5000,host='0.0.0.0')
658 658
659 659 except:
660 660 if self.debug:
661 661 print(f"[ErrFlask] Error en flask \n{traceback.format_exc()}")
662 662 self.write_status(f"[ErrFlask] Error en flask \n{traceback.format_exc()}")
663 663
664 664 else:
665 665 if self.debug:
666 666 print(f"[ErrFlask] Error en flask \n{traceback.format_exc()}")
667 667 self.write_status(f"[Flask] Servidor lanzado")
668 668
669 669
670 670
671 671
672 672
673 673
674 674
675 675
676 676 def method_estimator(self, share,lock):
677 677
678 678 #----------------------- Carga del modelo IA -------------------------------------#
679 679
680 680 timestamp_latest = datetime.now().timestamp()
681 681
682 682 self.estimator.load_weights() #Cargamos los pesos
683 683
684 684 if self.debug:
685 685 print("Proceso estimator ha sido lanzado.")
686 686
687 687 self.write_status("[Estimator] Proceso estimator ha sido lanzado.")
688 688
689 689 sleep(5)
690 690
691 691
692 692 while 1:
693 693
694 694 flag = False
695 695
696 696 try:
697 697
698 698 self.semaphore_estimator.acquire()
699 699
700 700 while not self.queue_estimator.empty():
701 701
702 702 flag = True #Se realizó la inferencia
703 703 timestamp_latest = datetime.now().timestamp()
704 704
705 705 if self.inference_mode == 'photo':
706 706 self.estimator.image = self.queue_estimator.get()
707 707
708 708 elif self.inference_mode == 'video':
709 709 self.estimator.video = self.queue_estimator.get()
710 710
711 711 elif self.inference_mode == 'server':
712 712 self.estimator.video = self.queue_estimator.get()
713 713
714 714 self.estimator.run()
715 715
716 716 self.semaphore_estimator.release()
717 717
718 718 timestamp = datetime.now().timestamp()
719 719
720 720 if (timestamp - timestamp_latest > 30*60):
721 721 '''
722 722 Controlamos que la ultima inferencia no se haya realizado hace más de 30 minutos.
723 723 Esto es util para prevenir cualquier tipo de incidente, ya sea cámara malograda entre otras formas.
724 724 '''
725 725
726 726 self.estimator.inference_value = 10
727 727
728 728
729 729 except:
730 730 exc = traceback.format_exc()
731 731 self.write_status(f"[ERROR] Ocurrió un error al realizar inferencia. Copia del error: {exc}")
732 732 sleep(10)
733 733
734 734 else:
735 735
736 736 '''
737 737 Solo compartimos el valor de la inferencia al otro estimator. Debido a que cada multiproceso
738 738 maneja una copia diferente del objeto Estimator. Por más que realicemos un cálculo, no se verá
739 739 reflejado en el otro objeto.
740 740 '''
741 741 if flag:
742 742 self.write_status("[Estimator] Se realizó la inferencia en el Queue Estimator. Variable ha sido compartida entre multiprocesos.")
743 743
744 744 with locked(lock):
745 745 if self.estimator.inference_value == None:
746 746 inference = 10
747 747 else:
748 748 inference = self.estimator.inference_value
749 749
750 750 share.value = inference
751 751
752 752 sleep(1)
753 753
754 754 gc.collect()
755 755
756 756 sleep(3)
757 757
758 758
759 759
760 760
761 761 def send_uart(self,uart,string):
762 762
763 763 try:
764 764 uart.write(string.encode('utf-8'))
765 765
766 766 except:
767 767
768 768 if self.debug:
769 769 print("Ocurrió un error al enviar datos por puerto UART.")
770 770
771 771 self.write_status("[ERROR] Ocurrió un error al enviar datos por el puerto UART.")
772 772
773 773
774 774
775 775 def __get_internet_bytes(self,):
776 776
777 777 a, b = psutil.net_io_counters().bytes_recv,psutil.net_io_counters().bytes_sent
778 778 recv = a - self.bytes_recv
779 779 send = b - self.bytes_send
780 780
781 781 self.bytes_recv_total += recv
782 782 self.bytes_send_total += send
783 783
784 784 self.bytes_recv = a
785 785 self.bytes_send = b
786 786
787 787 file_bytes_internet = "/others/b4g.txt"
788 788
789 789 #--------------------- control -------------------------------#
790 790 tmp_write = list()
791 791
792 792 try:
793 793
794 794 if os.path.exists(file_bytes_internet):
795 795
796 796
797 797 with open(file_bytes_internet,"r") as file:
798 798
799 799 line = numpy.array(file.read().splitlines(),dtype=object)
800 800
801 801
802 802 if line.shape[0]>1:
803 803
804 804 tmp_write.append(line[0])
805 805
806 806 tmp_write.append(f"{self.bytes_send_total}#{self.bytes_recv_total}")
807 807
808 808 with open(file_bytes_internet,"w") as file:
809 809
810 810 file.write(l+'\n' for l in tmp_write)
811 811
812 812 else:
813 813
814 814 self.__create_file_bytes()
815 815
816 816
817 817 gc.collect()
818 818 except:
819 819
820 820 self.write_status(f"[ERROR] Error: {traceback.format_exc()}")
821 821
822 822
823 823
824 824
825 825
826 826 def __process_values(self,share,flask_,lock):
827 827
828 828
829 829 try:
830 830
831 831 tmp_data = dict()
832 832
833 833 #------------------------ Calculamos el uso de internet --------------#
834 834
835 835
836 836 total_internet = round((self.bytes_recv_total + self.bytes_send_total)/(1024*1024),3)
837 837 #---------------------------------------------------------------------#
838 838
839 839 tmp_data["current_timestamp"] = datetime.now().timestamp()
840 840 tmp_data['init_timestamp'] = self.timestamp_init_system
841 841 tmp_data['version_rpi'] = self.__version__
842 842 tmp_data['location'] = self.obj_vars.location
843 843 tmp_data['latitude'] = self.obj_vars.latitude
844 844 tmp_data['longitude'] = self.obj_vars.longitude
845 845 tmp_data["id"] = self.id
846 846 tmp_data['camera_status'] = self.camera.status
847 847 tmp_data["disk_percent_use"] = psutil.disk_usage('.').percent
848 848 tmp_data['DEVICE_INFO'] = self.string_model
849 849 tmp_data['CPU_usage'] = psutil.cpu_percent(interval=1)
850 850 tmp_data['temperature'] = self.__get_temperature()
851 851 tmp_data["ALL_USE_E_MB"] = round(total_internet,4)
852 852
853 853
854 854 list_keys = self.dataOut.keys()
855 855
856 856 for key in list_keys:
857 857
858 858 if key == 'lidar':
859 859
860 860
861 861 tmp_data['lidar_status'] = self.dataOut[key].activate
862 862 tmp_data['lidar_dH'] = self.dataOut[key].dH_
863 863 tmp_data['h0'] = self.dataOut[key].H0
864 864
865 865 if self.dataOut[key].ERROR_WIRE == True:
866 866
867 867 tmp_data[key] = "WIRE_ERROR"
868 868
869 869 else:
870 870 tmp_data[key] = "OK"
871 871
872 872 elif 'ina' in key:
873 873 key_ = self.dataOut[key].key
874 874
875 875 tmp_data[f'bus_voltage_{key_}'] = round(self.dataOut[key].bus_voltage,4)
876 876 tmp_data[f'shunt_voltage_{key_}']= round(self.dataOut[key].shunt_voltage,4)
877 877 tmp_data[f'current_{key_}'] = round(self.dataOut[key].current,4)
878 878
879 879 else:
880 880
881 881 tmp_data[key] = self.dataOut[key].activate
882 882 tmp_data[f'it_{key}'] = self.dataOut[key].current_sensor()
883 883
884 884 #------------------------------- Inference method ------------------------------#
885 885
886 886 self.estimator.dataOut = self.dataOut
887 887
888 888 with locked(lock):
889 889 self.estimator.share = float(share.value)
890 890
891 891 tmp_data['camera_inference'] = self.estimator.share
892 892 tmp_data['string_inference'] = self.estimator.string_status
893 893
894 894 tmp_data['status'] = self.estimator.activate
895 895 tmp_data['count_status'] = self.estimator.activate_count
896 896
897 897 #-----------------------------------------------------#
898 898 #--------------- Compartiendo data a Flask -----------#
899 899 #-----------------------------------------------------#
900 900
901 901 try:
902 902 with locked(lock):
903 903
904 904 flask_.clear() #Limpiamos todos los datos compartidos
905 905
906 906 for key in tmp_data.keys():
907 907
908 908 flask_[key] = tmp_data[key]
909 909
910 910 except:
911 911 if self.debug:
912 912 print(f"[ERROR] Error al escribir datos en la variable compartida: {traceback.format_exc()}")
913 913
914 914 self.write_status(f"[ERROR] Error al escribir datos en la variable compartida: {traceback.format_exc()}")
915 915
916 916
917 917 payload = {'type':'json',
918 918 'content':tmp_data}
919 919
920 920 if self.store_data:
921 921
922 922 self.write_data(payload)
923 923
924 924
925 925 return tmp_data
926 926
927 927 except:
928 928 error = (traceback.format_exc())
929 929
930 930 if self.debug:
931 931 print(f"Ocurrió un error al leer los datos.{error}")
932 932
933 933 self.write_status(f"Ocurrió un error al leer datos. Copia del error {error}.")
934 934
935 935
936 936
937 937 def write_data(self,data):
938 938 now = datetime.now()
939 939 formatted_date_time = now.strftime("%d/%m/%Y %H:%M:%S") + " |"
940 940
941 941
942 942
943 943 try:
944 944 name = 'data.txt'
945 945 filename = os.path.join(self.path_save_json,name)
946 946
947 947 with open(filename,'a') as file:
948 948
949 949 file.write(formatted_date_time + str(json.dumps(data)) + '\n')
950 950
951 951 except:
952 952
953 953 if self.debug:
954 954
955 955 print(f"Ocurrió un error al guardar los datos en el archivo {name}")
956 956
957 957 self.write_status(f"[ERROR] Ocurrió un error al guardar los datos en el archivo {name}.")
958 958 else:
959 959
960 960 self.count_write_data += 1
961 961
962 962 try:
963 963
964 964 if(self.count_write_data == 100):
965 965
966 966 self.count_write_data = 0
967 967 self.__handle_size(filename)
968 968
969 969 except:
970 970 exc = traceback.format_exc()
971 971 self.write_status(f"[Error] Error desconocido en {exc}")
972 972
973 973 def method_mqtt(self,payload_json,lock):
974 974
975 975 #----------------------------- Control de internet --------------------------#
976 976
977 977 # Revisamos el internet cada 30 minutos
978 978 timestamp_internet = datetime.now().timestamp()
979 979 timestamp_mqtt = datetime.now().timestamp()
980 980
981 981 values = list()
982 982
983 983 self.check_internet()
984 984
985 985 while 1:
986 986 try:
987 987 while 1:
988 988 if self.flag_internet:
989 989
990 990 CLIENT_MQTT = str(self.id +"_pub")
991 991 mqtt_user = self.vars_mqtt.get("mqtt_user")
992 992 mqtt_pass = self.vars_mqtt.get("mqtt_pass")
993 993 mqtt_broker = self.vars_mqtt.get("mqtt_broker")
994 994 mqtt_port = self.vars_mqtt.get("mqtt_port")
995 995
996 996
997 997
998 998 self.client = mqtt.Client(CLIENT_MQTT)
999 999 self.client_image = mqtt.Client(CLIENT_MQTT+'image')
1000 1000
1001 1001
1002 1002 self.client.on_connect = on_connect
1003 1003 self.client_image.on_connect = on_connect
1004 1004
1005 1005 self.client.on_disconnect = on_disconnect
1006 1006 self.client_image.on_disconnect = on_disconnect
1007 1007
1008 1008
1009 1009 self.client.username_pw_set(mqtt_user, mqtt_pass)
1010 1010 self.client_image.username_pw_set(mqtt_user,mqtt_pass)
1011 1011
1012 1012
1013 1013 self.client.connect(mqtt_broker, mqtt_port,keepalive=300)
1014 1014 self.client_image.connect(mqtt_broker,mqtt_port,keepalive=300)
1015 1015
1016 1016
1017 1017
1018 1018 break
1019 1019
1020 1020 else:
1021 1021
1022 1022 #Esperamos un minuto
1023 1023 if self.debug:
1024 1024 print("No se cuenta con internet. Esperamos un minuto para lanzar el proceso MQTT.")
1025 1025
1026 1026 self.write_status("[WARNING] No se cuenta con internet. Esperando un minuto para lanzar el proceso MQTT.")
1027 1027
1028 1028 sleep(30)
1029 1029 self.check_internet()
1030 1030
1031 1031
1032 1032 if self.debug:
1033 1033
1034 1034 print("El proceso MQTT ha sido lanzado")
1035 1035
1036 1036 self.write_status("El proceso MQTT ha sido lanzado.")
1037 1037
1038 1038 photo_topic = os.path.join(self.vars_mqtt.get("photo_topic"),self.id)
1039 1039 data_topic = os.path.join(self.vars_mqtt.get("data_topic"),self.id)
1040 1040
1041 1041 while 1:
1042 1042
1043 1043 try:
1044 1044
1045 1045 while not self.queue.empty():
1046 1046
1047 1047 self.semaphore.acquire()
1048 1048
1049 1049 mensaje = self.queue.get()
1050 1050
1051 1051 self.semaphore.release()
1052 1052
1053 1053
1054 1054 if mensaje["type"] == 'image':
1055 1055
1056 1056 content = mensaje['content']
1057 1057
1058 1058 try:
1059 1059
1060 1060 if self.debug:
1061 1061 print("Enviando imagen por MQTT ...")
1062 1062
1063 1063 self.write_status("Enviando imagen por MQTT.")
1064 1064
1065 1065 self.client_image.publish(photo_topic,content['images'],qos=1)
1066 1066
1067 1067 if self.debug:
1068 1068 print("Imagen ha sido enviada por MQTT")
1069 1069
1070 1070 self.write_status("Imagen ha sido enviado por MQTT.")
1071 1071
1072 1072 except:
1073 1073
1074 1074 if self.debug:
1075 1075 print("Hubo un error al enviar la imagen. Se devuelve a la cola de envío.")
1076 1076
1077 1077 self.write_status("[ERROR] Error por envío de imagen MQTT.")
1078 1078
1079 1079 gc.collect()
1080 1080
1081 1081
1082 1082
1083 1083 if (datetime.now().timestamp() - timestamp_mqtt > self.vars.get("TIME_SEND_MQTT",3)):
1084 1084
1085 1085 try:
1086 1086 with locked(lock):
1087 1087 data = dict(payload_json)
1088 1088
1089 1089 tmp = dict()
1090 1090 tmp['values'] = data
1091 1091
1092 1092 payload = json.dumps(tmp)
1093 1093
1094 1094
1095 1095 try:
1096 1096
1097 1097 self.client.publish(data_topic,payload,qos=1)
1098 1098 except:
1099 1099
1100 1100 self.write_status(f"[ERROR] Error al enviar datos por MQTT: {traceback.format_exc()}")
1101 1101
1102 1102
1103 1103 except:
1104 1104
1105 1105 self.write_status(f"[ERROR] Error al leer datos del buffer compartido: {traceback.format_exc()}")
1106 1106
1107 1107 finally:
1108 1108
1109 1109 timestamp_mqtt = datetime.now().timestamp()
1110 1110
1111 1111
1112 1112 self.client_image.loop()
1113 1113 self.client.loop()
1114 1114
1115 1115
1116 1116 except:
1117 1117
1118 1118 self.write_status("[ERROR] Existe un problema al procesar el envío MQTT.")
1119 1119 sleep(1)
1120 1120 finally:
1121 1121
1122 1122 if(datetime.now().timestamp() - timestamp_internet > self.vars.get("SAMPLING_INTERNET",120)):
1123 1123
1124 1124 self.check_internet()
1125 1125
1126 1126 timestamp_internet = datetime.now().timestamp()
1127 1127
1128 1128 if not self.flag_internet:
1129 1129
1130 1130 if self.debug:
1131 1131 print("Se perdió la conexión a Internet.")
1132 1132
1133 1133 self.write_status("[ERROR] Se perdió la conexión a Internet.")
1134 1134 break
1135 1135
1136 1136 except:
1137 1137 if self.debug:
1138 1138 print(f"Ocurrió un error no manejado en la función principal de lahares. Copia del error: {error}")
1139 1139
1140 1140 error = traceback.format_exc()
1141 1141 self.write_status(f"Ocurrió un error inesperado. Copia del error: {error}")
1142 1142
1143 1143 sleep(5)
1144 1144
1145 1145
1146 1146 def method_video(self,):
1147 1147
1148 1148 '''
1149 1149 En este metodo extraemos 5 segundos de video o 135 frames para el entrenamiento del modelo.
1150 1150
1151 1151 UPDATE:
1152 1152 - Por temas de memoria, solo se trabajará con 10 frames. El formato estará comprimido en gzip
1153 1153 para poder obtener un menor tamaño en memoria mientras se comprime los archivos.
1154 1154 -
1155 1155 '''
1156 1156 self.check_internet(False)#Verbose False
1157 1157
1158 1158 if self.debug:
1159 1159 print("Lanzado el metodo para la adquisición de video.")
1160 1160
1161 1161
1162 1162 self.write_status("[METHOD] Lanzado el metodo para la adquisición de video")
1163 1163
1164 1164 timestamp_sampling = datetime.now().timestamp()
1165 1165
1166 1166 duration_frames = 10
1167 1167 sleep(4)
1168 1168
1169 1169 while 1:
1170 1170
1171 1171 current_time = datetime.now().timestamp()
1172 1172
1173 1173 try:
1174 1174
1175 1175
1176 1176 if (( timestamp_sampling - current_time )< 0 and self.camera.status and self.semaphore_get_video.acquire(False)):
1177 1177
1178 1178 self.write_status("Obteniendo video frames .....")
1179 1179
1180 1180 try:
1181 1181
1182 1182 count_frames = 0
1183 1183
1184 1184 file_video = None
1185 1185 file_video = io.BytesIO()
1186 1186
1187 1187
1188 1188 self.camera.control_brightness(brightness=100)#Realiza la validación de si es necesario elevar el brillo o no.
1189 1189
1190 1190 vid = cv2.VideoCapture(self.camera.url_rstp)
1191 1191
1192 1192 time_sleep = 0.35 if duration_frames<=10 else 0.2
1193 1193
1194 1194 now = datetime.now().timestamp()
1195 1195
1196 1196 with gzip.GzipFile(fileobj=file_video,mode='wb',compresslevel=9) as gz:
1197 1197
1198 1198 while 1:
1199 1199 now2 = datetime.now().timestamp()
1200 1200
1201 1201 try:
1202 1202 _,_ = vid.read()
1203 1203
1204 1204 except:
1205 1205 self.write_status("Error al capturar el frame")
1206 1206
1207 1207 else:
1208 1208 try:
1209 1209 if (now2 - now) > time_sleep:
1210 1210
1211 1211 while 1:
1212 1212
1213 1213 try:
1214 1214
1215 1215 _,frame = vid.read()
1216 1216
1217 1217 if frame is not None:
1218 1218
1219 1219 frame = cv2.resize(frame,(640,360))
1220 1220 frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
1221 1221 frame = (frame.astype(numpy.uint8)).tobytes()
1222 1222
1223 1223 gz.write(len(frame).to_bytes(4,'big'))
1224 1224 gz.write(deepcopy(frame))
1225 1225
1226 1226 frame = None
1227 1227 gc.collect()
1228 1228
1229 1229 count_frames += 1
1230 1230
1231 1231 now = datetime.now().timestamp()
1232 1232
1233 1233 break
1234 1234
1235 1235 except:
1236 1236 self.write_status(f"Ocurrió un error en video: {traceback.format_exc()}")
1237 1237 except:
1238 1238 self.write_status(f"Error al comprimir u obtener frames: {traceback.format_exc()}")
1239 1239
1240 1240 if count_frames == 10:
1241 1241 self.write_status("Se obtuvo 10 frames.")
1242 1242 break
1243 1243
1244 1244 try:
1245 1245 vid.release()
1246 1246 except:
1247 1247 pass
1248 1248
1249 1249 self.camera.control_brightness(brightness=0)
1250 1250 self.semaphore_get_video.release()
1251 1251
1252 1252 #-------------------------------------------------------------------#
1253 1253 # Por temas de latencia en tiempo, se realizará la inferencia en este metodo
1254 1254 #-------------------------------------------------------------------#
1255 1255
1256 1256 # if self.inference_mode == 'server':
1257 1257
1258 1258
1259 1259 # url = "http://38.10.105.243:7777/predict"
1260 1260
1261 1261
1262 1262 # input_data = {'instances':base64.b64encode(file_video.getvalue()).decode('utf-8'),
1263 1263 # 'id_user':"test-jicamarca",
1264 1264 # 'request_format':True,
1265 1265 # 'shape':(360,640)}
1266 1266
1267 1267 # headers = {
1268 1268 # 'Content-Type': 'application/json',
1269 1269 # 'Content-Encoding': 'gzip-B64',
1270 1270 # }
1271 1271 # input_data = json.dumps(input_data)
1272 1272
1273 1273 # compress = io.BytesIO()
1274 1274
1275 1275 # with gzip.GzipFile(fileobj=compress, mode='wb', compresslevel=9) as gz2:
1276 1276 # gz2.write(input_data.encode('utf-8'))
1277 1277
1278 1278 # if self.flag_internet:
1279 1279 # #Se cuenta con internet para realizar la inferencia al servidor.
1280 1280 # try:
1281 1281 # resp = requests.post(url,data=compress.getvalue(),headers=headers)
1282 1282
1283 1283 # except:
1284 1284
1285 1285 # compress = None
1286 1286 # file_video = None
1287 1287 # gc.collect()
1288 1288 # inference = None
1289 1289 # else:
1290 1290 # compress = None
1291 1291 # file_video = None
1292 1292 # gc.collect()
1293 1293
1294 1294 # if resp.status_code == 200:
1295 1295 # #El envio fue un exito
1296 1296 # inference = 1 - resp.json()['predictions'][0][0]
1297 1297 # self.write_status(f"Inferencia al servidor realizado con exito. Valor de inferencia: {inference}.")
1298 1298 # else:
1299 1299 # inference = None
1300 1300 # self.write_status(f"Se obtuvo otro codigo de respuesta al realizar inferencia al servidor. {resp.status_code}")
1301 1301
1302 1302
1303 1303 #-------------------------------------------------------------------#
1304 1304 #-------------------------------------------------------------------#
1305 1305 #-------------------------------------------------------------------#
1306 1306
1307 1307
1308 1308 self.semaphore_estimator.acquire()
1309 1309
1310 1310 if self.queue_estimator.qsize()>=2:
1311 1311 self.queue_estimator.get()
1312 1312 gc.collect()
1313 1313
1314 1314
1315 1315 self.queue_estimator.put({
1316 1316 'video': deepcopy(file_video),
1317 1317 'timestamp': datetime.now().timestamp()
1318 1318 })
1319 1319
1320 1320 file_video = None
1321 1321 gc.collect()
1322 1322
1323 1323 self.semaphore_estimator.release()
1324 1324
1325 1325
1326 1326
1327 1327 except:
1328 1328
1329 1329 try:
1330 1330 vid.release()
1331 1331 except:
1332 1332 pass
1333 1333
1334 1334 gc.collect()
1335 1335 self.write_status(f"[ERROR] Ocurrió un error en el proceso de obtener video. Se volverá a intentar en 15 segundos. Copia del error: {traceback.format_exc()}")
1336 1336 timestamp_sampling = datetime.now().timestamp() + 15
1337 1337
1338 1338 try:
1339 1339 self.semaphore_get_video.release()
1340 1340 except:
1341 1341 self.write_status(f"[ERROR] Code 100 Method")
1342 1342
1343 1343
1344 1344 else:
1345 1345
1346 1346 if count_frames == 10:
1347 1347 timestamp_sampling = datetime.now().timestamp() + self.vars.get("SAMPLING_TIME_VIDEO")
1348 1348 self.write_status(f"Se agregó 10 frames en bytes al queue estimator.")
1349 1349 else:
1350 1350 timestamp_sampling = datetime.now().timestamp() + 15
1351 1351 self.write_status(f"Frames insuficientes, se volverá a intentar en 15 segundos.")
1352 1352
1353 1353 elif (( current_time - timestamp_sampling )> 90):
1354 1354 self.write_status(f"[VIDEO] No se puede tomar el semaforo por más de 90 segundos. ")
1355 1355 timestamp_sampling = current_time
1356 1356
1357 1357 sleep(1)
1358 1358
1359 1359
1360 1360 except:
1361 1361 self.write_status(f"[ERROR VIDEO] {traceback.format_exc()}")
1362 1362
1363 1363 else:
1364 1364 #Borrar
1365 1365 with open("/tools/live_video.txt",'w') as f:
1366 1366
1367 1367 date = datetime.now()
1368 1368 chain = f'{date.day} {date.hour}:{date.minute}:{date.second}'
1369 1369 f.write(chain)
1370 1370
1371 1371 sleep(3)
1372 1372
1373 1373
1374 1374 def method_sensores(self,share,flask_,lock):
1375 1375
1376 1376 timestamp_camera = datetime.now().timestamp()
1377 1377 timestamp_sampling = datetime.now().timestamp()
1378 1378 self.timestamp_internet_ = datetime.now().timestamp()
1379 1379
1380 1380
1381 1381 sleep(1)
1382 1382
1383 1383 self.check_internet(False)
1384 1384
1385 1385 while 1:
1386 1386
1387 1387 #------------------- inicio de proceso ----------------------------#
1388 1388 try:
1389 1389 current_time = datetime.now().timestamp()
1390 1390
1391 1391 self.__get_internet_bytes()
1392 1392
1393 1393 if(timestamp_sampling - current_time < 0 ):
1394 1394
1395 1395 '''
1396 1396 Aqui se ejecutan los estados de los sensores.
1397 1397 '''
1398 1398
1399 1399 try:
1400 1400
1401 1401 key_sensor = self.dataOut.keys()
1402 1402
1403 1403 for key in key_sensor:
1404 1404
1405 1405 try:
1406 1406 self.dataOut[key].run()
1407 1407 except:
1408 1408 if self.debug:
1409 1409 print(f"[ERROR] Error actualizando los datos: {traceback.format_exc()}")
1410 1410 self.write_status(f"[ERROR] Error actualizando los datos: {traceback.format_exc()}")
1411 1411
1412 1412 self.__process_values(share,flask_,lock)
1413 1413
1414 1414 except:
1415 1415 if self.debug:
1416 1416 print(f"[ERROR] Error decodificando los datos: {traceback.format_exc()}")
1417 1417 self.write_status(f"[ERROR] Error decodificando los datos: {traceback.format_exc()}")
1418 1418
1419 1419 finally:
1420 1420
1421 1421 timestamp_sampling = current_time + self.vars.get("SAMPLING_TIME_SENSOR")
1422 1422
1423 1423
1424 1424 if(timestamp_camera - current_time < 0 and self.camera.status and self.semaphore_get_video.acquire(False)):
1425 1425
1426 1426 '''
1427 1427 Se adquiere fotos para enviarse por MQTT.
1428 1428 '''
1429 1429 frame = None
1430 1430
1431 1431 try:
1432 1432
1433 1433 self.camera.control_brightness(brightness=100)
1434 1434 vid = cv2.VideoCapture(self.camera.url_rstp)
1435 1435
1436 1436 while 1:
1437 1437 _, frame = vid.read()
1438 1438
1439 1439 if frame is not None:
1440 1440
1441 1441 break
1442 1442
1443 1443 except:
1444 1444 try:
1445 1445 vid.release()
1446 1446 self.camera.control_brightness(brightness=0)
1447 1447
1448 1448 except:
1449 1449 pass
1450 1450
1451 1451 self.semaphore_get_video.release()
1452 1452
1453 1453 if self.debug:
1454 1454 print(f"Error: {str(traceback.format_exc())}")
1455 1455
1456 1456 self.write_status(f"[ERROR] Error generado en la adquisición de fotografía {str(traceback.format_exc())}")
1457 1457
1458 1458 timestamp_camera = datetime.now().timestamp() + 5
1459 1459
1460 1460 else:
1461 1461
1462 1462 try:
1463 1463 vid.release()
1464 1464 except:
1465 1465 pass
1466 1466
1467 1467 self.camera.control_brightness(brightness=0)
1468 1468 self.semaphore_get_video.release()
1469 1469 #---------------------- imagen obtenida ---------------------#
1470 1470 timestamp_camera = datetime.now().timestamp() + self.vars.get("SAMPLING_TIME_CAMERA")
1471 1471
1472 1472 buffer = cv2.imencode('.jpg',frame)[1]
1473 1473
1474 1474 data = {
1475 1475 'images' : buffer.tobytes(),
1476 1476 'timestamp': datetime.now().timestamp()
1477 1477 }
1478 1478
1479 1479 payload = {
1480 1480 'type' : 'image',
1481 1481 'content': data
1482 1482 }
1483 1483
1484 1484
1485 1485 #-----------------------------------------------------------
1486 1486 #Definimos un tamaño de 3 imagenes, para no llenar el buffer
1487 1487
1488 1488 if self.inference_mode == 'photo':
1489 1489 self.semaphore_estimator.acquire()
1490 1490
1491 1491 payload_estimator = {
1492 1492 'image':numpy.array(frame),
1493 1493 'timestamp':datetime.now().timestamp()
1494 1494 }
1495 1495 if self.queue_estimator.qsize()>=3:
1496 1496 self.queue_estimator.get()
1497 1497
1498 1498 self.queue_estimator.put(payload_estimator)
1499 1499 self.semaphore_estimator.release()
1500 1500
1501 1501 #---------------- Guardamos en la cola las imagenes a ser enviadas -----------------#
1502 1502 self.semaphore.acquire()
1503 1503 if self.flag_internet:
1504 1504 self.queue.put(payload)
1505 1505
1506 1506 if self.queue.qsize()>=10:
1507 1507 self.queue.get()
1508 1508 gc.collect()
1509 1509
1510 1510 self.semaphore.release()
1511 1511
1512 1512 gc.collect()
1513 1513
1514 1514 ####################################
1515 1515 ## Guardando en la ruta de Flask ###
1516 1516 ####################################
1517 1517
1518 1518 filename_flask = os.path.join('/tools/static',"latest.jpg")
1519 1519
1520 1520 with open(filename_flask,'wb') as filew:
1521 1521
1522 1522 filew.write(buffer.tobytes())
1523 1523
1524 1524 ####################################
1525 1525 ### Guardado de datos ###
1526 1526 ####################################
1527 1527
1528 1528
1529 1529 if self.store_data:
1530 1530
1531 1531 name = '{}.jpg'.format(current_time)
1532 1532
1533 1533 filename = os.path.join(self.path_save_img,name)
1534 1534
1535 1535
1536 1536 try:
1537 1537 with open(filename,'wb') as file:
1538 1538
1539 1539 file.write(buffer.tobytes())
1540 1540
1541 1541
1542 1542 except:
1543 1543 if self.debug:
1544 1544 print("Ocurrió un error al guardar la imagen {}".format(name))
1545 1545 self.write_status("[ERROR] Ocurrió un error al guardar la imagen.")
1546 1546
1547 1547 else:
1548 1548 if self.debug:
1549 1549 print(f"Se guardó la imagen {name}")
1550 1550
1551 1551 self.write_status(f"Se guardó la imagen {name}.")
1552 1552
1553 1553 gc.collect()
1554 1554 except:
1555 1555
1556 1556 if self.debug:
1557 1557 print("Ocurrió un error en la recolección de datos.")
1558 1558
1559 1559 self.write_status(f"Ocurrió un error no identificado en el proceso de recolección de data sensores. Copia del error: {traceback.format_exc()}")
1560 1560
1561 1561 sleep(5) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now