backend code fully commented
This commit is contained in:
parent
90235a73f1
commit
22bcf7310f
@ -3,6 +3,7 @@ created by caliskan at 19.04.2023
|
|||||||
|
|
||||||
This file contains all functions, which handle the different cases.
|
This file contains all functions, which handle the different cases.
|
||||||
Every function should return json format with the wanted data from the database
|
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
|
import paho.mqtt.client as mqtt
|
||||||
from plantdatabase import PlantDataBase
|
from plantdatabase import PlantDataBase
|
||||||
@ -19,8 +20,21 @@ from robot import Robot
|
|||||||
|
|
||||||
def data_sensordata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase,
|
def data_sensordata(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 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"))
|
str_in = str(message.payload.decode("UTF-8"))
|
||||||
payload = json.loads(str_in)
|
payload = json.loads(str_in)
|
||||||
|
|
||||||
logging.info("ROBOT_DATA_SENSORDATA Received data: " + json.dumps(payload))
|
logging.info("ROBOT_DATA_SENSORDATA Received data: " + json.dumps(payload))
|
||||||
drive_data = {
|
drive_data = {
|
||||||
"PlantID": [payload['PlantID']],
|
"PlantID": [payload['PlantID']],
|
||||||
@ -28,39 +42,54 @@ def data_sensordata(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, my
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print(drive_data)
|
# Delete order from order list
|
||||||
print(robot.order_handler)
|
|
||||||
robot.delete_order(drive_data)
|
robot.delete_order(drive_data)
|
||||||
|
# Save data in database
|
||||||
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))
|
logging.debug("Inserted to data base: " + json.dumps(payload))
|
||||||
|
|
||||||
|
# Send received data to frontend
|
||||||
action_getalldata(client, userdata, message, mydatabase)
|
action_getalldata(client, userdata, message, mydatabase)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Could not delete order: " + str(e))
|
logging.error("Could not delete order: " + str(e))
|
||||||
|
|
||||||
|
|
||||||
def data_sensordataall(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase,
|
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"))
|
str_in = str(message.payload.decode("UTF-8"))
|
||||||
payload = json.loads(str_in)
|
payload = json.loads(str_in)
|
||||||
logging.info("ROBOT_DATA_SENSORDATAALL Received data: " + json.dumps(payload))
|
logging.info("ROBOT_DATA_SENSORDATAALL Received data: " + json.dumps(payload))
|
||||||
|
|
||||||
|
# Create list of plant_ids and create dataset
|
||||||
plant_ids = []
|
plant_ids = []
|
||||||
|
|
||||||
for i in payload['SensorData']:
|
for i in payload['SensorData']:
|
||||||
plant_ids.append(i["PlantID"])
|
plant_ids.append(i["PlantID"])
|
||||||
print("Plant Names:", str(plant_ids))
|
|
||||||
drive_data = {
|
drive_data = {
|
||||||
"PlantID": plant_ids,
|
"PlantID": plant_ids,
|
||||||
"ActionID": payload['ActionID']
|
"ActionID": payload['ActionID']
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print(robot.order_handler)
|
# Delete order from order list
|
||||||
print(drive_data)
|
|
||||||
robot.delete_order(drive_data)
|
robot.delete_order(drive_data)
|
||||||
|
|
||||||
|
# Insert all received data files in plant_database
|
||||||
for i in payload['SensorData']:
|
for i in payload['SensorData']:
|
||||||
mydatabase.insert_measurement_data(plant_id=i['PlantID'],
|
mydatabase.insert_measurement_data(plant_id=i['PlantID'],
|
||||||
sensordata_temp=i['AirTemperature'],
|
sensordata_temp=i['AirTemperature'],
|
||||||
@ -68,46 +97,96 @@ def data_sensordataall(client: mqtt.Client, userdata, message: mqtt.MQTTMessage,
|
|||||||
sensordata_soil_moisture=i['SoilMoisture'],
|
sensordata_soil_moisture=i['SoilMoisture'],
|
||||||
sensordata_brightness=i['Brightness'])
|
sensordata_brightness=i['Brightness'])
|
||||||
logging.debug("Inserted to data base: " + json.dumps(payload))
|
logging.debug("Inserted to data base: " + json.dumps(payload))
|
||||||
|
|
||||||
|
# Send all the plant data to the frontend
|
||||||
action_getalldata(client, userdata, message, mydatabase)
|
action_getalldata(client, userdata, message, mydatabase)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Could not delete order: " + str(e))
|
logging.error("Could not delete order: " + str(e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def data_position(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot):
|
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")))
|
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"])
|
robot.store_position(json.loads(message.payload.decode("UTF-8"))["Position"])
|
||||||
position_data = {
|
position_data = {
|
||||||
"Position": robot.get_position(),
|
"Position": robot.get_position(),
|
||||||
"Timestamp": str(datetime.now())
|
"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))
|
client.publish(Topics['BACKEND_DATA_POSITION'], json.dumps(position_data))
|
||||||
|
|
||||||
|
|
||||||
def data_battery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot):
|
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"))))
|
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"])
|
robot.store_battery(json.loads(message.payload.decode("UTF-8"))["Battery"])
|
||||||
battery_data = {
|
battery_data = {
|
||||||
"Battery": robot.get_battery(),
|
"Battery": robot.get_battery(),
|
||||||
"Timestamp": str(datetime.now())
|
"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_BATTERY'], json.dumps(battery_data))
|
||||||
client.publish(Topics['BACKEND_DATA_ROBOTREADY'], str(robot.get_robot_status()))
|
client.publish(Topics['BACKEND_DATA_ROBOTREADY'], str(robot.get_robot_status()))
|
||||||
|
|
||||||
|
|
||||||
def data_error(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot):
|
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"))
|
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())
|
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"))
|
client.publish(Topics['BACKEND_DATA_ERROR'], message.payload.decode("UTF-8"))
|
||||||
|
|
||||||
|
|
||||||
def data_robotready(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, robot: Robot):
|
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')
|
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:
|
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()))
|
client.publish(Topics['ROBOT_ACTION_DRIVE'], json.dumps(robot.get_next_order()))
|
||||||
logging.info("Waiting Order send to Robot")
|
logging.info("Waiting Order send to Robot")
|
||||||
|
|
||||||
logging.info("ROBOT_DATA_ROBOTREADY status updated: " + str(robot.get_robot_status()))
|
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()))
|
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,
|
def action_drive(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase,
|
||||||
robot: Robot):
|
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"))))
|
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())
|
action_id = str(uuid.uuid4())
|
||||||
drive_data = {
|
drive_data = {
|
||||||
"PlantID": plant_id,
|
"PlantID": plant_id,
|
||||||
"ActionID": action_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:
|
if robot.get_order_number() < 6 and robot.get_robot_status() is True:
|
||||||
robot.add_order({"PlantID": [plant_id], "ActionID": action_id})
|
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))
|
client.publish(Topics['ROBOT_ACTION_DRIVE'], json.dumps(drive_data))
|
||||||
logging.info("BACKEND_ACTION_DRIVE Drive Command published: " + json.dumps(drive_data))
|
logging.info("BACKEND_ACTION_DRIVE Drive Command published: " + json.dumps(drive_data))
|
||||||
else:
|
else:
|
||||||
if robot.get_order_number() < 6:
|
if robot.get_order_number() < 6:
|
||||||
|
# Add to order list if robot not available
|
||||||
robot.add_order(drive_data)
|
robot.add_order(drive_data)
|
||||||
logging.info("BACKEND_ACTION_DRIVE New data added to order list: " + str(drive_data))
|
logging.info("BACKEND_ACTION_DRIVE New data added to order list: " + str(drive_data))
|
||||||
elif robot.get_order_number() >= 6:
|
elif robot.get_order_number() >= 6:
|
||||||
|
# Discard order if list full
|
||||||
logging.error("Could not add Order to list. Order discarded")
|
logging.error("Could not add Order to list. Order discarded")
|
||||||
client.publish(Topics['BACKEND_DATA_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):
|
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_names = mydatabase.get_plant_names()
|
||||||
plant_ids = []
|
plant_ids = []
|
||||||
print(plant_names)
|
|
||||||
|
|
||||||
for names in plant_names:
|
for names in plant_names:
|
||||||
_id = mydatabase.get_plant_id(names[0])
|
_id = mydatabase.get_plant_id(names[0])
|
||||||
plant_ids.append(_id)
|
plant_ids.append(_id)
|
||||||
|
|
||||||
|
# Create a new order number
|
||||||
action_id = str(uuid.uuid4())
|
action_id = str(uuid.uuid4())
|
||||||
drive_data = {
|
drive_data = {
|
||||||
"PlantID": plant_ids,
|
"PlantID": plant_ids,
|
||||||
"ActionID": action_id
|
"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:
|
if robot.get_order_number() < 6 and robot.get_robot_status() is True:
|
||||||
robot.add_order(drive_data)
|
robot.add_order(drive_data)
|
||||||
client.publish(Topics['ROBOT_ACTION_DRIVEALL'], json.dumps(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):
|
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'])
|
client.publish(Topics['ROBOT_ACTION_GETPOSITION'])
|
||||||
logging.info("BACKEND_ACTION_GETPOSITION message forwarded to Robot")
|
logging.info("BACKEND_ACTION_GETPOSITION message forwarded to Robot")
|
||||||
|
|
||||||
|
|
||||||
def action_getbattery(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
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'])
|
client.publish(Topics['ROBOT_ACTION_GETBATTERY'])
|
||||||
logging.info("BACKEND_ACTION_GETBATTERY message forwarded to Robot")
|
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):
|
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()
|
plant_names = mydatabase.get_plant_names()
|
||||||
print("SUIII" + str(plant_names))
|
|
||||||
alldata = []
|
alldata = []
|
||||||
|
|
||||||
|
# Get the latest data from all registered plant names by using the plant names
|
||||||
for i in plant_names:
|
for i in plant_names:
|
||||||
alldata.append(mydatabase.get_latest_data(plant_name=i[0]))
|
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))
|
client.publish(Topics['BACKEND_DATA_SENSORDATAALL'], json.dumps(alldata))
|
||||||
logging.info("BACKEND_DATA_SENSORDATAALL got data from database:" + 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):
|
||||||
|
"""
|
||||||
|
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"))
|
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"])
|
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"],
|
mydatabase.insert_measurement_data(plant_id=plant_data["PlantID"],
|
||||||
sensordata_temp=plant_data["AirTemperature"],
|
sensordata_temp=plant_data["AirTemperature"],
|
||||||
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"])
|
||||||
logging.info("BACKEND_ACTION_NEWPLANT new plant data received and inserted: " + str(plant_data))
|
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)
|
action_getalldata(client, userdata, message, mydatabase)
|
||||||
|
|
||||||
|
|
||||||
def action_configureplant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
|
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"))
|
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"])
|
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"],
|
mydatabase.insert_measurement_data(plant_id=plant_data["PlantID"],
|
||||||
sensordata_temp=plant_data["AirTemperature"],
|
sensordata_temp=plant_data["AirTemperature"],
|
||||||
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"])
|
||||||
logging.info("BACKEND_ACTION_CONFIGUREPLANT configure plant data received and inserted: " + str(plant_data))
|
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)
|
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):
|
||||||
|
"""
|
||||||
|
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_plant = json.loads(message.payload.decode("UTF-8"))
|
||||||
|
|
||||||
|
# Delete the plant
|
||||||
mydatabase.delete_plant(plant_id=delete_plant)
|
mydatabase.delete_plant(plant_id=delete_plant)
|
||||||
logging.info("BACKEND_ACTION_DELETEPLANT delete plant data received and deleted: " + str(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)
|
action_getalldata(client, userdata, message, mydatabase)
|
||||||
|
|
||||||
|
|
||||||
def action_countplants(client: mqtt.Client, userdata, message: mqtt.MQTTMessage, mydatabase: PlantDataBase):
|
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()
|
count = mydatabase.plant_count()
|
||||||
|
|
||||||
|
# Create Object and send to the FrontEnd
|
||||||
count_payload = {
|
count_payload = {
|
||||||
"CurrentCount": count,
|
"CurrentCount": count,
|
||||||
"MaxCount": MAX_PLANT_COUNT
|
"MaxCount": MAX_PLANT_COUNT
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
created by caliskan at 19.04.2023
|
created by caliskan at 19.04.2023
|
||||||
|
|
||||||
contains all constants for the backend architecture of the smart garden project
|
contains all constants for the backend architecture of the smart garden project. This file contains no executable script
|
||||||
|
and is only for documentation purpose
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MQTT_BROKER_LOCAL = "192.168.0.102"
|
MQTT_BROKER_LOCAL = "192.168.0.102"
|
||||||
|
@ -21,13 +21,13 @@ 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 _robot: robot object for
|
||||||
:param _mydatabase:
|
:param _mydatabase: object, that contains the custom plant database and methods for its usage
|
||||||
:param _client: mqtt client object
|
:param _client: mqtt client object
|
||||||
:param _userdata:
|
:param _userdata:
|
||||||
:param _flags:
|
:param _flags:
|
||||||
:param _rc: connection flag
|
:param _rc: connection flag
|
||||||
:return:
|
:return: none
|
||||||
"""
|
"""
|
||||||
if _rc == 0:
|
if _rc == 0:
|
||||||
print("connected")
|
print("connected")
|
||||||
@ -100,10 +100,15 @@ def on_connect(_client: mqtt.Client, _userdata, _flags, _rc, _mydatabase, _robot
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# Create Robot Object
|
||||||
robot = Robot()
|
robot = Robot()
|
||||||
|
|
||||||
|
# Connect to Plant_Database and create tables if database did not exist
|
||||||
my_database = PlantDataBase(database_name=DATABASE_NAME)
|
my_database = PlantDataBase(database_name=DATABASE_NAME)
|
||||||
my_database.create_tables()
|
my_database.create_tables()
|
||||||
mqttclient = mqtt.Client(BACKEND_CLIENT_ID, transport="websockets")
|
|
||||||
|
# Create MQTT Client and connect to local broker
|
||||||
|
mqttclient = mqtt.Client(BACKEND_CLIENT_ID, transport="websockets") # transport websockets required for local broker
|
||||||
mqttclient.on_connect = lambda client, userdata, flags, rc: on_connect(_client=client,
|
mqttclient.on_connect = lambda client, userdata, flags, rc: on_connect(_client=client,
|
||||||
_userdata=userdata,
|
_userdata=userdata,
|
||||||
_flags=flags,
|
_flags=flags,
|
||||||
@ -111,10 +116,14 @@ def main():
|
|||||||
_mydatabase=my_database,
|
_mydatabase=my_database,
|
||||||
_robot=robot)
|
_robot=robot)
|
||||||
mqttclient.connect(MQTT_BROKER_LOCAL)
|
mqttclient.connect(MQTT_BROKER_LOCAL)
|
||||||
|
|
||||||
|
# Initialize logger and save in server.log file
|
||||||
logging.basicConfig(filename="server.log", filemode="a", encoding="utf-8", level=logging.DEBUG,
|
logging.basicConfig(filename="server.log", filemode="a", encoding="utf-8", level=logging.DEBUG,
|
||||||
format='%(asctime)s %(name)s %(levelname)s %(message)s',
|
format='%(asctime)s %(name)s %(levelname)s %(message)s',
|
||||||
datefmt="%d-%m-%Y %H:%M:%S")
|
datefmt="%d-%m-%Y %H:%M:%S")
|
||||||
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
|
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
|
||||||
|
|
||||||
|
# Starting mqttclient infinite loop
|
||||||
mqttclient.loop_forever()
|
mqttclient.loop_forever()
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,25 +6,43 @@ import logging
|
|||||||
|
|
||||||
class PlantDataBase:
|
class PlantDataBase:
|
||||||
"""
|
"""
|
||||||
Class to create Makeathon database
|
Class of a PlantDataBase Object. It contains functions specifically for the plantdatabase.
|
||||||
|
Usage:
|
||||||
|
- First declare object
|
||||||
|
- Then use create_tables() to create the tables (NECESSARY !!)
|
||||||
|
- After that use the methods of this class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, database_name: str):
|
def __init__(self, database_name: str):
|
||||||
self.db_file = database_name # 'backend_database.db'
|
"""
|
||||||
|
Only Constructor of the Class. Pass name of database to connect to. If not available it will create a new one.
|
||||||
|
:param database_name: Name of the SQLITE database file system
|
||||||
|
"""
|
||||||
|
self.db_file = database_name # 'backend_database.db' usually used name
|
||||||
self.conn = None
|
self.conn = None
|
||||||
try:
|
try:
|
||||||
|
# connect or create new database if not available
|
||||||
self.conn = sqlite3.connect(self.db_file, check_same_thread=False)
|
self.conn = sqlite3.connect(self.db_file, check_same_thread=False)
|
||||||
except sqlite3.Error as e:
|
except sqlite3.Error as e:
|
||||||
logging.error("Database init error: " + str(e))
|
logging.error("Database init error: " + str(e))
|
||||||
|
|
||||||
|
# cursor on the database
|
||||||
self.cur = self.conn.cursor()
|
self.cur = self.conn.cursor()
|
||||||
|
|
||||||
def create_tables(self):
|
def create_tables(self):
|
||||||
|
"""
|
||||||
|
Use this method to create the plants and measurement_values tables. Call this function before using the data
|
||||||
|
handling methods below
|
||||||
|
:return: True if successfully, False if not
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Create plants table if not already existing
|
||||||
table_config = "CREATE TABLE IF NOT EXISTS plants " \
|
table_config = "CREATE TABLE IF NOT EXISTS plants " \
|
||||||
"(PlantID INTEGER PRIMARY KEY," \
|
"(PlantID INTEGER PRIMARY KEY," \
|
||||||
"PlantName TEXT)"
|
"PlantName TEXT)"
|
||||||
self.cur.execute(table_config)
|
self.cur.execute(table_config)
|
||||||
|
|
||||||
|
# Create measurement_values_table if not already existing
|
||||||
table_config = "CREATE TABLE IF NOT EXISTS measurement_values " \
|
table_config = "CREATE TABLE IF NOT EXISTS measurement_values " \
|
||||||
"(measurementID INTEGER PRIMARY KEY," \
|
"(measurementID INTEGER PRIMARY KEY," \
|
||||||
"Timestamp DATETIME DEFAULT (datetime('now', 'localtime'))," \
|
"Timestamp DATETIME DEFAULT (datetime('now', 'localtime'))," \
|
||||||
@ -42,6 +60,12 @@ class PlantDataBase:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def insert_plant(self, plantname: str, plant_id: int) -> bool:
|
def insert_plant(self, plantname: str, plant_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Insert a new plant with an id and a plantname
|
||||||
|
:param plantname: name of plant
|
||||||
|
:param plant_id: id of plant (position in the bed, must be unique!!)
|
||||||
|
:return: True if successfully, False if not
|
||||||
|
"""
|
||||||
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()
|
||||||
@ -51,6 +75,12 @@ class PlantDataBase:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def configure_plant(self, plant_id: int, plantname: str) -> bool:
|
def configure_plant(self, plant_id: int, plantname: str) -> bool:
|
||||||
|
"""
|
||||||
|
Change a plants parameters in the database by using its PlantID as a search criteria
|
||||||
|
:param plant_id: id of plant
|
||||||
|
:param plantname: name of plant
|
||||||
|
:return: True if successfully, False if not
|
||||||
|
"""
|
||||||
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))
|
||||||
@ -61,6 +91,11 @@ class PlantDataBase:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def delete_plant(self, plant_id):
|
def delete_plant(self, plant_id):
|
||||||
|
"""
|
||||||
|
Delete a plant from the database
|
||||||
|
:param plant_id: PlantID of plant to delete
|
||||||
|
:return: True if successfully, False if not
|
||||||
|
"""
|
||||||
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()
|
||||||
@ -74,6 +109,15 @@ class PlantDataBase:
|
|||||||
sensordata_humidity,
|
sensordata_humidity,
|
||||||
sensordata_soil_moisture,
|
sensordata_soil_moisture,
|
||||||
sensordata_brightness) -> bool:
|
sensordata_brightness) -> bool:
|
||||||
|
"""
|
||||||
|
Insert a measurement value of plantID
|
||||||
|
:param plant_id: plantID of plant
|
||||||
|
:param sensordata_temp: Temperature
|
||||||
|
:param sensordata_humidity: Air Humidity value
|
||||||
|
:param sensordata_soil_moisture: Soil Moisture value
|
||||||
|
:param sensordata_brightness: brightness value
|
||||||
|
:return: True if successfully, False if not
|
||||||
|
"""
|
||||||
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"SoilMoisture, Brightness) VALUES "
|
||||||
@ -87,10 +131,11 @@ class PlantDataBase:
|
|||||||
|
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
Gets the newest parameter of specific plant and returns all parameters in json format
|
Gets the newest parameter of specific plant and returns all parameters in json format.
|
||||||
:param plant_id:
|
Either pass plant_name OR plant_id, BOTH passed -> ERROR
|
||||||
:param plant_name:
|
:param plant_id: PlantID of plant
|
||||||
:return:
|
:param plant_name: Name of Plant
|
||||||
|
:return: JSON with data if successfully, else none
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if plant_name is not None and plant_id is None:
|
if plant_name is not None and plant_id is None:
|
||||||
@ -118,6 +163,11 @@ class PlantDataBase:
|
|||||||
logging.error("Could not get measurement values: " + str(e))
|
logging.error("Could not get measurement values: " + str(e))
|
||||||
|
|
||||||
def delete_data(self, table_name):
|
def delete_data(self, table_name):
|
||||||
|
"""
|
||||||
|
Delete all data from a specific tabel
|
||||||
|
:param table_name: tabel you want to delete
|
||||||
|
:return: True if successfully, False if not
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self.cur.execute(f'DELETE FROM {table_name}')
|
self.cur.execute(f'DELETE FROM {table_name}')
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
@ -128,7 +178,7 @@ class PlantDataBase:
|
|||||||
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: Count of registered plants as int
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.cur.execute("SELECT COUNT(*) FROM plants")
|
self.cur.execute("SELECT COUNT(*) FROM plants")
|
||||||
@ -137,6 +187,10 @@ class PlantDataBase:
|
|||||||
logging.error("Could not count plants: " + str(e))
|
logging.error("Could not count plants: " + str(e))
|
||||||
|
|
||||||
def get_plant_names(self) -> list:
|
def get_plant_names(self) -> list:
|
||||||
|
"""
|
||||||
|
Use this method to get a list of the Names of all registered plants
|
||||||
|
:return: list containing plantNames
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self.cur.execute("SELECT PlantName FROM plants")
|
self.cur.execute("SELECT PlantName FROM plants")
|
||||||
return self.cur.fetchall()
|
return self.cur.fetchall()
|
||||||
@ -144,6 +198,11 @@ class PlantDataBase:
|
|||||||
logging.error("Could not get plant names: " + str(e))
|
logging.error("Could not get plant names: " + str(e))
|
||||||
|
|
||||||
def get_plant_id(self, plant_name: str) -> int:
|
def get_plant_id(self, plant_name: str) -> int:
|
||||||
|
"""
|
||||||
|
Use this method to get the PlantID of a registered plant by its name
|
||||||
|
:param plant_name: name of registered plant
|
||||||
|
:return: ID of plant as int
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self.cur.execute("SELECT PlantID FROM plants WHERE PlantName=?", (plant_name,))
|
self.cur.execute("SELECT PlantID FROM plants WHERE PlantName=?", (plant_name,))
|
||||||
return self.cur.fetchone()[0]
|
return self.cur.fetchone()[0]
|
||||||
@ -151,4 +210,7 @@ class PlantDataBase:
|
|||||||
logging.error("Could not get plant id: " + str(e))
|
logging.error("Could not get plant id: " + str(e))
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
"""
|
||||||
|
Destructor of Class. Disconnects from database.
|
||||||
|
"""
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
class Robot:
|
class Robot:
|
||||||
"""
|
"""
|
||||||
This class contains the features of the robot. It is used as an interface for the main to avoid global variables and
|
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
|
store them instead an instance of this robot object. It saves the orders and some basic information of the robots status
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.robot_ready = True
|
self.robot_ready = True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user