|
|
@@ -3,6 +3,7 @@ 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 |
|
|
|
The functions are called, when data is received on the according channel |
|
|
|
""" |
|
|
|
import paho.mqtt.client as mqtt |
|
|
|
from plantdatabase import PlantDataBase |
|
|
@@ -19,8 +20,21 @@ from robot import Robot |
|
|
|
|
|
|
|
def data_sensordata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, |
|
|
|
robot: Robot): |
|
|
|
""" |
|
|
|
This function is used to store received data from the robot inside the plant_database |
|
|
|
USAGE FOR DATA OF ONE PLANT |
|
|
|
:param client: mqtt client |
|
|
|
:param userdata: |
|
|
|
:param message: received data |
|
|
|
:param mydatabase: database information |
|
|
|
:param robot: robot object |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Load the message and convert to json |
|
|
|
str_in = str(message.payload.decode("UTF-8")) |
|
|
|
payload = json.loads(str_in) |
|
|
|
|
|
|
|
logging.info("ROBOT_DATA_SENSORDATA Received data: " + json.dumps(payload)) |
|
|
|
drive_data = { |
|
|
|
"PlantID": [payload['PlantID']], |
|
|
@@ -28,86 +42,151 @@ def data_sensordata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, my |
|
|
|
} |
|
|
|
|
|
|
|
try: |
|
|
|
print(drive_data) |
|
|
|
print(robot.order_handler) |
|
|
|
# Delete order from order list |
|
|
|
robot.delete_order(drive_data) |
|
|
|
# Save data in database |
|
|
|
mydatabase.insert_measurement_data(plant_id=payload['PlantID'], |
|
|
|
sensordata_temp=payload['AirTemperature'], |
|
|
|
sensordata_humidity=payload['AirHumidity'], |
|
|
|
sensordata_soil_moisture=payload['SoilMoisture'], |
|
|
|
sensordata_brightness=payload['Brightness']) |
|
|
|
logging.debug("Inserted to data base: " + json.dumps(payload)) |
|
|
|
|
|
|
|
# Send received data to frontend |
|
|
|
action_getalldata(client, userdata, message, mydatabase) |
|
|
|
except Exception as e: |
|
|
|
logging.error("Could not delete order: " + str(e)) |
|
|
|
|
|
|
|
|
|
|
|
def data_sensordataall(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, |
|
|
|
robot: Robot): |
|
|
|
robot: Robot): |
|
|
|
""" |
|
|
|
This function is used to store received data from the robot inside the plant_database |
|
|
|
USAGE FOR DATA OF ALL PLANTS |
|
|
|
:param client: |
|
|
|
:param userdata: |
|
|
|
:param message: |
|
|
|
:param mydatabase: |
|
|
|
:param robot: |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Load the message and convert to json |
|
|
|
str_in = str(message.payload.decode("UTF-8")) |
|
|
|
payload = json.loads(str_in) |
|
|
|
logging.info("ROBOT_DATA_SENSORDATAALL Received data: " + json.dumps(payload)) |
|
|
|
|
|
|
|
# Create list of plant_ids and create dataset |
|
|
|
plant_ids = [] |
|
|
|
|
|
|
|
for i in payload['SensorData']: |
|
|
|
plant_ids.append(i["PlantID"]) |
|
|
|
print("Plant Names:", str(plant_ids)) |
|
|
|
drive_data = { |
|
|
|
"PlantID": plant_ids, |
|
|
|
"ActionID": payload['ActionID'] |
|
|
|
} |
|
|
|
|
|
|
|
try: |
|
|
|
print(robot.order_handler) |
|
|
|
print(drive_data) |
|
|
|
# Delete order from order list |
|
|
|
robot.delete_order(drive_data) |
|
|
|
|
|
|
|
# Insert all received data files in plant_database |
|
|
|
for i in payload['SensorData']: |
|
|
|
mydatabase.insert_measurement_data(plant_id=i['PlantID'], |
|
|
|
sensordata_temp=i['AirTemperature'], |
|
|
|
sensordata_humidity=i['AirHumidity'], |
|
|
|
sensordata_soil_moisture=i['SoilMoisture'], |
|
|
|
sensordata_brightness=i['Brightness']) |
|
|
|
sensordata_temp=i['AirTemperature'], |
|
|
|
sensordata_humidity=i['AirHumidity'], |
|
|
|
sensordata_soil_moisture=i['SoilMoisture'], |
|
|
|
sensordata_brightness=i['Brightness']) |
|
|
|
logging.debug("Inserted to data base: " + json.dumps(payload)) |
|
|
|
|
|
|
|
# Send all the plant data to the frontend |
|
|
|
action_getalldata(client, userdata, message, mydatabase) |
|
|
|
except Exception as e: |
|
|
|
logging.error("Could not delete order: " + str(e)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def data_position(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): |
|
|
|
""" |
|
|
|
This function is used to receive the robot position and insert it in the robot object |
|
|
|
:param client: mqtt client |
|
|
|
:param userdata: |
|
|
|
:param message: received data |
|
|
|
:param robot: robot object to store position |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
logging.info("ROBOT_DATA_POSITION Received data: " + json.dumps(message.payload.decode("UTF-8"))) |
|
|
|
|
|
|
|
# Store received position data in robot object |
|
|
|
robot.store_position(json.loads(message.payload.decode("UTF-8"))["Position"]) |
|
|
|
position_data = { |
|
|
|
"Position": robot.get_position(), |
|
|
|
"Timestamp": str(datetime.now()) |
|
|
|
} |
|
|
|
# Send the position as a json object to the frontend channel |
|
|
|
client.publish(Topics['BACKEND_DATA_POSITION'], json.dumps(position_data)) |
|
|
|
|
|
|
|
|
|
|
|
def data_battery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): |
|
|
|
""" |
|
|
|
This function is used to receive the robot position and insert it in the robot object |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: received data |
|
|
|
:param robot: robot object to store the received information |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
logging.info("ROBOT_DATA_BATTERY Received data: " + str(json.dumps(message.payload.decode("UTF-8")))) |
|
|
|
# Store battery status in robot object |
|
|
|
robot.store_battery(json.loads(message.payload.decode("UTF-8"))["Battery"]) |
|
|
|
battery_data = { |
|
|
|
"Battery": robot.get_battery(), |
|
|
|
"Timestamp": str(datetime.now()) |
|
|
|
} |
|
|
|
# Send Battery status and Robot-Ready Status as json objects to frontend |
|
|
|
client.publish(Topics['BACKEND_DATA_BATTERY'], json.dumps(battery_data)) |
|
|
|
client.publish(Topics['BACKEND_DATA_ROBOTREADY'], str(robot.get_robot_status())) |
|
|
|
|
|
|
|
|
|
|
|
def data_error(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): |
|
|
|
""" |
|
|
|
This function is called when the robot sends an error message and forwards it to the frontend |
|
|
|
:param client: mqtt client |
|
|
|
:param userdata: |
|
|
|
:param message: received error message |
|
|
|
:param robot: robot objectg |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Store last error in robot object |
|
|
|
robot.store_last_error(message.payload.decode("UTF-8")) |
|
|
|
# Write error into server log |
|
|
|
logging.error("ROBOT_DATA_ERROR new error received from Robot: " + robot.get_last_error()) |
|
|
|
# Send error data to FrontEnd Channel to display it to the user |
|
|
|
client.publish(Topics['BACKEND_DATA_ERROR'], message.payload.decode("UTF-8")) |
|
|
|
|
|
|
|
|
|
|
|
def data_robotready(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot): |
|
|
|
""" |
|
|
|
This function is used to update the Robot-Ready Status of the Robot and inform the FrontEnd about it |
|
|
|
:param client: mqtt client |
|
|
|
:param userdata: |
|
|
|
:param message: received data |
|
|
|
:param robot: robot object |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Update the robot status |
|
|
|
robot.change_robot_status(message.payload.decode("UTF-8") == 'True') |
|
|
|
|
|
|
|
# If possible send new waiting order to the robot |
|
|
|
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())) |
|
|
|
|
|
|
|
# Send new robot-ready status to frontend channel |
|
|
|
client.publish(Topics['BACKEND_DATA_ROBOTREADY'], str(robot.get_robot_status())) |
|
|
|
|
|
|
|
|
|
|
@@ -115,43 +194,72 @@ def data_robotready(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, ro |
|
|
|
|
|
|
|
def action_drive(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase, |
|
|
|
robot: Robot): |
|
|
|
""" |
|
|
|
This function is called when a drive command from the FrontEnd is received and forwards the order to the robot or |
|
|
|
stores it in the order list. |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: information of plant to drive to |
|
|
|
:param mydatabase: plant_database |
|
|
|
:param robot: robot object |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
# Get PlantID from received PlantName |
|
|
|
plant_id = mydatabase.get_plant_id(plant_name=json.loads(str(message.payload.decode("UTF-8")))) |
|
|
|
print(str(plant_id)) |
|
|
|
|
|
|
|
# Generate a new ActionID |
|
|
|
action_id = str(uuid.uuid4()) |
|
|
|
drive_data = { |
|
|
|
"PlantID": plant_id, |
|
|
|
"ActionID": action_id |
|
|
|
} |
|
|
|
|
|
|
|
# Store order in order list or discard if list already contains 5 orders |
|
|
|
if robot.get_order_number() < 6 and robot.get_robot_status() is True: |
|
|
|
robot.add_order({"PlantID": [plant_id], "ActionID": action_id}) |
|
|
|
# Send order to robot, if robot is available |
|
|
|
client.publish(Topics['ROBOT_ACTION_DRIVE'], json.dumps(drive_data)) |
|
|
|
logging.info("BACKEND_ACTION_DRIVE Drive Command published: " + json.dumps(drive_data)) |
|
|
|
else: |
|
|
|
if robot.get_order_number() < 6: |
|
|
|
# Add to order list if robot not available |
|
|
|
robot.add_order(drive_data) |
|
|
|
logging.info("BACKEND_ACTION_DRIVE New data added to order list: " + str(drive_data)) |
|
|
|
elif robot.get_order_number() >= 6: |
|
|
|
# Discard order if list full |
|
|
|
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, |
|
|
|
robot: Robot): |
|
|
|
""" |
|
|
|
This function is called when the frontend sends a drive_all command which lets the robot drive to all registered plants. |
|
|
|
Same as action_drive(), but for all plants. |
|
|
|
:param client: |
|
|
|
:param userdata: |
|
|
|
:param message: |
|
|
|
:param mydatabase: |
|
|
|
:param robot: |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Get all plantnames from the database and extract the id from them |
|
|
|
plant_names = mydatabase.get_plant_names() |
|
|
|
plant_ids = [] |
|
|
|
print(plant_names) |
|
|
|
|
|
|
|
for names in plant_names: |
|
|
|
_id = mydatabase.get_plant_id(names[0]) |
|
|
|
plant_ids.append(_id) |
|
|
|
|
|
|
|
# Create a new order number |
|
|
|
action_id = str(uuid.uuid4()) |
|
|
|
drive_data = { |
|
|
|
"PlantID": plant_ids, |
|
|
|
"ActionID": action_id |
|
|
|
} |
|
|
|
print(drive_data) |
|
|
|
|
|
|
|
# Send drive command to Robot if possible (same as action_drive()) |
|
|
|
if robot.get_order_number() < 6 and robot.get_robot_status() is True: |
|
|
|
robot.add_order(drive_data) |
|
|
|
client.publish(Topics['ROBOT_ACTION_DRIVEALL'], json.dumps(drive_data)) |
|
|
@@ -166,64 +274,153 @@ def action_driveall(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, my |
|
|
|
|
|
|
|
|
|
|
|
def action_getposition(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): |
|
|
|
""" |
|
|
|
This function is called when the frontend demands the robots position from the backend. It forwards the command to |
|
|
|
the robot. |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: |
|
|
|
:param mydatabase: |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Send command to robot |
|
|
|
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): |
|
|
|
""" |
|
|
|
This function is called when the frontend demands the robots battery status from the backend. It forwards the |
|
|
|
command to the robot to get new information. |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Send command to robot |
|
|
|
client.publish(Topics['ROBOT_ACTION_GETBATTERY']) |
|
|
|
logging.info("BACKEND_ACTION_GETBATTERY message forwarded to Robot") |
|
|
|
battery_data = { |
|
|
|
"Battery": 66, |
|
|
|
"Timestamp": str(datetime.now()) |
|
|
|
} |
|
|
|
print(battery_data) |
|
|
|
client.publish(Topics['BACKEND_DATA_BATTERY'], json.dumps(battery_data)) |
|
|
|
|
|
|
|
|
|
|
|
def action_getalldata(client: mqtt.Client, userdata, message: Union[mqtt.MQTTMessage, list], mydatabase: PlantDataBase): |
|
|
|
""" |
|
|
|
This function is called when the frontend demands the last data of the registered plants. It gets the last data from |
|
|
|
the local database and forwards it to the frontend |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: |
|
|
|
:param mydatabase: database object, where the plant data is stored |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# get the all PlantNames |
|
|
|
plant_names = mydatabase.get_plant_names() |
|
|
|
print("SUIII" + str(plant_names)) |
|
|
|
alldata = [] |
|
|
|
|
|
|
|
# Get the latest data from all registered plant names by using the plant names |
|
|
|
for i in plant_names: |
|
|
|
alldata.append(mydatabase.get_latest_data(plant_name=i[0])) |
|
|
|
|
|
|
|
# Send the data as a list to the frontends channel |
|
|
|
client.publish(Topics['BACKEND_DATA_SENSORDATAALL'], json.dumps(alldata)) |
|
|
|
logging.info("BACKEND_DATA_SENSORDATAALL got data from database:" + str(alldata)) |
|
|
|
|
|
|
|
|
|
|
|
def action_newplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): |
|
|
|
""" |
|
|
|
This function is called when the frontend wants to register a new plant. The information of the new plant is |
|
|
|
delivered from the frontend and used to register the plant |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: data from the frontend |
|
|
|
:param mydatabase: local database |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Load the plant data as json |
|
|
|
plant_data = json.loads(message.payload.decode("UTF-8")) |
|
|
|
|
|
|
|
# Insert the plant in the database |
|
|
|
mydatabase.insert_plant(plantname=plant_data["PlantName"], plant_id=plant_data["PlantID"]) |
|
|
|
|
|
|
|
# Insert a first measurement value in the database |
|
|
|
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"]) |
|
|
|
logging.info("BACKEND_ACTION_NEWPLANT new plant data received and inserted: " + str(plant_data)) |
|
|
|
|
|
|
|
# Send all new plant data to the frontend to update it |
|
|
|
action_getalldata(client, userdata, message, mydatabase) |
|
|
|
|
|
|
|
|
|
|
|
def action_configureplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): |
|
|
|
""" |
|
|
|
This function is called when a parameter of a plant is changed by the frontend. It updates the information in the |
|
|
|
database and sends the updated data to the frontend |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: received data from frontend |
|
|
|
:param mydatabase: local database |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Load the received data as json |
|
|
|
plant_data = json.loads(message.payload.decode("UTF-8")) |
|
|
|
|
|
|
|
# Update the plant in the database |
|
|
|
mydatabase.configure_plant(plant_id=plant_data["PlantID"], plantname=plant_data["PlantName"]) |
|
|
|
# Insert measurement_data into the database (from frontend) |
|
|
|
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"]) |
|
|
|
logging.info("BACKEND_ACTION_CONFIGUREPLANT configure plant data received and inserted: " + str(plant_data)) |
|
|
|
|
|
|
|
# Update the frontend with the current data |
|
|
|
action_getalldata(client, userdata, message, mydatabase) |
|
|
|
|
|
|
|
|
|
|
|
def action_deleteplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): |
|
|
|
""" |
|
|
|
This function is called when the frontend wants to delete a registered plant from the database. |
|
|
|
:param client: |
|
|
|
:param userdata: |
|
|
|
:param message: Received data from the frontend |
|
|
|
:param mydatabase: local database object |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Load the Plant-Name from the message |
|
|
|
delete_plant = json.loads(message.payload.decode("UTF-8")) |
|
|
|
|
|
|
|
# Delete the plant |
|
|
|
mydatabase.delete_plant(plant_id=delete_plant) |
|
|
|
logging.info("BACKEND_ACTION_DELETEPLANT delete plant data received and deleted: " + str(delete_plant)) |
|
|
|
|
|
|
|
# Send current data to frontend to update it |
|
|
|
action_getalldata(client, userdata, message, mydatabase) |
|
|
|
|
|
|
|
|
|
|
|
def action_countplants(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase): |
|
|
|
""" |
|
|
|
This function is called when the frontend requires the count of the currently registerd plants. It sends the number |
|
|
|
and maximal possible plant number to the frontend as a json object |
|
|
|
:param client: mqtt client object |
|
|
|
:param userdata: |
|
|
|
:param message: |
|
|
|
:param mydatabase: local database |
|
|
|
:return: none |
|
|
|
""" |
|
|
|
|
|
|
|
# Count plants |
|
|
|
count = mydatabase.plant_count() |
|
|
|
|
|
|
|
# Create Object and send to the FrontEnd |
|
|
|
count_payload = { |
|
|
|
"CurrentCount": count, |
|
|
|
"MaxCount": MAX_PLANT_COUNT |