Add telemetry database insertion
This commit is contained in:
@@ -48,7 +48,7 @@ class ClientApi:
|
|||||||
This method makes a synchronous HTTP request by default. To make an
|
This method makes a synchronous HTTP request by default. To make an
|
||||||
asynchronous HTTP request, please pass async_req=True
|
asynchronous HTTP request, please pass async_req=True
|
||||||
|
|
||||||
>>> thread = api.create(client_dto, async_req=True)
|
>>> thread = api.create_forwarder(client_dto, async_req=True)
|
||||||
>>> result = thread.get()
|
>>> result = thread.get()
|
||||||
|
|
||||||
:param client_dto: (required)
|
:param client_dto: (required)
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import threading
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
from src.controllers.AuthController import AuthController
|
from src.controllers.AuthController import AuthController
|
||||||
from src.controllers.ClientController import ClientController
|
from src.controllers.ClientController import ClientController
|
||||||
from src.controllers.message_controller import MessageController
|
from src.controllers.message_controller import MessageController
|
||||||
from src.controllers.mqtt_forwarder import MQTTForwarder
|
from src.controllers.mqtt_forwarder import create_forwarder
|
||||||
from src.models.AuthData import AuthData
|
from src.models.AuthData import AuthData
|
||||||
|
from src.services.mongo_service import MongoService
|
||||||
from src.services.mqtt_service import MQTTService
|
from src.services.mqtt_service import MQTTService
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
database_uri = os.getenv("MONGO_URI", "mongodb://localhost:27017")
|
||||||
|
database_name = os.getenv("DATABASE_NAME", "default")
|
||||||
|
|
||||||
local_broker_address = os.environ.get("LOCAL_BROKER_ADDRESS", "127.0.0.1")
|
local_broker_address = os.environ.get("LOCAL_BROKER_ADDRESS", "127.0.0.1")
|
||||||
local_broker_port = int(os.environ.get("LOCAL_BROKER_PORT", 1883))
|
local_broker_port = int(os.environ.get("LOCAL_BROKER_PORT", 1883))
|
||||||
|
|
||||||
@@ -25,78 +26,22 @@ api_broker_port = int(os.environ.get("API_BROKER_PORT", 1883))
|
|||||||
|
|
||||||
auth_data = AuthData()
|
auth_data = AuthData()
|
||||||
|
|
||||||
|
database_service = MongoService(database_uri, database_name)
|
||||||
|
|
||||||
auth_controller = AuthController(app, auth_data, "https://192.168.15.120:8000")
|
auth_controller = AuthController(app, auth_data, "https://192.168.15.120:8000")
|
||||||
client_controller = ClientController(app, auth_data, "https://192.168.15.120:8000")
|
client_controller = ClientController(app, auth_data, "https://192.168.15.120:8000")
|
||||||
message_controller = MessageController(app, auth_data, "https://192.168.15.120:8000")
|
message_controller = MessageController(app, auth_data, "https://192.168.15.120:8000", database_service)
|
||||||
|
|
||||||
|
def handle_login(data):
|
||||||
|
local_broker, api_broker, forwarder = create_forwarder(data, local_broker_address, local_broker_port, api_broker_address, api_broker_port)
|
||||||
|
|
||||||
def handle_message_received(topic: str, payload: str):
|
|
||||||
try:
|
|
||||||
print("=== MQTT MESSAGE RECEIVED ===", flush=True)
|
|
||||||
print("Raw payload:", payload, flush=True)
|
|
||||||
print("Payload type:", type(payload), flush=True)
|
|
||||||
|
|
||||||
data = json.loads(payload)
|
|
||||||
print("Parsed payload:", data, flush=True)
|
|
||||||
|
|
||||||
url = "https://192.168.15.125:1880/message/receive"
|
|
||||||
|
|
||||||
response = requests.post(
|
|
||||||
url,
|
|
||||||
json=data,
|
|
||||||
verify=False,
|
|
||||||
timeout=5
|
|
||||||
)
|
|
||||||
|
|
||||||
print("=== NODE-RED RESPONSE ===", flush=True)
|
|
||||||
print("Status code:", response.status_code, flush=True)
|
|
||||||
print("Headers:", response.headers, flush=True)
|
|
||||||
print("Raw response:", repr(response.text), flush=True)
|
|
||||||
|
|
||||||
if response.text.strip():
|
|
||||||
content_type = response.headers.get("Content-Type", "")
|
|
||||||
if content_type.startswith("application/json"):
|
|
||||||
print("Response JSON:", response.json(), flush=True)
|
|
||||||
else:
|
|
||||||
print("Response is not JSON", flush=True)
|
|
||||||
else:
|
|
||||||
print("Node-RED returned an empty response body", flush=True)
|
|
||||||
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
print("Incoming payload is NOT valid JSON:", e, flush=True)
|
|
||||||
|
|
||||||
except requests.RequestException as e:
|
|
||||||
print("HTTP request to Node-RED failed:", e, flush=True)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print("Unexpected error:", e, flush=True)
|
|
||||||
|
|
||||||
def start_mqtt(data : AuthData):
|
|
||||||
client_id = data.get_client_id()
|
client_id = data.get_client_id()
|
||||||
|
api_broker.subscribe(f"/chat/{client_id}/message", message_controller.handle_message_received)
|
||||||
|
forwarder.start(f"/customer/telemetry/#", f"/board-mate/{client_id}/telemetry", api_broker.publish)
|
||||||
|
|
||||||
local_broker = MQTTService(
|
|
||||||
local_broker_address,
|
|
||||||
local_broker_port,
|
|
||||||
client_id=client_id,
|
|
||||||
username="main",
|
|
||||||
password="hepl",
|
|
||||||
)
|
|
||||||
|
|
||||||
api_broker = MQTTService(
|
|
||||||
api_broker_address,
|
|
||||||
api_broker_port,
|
|
||||||
client_id=client_id,
|
|
||||||
username="customer",
|
|
||||||
password="hepl",
|
|
||||||
)
|
|
||||||
|
|
||||||
api_broker.subscribe(f"/chat/{client_id}/message", handle_message_received)
|
|
||||||
|
|
||||||
forwarder = MQTTForwarder(client_id, local_broker, api_broker)
|
|
||||||
forwarder.start(f"/customer/telemetry/#", f"/board-mate/{client_id}/telemetry")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
auth_controller.set_on_login(start_mqtt)
|
auth_controller.set_on_login(handle_login)
|
||||||
|
|
||||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ services:
|
|||||||
- mongo
|
- mongo
|
||||||
- mosquitto
|
- mosquitto
|
||||||
environment:
|
environment:
|
||||||
- MONGO_URI=mongodb://mongo:27017/mydb
|
- MONGO_URI=mongodb://user:psk358xpg@customer-database:27017
|
||||||
|
- DATABASE_NAME=customer-db
|
||||||
- LOCAL_BROKER_USERNAME=main
|
- LOCAL_BROKER_USERNAME=main
|
||||||
- LOCAL_BROKER_PASSWORD=hepl
|
- LOCAL_BROKER_PASSWORD=hepl
|
||||||
- LOCAL_BROKER_ADDRESS=customer-broker
|
- LOCAL_BROKER_ADDRESS=customer-broker
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
db = db.getSiblingDB("customer-db");
|
db = db.getSiblingDB("customer-db");
|
||||||
|
|
||||||
db.createCollection("systems");
|
db.createCollection("messages");
|
||||||
|
db.createCollection("clients");
|
||||||
|
db.createCollection("rooms");
|
||||||
|
db.createCollection("telemetry");
|
||||||
db.createCollection("games");
|
db.createCollection("games");
|
||||||
|
|
||||||
|
|
||||||
db.createUser({
|
db.createUser({
|
||||||
user: "user",
|
user: "user",
|
||||||
pwd: "psk358xpg",
|
pwd: "psk358xpg",
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
aenum
|
||||||
flask
|
flask
|
||||||
|
requests
|
||||||
paho-mqtt
|
paho-mqtt
|
||||||
python_dateutil
|
python_dateutil
|
||||||
setuptools
|
python-dotenv
|
||||||
urllib3
|
|
||||||
pydantic
|
pydantic
|
||||||
typing-extensions
|
pymongo
|
||||||
requests
|
|
||||||
python-dotenv
|
python-dotenv
|
||||||
pyyaml
|
pyyaml
|
||||||
aenum
|
setuptools
|
||||||
python-dotenv
|
typing-extensions
|
||||||
|
urllib3
|
||||||
|
|
||||||
file:./api-resources/auth-components
|
file:./api-resources/auth-components
|
||||||
file:./api-resources/client-components
|
file:./api-resources/client-components
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import requests
|
||||||
from board_mate.message import Configuration, ApiClient, MessageDto, MessageApi, MessagePostRequestDto
|
from board_mate.message import Configuration, ApiClient, MessageDto, MessageApi, MessagePostRequestDto
|
||||||
from flask import jsonify, request
|
from flask import jsonify, request
|
||||||
from pydantic import StrictStr
|
from pydantic import StrictStr
|
||||||
|
|
||||||
from src.models.AuthData import AuthData
|
from src.models.AuthData import AuthData
|
||||||
|
from src.services.mongo_service import MongoService
|
||||||
from src.services.mqtt_service import MQTTService
|
from src.services.mqtt_service import MQTTService
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@@ -11,12 +13,14 @@ class MessageController:
|
|||||||
|
|
||||||
_client_id : MQTTService = None
|
_client_id : MQTTService = None
|
||||||
_auth_data : AuthData = None
|
_auth_data : AuthData = None
|
||||||
|
_database_service : MongoService = None
|
||||||
|
|
||||||
def __init__(self, app, auth_data : AuthData, host : str):
|
def __init__(self, app, auth_data : AuthData, host : str, database_service : MongoService):
|
||||||
self._register_routes(app)
|
self._register_routes(app)
|
||||||
self.config = Configuration(host=host)
|
self.config = Configuration(host=host)
|
||||||
self.config.verify_ssl=False
|
self.config.verify_ssl=False
|
||||||
self._auth_data = auth_data
|
self._auth_data = auth_data
|
||||||
|
self._database_service = database_service
|
||||||
|
|
||||||
def _register_routes(self, app):
|
def _register_routes(self, app):
|
||||||
app.add_url_rule("/message/send", view_func=self.send, methods=['POST'])
|
app.add_url_rule("/message/send", view_func=self.send, methods=['POST'])
|
||||||
@@ -40,8 +44,33 @@ class MessageController:
|
|||||||
)
|
)
|
||||||
|
|
||||||
message_api.post_message(new_message)
|
message_api.post_message(new_message)
|
||||||
|
|
||||||
return jsonify({"success" : True, "message": None}), 200
|
return jsonify({"success" : True, "message": None}), 200
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
return jsonify({"success" : False, "message" : f"An error occurred : {self._auth_data.get_token()} {self._auth_data.get_client_id()} \n {e}"}), 500
|
return jsonify({"success" : False, "message" : f"An error occurred : {self._auth_data.get_token()} {self._auth_data.get_client_id()} \n {e}"}), 500
|
||||||
|
|
||||||
|
def handle_message_received(self, topic: str, payload: str) -> None :
|
||||||
|
try:
|
||||||
|
|
||||||
|
data = json.loads(payload)
|
||||||
|
print("Parsed payload:", data, flush=True)
|
||||||
|
|
||||||
|
url = "https://192.168.15.125:1880/message/receive"
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
url,
|
||||||
|
json=data,
|
||||||
|
verify=False,
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
|
||||||
|
self._database_service.insert("messages", data)
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print("Incoming payload is NOT valid JSON:", e, flush=True)
|
||||||
|
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print("HTTP request to Node-RED failed:", e, flush=True)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Unexpected error:", e, flush=True)
|
||||||
|
|||||||
@@ -1,21 +1,60 @@
|
|||||||
|
import json
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
from src.models.AuthData import AuthData
|
||||||
|
from src.services.mongo_service import MongoService
|
||||||
from src.services.mqtt_service import MQTTService
|
from src.services.mqtt_service import MQTTService
|
||||||
|
|
||||||
class MQTTForwarder:
|
class MQTTForwarder:
|
||||||
|
|
||||||
client_id : str
|
_client_id : str
|
||||||
local_broker : MQTTService
|
_local_broker : MQTTService
|
||||||
central_broker : MQTTService
|
_central_broker : MQTTService
|
||||||
|
_db_service : MongoService
|
||||||
|
|
||||||
def __init__(self, client_id : str, local_mqtt: MQTTService, central_mqtt: MQTTService):
|
def __init__(self, client_id : str, local_mqtt: MQTTService, central_mqtt: MQTTService, db_service : MongoService):
|
||||||
self.client_id = client_id
|
self._client_id = client_id
|
||||||
self.local_broker = local_mqtt
|
self._local_broker = local_mqtt
|
||||||
self.central_broker = central_mqtt
|
self._central_broker = central_mqtt
|
||||||
|
self._db_service = db_service
|
||||||
|
|
||||||
def start(self, src_topic: str, dst_topic: str):
|
def start(self, src_topic: str, dst_topic: str, handler : Callable[[str, str], None]):
|
||||||
try:
|
try:
|
||||||
def forward_handler(topic: str, msg: str):
|
def forward_handler(topic: str, msg: str):
|
||||||
self.central_broker.publish(dst_topic, msg)
|
#self.central_broker.publish(dst_topic, msg)
|
||||||
|
data = json.loads(msg)
|
||||||
|
self._db_service.insert("telemetry", data)
|
||||||
|
handler(dst_topic, msg)
|
||||||
|
|
||||||
self.local_broker.subscribe(src_topic, forward_handler)
|
self._local_broker.subscribe(src_topic, forward_handler)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"An error occurred while forwarding from {src_topic} to {dst_topic}: {e}")
|
print(f"An error occurred while forwarding from {src_topic} to {dst_topic}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def create_forwarder(auth_data : AuthData,
|
||||||
|
local_broker_address : str, local_broker_port : int,
|
||||||
|
api_broker_address : str, api_broker_port : int) -> tuple[MQTTService, MQTTService, MQTTForwarder]:
|
||||||
|
client_id = auth_data.get_client_id()
|
||||||
|
|
||||||
|
local_broker = MQTTService(
|
||||||
|
local_broker_address,
|
||||||
|
local_broker_port,
|
||||||
|
client_id=client_id,
|
||||||
|
username="main",
|
||||||
|
password="hepl",
|
||||||
|
)
|
||||||
|
|
||||||
|
api_broker = MQTTService(
|
||||||
|
api_broker_address,
|
||||||
|
api_broker_port,
|
||||||
|
client_id=client_id,
|
||||||
|
username="customer",
|
||||||
|
password="hepl",
|
||||||
|
)
|
||||||
|
|
||||||
|
forwarder = MQTTForwarder(client_id, local_broker, api_broker)
|
||||||
|
|
||||||
|
return local_broker, api_broker, forwarder
|
||||||
|
|
||||||
|
|||||||
44
api-customer/src/services/mongo_service.py
Normal file
44
api-customer/src/services/mongo_service.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from pymongo.synchronous.database import Database
|
||||||
|
|
||||||
|
|
||||||
|
class MongoService:
|
||||||
|
|
||||||
|
_client : MongoClient
|
||||||
|
_db : Database
|
||||||
|
|
||||||
|
def __init__(self, uri : str, database : str):
|
||||||
|
self._client = MongoClient(uri)
|
||||||
|
self._db = self._client[database]
|
||||||
|
|
||||||
|
|
||||||
|
def insert(self, collection : str, data : object):
|
||||||
|
collection = self._db[collection]
|
||||||
|
payload = self._to_document(data)
|
||||||
|
result = collection.insert_one(payload)
|
||||||
|
return result.inserted_id
|
||||||
|
|
||||||
|
def find(self, collection: str, field: str, value):
|
||||||
|
col = self._db[collection]
|
||||||
|
return list(col.find({field: value}))
|
||||||
|
|
||||||
|
def _to_document(self, obj):
|
||||||
|
if obj is None or isinstance(obj, (str, int, float, bool)):
|
||||||
|
return obj
|
||||||
|
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return [self._to_document(i) for i in obj]
|
||||||
|
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return {k: self._to_document(v) for k, v in obj.items()}
|
||||||
|
|
||||||
|
if hasattr(obj, "__dict__"):
|
||||||
|
return {
|
||||||
|
k: self._to_document(v)
|
||||||
|
for k, v in vars(obj).items()
|
||||||
|
if not k.startswith("_")
|
||||||
|
}
|
||||||
|
|
||||||
|
return str(obj)
|
||||||
@@ -41,7 +41,8 @@ class MQTTService:
|
|||||||
self._connected = False
|
self._connected = False
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
if not self._connected:
|
if self._connected: return
|
||||||
|
|
||||||
print(f"Connecting to {self.address}...")
|
print(f"Connecting to {self.address}...")
|
||||||
self.client.connect(self.address, self.port)
|
self.client.connect(self.address, self.port)
|
||||||
self.client.loop_start()
|
self.client.loop_start()
|
||||||
@@ -52,8 +53,6 @@ class MQTTService:
|
|||||||
if not self._connected:
|
if not self._connected:
|
||||||
raise ConnectionError(f"Cannot connect to MQTT broker at {self.address}:{self.port}")
|
raise ConnectionError(f"Cannot connect to MQTT broker at {self.address}:{self.port}")
|
||||||
print(f"Successfully connected to {self.address}")
|
print(f"Successfully connected to {self.address}")
|
||||||
else :
|
|
||||||
print(f"Already connected to {self.address}...")
|
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
if self._connected:
|
if self._connected:
|
||||||
|
|||||||
@@ -7,29 +7,29 @@ from serial import Serial
|
|||||||
class SerialReader:
|
class SerialReader:
|
||||||
|
|
||||||
serial : Serial = None
|
serial : Serial = None
|
||||||
__thread : Thread | None = None
|
_thread : Thread | None = None
|
||||||
__listeners : list[Callable] = None
|
_listeners : list[Callable] = None
|
||||||
|
|
||||||
def __init__(self, port, baudrate):
|
def __init__(self, port, baudrate):
|
||||||
self.serial = Serial(port, baudrate)
|
self.serial = Serial(port, baudrate)
|
||||||
self._run_event = threading.Event()
|
self._run_event = threading.Event()
|
||||||
self.__listeners = []
|
self._listeners = []
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
self._run_event.set()
|
self._run_event.set()
|
||||||
if self.__thread is None or not self.__thread.is_alive():
|
if self._thread is None or not self._thread.is_alive():
|
||||||
self.__thread = Thread(target=self._read, daemon=True)
|
self._thread = Thread(target=self._read, daemon=True)
|
||||||
self.__thread.start()
|
self._thread.start()
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
if self._run_event.is_set():
|
if self._run_event.is_set():
|
||||||
self._run_event.clear()
|
self._run_event.clear()
|
||||||
|
|
||||||
def subscribe(self, listener : Callable[[str], None]) -> None:
|
def subscribe(self, listener : Callable[[str], None]) -> None:
|
||||||
self.__listeners.append(listener)
|
self._listeners.append(listener)
|
||||||
|
|
||||||
def _notify(self, data : str):
|
def _notify(self, data : str):
|
||||||
for listener in self.__listeners:
|
for listener in self._listeners:
|
||||||
listener(data)
|
listener(data)
|
||||||
|
|
||||||
def _read(self):
|
def _read(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user