<?xml version="1.0" encoding="UTF-8"?> | |||||
<project version="4"> | |||||
<component name="PublishConfigData" serverName="backend" remoteFilesAllowedToDisappearOnAutoupload="false"> | |||||
<serverData> | |||||
<paths name="backend"> | |||||
<serverdata> | |||||
<mappings> | |||||
<mapping deploy="/home/lego/SMARTGARDENING" local="$PROJECT_DIR$/software" web="/" /> | |||||
</mappings> | |||||
</serverdata> | |||||
</paths> | |||||
</serverData> | |||||
</component> | |||||
</project> |
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project version="4"> | |||||
<component name="SshConfigs"> | |||||
<configs> | |||||
<sshConfig authType="PASSWORD" host="lego-K53SV" id="40359a92-2c87-41df-943b-cfac73c7ea3d" port="22" nameFormat="DESCRIPTIVE" username="lego" useOpenSSHConfig="true" /> | |||||
</configs> | |||||
</component> | |||||
</project> |
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project version="4"> | |||||
<component name="WebServers"> | |||||
<option name="servers"> | |||||
<webServer id="1b3e9e3f-7394-4cbc-a32d-b95ef2abda52" name="backend"> | |||||
<fileTransfer accessType="SFTP" host="lego-K53SV" port="22" sshConfigId="40359a92-2c87-41df-943b-cfac73c7ea3d" sshConfig="lego@lego-K53SV:22 password"> | |||||
<advancedOptions> | |||||
<advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" /> | |||||
</advancedOptions> | |||||
</fileTransfer> | |||||
</webServer> | |||||
</option> | |||||
</component> | |||||
</project> |
Werkzeug==2.2.3 | Werkzeug==2.2.3 | ||||
zipp==3.15.0 | zipp==3.15.0 | ||||
python-ev3dev2==2.1.0.post1 | python-ev3dev2==2.1.0.post1 | ||||
pytest | |||||
pytest==7.3.1 |
import json | import json | ||||
import uuid | import uuid | ||||
from typing import Union | from typing import Union | ||||
from datetime import datetime | |||||
import logging | |||||
from robot import Robot | |||||
# Robot Channel Reactions | # Robot Channel Reactions | ||||
def data_sensordata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, | def data_sensordata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, | ||||
order_handler: list): | |||||
print("message received") | |||||
# TODO: Store data in database | |||||
robot: Robot): | |||||
str_in = str(message.payload.decode("UTF-8")) | str_in = str(message.payload.decode("UTF-8")) | ||||
payload = json.loads(str_in) | payload = json.loads(str_in) | ||||
print("Received data: ", json.dumps(payload)) | |||||
logging.info("ROBOT_DATA_SENSORDATA Received data: " + json.dumps(payload)) | |||||
drive_data = { | |||||
"PlantID": payload['PlantID'], | |||||
"ActionID": payload['ActionID'] | |||||
} | |||||
order_handler.remove(payload['ActionID']) | |||||
try: | |||||
robot.delete_order(drive_data) | |||||
except Exception as e: | |||||
logging.error("Could not delete order: " + str(e)) | |||||
mydatabase.insert_measurement_data(plant_id=payload['PlantID'], | mydatabase.insert_measurement_data(plant_id=payload['PlantID'], | ||||
sensordata_temp=payload['AirTemperature'], | sensordata_temp=payload['AirTemperature'], | ||||
sensordata_humidity=payload['AirHumidity'], | sensordata_humidity=payload['AirHumidity'], | ||||
sensordata_soil_moisture=payload['SoilMoisture'], | sensordata_soil_moisture=payload['SoilMoisture'], | ||||
sensordata_brightness=payload['Brightness']) | sensordata_brightness=payload['Brightness']) | ||||
logging.debug("Inserted to data base: " + json.dumps(payload)) | |||||
def data_position(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | |||||
# TODO: Forward to frontend in json format | |||||
client.publish(Topics['BACKEND_DATA_POSITION'], message.payload.decode("utf-8")) | |||||
def data_position(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): | |||||
logging.info("ROBOT_DATA_POSITION Received data: " + json.dumps(message.payload.decode("UTF-8"))) | |||||
robot.store_position(json.loads(message.payload.decode("UTF-8"))["Position"]) | |||||
position_data = { | |||||
"Position": robot.get_position(), | |||||
"Timestamp": str(datetime.now()) | |||||
} | |||||
client.publish(Topics['BACKEND_DATA_POSITION'], json.dumps(position_data)) | |||||
def data_battery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | |||||
# TODO: Forward to frontend in json format | |||||
client.publish(Topics['BACKEND_DATA_BATTERY'], message.payload.decode("utf-8")) | |||||
def data_battery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): | |||||
logging.info("ROBOT_DATA_BATTERY Received data: " + json.dumps(message.payload.decode("UTF-8"))) | |||||
robot.store_battery(json.loads(message.payload.decode("UTF-8"))["Battery"]) | |||||
battery_data = { | |||||
"Battery": robot.get_battery(), | |||||
"Timestamp": str(datetime.now()) | |||||
} | |||||
client.publish(Topics['BACKEND_DATA_BATTERY'], json.dumps(battery_data)) | |||||
# FrontEnd Channel Reactions | # FrontEnd Channel Reactions | ||||
def action_drive(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, | def action_drive(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, | ||||
order_handler: list): | |||||
# TODO: ROBOT READY CHECK | |||||
if len(order_handler) < 5: | |||||
order_handler.append(uuid.uuid4()) | |||||
robot: Robot): | |||||
plant_id = mydatabase.get_plant_id(plant_name=json.loads(message.payload.decode("UTF-8"))["PlantName"]) | |||||
action_id = str(uuid.uuid4()) | |||||
drive_data = { | |||||
"PlantID": plant_id, | |||||
"ActionID": action_id | |||||
} | |||||
if robot.get_order_number() < 5 and robot.get_robot_status() is True: | |||||
robot.add_order(drive_data) | |||||
client.publish(Topics['ROBOT_ACTION_DRIVE'], json.dumps(drive_data)) | |||||
logging.info("BACKEND_ACTION_DRIVE Drive Command published: " + json.dumps(drive_data)) | |||||
else: | else: | ||||
# TODO: What to do when no place in order_list left | |||||
pass | |||||
client.publish(Topics['ROBOT_ACTION_DRIVE'], message.payload.decode("utf-8")) | |||||
if robot.get_order_number() < 5: | |||||
robot.add_order(drive_data) | |||||
logging.info("BACKEND_ACTION_DRIVE New data added to order list: " + str(drive_data)) | |||||
elif robot.get_order_number() >= 5: | |||||
logging.error("Could not add Order to list. Order discarded") | |||||
client.publish(Topics['BACKEND_DATA_ERROR'], "Could not add Order to list. Order discarded") | |||||
def action_driveall(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | |||||
def action_driveall(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, | |||||
robot: Robot): | |||||
# TODO: Implement here | # TODO: Implement here | ||||
pass | |||||
print("HELLO") | |||||
def action_getposition(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | def action_getposition(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | ||||
client.publish(Topics['ROBOT_ACTION_GETPOSITION']) | client.publish(Topics['ROBOT_ACTION_GETPOSITION']) | ||||
logging.info("BACKEND_ACTION_GETPOSITION message forwarded to Robot") | |||||
def action_getbattery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | |||||
def action_getbattery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage): | |||||
client.publish(Topics['ROBOT_ACTION_GETBATTERY']) | client.publish(Topics['ROBOT_ACTION_GETBATTERY']) | ||||
logging.info("BACKEND_ACTION_GETBATTERY message forwarded to Robot") | |||||
def action_getalldata(client: mqtt.Client, userdata, message: Union[mqtt.MQTTMessage, list], mydatabase: PlantDataBase): | def action_getalldata(client: mqtt.Client, userdata, message: Union[mqtt.MQTTMessage, list], mydatabase: PlantDataBase): | ||||
plant_names = mydatabase.get_plant_names() | plant_names = mydatabase.get_plant_names() | ||||
print(type(plant_names)) | |||||
alldata = [] | alldata = [] | ||||
for i in plant_names: | for i in plant_names: | ||||
print("I Type: " + str(type(i))) | |||||
print("I: " + i[0]) | |||||
alldata.append(mydatabase.get_latest_data(plant_name=i[0])) | alldata.append(mydatabase.get_latest_data(plant_name=i[0])) | ||||
client.publish(Topics['BACKEND_DATA_SENSORDATAALL'], json.dumps(alldata)) | client.publish(Topics['BACKEND_DATA_SENSORDATAALL'], json.dumps(alldata)) | ||||
print("BACKEND_DATA_SENSORDATAALL SEND DATA:" + str(alldata)) | |||||
logging.info("BACKEND_DATA_SENSORDATAALL got data from database:" + str(alldata)) | |||||
def action_newplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | def action_newplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | ||||
sensordata_humidity=plant_data["AirHumidity"], | sensordata_humidity=plant_data["AirHumidity"], | ||||
sensordata_soil_moisture=plant_data["SoilMoisture"], | sensordata_soil_moisture=plant_data["SoilMoisture"], | ||||
sensordata_brightness=plant_data["Brightness"]) | sensordata_brightness=plant_data["Brightness"]) | ||||
print("BACKEND_ACTION_NEWPLANT RECEIVED DATA: " + str(plant_data)) | |||||
print(mydatabase.get_plant_names()) | |||||
logging.info("BACKEND_ACTION_NEWPLANT new plant data received and inserted: " + str(plant_data)) | |||||
action_getalldata(client, userdata, message, mydatabase) | action_getalldata(client, userdata, message, mydatabase) | ||||
sensordata_humidity=plant_data["AirHumidity"], | sensordata_humidity=plant_data["AirHumidity"], | ||||
sensordata_soil_moisture=plant_data["SoilMoisture"], | sensordata_soil_moisture=plant_data["SoilMoisture"], | ||||
sensordata_brightness=plant_data["Brightness"]) | sensordata_brightness=plant_data["Brightness"]) | ||||
print("BACKEND_ACTION_CONFIGUREPLANT RECEIVED DATA: " + str(plant_data)) | |||||
logging.info("BACKEND_ACTION_CONFIGUREPLANT configure plant data received and inserted: " + str(plant_data)) | |||||
action_getalldata(client, userdata, message, mydatabase) | action_getalldata(client, userdata, message, mydatabase) | ||||
def action_deleteplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | def action_deleteplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): | ||||
delete_plant = json.loads(message.payload.decode("UTF-8")) | delete_plant = json.loads(message.payload.decode("UTF-8")) | ||||
mydatabase.delete_plant(plant_id=delete_plant) | mydatabase.delete_plant(plant_id=delete_plant) | ||||
print("BACKEND_ACTION_DELETEPLANT RECEIVED DATA: " + str(delete_plant)) | |||||
logging.info("BACKEND_ACTION_DELETEPLANT delete plant data received and deleted: " + str(delete_plant)) | |||||
action_getalldata(client, userdata, message, mydatabase) | action_getalldata(client, userdata, message, mydatabase) | ||||
"MaxCount": MAX_PLANT_COUNT | "MaxCount": MAX_PLANT_COUNT | ||||
} | } | ||||
client.publish(Topics["BACKEND_DATA_PLANTCOUNT"], json.dumps(count_payload)) | client.publish(Topics["BACKEND_DATA_PLANTCOUNT"], json.dumps(count_payload)) | ||||
print("BACKEND_DATA_PLANTCOUNT SEND DATA: " + str(count_payload)) | |||||
logging.info("BACKEND_DATA_PLANTCOUNT forwarded plant count to FrontEnd: " + str(count_payload)) | |||||
def data_error(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): | |||||
robot.store_last_error(message.payload.decode("UTF-8")) | |||||
logging.error("ROBOT_DATA_ERROR new error received from Robot: " + robot.get_last_error()) | |||||
client.publish(Topics['BACKEND_DATA_ERROR'], message.payload.decode("UTF-8")) | |||||
def data_robotready(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): | |||||
robot.change_robot_status(message.payload.decode("UTF-8") == 'True') | |||||
if robot.get_robot_status() is True and robot.get_order_number() >= 1: | |||||
client.publish(Topics['ROBOT_ACTION_DRIVE'], json.dumps(robot.get_next_order())) | |||||
logging.info("Waiting Order send to Robot") | |||||
logging.info("ROBOT_DATA_ROBOTREADY status updated: " + str(robot.get_robot_status())) | |||||
client.publish(Topics['BACKEND_DATA_ROBOTREADY'], message.payload.decode("UTF-8")) |
import paho.mqtt.client as mqtt | import paho.mqtt.client as mqtt | ||||
import software.defines | |||||
from software.defines import MQTT_BROKER_LOCAL | from software.defines import MQTT_BROKER_LOCAL | ||||
from random import randrange, uniform | from random import randrange, uniform | ||||
import time | import time | ||||
import json | import json | ||||
from software.defines import Topics, PLANTDATA | from software.defines import Topics, PLANTDATA | ||||
mqttBroker = "192.168.178.182" | |||||
mqttBroker = software.defines.MQTT_BROKER_GLOBAL | |||||
def on_connect(client, userdata, flags, rc): | def on_connect(client, userdata, flags, rc): | ||||
client.connect(mqttBroker) | client.connect(mqttBroker) | ||||
plantdata = { | plantdata = { | ||||
"AirTemperature": 20.4, | |||||
"AirHumidity": 7.0, | |||||
"SoilMoisture": 5.0, | |||||
"Brightness": 39, | |||||
"PlantID": 2, | |||||
"Timestamp": "hallo", | |||||
"MeasurementID": 187 | |||||
"PlantName": "Kemal" | |||||
} | } | ||||
print(type(PLANTDATA)) | print(type(PLANTDATA)) | ||||
while True: | |||||
client.publish("TEST", json.dumps(plantdata)) | |||||
print(json.dumps(plantdata)) | |||||
time.sleep(2) | |||||
client.publish("BACKEND/ACTION/GETBATTERY", json.dumps(plantdata)) | |||||
print(json.dumps(plantdata)) | |||||
time.sleep(2) |
from software.defines import MQTT_BROKER_LOCAL, MQTT_BROKER_GLOBAL, Topics, BACKEND_CLIENT_ID, DATABASE_NAME | from software.defines import MQTT_BROKER_LOCAL, MQTT_BROKER_GLOBAL, Topics, BACKEND_CLIENT_ID, DATABASE_NAME | ||||
from plantdatabase import PlantDataBase | from plantdatabase import PlantDataBase | ||||
import data_functions | import data_functions | ||||
import logging | |||||
import sys | |||||
from robot import Robot | |||||
# inits | |||||
mydatabase = PlantDataBase(database_name=DATABASE_NAME) | |||||
mydatabase.create_tables() | |||||
order_handler = [] # will contain UUIDS with Order IDs | |||||
def on_connect(_client: mqtt.Client, _userdata, _flags, _rc): | |||||
def on_connect(_client: mqtt.Client, _userdata, _flags, _rc, _mydatabase, _robot): | |||||
""" | """ | ||||
This method gets called, when it connects to a mqtt broker. | This method gets called, when it connects to a mqtt broker. | ||||
It is used to subscribe to the specific topics | It is used to subscribe to the specific topics | ||||
:param _robot: | |||||
:param _mydatabase: | |||||
:param _client: mqtt client object | :param _client: mqtt client object | ||||
:param _userdata: | :param _userdata: | ||||
:param _flags: | :param _flags: | ||||
# From Robot: | # From Robot: | ||||
_client.subscribe(Topics['ROBOT_DATA_SENSORDATA']) | _client.subscribe(Topics['ROBOT_DATA_SENSORDATA']) | ||||
_client.message_callback_add(Topics['ROBOT_DATA_SENSORDATA'], lambda client, userdata, message: data_functions. | _client.message_callback_add(Topics['ROBOT_DATA_SENSORDATA'], lambda client, userdata, message: data_functions. | ||||
data_sensordata(client, userdata, message, mydatabase, order_handler)) | |||||
data_sensordata(client, userdata, message, _mydatabase, _robot)) | |||||
_client.subscribe(Topics['ROBOT_DATA_POSITION']) | _client.subscribe(Topics['ROBOT_DATA_POSITION']) | ||||
_client.message_callback_add(Topics['ROBOT_DATA_POSITION'], data_functions.data_position) | _client.message_callback_add(Topics['ROBOT_DATA_POSITION'], data_functions.data_position) | ||||
_client.subscribe(Topics['ROBOT_DATA_BATTERY']) | _client.subscribe(Topics['ROBOT_DATA_BATTERY']) | ||||
_client.message_callback_add(Topics['ROBOT_DATA_BATTERY'], lambda client, userdata, message: data_functions. | _client.message_callback_add(Topics['ROBOT_DATA_BATTERY'], lambda client, userdata, message: data_functions. | ||||
data_battery(client, userdata, message, mydatabase)) | |||||
data_battery(client, userdata, message)) | |||||
# client.subscribe('Robot/Data/Picture') | # client.subscribe('Robot/Data/Picture') | ||||
# From FrontEnd: | # From FrontEnd: | ||||
_client.subscribe(Topics['BACKEND_ACTION_DRIVE']) | _client.subscribe(Topics['BACKEND_ACTION_DRIVE']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_DRIVE'], lambda client, userdata, message: data_functions. | _client.message_callback_add(Topics['BACKEND_ACTION_DRIVE'], lambda client, userdata, message: data_functions. | ||||
action_drive(client, userdata, message, mydatabase, order_handler)) | |||||
action_drive(client, userdata, message, _mydatabase, _robot)) | |||||
_client.subscribe(Topics['BACKEND_ACTION_DRIVEALL']) | _client.subscribe(Topics['BACKEND_ACTION_DRIVEALL']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_DRIVE'], lambda client, userdata, message: data_functions. | |||||
action_driveall(client, userdata, message, mydatabase)) | |||||
_client.message_callback_add(Topics['BACKEND_ACTION_DRIVEALL'], lambda client, userdata, message: data_functions. | |||||
action_driveall(client, userdata, message, _mydatabase, _robot)) | |||||
_client.subscribe(Topics['BACKEND_ACTION_GETPOSITION']) | _client.subscribe(Topics['BACKEND_ACTION_GETPOSITION']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_GETPOSITION'], data_functions.action_getposition) | _client.message_callback_add(Topics['BACKEND_ACTION_GETPOSITION'], data_functions.action_getposition) | ||||
_client.subscribe(Topics['BACKEND_ACTION_GETALLDATA']) | _client.subscribe(Topics['BACKEND_ACTION_GETALLDATA']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_GETALLDATA'], | _client.message_callback_add(Topics['BACKEND_ACTION_GETALLDATA'], | ||||
lambda client, userdata, message: data_functions. | lambda client, userdata, message: data_functions. | ||||
action_getalldata(client, userdata, message, mydatabase)) | |||||
action_getalldata(client, userdata, message, _mydatabase)) | |||||
_client.subscribe(Topics['BACKEND_ACTION_NEWPLANT']) | _client.subscribe(Topics['BACKEND_ACTION_NEWPLANT']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_NEWPLANT'], lambda client, userdata, message: data_functions. | _client.message_callback_add(Topics['BACKEND_ACTION_NEWPLANT'], lambda client, userdata, message: data_functions. | ||||
action_newplant(client, userdata, message, mydatabase)) | |||||
action_newplant(client, userdata, message, _mydatabase)) | |||||
_client.subscribe(Topics['BACKEND_ACTION_CONFIGUREPLANT']) | _client.subscribe(Topics['BACKEND_ACTION_CONFIGUREPLANT']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_CONFIGUREPLANT'], lambda client, userdata, message: data_functions. | _client.message_callback_add(Topics['BACKEND_ACTION_CONFIGUREPLANT'], lambda client, userdata, message: data_functions. | ||||
action_configureplant(client, userdata, message, mydatabase)) | |||||
action_configureplant(client, userdata, message, _mydatabase)) | |||||
_client.subscribe(Topics['BACKEND_ACTION_DELETEPLANT']) | _client.subscribe(Topics['BACKEND_ACTION_DELETEPLANT']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_DELETEPLANT'], | _client.message_callback_add(Topics['BACKEND_ACTION_DELETEPLANT'], | ||||
lambda client, userdata, message: data_functions. | lambda client, userdata, message: data_functions. | ||||
action_deleteplant(client, userdata, message, mydatabase)) | |||||
action_deleteplant(client, userdata, message, _mydatabase)) | |||||
_client.subscribe(Topics['BACKEND_ACTION_PLANTCOUNT']) | _client.subscribe(Topics['BACKEND_ACTION_PLANTCOUNT']) | ||||
_client.message_callback_add(Topics['BACKEND_ACTION_PLANTCOUNT'], lambda client, userdata, message: data_functions. | _client.message_callback_add(Topics['BACKEND_ACTION_PLANTCOUNT'], lambda client, userdata, message: data_functions. | ||||
action_countplants(client, userdata, message, mydatabase)) | |||||
action_countplants(client, userdata, message, _mydatabase)) | |||||
_client.subscribe(Topics['ROBOT_DATA_ERROR']) | |||||
_client.message_callback_add(Topics['ROBOT_DATA_ERROR'], lambda client, userdata, message: data_functions. | |||||
data_error(client, userdata, message, _robot)) | |||||
_client.subscribe(Topics['ROBOT_DATA_ROBOTREADY']) | |||||
_client.message_callback_add(Topics['ROBOT_DATA_ROBOTREADY'], lambda client, userdata, message: data_functions. | |||||
data_robotready(client, userdata, message, _robot)) | |||||
# END TOPIC SUBSCRIPTIONS | # END TOPIC SUBSCRIPTIONS | ||||
else: | else: | ||||
print("connection failed") | print("connection failed") | ||||
def main(): | def main(): | ||||
client = mqtt.Client(BACKEND_CLIENT_ID) | |||||
client.on_connect = on_connect | |||||
client.connect(MQTT_BROKER_GLOBAL) | |||||
client.loop_forever() | |||||
robot = Robot() | |||||
my_database = PlantDataBase(database_name=DATABASE_NAME) | |||||
my_database.create_tables() | |||||
mqttclient = mqtt.Client(BACKEND_CLIENT_ID) | |||||
mqttclient.on_connect = lambda client, userdata, flags, rc: on_connect(_client=client, | |||||
_userdata=userdata, | |||||
_flags=flags, | |||||
_rc=rc, | |||||
_mydatabase=my_database, | |||||
_robot=robot) | |||||
mqttclient.connect(MQTT_BROKER_GLOBAL) | |||||
logging.basicConfig(filename="server.log", filemode="a", encoding="utf-8", level=logging.DEBUG, | |||||
format='%(asctime)s %(name)s %(levelname)s %(message)s', | |||||
datefmt="%d-%m-%Y %H:%M:%S") | |||||
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) | |||||
mqttclient.loop_forever() | |||||
if __name__ == "__main__": | if __name__ == "__main__": |
# file to create a database via python script | # file to create a database via python script | ||||
import sqlite3 | import sqlite3 | ||||
from typing import Optional | from typing import Optional | ||||
import logging | |||||
class PlantDataBase: | class PlantDataBase: | ||||
self.conn = None | self.conn = None | ||||
try: | try: | ||||
self.conn = sqlite3.connect(self.db_file, check_same_thread=False) | self.conn = sqlite3.connect(self.db_file, check_same_thread=False) | ||||
print(sqlite3.version) | |||||
except sqlite3.Error as e: | except sqlite3.Error as e: | ||||
print(e) | |||||
logging.error("Database init error: " + str(e)) | |||||
self.cur = self.conn.cursor() | self.cur = self.conn.cursor() | ||||
def create_tables(self): | def create_tables(self): | ||||
self.cur.execute(table_config) | self.cur.execute(table_config) | ||||
return True | return True | ||||
except sqlite3.Warning as e: | except sqlite3.Warning as e: | ||||
return e | |||||
logging.error("Could not create tables: " + str(e)) | |||||
return False | |||||
def insert_plant(self, plantname: str, plant_id: int): | |||||
def insert_plant(self, plantname: str, plant_id: int) -> bool: | |||||
try: | try: | ||||
self.cur.execute("INSERT INTO plants (PlantName, PlantID) VALUES (?,?)", (plantname, plant_id)) | self.cur.execute("INSERT INTO plants (PlantName, PlantID) VALUES (?,?)", (plantname, plant_id)) | ||||
self.conn.commit() | self.conn.commit() | ||||
return True | return True | ||||
except (sqlite3.NotSupportedError, sqlite3.Warning) as e: | |||||
return e | |||||
except Exception as e: | |||||
logging.error("Could not insert plant: " + str(e)) | |||||
return False | |||||
def configure_plant(self, plant_id: int, plantname: str): | |||||
def configure_plant(self, plant_id: int, plantname: str) -> bool: | |||||
try: | try: | ||||
self.cur.execute("UPDATE plants SET PlantID = ?, PlantName = ? WHERE PlantID= ?", | self.cur.execute("UPDATE plants SET PlantID = ?, PlantName = ? WHERE PlantID= ?", | ||||
(plant_id, plantname, plant_id)) | (plant_id, plantname, plant_id)) | ||||
self.conn.commit() | self.conn.commit() | ||||
return True | return True | ||||
except (sqlite3.NotSupportedError, sqlite3.Warning) as e: | |||||
return e | |||||
except Exception as e: | |||||
logging.error("Could not configure plant: " + str(e)) | |||||
return False | |||||
def delete_plant(self, plant_id): | def delete_plant(self, plant_id): | ||||
try: | try: | ||||
self.cur.execute('DELETE FROM plants WHERE PlantID = ?', (plant_id,)) | self.cur.execute('DELETE FROM plants WHERE PlantID = ?', (plant_id,)) | ||||
self.conn.commit() | self.conn.commit() | ||||
return True | return True | ||||
except (sqlite3.NotSupportedError, sqlite3.Warning) as e: | |||||
return e | |||||
except Exception as e: | |||||
logging.error("Could not delete plant: " + str(e)) | |||||
return False | |||||
def insert_measurement_data(self, plant_id, | def insert_measurement_data(self, plant_id, | ||||
sensordata_temp, | sensordata_temp, | ||||
sensordata_brightness) -> bool: | sensordata_brightness) -> bool: | ||||
try: | try: | ||||
self.cur.execute(f"INSERT INTO measurement_values (PlantID, AirTemperature, AirHumidity," | 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})") | |||||
f"SoilMoisture, Brightness) VALUES " | |||||
f"({plant_id}, {sensordata_temp}, {sensordata_humidity}, {sensordata_soil_moisture}" | |||||
f", {sensordata_brightness})") | |||||
self.conn.commit() | self.conn.commit() | ||||
return True | return True | ||||
except (sqlite3.NotSupportedError, sqlite3.Warning) as e: | |||||
return e | |||||
except Exception as e: | |||||
logging.error("Could not insert measurement data: " + str(e)) | |||||
return False | |||||
def get_latest_data(self, plant_name: Optional[str] = None, plant_id: Optional[int] = None): | def get_latest_data(self, plant_name: Optional[str] = None, plant_id: Optional[int] = None): | ||||
""" | """ | ||||
"PlantName": plant_name | "PlantName": plant_name | ||||
} | } | ||||
return json_file | 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.conn.commit() | |||||
return True | |||||
except Exception as e: | |||||
logging.error("Could not get measurement values: " + str(e)) | |||||
# TODO: Kemals Scheiß implementieren | |||||
def delete_data(self, table_name): | |||||
try: | |||||
self.cur.execute(f'DELETE FROM {table_name}') | |||||
self.conn.commit() | |||||
return True | |||||
except Exception as e: | |||||
logging.error("Could not delete data: " + str(e)) | |||||
def plant_count(self) -> int: | def plant_count(self) -> int: | ||||
""" | """ | ||||
returns the number of plants registered in the database | returns the number of plants registered in the database | ||||
:return: | :return: | ||||
""" | """ | ||||
self.cur.execute("SELECT COUNT(*) FROM plants") | |||||
return self.cur.fetchone()[0] | |||||
try: | |||||
self.cur.execute("SELECT COUNT(*) FROM plants") | |||||
return self.cur.fetchone()[0] | |||||
except Exception as e: | |||||
logging.error("Could not count plants: " + str(e)) | |||||
def get_plant_names(self) -> list: | def get_plant_names(self) -> list: | ||||
self.cur.execute("SELECT PlantName FROM plants") | |||||
return self.cur.fetchall() | |||||
try: | |||||
self.cur.execute("SELECT PlantName FROM plants") | |||||
return self.cur.fetchall() | |||||
except Exception as e: | |||||
logging.error("Could not get plant names: " + str(e)) | |||||
def get_plant_id(self, plant_name: str) -> int: | |||||
try: | |||||
self.cur.execute("SELECT PlantID FROM plants WHERE PlantName=?", (plant_name,)) | |||||
return self.cur.fetchone()[0] | |||||
except Exception as e: | |||||
logging.error("Could not get plant id: " + str(e)) | |||||
def __del__(self): | def __del__(self): | ||||
self.conn.close() | self.conn.close() |
class Robot: | |||||
""" | |||||
This class contains the features of the robot. It is used as an interface for the main to avoid global variables and | |||||
store them instead in an instance of this robot object | |||||
""" | |||||
def __init__(self): | |||||
self.robot_ready = True | |||||
self.order_handler = [] | |||||
self.battery = 0 | |||||
self.position = "" | |||||
self.last_error = "" | |||||
def change_robot_status(self, status: bool): | |||||
self.robot_ready = status | |||||
def add_order(self, drivedata): | |||||
self.order_handler.append(drivedata) | |||||
def delete_order(self, drivedata): | |||||
self.order_handler.remove(drivedata) | |||||
def get_next_order(self): | |||||
return self.order_handler[0] | |||||
def get_order_number(self): | |||||
return len(self.order_handler) | |||||
def store_battery(self, battery): | |||||
self.battery = battery | |||||
def store_position(self, position): | |||||
self.position = position | |||||
def store_last_error(self, error): | |||||
self.last_error = error | |||||
def get_battery(self): | |||||
return self.battery | |||||
def get_position(self): | |||||
return self.position | |||||
def get_last_error(self): | |||||
return self.last_error | |||||
def get_robot_status(self): | |||||
return self.robot_ready |
"ROBOT_DATA_BATTERY": "ROBOT/DATA/BATTERY", | "ROBOT_DATA_BATTERY": "ROBOT/DATA/BATTERY", | ||||
"ROBOT_DATA_POSITION": "ROBOT/DATA/POSITION", | "ROBOT_DATA_POSITION": "ROBOT/DATA/POSITION", | ||||
"ROBOT_DATA_PICTURE": "ROBOT/DATA/PICTURE", | "ROBOT_DATA_PICTURE": "ROBOT/DATA/PICTURE", | ||||
"ROBOT_DATA_ERROR": "ROBOT/DATA/ERROR", | |||||
"ROBOT_DATA_ROBOTREADY": "ROBOT/DATA/ROBOTREADY", | |||||
"BACKEND_ACTION_DRIVE": "BACKEND/ACTION/DRIVE", | "BACKEND_ACTION_DRIVE": "BACKEND/ACTION/DRIVE", | ||||
"BACKEND_ACTION_DRIVEALL": "BACKEND/ACTION/DRIVEALL", | "BACKEND_ACTION_DRIVEALL": "BACKEND/ACTION/DRIVEALL", | ||||
"BACKEND_DATA_BATTERY": "BACKEND/DATA/BATTERY", | "BACKEND_DATA_BATTERY": "BACKEND/DATA/BATTERY", | ||||
"BACKEND_DATA_PICTURE": "BACKEND/DATA/PICTURE", | "BACKEND_DATA_PICTURE": "BACKEND/DATA/PICTURE", | ||||
"BACKEND_DATA_PLANTCOUNT": "BACKEND/DATA/PLANTCOUNT", | "BACKEND_DATA_PLANTCOUNT": "BACKEND/DATA/PLANTCOUNT", | ||||
"BACKEND_DATA_ERROR": "BACKEND/DATA/ERROR", | |||||
"BACKEND_DATA_ROBOTREADY": "BACKEND/DATA/ROBOTREADY" | |||||
} | } | ||||