diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..36085a8 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/software/backend/backend_database.db + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..5d33860 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/software/backend/backend_database.db b/software/backend/backend_database.db new file mode 100644 index 0000000..406143c Binary files /dev/null and b/software/backend/backend_database.db differ diff --git a/software/backend/data_handling_functions.py b/software/backend/data_handling_functions.py new file mode 100644 index 0000000..a495826 --- /dev/null +++ b/software/backend/data_handling_functions.py @@ -0,0 +1,6 @@ +""" +created by caliskan at 19.04.2023 + +This file contains all functions, which handle the different cases. +Every function should return json format with the wanted data from the database +""" \ No newline at end of file diff --git a/software/backend/defines.py b/software/backend/defines.py new file mode 100644 index 0000000..2574007 --- /dev/null +++ b/software/backend/defines.py @@ -0,0 +1,8 @@ +""" +created by caliskan at 19.04.2023 + +contains all constants for the backend architecture of the smart garden project +""" + +MQTT_BROKER_LOCAL = "" +MQTT_BROKER_GLOBAL = "mqtt.eclipseprojects.io" diff --git a/software/backend/dev_test_examples/mqtt_publisher.py b/software/backend/dev_test_examples/mqtt_publisher.py new file mode 100644 index 0000000..e005f0b --- /dev/null +++ b/software/backend/dev_test_examples/mqtt_publisher.py @@ -0,0 +1,16 @@ + +import paho.mqtt.client as mqtt +from random import randrange, uniform +import time + +mqttBroker ="mqtt.eclipseprojects.io" + +client = mqtt.Client("Temperature_Inside") +client.connect(mqttBroker) + +while True: + randNumber = uniform(20.0, 21.0) + client.publish("planttest", randNumber) + print("Just published " + str(randNumber) + " to topic TEMPERATURE") + time.sleep(0.5) + diff --git a/software/backend/dev_test_examples/mqtt_subscriber.py b/software/backend/dev_test_examples/mqtt_subscriber.py new file mode 100644 index 0000000..3eed0c6 --- /dev/null +++ b/software/backend/dev_test_examples/mqtt_subscriber.py @@ -0,0 +1,18 @@ +import paho.mqtt.client as mqtt +import time + +def on_message(client, userdata, message): + print("received message: " ,str(message.payload.decode("utf-8"))) + +mqttBroker ="mqtt.eclipseprojects.io" + +client = mqtt.Client("Smartphone") +client.connect(mqttBroker) + +client.loop_start() + +client.subscribe("TEMPERATURE") +client.on_message=on_message + +time.sleep(30) +client.loop_stop() \ No newline at end of file diff --git a/software/backend/main.py b/software/backend/main.py new file mode 100644 index 0000000..40a9505 --- /dev/null +++ b/software/backend/main.py @@ -0,0 +1,54 @@ +""" +created by caliskan at 19.04.2023 + +This file contains the main script for the backend server of smart garden project +It has the task to be a bridge between the frontend and robot. +It also contains a database with the current plant data +Used protocol for interaction: mqtt (paho-mqtt module) +""" + +# imports +import paho.mqtt.client as mqtt +from defines import MQTT_BROKER_LOCAL, MQTT_BROKER_GLOBAL +from plantdatabase import PlantDataBase + +def on_message(client, userdata, message): + print(f'message received! {message.topic}') + + +def on_connect(client, userdata, flags, rc): + if rc == 0: + print("connected") + + # TOPIC SUBSCRIPTIONS + client.subscribe("planttest") + # Robot topics + client.subscribe("Robot/Data/SensorData") + client.subscribe("Robot/Data/Position") + client.subscribe("Robot/Data/Battery") + client.subscribe("Robot/Data/Picture") + + # FrontEnd topics + client.subscribe("BackEnd/Data/SensorData") + client.subscribe("BackEnd/Data/SensorDataAll") + client.subscribe("BackEnd/Data/Position") + client.subscribe("BackEnd/Data/Battery") + client.subscribe("BackEnd/Data/Picture") + + # END TOPIC SUBSCRIPTIONS + else: + print("connection failed") + + +def main(): + mydatabase = PlantDataBase() + mydatabase.create_table() + client = mqtt.Client() + client.on_connect = on_connect + client.on_message = on_message + client.connect(MQTT_BROKER_GLOBAL) + client.loop_forever() + + +if __name__ == "__main__": + main() diff --git a/software/backend/mqtt_client_basic.py b/software/backend/mqtt_client_basic.py deleted file mode 100644 index 56652d0..0000000 --- a/software/backend/mqtt_client_basic.py +++ /dev/null @@ -1,29 +0,0 @@ -import paho.mqtt.client as mqtt - - -def on_connect(_client, userdata, flags, rc): - print("Connected with result code " + str(rc)) - client.subscribe("test/topic") - - -def on_message(_client, userdata, msg): - print(msg.topic + " " + str(msg.payload)) - - -client = mqtt.Client() -client.on_connect = on_connect -client.on_message = on_message - -client.connect("localhost", 1883, 60) - -client.loop_forever() - -"""In this example, we create a new instance of the mqtt.Client class and set up the on_connect and on_message -callback functions. The on_connect function is called when the client successfully connects to the broker, -and the on_message function is called when a message is received on a subscribed topic. - -Then, we call the connect method to connect to the MQTT broker running on the local machine on port 1883, and set the -keepalive value to 60 seconds. - -Finally, we call the loop_forever method to start the client and begin processing incoming messages. This method -blocks the program execution and runs the client loop in a loop until the client is disconnected. (ChatGPT)""" diff --git a/software/backend/plantdatabase.py b/software/backend/plantdatabase.py new file mode 100644 index 0000000..3a8b27c --- /dev/null +++ b/software/backend/plantdatabase.py @@ -0,0 +1,77 @@ +# file to create a database via python script +import sqlite3 + + +class PlantDataBase: + """ + Class to create Makeathon database + """ + def __init__(self): + self.db_file = 'backend_database.db' + self.conn = None + try: + self.conn = sqlite3.connect(self.db_file, check_same_thread=False) + print(sqlite3.version) + except sqlite3.Error as e: + print(e) + self.cur = self.conn.cursor() + + def create_table(self): + table_config = "CREATE TABLE IF NOT EXISTS plants " \ + "(plant_ID INTEGER PRIMARY KEY AUTOINCREMENT," \ + " gps TEXT," \ + " plant_type TEXT)" + self.cur.execute(table_config) + + table_config = "CREATE TABLE IF NOT EXISTS measurement_values " \ + "(measurement_id INTEGER PRIMARY KEY AUTOINCREMENT," \ + "Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP," \ + "plant_ID INTEGER, " \ + "sensordata_temp REAL," \ + "sensordata_humidity REAL," \ + "sensordata_ground_humidity REAL," \ + "pest_infestation INTEGER," \ + "light_intensity REAL," \ + "FOREIGN KEY (plant_ID)" \ + " REFERENCES plants (plant_ID) )" + self.cur.execute(table_config) + + def insert_plant(self, gps: str, plant_type: str): + self.cur.execute(f"INSERT INTO plants (gps, plant_type) VALUES ({gps}, {plant_type})") + self.conn.commit() + + def insert_measurement_data(self, plant_id, + sensordata_temp, + sensordata_humidity, + sensordata_ground_humidity, + pest_infestation, + light_intensity): + self.cur.execute(f"INSERT INTO measurement_values (plant_ID, sensordata_temp, sensordata_humidity," + f" sensordata_ground_humidity, pest_infestation, light_intensity) VALUES " + f"({plant_id}, {sensordata_temp}, {sensordata_humidity}, {sensordata_ground_humidity}, {pest_infestation}" + f", {light_intensity})") + self.conn.commit() + + def get_latest_data(self, plant_id) -> dict: + """ + Gets the newest parameter of specific plant and returns all parameters in json format + :param plant_id: + :return: + """ + self.cur.execute(f"SELECT * FROM measurement_values where plant_ID = {plant_id} ORDER BY Timestamp DESC LIMIT 1") + data = self.cur.fetchone() + json_file = { + "measurement_id": data[0], + "plant_id": data[2], + "timestamp": data[1], + "sensordata_temp": data[3], + "sensordata_humidity": data[4], + "sensordata_ground_humidity": data[5], + "pest_infestation": data[6], + "light_intensity": data[7] + } + return json_file + + def delete_data(self, table_name): + self.cur.execute(f"DELETE FROM {table_name}") + self.conn.commit()