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