Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge final #78

Merged
merged 22 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion backend/API/app/apps.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from django.apps import AppConfig

class AppConfig(AppConfig):
name = 'app'
name = 'app'

def ready(self):
from app.signals import data_post_save
14 changes: 10 additions & 4 deletions backend/API/app/management/commands/startserver.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import os
import logging

import threading
from django.core.management.base import BaseCommand
from django.core.management import call_command
from project.settings.dev_settings import DEFAULT_IP, DEFAULT_PORT
from app.processmqttlistenerstarter import start_process_mqtt_listener
from project.loggerconfig import setup_logger
from app.threadredislistenerstarter import start_thread_redis_listener

class Command(BaseCommand):
help = 'Run the Django development server using environment variables for IP and port'

def handle(self, *args, **kwargs):
if os.environ.get('RUN_MAIN'):
setup_logger()
logging.getLogger('API').warning('Your are in development mode ! \nSome logs are not enabled in certain files because they slow down the server. \nCheck the relevant file and uncomment the log if necessary')
logging.getLogger('API').warning('You are in development mode ! \nSome logs are not enabled in certain files because they slow down the server. \nCheck the relevant file and uncomment the log if necessary')

# Démarrer le listener Redis dans un thread
start_thread_redis_listener()

# Démarrer le process MQTT listener
start_process_mqtt_listener()
call_command('runserver', f'{DEFAULT_IP}:{DEFAULT_PORT}')

call_command('runserver', f'{DEFAULT_IP}:{DEFAULT_PORT}')
6 changes: 4 additions & 2 deletions backend/API/app/processmqttlistenerstarter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
def start_process_mqtt_listener() :
import logging

from app.usecases.mqttlistener import MqttClientProcess
from app.usecases.mqtt import MqttClientProcess

logger = logging.getLogger('API')

Expand All @@ -15,4 +15,6 @@ def start_process_mqtt_listener() :
mqtt_process_2 = MqttClientProcess(topic_2)
mqtt_process_2.daemon = True
mqtt_process_2.start()
logger.info("MQQT listener process on topic 2 started")
logger.info("MQQT listener process on topic 2 started")

return mqtt_process_1, mqtt_process_2
2 changes: 2 additions & 0 deletions backend/API/app/signals/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .datasignals import data_post_save
from .sensorsignals import sensor_post_save
42 changes: 42 additions & 0 deletions backend/API/app/signals/datasignals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import logging
import json

from django.db.models.signals import post_save
from django.dispatch import receiver
from app.models import Data, Sensor
from app.usecases.redis import redis_sender
from django.core import serializers

logger = logging.getLogger('API')

@receiver(post_save, sender=Data)
def data_post_save(sender, instance, **kwargs):
logger.debug("Data save, signal received")

json_data = serializers.serialize('json', [instance])

data_dict = json.loads(json_data)

fields_data = data_dict[0]['fields']

sensor = Sensor.objects.get(deveui=fields_data['sensor'])

fields_data.pop('sensor', None)


if sensor.room is not None and sensor.batterylevel is not None and sensor.externalpowersource is not None :
redis_sender(f'Data/', {"room" : sensor.room, "data" : fields_data, "batterylevel" : sensor.batterylevel, "externalPower" : str(sensor.externalpowersource).lower() })
logger.debug("Data/")

redis_sender(f'Data/{sensor.room}/', fields_data)
logger.debug(f"Data/{sensor.room}/")

if sensor.building is not None :
redis_sender(f'Data/{sensor.building}/', {"room" : sensor.room, "data" : fields_data, "batterylevel" : sensor.batterylevel, "externalPower" : str(sensor.externalpowersource).lower() })
logger.debug(f"Data/{sensor.building}/")

if sensor.floor is not None :
redis_sender(f'Data/{sensor.building}/{sensor.floor}/', {"room" : sensor.room, "data" : fields_data, "batterylevel" : sensor.batterylevel, "externalPower" : str(sensor.externalpowersource).lower() })
logger.debug(f"Data/{sensor.building}/{sensor.floor}/")

logger.debug(f"Data sent to Redis on {sensor.room}")
26 changes: 26 additions & 0 deletions backend/API/app/signals/sensorsignals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import logging
import json

from django.db.models.signals import post_save
from django.dispatch import receiver
from app.models import Data, Sensor
from app.usecases.redis import redis_sender
from django.core import serializers

logger = logging.getLogger('API')

@receiver(post_save, sender=Sensor)
def sensor_post_save(sender, instance, **kwargs):
json_data = serializers.serialize('json', [instance])

data_dict = json.loads(json_data)

fields_data = data_dict[0]['fields']

fields_data.pop('deveui', None)
fields_data.pop('building', None)
fields_data.pop('floor', None)
fields_data.pop('batterylevel', None)
fields_data.pop('externalpowersource', None)

redis_sender(f'Sensor/', fields_data)
10 changes: 10 additions & 0 deletions backend/API/app/threadredislistenerstarter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import threading

def start_thread_redis_listener():
from app.usecases.redis import redis_listener
stop_event = threading.Event()
thread = threading.Thread(target=redis_listener, args=(stop_event,))
thread.daemon = True
thread.start()

return thread, stop_event
1 change: 0 additions & 1 deletion backend/API/app/usecases/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from .mqttlistener import MqttClientProcess
from .createsensordata import create_sensor_data
1 change: 1 addition & 0 deletions backend/API/app/usecases/mqtt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .mqttlistener import MqttClientProcess
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,8 @@ def reconnect(self):
except Exception as e:
logger.error(f"Failed to reconnect to MQTT broker. Retrying in 10 seconds... Error: {e}")
time.sleep(10)

def stop(self):
self.is_running = False
self.client.disconnect()
self.terminate()
2 changes: 2 additions & 0 deletions backend/API/app/usecases/redis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .redissender import redis_sender
from .redislistener import redis_listener
44 changes: 44 additions & 0 deletions backend/API/app/usecases/redis/redislistener.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import json
import redis
import logging

from django_eventstream import send_event

logger = logging.getLogger('API')

def redis_listener(stop_event):
r = redis.Redis(host='127.0.0.1', port=6379, db=0)
listener = r.pubsub()
listener.subscribe('sse')

for message in listener.listen():
if stop_event.is_set():
break
if message['type'] == 'message':
message_decode = message['data'].decode('utf-8').replace('"', "").replace("'", '"')
logger.debug(f"Message redis reçu: {message_decode}")

message_data = json.loads(message_decode)
logger.debug(f"Message redis reçu: {message_data}")

if 'Data' in message_data['type']:
logger.debug(f"Data received from Redis on {message_data['type']}")
formatted_message = message_data['message']
send_event(message_data['type'], 'message', formatted_message)
logger.debug(f"Event sent on {message_data['type']}")

elif message_data['type'] == 'Data/':
logger.debug("Data received from Redis NoRoom")
formatted_message = message_data['message']
# formatted_message['room'] = formatted_message['room'].encode('utf-8')
logger.debug(f"Event sent on Data")
send_event(message_data['type'], 'message', formatted_message)

elif message_data['type'] == 'Sensor/':
logger.debug("Sensor received from Redis NoRoom")
formatted_message = message_data['message']
logger.debug(f"Event sent on Sensor")
send_event(message_data['type'], 'message', formatted_message)

listener.unsubscribe('sse')
logger.info("Redis listener stopped")
11 changes: 11 additions & 0 deletions backend/API/app/usecases/redis/redissender.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import redis

def redis_sender(channel_name, message):
r = redis.Redis(host='127.0.0.1', port=6379, db=0)
r.publish('sse', str(
{
'type': f'{channel_name}',
'message': f'{message}'
}
)
)
3 changes: 2 additions & 1 deletion backend/API/app/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
from .byroomview import ByRoomViewSet
from .sensorview import SensorViewSet
from .searchview import SearchViewSet
from .autocompletsearchview import AutoCompletSearchViewSet
from .autocompletsearchview import AutoCompletSearchViewSet
from .eventsdescription import events_description
21 changes: 17 additions & 4 deletions backend/API/app/views/byroomview.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,27 @@ def filter_sensor_data(self, sensor, date_from, date_to, depth, last_data=None):
queryset = queryset.filter(time__gte=date_from)
if date_to:
queryset = queryset.filter(time__lte=date_to)

# Appliquer le filtrage pour la profondeur et les dernières données

if depth > 0:
filtered_data = list(queryset)
sensor.filtered_data = filtered_data[-int(last_data):] if last_data else filtered_data
# Gérer spécifiquement last_data égal à 0
if last_data is not None:
if int(last_data) == 0:
sensor.filtered_data = []
else:
sensor.filtered_data = filtered_data[-int(last_data):]
else:
sensor.filtered_data = filtered_data
sensor.sensor = sensor
elif depth == 0:
sensor.data_ids = [data.id for data in queryset][-int(last_data):] if last_data else [data.id for data in queryset]
# Gérer spécifiquement last_data égal à 0
if last_data is not None:
if int(last_data) == 0:
sensor.data_ids = []
else:
sensor.data_ids = [data.id for data in queryset][-int(last_data):]
else:
sensor.data_ids = [data.id for data in queryset]
sensor.sensor_id = sensor.deveui
return sensor

Expand Down
9 changes: 9 additions & 0 deletions backend/API/app/views/eventsdescription.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.http import JsonResponse

def events_description(request):
return JsonResponse({
"endpoints": {
"Sensor": "/Events/Sensor/",
"Data": "/Events/Data/"
}
})
34 changes: 28 additions & 6 deletions backend/API/gunicorn_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,44 @@
import os

from project.loggerconfig import setup_logger, setup_gunicorn_loggers
from project.settings.prod_settings import DEFAULT_IP, DEFAULT_PORT, CERTFILE, KEYFILE
from project.settings.prod_settings import DEFAULT_IP, DEFAULT_PORT#, CERTFILE, KEYFILE
from app.processmqttlistenerstarter import start_process_mqtt_listener
from app.threadredislistenerstarter import start_thread_redis_listener
from django.core.wsgi import get_wsgi_application


bind = f"{DEFAULT_IP}:{DEFAULT_PORT}"
custom_logger = setup_logger()
workers = multiprocessing.cpu_count() * 2 + 1
certfile = CERTFILE
keyfile = KEYFILE
workers = multiprocessing.cpu_count() * 2
# certfile = CERTFILE
# keyfile = KEYFILE

def on_starting(server):
setup_gunicorn_loggers(custom_logger)
get_wsgi_application()

def when_ready(server):
custom_logger.info("Starting MQTT listener process")
os.environ.get("DJANGO_SETTINGS_MODULE")
get_wsgi_application()
start_process_mqtt_listener()
mqttlisteners = start_process_mqtt_listener()
server.mqttlisteners = mqttlisteners

def post_fork(server, worker):
worker_id = worker.pid
custom_logger.info(f"Starting Redis listener in worker {worker_id}")
thread, stop_event = start_thread_redis_listener()
worker.redis_listener_thread = thread
worker.redis_stop_event = stop_event
custom_logger.info(f"Redis listener running in worker {worker_id}")

def worker_exit(server, worker):
worker.redis_stop_event.set()
worker.redis_listener_thread.join()
custom_logger.info(f"Redis listener stopped in worker {worker.pid}")


def on_exit(server):
custom_logger.info("Stopping MQTT listener process")
for listener in server.mqttlisteners:
listener.stop()
custom_logger.info("MQTT listener process stopped")
37 changes: 35 additions & 2 deletions backend/API/project/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,43 @@
"""

import os
import django_eventstream.routing

from django.urls import path, re_path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from django.core.asgi import get_asgi_application
from app.processmqttlistenerstarter import start_process_mqtt_listener

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

application = get_asgi_application()
application = ProtocolTypeRouter({
'http': URLRouter([
path('Events/Sensor/', AuthMiddlewareStack(
URLRouter(django_eventstream.routing.urlpatterns)
), {'format-channels': ['Sensor/']}),

path('Events/Data/', AuthMiddlewareStack(
URLRouter(django_eventstream.routing.urlpatterns)
), {'format-channels': ['Data/']}),

re_path(r'^Events/Data/(?P<RoomOrBuilding>[\w\-]+)/$',
AuthMiddlewareStack(
URLRouter(
django_eventstream.routing.urlpatterns
)
),
{'format-channels': ['Data/{RoomOrBuilding}/']}
),

re_path(r'^Events/Data/(?P<Building>\w+)/(?P<Floor>\w+)/$',
AuthMiddlewareStack(
URLRouter(
django_eventstream.routing.urlpatterns
)
),
{'format-channels': ['Data/{Building}/{Floor}/']}
),

re_path(r'', get_asgi_application()),
]),
})
Loading
Loading