@@ -8,5 +8,29 @@ | |||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/software/backend/backend_database.db</jdbc-url> | |||
<working-dir>$ProjectFileDir$</working-dir> | |||
</data-source> | |||
<data-source source="LOCAL" name="backend_database" uuid="4f1f9bd8-463b-4b3b-b101-cc4b9b84e2e1"> | |||
<driver-ref>sqlite.xerial</driver-ref> | |||
<synchronize>true</synchronize> | |||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver> | |||
<jdbc-url>jdbc:sqlite:C:\Users\bcali\Documents\Uni\BEI6\Projektarbeit\projektarbeit_duelger_waldhauser_caliskan\software\backend\tests\backend_database.db</jdbc-url> | |||
<working-dir>$ProjectFileDir$</working-dir> | |||
<libraries> | |||
<library> | |||
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.40.1/sqlite-jdbc-3.40.1.jar</url> | |||
</library> | |||
</libraries> | |||
</data-source> | |||
<data-source source="LOCAL" name="test_database" uuid="a72361b0-954e-42bd-8930-b14a0b4c1023"> | |||
<driver-ref>sqlite.xerial</driver-ref> | |||
<synchronize>true</synchronize> | |||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver> | |||
<jdbc-url>jdbc:sqlite:C:\Users\bcali\Documents\Uni\BEI6\Projektarbeit\projektarbeit_duelger_waldhauser_caliskan\software\backend\tests\test_database.db</jdbc-url> | |||
<working-dir>$ProjectFileDir$</working-dir> | |||
<libraries> | |||
<library> | |||
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.40.1/sqlite-jdbc-3.40.1.jar</url> | |||
</library> | |||
</libraries> | |||
</data-source> | |||
</component> | |||
</project> |
@@ -29,3 +29,4 @@ urllib3==1.26.14 | |||
Werkzeug==2.2.3 | |||
zipp==3.15.0 | |||
python-ev3dev2==2.1.0.post1 | |||
pytest |
@@ -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) |
@@ -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)) |
@@ -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") |
@@ -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() |
@@ -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 |
@@ -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 |