diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
index 36085a8..b6b05a6 100644
--- a/.idea/dataSources.xml
+++ b/.idea/dataSources.xml
@@ -8,5 +8,29 @@
jdbc:sqlite:$PROJECT_DIR$/software/backend/backend_database.db
$ProjectFileDir$
+
+ sqlite.xerial
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:C:\Users\bcali\Documents\Uni\BEI6\Projektarbeit\projektarbeit_duelger_waldhauser_caliskan\software\backend\tests\backend_database.db
+ $ProjectFileDir$
+
+
+ file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.40.1/sqlite-jdbc-3.40.1.jar
+
+
+
+
+ sqlite.xerial
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:C:\Users\bcali\Documents\Uni\BEI6\Projektarbeit\projektarbeit_duelger_waldhauser_caliskan\software\backend\tests\test_database.db
+ $ProjectFileDir$
+
+
+ file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.40.1/sqlite-jdbc-3.40.1.jar
+
+
+
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 24e9b00..c983847 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -29,3 +29,4 @@ urllib3==1.26.14
Werkzeug==2.2.3
zipp==3.15.0
python-ev3dev2==2.1.0.post1
+pytest
\ No newline at end of file
diff --git a/software/backend/backend_database.db b/software/backend/backend_database.db
index cb236c6..b38b3c6 100644
Binary files a/software/backend/backend_database.db and b/software/backend/backend_database.db differ
diff --git a/software/backend/createdatabase_and_test.py b/software/backend/createdatabase.py
similarity index 58%
rename from software/backend/createdatabase_and_test.py
rename to software/backend/createdatabase.py
index 454c505..989ee18 100644
--- a/software/backend/createdatabase_and_test.py
+++ b/software/backend/createdatabase.py
@@ -2,22 +2,20 @@ import random
from plantdatabase import PlantDataBase
-mydatabase = PlantDataBase()
-mydatabase.create_table()
+mydatabase = PlantDataBase('backend_database.db')
+mydatabase.create_tables()
-for i in range(1,6):
- mydatabase.insert_plant(_gps='gps', plantname=f"Pflanze{i}")
+for i in range(1, 6):
+ mydatabase.insert_plant(plantname=f"Pflanze{i}")
-for i in range(1,7):
+for i in range(1, 7):
plant_id = i
temp = random.random()
humidity = random.random()
soil_moisture = random.random()
- pest_infestation = 0
light_intensity = random.random()
mydatabase.insert_measurement_data(plant_id=plant_id,
sensordata_temp=temp,
sensordata_humidity=humidity,
sensordata_soil_moisture=soil_moisture,
- pest_infestation=pest_infestation,
- light_intensity=light_intensity)
+ sensordata_brightness=light_intensity)
diff --git a/software/backend/data_functions.py b/software/backend/data_functions.py
index 8ecd4fa..029f9e7 100644
--- a/software/backend/data_functions.py
+++ b/software/backend/data_functions.py
@@ -6,7 +6,7 @@ Every function should return json format with the wanted data from the database
"""
import paho.mqtt.client as mqtt
from plantdatabase import PlantDataBase
-from software.defines import Topics
+from software.defines import Topics, MAX_PLANT_COUNT
import json
import uuid
@@ -27,8 +27,7 @@ def data_sensordata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, my
sensordata_temp=payload['AirTemperature'],
sensordata_humidity=payload['AirHumidity'],
sensordata_soil_moisture=payload['SoilMoisture'],
- pest_infestation=0,
- light_intensity=payload['Brightness'])
+ sensordata_brightness=payload['Brightness'])
def data_position(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
@@ -68,23 +67,46 @@ def action_getbattery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage,
def action_getalldata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
- # TODO: get data from database
+ plant_names = json.loads(message.payload.decode("UTF-8"))
+ print(plant_names)
alldata = []
- for i in range(1, 7):
- alldata.append(mydatabase.get_latest_data(plant_id=i))
+ for i in plant_names:
+ alldata.append(mydatabase.get_latest_data(plant_name=i))
client.publish(Topics['BACKEND_DATA_SENSORDATAALL'], json.dumps(alldata))
def action_newplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
- # TODO: insert new plant to database
- pass
+ plant_data = json.loads(message.payload.decode("UTF-8"))
+ mydatabase.insert_plant(plantname=plant_data["PlantName"], plant_id=plant_data["PlantID"])
+ mydatabase.insert_measurement_data(plant_id=plant_data["PlantID"],
+ sensordata_temp=plant_data["AirTemperature"],
+ sensordata_humidity=plant_data["AirHumidity"],
+ sensordata_soil_moisture=plant_data["SoilMoisture"],
+ sensordata_brightness=plant_data["Brightness"])
+ print("BACKEND_ACTION_NEWPLANT RECEIVED DATA: " + str(plant_data))
def action_configureplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
- # TODO: configure plant
- pass
+ plant_data = json.loads(message.payload.decode("UTF-8"))
+ mydatabase.configure_plant(plant_id=plant_data["plant_ID"], plantname=plant_data["PlantName"])
+ mydatabase.insert_measurement_data(plant_id=plant_data["PlantID"],
+ sensordata_temp=plant_data["AirTemperature"],
+ sensordata_humidity=plant_data["AirHumidity"],
+ sensordata_soil_moisture=plant_data["SoilMoisture"],
+ sensordata_brightness=plant_data["Brightness"])
+ print("BACKEND_ACTION_CONFIGUREPLANT RECEIVED DATA: " + str(plant_data))
def action_deleteplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
- # TODO: delete plant from database (from ID)
- pass
+ delete_plant = json.loads(message.payload.decode("UTF-8"))
+ mydatabase.delete_plant(plant_id=delete_plant["PlantID"])
+ print("BACKEND_ACTION_DELETEPLANT RECEIVED DATA: " + str(delete_plant))
+
+
+def action_countplants(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
+ count_payload = {
+ "CurrentCount": mydatabase.plant_count(),
+ "MaxCount": MAX_PLANT_COUNT
+ }
+ print("BACKEND_DATA_PLANTCOUNT SENDED DATA:" + str(count_payload))
+ client.publish(Topics["BACKEND_ACTION_PLANTCOUNT"], json.dumps(count_payload, indent=4))
diff --git a/software/backend/main.py b/software/backend/main.py
index c0bc31e..8aae264 100644
--- a/software/backend/main.py
+++ b/software/backend/main.py
@@ -9,13 +9,13 @@ Used protocol for interaction: mqtt (paho-mqtt module)
# imports
import paho.mqtt.client as mqtt
-from software.defines import MQTT_BROKER_LOCAL, MQTT_BROKER_GLOBAL, Topics, BACKEND_CLIENT_ID
+from software.defines import MQTT_BROKER_LOCAL, MQTT_BROKER_GLOBAL, Topics, BACKEND_CLIENT_ID, DATABASE_NAME
from plantdatabase import PlantDataBase
import data_functions
# inits
-mydatabase = PlantDataBase()
-mydatabase.create_table()
+mydatabase = PlantDataBase(database_name=DATABASE_NAME)
+mydatabase.create_tables()
order_handler = [] # will contain UUIDS with Order IDs
@@ -81,6 +81,10 @@ def on_connect(_client: mqtt.Client, _userdata, _flags, _rc):
lambda client, userdata, message: data_functions.
action_deleteplant(client, userdata, message, mydatabase))
+ _client.subscribe(Topics['BACKEND_ACTION_PLANTCOUNT'])
+ _client.message_callback_add(Topics['BACKEND_ACTION_PLANTCOUNT'], lambda client, userdata, message: data_functions.
+ action_countplants(client, userdata, message, mydatabase))
+
# END TOPIC SUBSCRIPTIONS
else:
print("connection failed")
diff --git a/software/backend/plantdatabase.py b/software/backend/plantdatabase.py
index 531ea58..4a30a1b 100644
--- a/software/backend/plantdatabase.py
+++ b/software/backend/plantdatabase.py
@@ -1,13 +1,15 @@
# file to create a database via python script
import sqlite3
+from typing import Optional
class PlantDataBase:
"""
Class to create Makeathon database
"""
- def __init__(self):
- self.db_file = 'backend_database.db'
+
+ def __init__(self, database_name: str):
+ self.db_file = database_name # 'backend_database.db'
self.conn = None
try:
self.conn = sqlite3.connect(self.db_file, check_same_thread=False)
@@ -16,69 +18,113 @@ class PlantDataBase:
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," \
- "plantName TEXT)"
- self.cur.execute(table_config)
+ def create_tables(self):
+ try:
+ table_config = "CREATE TABLE IF NOT EXISTS plants " \
+ "(PlantID INTEGER PRIMARY KEY," \
+ "PlantName 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_soil_moisture REAL," \
- "light_intensity REAL," \
- "FOREIGN KEY (plant_ID)" \
- "REFERENCES plants (plant_ID) )"
- self.cur.execute(table_config)
+ table_config = "CREATE TABLE IF NOT EXISTS measurement_values " \
+ "(measurementID INTEGER PRIMARY KEY," \
+ "Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP," \
+ "PlantID INTEGER, " \
+ "AirTemperature REAL," \
+ "AirHumidity REAL," \
+ "SoilMoisture REAL," \
+ "Brightness REAL," \
+ "FOREIGN KEY (PlantID)" \
+ "REFERENCES plants (PlantID) )"
+ self.cur.execute(table_config)
+ return True
+ except sqlite3.Warning as e:
+ return e
- def insert_plant(self, _gps: str, plantname):
- self.cur.execute("INSERT INTO plants (gps, plantName) VALUES (?,?)", (_gps, plantname))
- self.conn.commit()
+ def insert_plant(self, plantname: str, plant_id: int):
+ try:
+ self.cur.execute("INSERT INTO plants (PlantName, PlantID) VALUES (?,?)", (plantname, plant_id))
+ self.conn.commit()
+ return True
+ except (sqlite3.NotSupportedError, sqlite3.Warning) as e:
+ return e
+
+ def configure_plant(self, plant_id: int, plantname: str):
+ try:
+ self.cur.execute("UPDATE plants SET PlantID = ?, PlantName = ? WHERE PlantID= ?",
+ (plant_id, plantname, plant_id))
+ self.conn.commit()
+ return True
+ except (sqlite3.NotSupportedError, sqlite3.Warning) as e:
+ return e
+
+ def delete_plant(self, plant_id):
+ try:
+ self.cur.execute('DELETE FROM plants WHERE PlantID = ?', (plant_id,))
+ self.conn.commit()
+ return True
+ except (sqlite3.NotSupportedError, sqlite3.Warning) as e:
+ return e
def insert_measurement_data(self, plant_id,
sensordata_temp,
sensordata_humidity,
sensordata_soil_moisture,
- light_intensity):
- self.cur.execute(f"INSERT INTO measurement_values (plant_ID, sensordata_temp, sensordata_humidity,"
- f" sensordata_soil_moisture, light_intensity) VALUES "
- f"({plant_id}, {sensordata_temp}, {sensordata_humidity}, {sensordata_soil_moisture}"
- f", {light_intensity})")
- self.conn.commit()
+ sensordata_brightness) -> bool:
+ try:
+ self.cur.execute(f"INSERT INTO measurement_values (PlantID, AirTemperature, AirHumidity,"
+ f"SoilMoisture, Brightness) VALUES "
+ f"({plant_id}, {sensordata_temp}, {sensordata_humidity}, {sensordata_soil_moisture}"
+ f", {sensordata_brightness})")
+ self.conn.commit()
+ return True
+ except (sqlite3.NotSupportedError, sqlite3.Warning) as e:
+ return e
- def get_latest_data(self, plant_id) -> dict:
+ def get_latest_data(self, plant_name: Optional[str] = None, plant_id: Optional[int] = None):
"""
Gets the newest parameter of specific plant and returns all parameters in json format
:param plant_id:
+ :param plant_name:
:return:
"""
- self.cur.execute(f"SELECT * FROM measurement_values where plant_ID = {plant_id} ORDER BY Timestamp DESC LIMIT 1")
- data = self.cur.fetchone()
- self.cur.execute(f"SELECT plantName FROM plants where plant_ID = {plant_id}")
- name = self.cur.fetchone()
- print(data)
- print(name[0])
- json_file = {
- "MeasurementID": data[0],
- "PlantID": data[2],
- "Timestamp": data[1],
- "AirTemperature": data[3],
- "AirHumidity": data[4],
- "SoilMoisture": data[5],
- "Brightness": data[6],
- "PlantName": name
- }
- return json_file
+ try:
+ if plant_name is not None and plant_id is None:
+ self.cur.execute("SELECT PlantID FROM plants where PlantName = ?", (plant_name,))
+ plant_id = self.cur.fetchone()[0]
+ elif (plant_id is not None and plant_name is not None) or (plant_id is None and plant_name is None):
+ raise TypeError("Can't pass plant_id and plant_name to the function. Just one allowed !")
+
+ self.cur.execute("SELECT * FROM measurement_values where PlantID = ? ORDER BY Timestamp DESC LIMIT 1",
+ (plant_id,))
+ data = self.cur.fetchone()
+ json_file = {
+ "MeasurementID": data[0],
+ "PlantID": data[2],
+ "Timestamp": data[1],
+ "AirTemperature": data[3],
+ "AirHumidity": data[4],
+ "SoilMoisture": data[5],
+ "Brightness": data[6],
+ "PlantName": plant_name
+ }
+ return json_file
+ except (sqlite3.Warning, TypeError) as e:
+ return e
def delete_data(self, table_name):
- self.cur.execute(f"DELETE FROM {table_name}")
+ self.cur.execute(f'DELETE FROM {table_name}')
self.conn.commit()
+ return True
# TODO: Kemals Scheiß implementieren
- def delete_plant(self, plant_id):
- self.cur.execute('DELETE FROM plants WHERE plant_ID = ?', (plant_id,))
- self.conn.commit()
+ def plant_count(self) -> int:
+ """
+ returns the number of plants registered in the database
+ :return:
+ """
+ self.cur.execute("SELECT COUNT(*) FROM plants")
+ return self.cur.fetchone()[0]
+
+ def __del__(self):
+ self.conn.close()
diff --git a/software/backend/tests/test_database.db b/software/backend/tests/test_database.db
new file mode 100644
index 0000000..d38a441
Binary files /dev/null and b/software/backend/tests/test_database.db differ
diff --git a/software/backend/tests/test_plantdatabase.py b/software/backend/tests/test_plantdatabase.py
new file mode 100644
index 0000000..73b9578
--- /dev/null
+++ b/software/backend/tests/test_plantdatabase.py
@@ -0,0 +1,50 @@
+#
+# created by caliskan
+# use this file to test your plantdatabase changes
+
+from software.backend.plantdatabase import PlantDataBase
+import pytest
+
+
+def test_create_table():
+ testdatabase = PlantDataBase(database_name='test_database.db')
+ assert testdatabase.create_tables() is True
+
+
+def test_insert_and_delete_plant():
+ testdatabase = PlantDataBase(database_name='test_database.db')
+
+ assert testdatabase.create_tables() is True
+ assert testdatabase.delete_data("plants") is True
+ assert testdatabase.insert_plant(plantname="Bertha", plant_id=1) is True
+ assert testdatabase.plant_count() == 1
+ assert testdatabase.delete_plant(plant_id=1) is True
+ assert testdatabase.plant_count() == 0
+
+
+def test_insert_and_get_measurement_values():
+ test_plant_id = 2
+ test_temp = 22.4
+ test_humidity = 93.4
+ test_soil_moisture = 12.5
+ test_brightness = 66
+ test_plant_name = "Bertha"
+
+ testdatabase = PlantDataBase(database_name='test_database.db')
+ assert testdatabase.create_tables() is True
+ assert testdatabase.delete_data("plants") is True
+ assert testdatabase.insert_plant(plantname=test_plant_name, plant_id=test_plant_id) is True
+
+ assert testdatabase.insert_measurement_data(plant_id=test_plant_id,
+ sensordata_temp=test_temp,
+ sensordata_humidity=test_humidity,
+ sensordata_soil_moisture=test_soil_moisture,
+ sensordata_brightness=test_brightness) is True
+ test_plant_data = testdatabase.get_latest_data(plant_name=test_plant_name)
+ print(test_plant_data)
+ assert test_plant_data["PlantID"] == test_plant_id
+ assert test_plant_data["AirTemperature"] == test_temp
+ assert test_plant_data["AirHumidity"] == test_humidity
+ assert test_plant_data["SoilMoisture"] == test_soil_moisture
+ assert test_plant_data["Brightness"] == test_brightness
+ assert test_plant_data["PlantName"] == test_plant_name
diff --git a/software/defines.py b/software/defines.py
index 974eca1..9bc3e77 100644
--- a/software/defines.py
+++ b/software/defines.py
@@ -8,6 +8,8 @@ MQTT_BROKER_LOCAL = "192.168.0.199"
MQTT_BROKER_GLOBAL = "mqtt.eclipseprojects.io"
RASPI_CLIENT_ID = "smart_farming_raspi"
BACKEND_CLIENT_ID = "smart_farming_server"
+MAX_PLANT_COUNT = 6
+DATABASE_NAME = 'backend_database.db'
# Topics:
Topics = {
@@ -21,7 +23,7 @@ Topics = {
"ROBOT_DATA_PICTURE": "ROBOT/DATA/PICTURE",
"BACKEND_ACTION_DRIVE": "BACKEND/ACTION/DRIVE",
- "BACKEND_ACTION_DRIVEPALL": "BACKEND/ACTION/DRIVEALL",
+ "BACKEND_ACTION_DRIVEALL": "BACKEND/ACTION/DRIVEALL",
"BACKEND_ACTION_GETPOSITION": "BACKEND/ACTION/GETPOSITION",
"BACKEND_ACTION_GETBATTERY": "BACKEND/ACTION/GETBATTERY",
"BACKEND_ACTION_GETALLDATA": "BACKEND/ACTION/GETALLDATA",
@@ -111,7 +113,7 @@ BATTERY = {
PLANTCOUNT = {
"CurrenCount": 0,
- "maxCount": 0
+ "MaxCount": 0
}
# endregion
@@ -136,6 +138,8 @@ DELETEPLANT = {
# GETBATTERY -> no message needed
-# GETALLDATA -> no message needed
+GETALLDATA = {
+ "PlantNames": []
+}
# endregion