Compare commits
10
Commits
e975585f57
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
556736ccf5 | ||
|
|
410e27adc3 | ||
|
|
f6f6421620 | ||
|
|
5e14633f42 | ||
|
|
1778a211bb | ||
|
|
22bcf7310f | ||
|
|
90235a73f1 | ||
|
|
2c065573a5 | ||
|
|
2a3136503a | ||
|
|
d9ad3bce0c |
No files matched your search
Generated
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
<component name="WebServers">
|
<component name="WebServers">
|
||||||
<option name="servers">
|
<option name="servers">
|
||||||
<webServer id="1b3e9e3f-7394-4cbc-a32d-b95ef2abda52" name="backend">
|
<webServer id="1b3e9e3f-7394-4cbc-a32d-b95ef2abda52" name="backend">
|
||||||
<fileTransfer accessType="SFTP" host="192.168.137.197" port="22" sshConfigId="8ca8aafc-5b14-4074-b370-599a5b56368a" sshConfig="lego@192.168.137.197:22 password">
|
<fileTransfer accessType="SFTP" host="lego-K53SV" port="22" sshConfigId="40359a92-2c87-41df-943b-cfac73c7ea3d" sshConfig="lego@lego-K53SV:22 password">
|
||||||
<advancedOptions>
|
<advancedOptions>
|
||||||
<advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" />
|
<advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" />
|
||||||
</advancedOptions>
|
</advancedOptions>
|
||||||
|
|||||||
Binary file not shown.
+4442
-2946
File diff suppressed because it is too large.
Load diff
@@ -24,8 +24,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-IMG">
|
<div class="item-IMG">
|
||||||
<img class="IMG-Plants" src="assets/images/gruppe1.jpg" alt="Plant is not available">
|
<img class="IMG-Plants" src="assets/images/Pflanze.png" alt="Plant is not available">
|
||||||
<img class="IMG-Plants" src="assets/images/gruppe2.jpg" alt="Plant is not available">
|
<img class="IMG-Plants" src="assets/images/Pflanze.png" alt="Plant is not available">
|
||||||
</div>
|
</div>
|
||||||
<div class="item-delete">
|
<div class="item-delete">
|
||||||
<button class="item-Configure" mat-icon-button color="primary" (click)="onConfigurePlant()">
|
<button class="item-Configure" mat-icon-button color="primary" (click)="onConfigurePlant()">
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export class PlantsComponent {
|
|||||||
filteredOptions: Observable<string[]>;
|
filteredOptions: Observable<string[]>;
|
||||||
plants$: Observable<Plant[]> = this.storeService.currentPlants;
|
plants$: Observable<Plant[]> = this.storeService.currentPlants;
|
||||||
|
|
||||||
|
|
||||||
constructor(public dialog: MatDialog, public storeService: StoreService, private mqttRequestService: MqttRequestService) {
|
constructor(public dialog: MatDialog, public storeService: StoreService, private mqttRequestService: MqttRequestService) {
|
||||||
// sobald es eine änderung bei den pflanzenNamen gibt, wird bei der suchleiste ein neuer filter eingesetzt
|
// sobald es eine änderung bei den pflanzenNamen gibt, wird bei der suchleiste ein neuer filter eingesetzt
|
||||||
this.options$.subscribe(response => {
|
this.options$.subscribe(response => {
|
||||||
@@ -58,12 +57,10 @@ export class PlantsComponent {
|
|||||||
this.selectedPlant = value;
|
this.selectedPlant = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onOptionDefault() {
|
onOptionDefault() {
|
||||||
this.selectedPlant = 'undefined';
|
this.selectedPlant = 'undefined';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private _filter(value: string, options: string[]): string[] {
|
private _filter(value: string, options: string[]): string[] {
|
||||||
const filterValue = value.toLowerCase();
|
const filterValue = value.toLowerCase();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="container"
|
<div class="container"
|
||||||
*ngIf="robotBatteryResponse$ !== undefined && (robotBatteryResponse$ |async) !== undefined; else errorLoadingData">
|
*ngIf="robotBatteryResponse$ !== undefined && (robotBatteryResponse$ |async) !== undefined; else errorLoadingData">
|
||||||
<div class="robotImage">
|
<div class="robotImage">
|
||||||
<img src="assets/images/gruppe1.jpg" alt="Plant is not available">
|
<img src="assets/images/Robot.png" alt="Plant is not available">
|
||||||
</div>
|
</div>
|
||||||
<div class="robotAkku">
|
<div class="robotAkku">
|
||||||
<h1 class="akkuMsg">Robot Akku:</h1>
|
<h1 class="akkuMsg">Robot Akku:</h1>
|
||||||
|
|||||||
@@ -66,19 +66,6 @@ export class MqttRequestService {
|
|||||||
console.error('Error:', err);
|
console.error('Error:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
// this.mqttService.subscribeToTopic("BACKEND/DATA/POSITION").subscribe(data => {
|
|
||||||
// if (typeof data !== "object") {
|
|
||||||
// const payload = JSON.parse(data) as Position;
|
|
||||||
// this.mqttSetDataService.setRobotPosition(payload);
|
|
||||||
// }
|
|
||||||
// }, err => {
|
|
||||||
// this.dialog.open(ErrorDialogComponent, {
|
|
||||||
// data: err,
|
|
||||||
// width: '400px',
|
|
||||||
// });
|
|
||||||
// console.error('Error:', err);
|
|
||||||
// });
|
|
||||||
|
|
||||||
this.mqttService.subscribeToTopic("BACKEND/DATA/BATTERY").subscribe(data => {
|
this.mqttService.subscribeToTopic("BACKEND/DATA/BATTERY").subscribe(data => {
|
||||||
if (typeof data !== "object") {
|
if (typeof data !== "object") {
|
||||||
const payload = JSON.parse(data) as RobotBattery;
|
const payload = JSON.parse(data) as RobotBattery;
|
||||||
@@ -119,22 +106,6 @@ export class MqttRequestService {
|
|||||||
console.error('Error:', err);
|
console.error('Error:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
// this.mqttService.subscribeToTopic("BACKEND/DATA/PICTURE").subscribe(data => {
|
|
||||||
// if (typeof data !== "object") {
|
|
||||||
// const payload = JSON.parse(data) as Plant;
|
|
||||||
// this.mqttSetDataService.setDataPlant(payload);
|
|
||||||
// }
|
|
||||||
// }, err => {
|
|
||||||
// console.error('Error:', err);
|
|
||||||
// });
|
|
||||||
|
|
||||||
//Request to get ALL DATA every 10sec!!
|
|
||||||
// this.timerSubscription = timer(0, 10000).pipe(
|
|
||||||
// map(() => {
|
|
||||||
// this.mqttService.publishToTopic('BACKEND/ACTION/GETALLDATA');
|
|
||||||
// })
|
|
||||||
// ).subscribe();
|
|
||||||
|
|
||||||
//Publish to the Topic to recieve the first Data
|
//Publish to the Topic to recieve the first Data
|
||||||
this.publishToPLANTCOUNT();
|
this.publishToPLANTCOUNT();
|
||||||
this.publishToGETBATTERY();
|
this.publishToGETBATTERY();
|
||||||
@@ -145,18 +116,14 @@ export class MqttRequestService {
|
|||||||
publishToGETALLDATA() {
|
publishToGETALLDATA() {
|
||||||
this.mqttService.publishToTopic('BACKEND/ACTION/GETALLDATA', JSON.stringify(this.storeService.getAllPlantNames()));
|
this.mqttService.publishToTopic('BACKEND/ACTION/GETALLDATA', JSON.stringify(this.storeService.getAllPlantNames()));
|
||||||
}
|
}
|
||||||
publishToDRIVE() {
|
|
||||||
this.mqttService.publishToTopic('BACKEND/ACTION/DRIVE');
|
|
||||||
}
|
|
||||||
publishToGETBATTERY() {
|
publishToGETBATTERY() {
|
||||||
this.mqttService.publishToTopic('BACKEND/ACTION/GETBATTERY');
|
this.mqttService.publishToTopic('BACKEND/ACTION/GETBATTERY');
|
||||||
}
|
}
|
||||||
|
|
||||||
publishToROBOTREADY() {
|
publishToROBOTREADY() {
|
||||||
this.mqttService.publishToTopic('BACKEND/ACTION/ROBOTREADY');
|
this.mqttService.publishToTopic('BACKEND/ACTION/ROBOTREADY');
|
||||||
}
|
}
|
||||||
publishToGETPOSITION() {
|
|
||||||
this.mqttService.publishToTopic('BACKEND/ACTION/GETPOSITION');
|
|
||||||
}
|
|
||||||
|
|
||||||
publishToNEWPLANT(plant: Plant) {
|
publishToNEWPLANT(plant: Plant) {
|
||||||
this.mqttService.publishToTopic('BACKEND/ACTION/NEWPLANT', JSON.stringify(plant));
|
this.mqttService.publishToTopic('BACKEND/ACTION/NEWPLANT', JSON.stringify(plant));
|
||||||
@@ -173,6 +140,7 @@ export class MqttRequestService {
|
|||||||
publishToPLANTCOUNT() {
|
publishToPLANTCOUNT() {
|
||||||
this.mqttService.publishToTopic('BACKEND/ACTION/PLANTCOUNT');
|
this.mqttService.publishToTopic('BACKEND/ACTION/PLANTCOUNT');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* publishToINPUT wird die Spracheingabe übermittelt, die dann mit
|
* publishToINPUT wird die Spracheingabe übermittelt, die dann mit
|
||||||
* mehreren if else anweisungen einen Request an das Backend sendet und
|
* mehreren if else anweisungen einen Request an das Backend sendet und
|
||||||
|
|||||||
@@ -32,5 +32,4 @@ export class MqttSetDataService {
|
|||||||
setPlantCount(data: PlantCount): void {
|
setPlantCount(data: PlantCount): void {
|
||||||
this.storeService.setPlantCount(data);
|
this.storeService.setPlantCount(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,8 @@ export class MqttService {
|
|||||||
|
|
||||||
// Connection zu dem MQTT Broker
|
// Connection zu dem MQTT Broker
|
||||||
constructor() {
|
constructor() {
|
||||||
// this.client = connect('wss://mqtt.eclipseprojects.io:443/mqtt'); //Je nachdem welchen Link der Broker hat
|
this.client = connect('wss://mqtt.eclipseprojects.io:443/mqtt'); //Online Broker
|
||||||
this.client = connect('mqtt://192.168.137.197:1883', { clientId: 'kemal' });
|
// this.client = connect('mqtt://192.168.137.197:1883', { clientId: 'kemal' }); //lokaler Broker
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribeToTopic(topic: string): Observable<any> {
|
public subscribeToTopic(topic: string): Observable<any> {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
<img class="img-item" src="assets/images/logo.png" alt="not available">
|
<img class="img-item" src="assets/images/logo.png" alt="not available">
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="item-header" mat-dialog-title>Map</p>
|
<p class="item-header" mat-dialog-title>Map</p>
|
||||||
<img class="item-img" src="assets/images/gruppe1.jpg" alt="not available">
|
<img class="item-img" src="assets/images/Map.png" alt="not available">
|
||||||
<div class="item-button" mat-dialog-actions>
|
<div class="item-button" mat-dialog-actions>
|
||||||
<button mat-button mat-dialog-close>Close</button>
|
<button mat-button mat-dialog-close>Close</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 313 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 218 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 114 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 257 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 257 KiB |
Binary file not shown.
@@ -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,105 +20,249 @@ 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']],
|
||||||
"ActionID": payload['ActionID']
|
"ActionID": payload['ActionID']
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Delete order from order list
|
||||||
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)
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
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"])
|
||||||
|
drive_data = {
|
||||||
|
"PlantID": plant_ids,
|
||||||
|
"ActionID": payload['ActionID']
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 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'])
|
||||||
|
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:
|
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()))
|
||||||
|
|
||||||
|
|
||||||
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()))
|
||||||
client.publish(Topics['BACKEND_DATA_ROBOTREADY'], message.payload.decode("UTF-8"))
|
|
||||||
|
# Send new robot-ready status to frontend channel
|
||||||
|
client.publish(Topics['BACKEND_DATA_ROBOTREADY'], str(robot.get_robot_status()))
|
||||||
|
|
||||||
|
|
||||||
# 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,
|
||||||
robot: Robot):
|
robot: Robot):
|
||||||
plant_id = mydatabase.get_plant_id(plant_name=json.loads(message.payload.decode("UTF-8"))["PlantName"])
|
"""
|
||||||
|
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"))))
|
||||||
|
|
||||||
|
# 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(drive_data)
|
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)
|
_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(plant_ids))
|
client.publish(Topics['ROBOT_ACTION_DRIVEALL'], json.dumps(drive_data))
|
||||||
logging.info("BACKEND_ACTION_DRIVEALL Drive Command published: " + json.dumps(drive_data))
|
logging.info("BACKEND_ACTION_DRIVEALL Drive Command published: " + json.dumps(drive_data))
|
||||||
else:
|
else:
|
||||||
if robot.get_order_number() < 6:
|
if robot.get_order_number() < 6:
|
||||||
@@ -129,63 +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()
|
||||||
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))
|
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,10 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
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.137.197"
|
MQTT_BROKER_LOCAL = "192.168.0.102"
|
||||||
MQTT_BROKER_GLOBAL = "mqtt.eclipseprojects.io"
|
MQTT_BROKER_GLOBAL = "mqtt.eclipseprojects.io"
|
||||||
RASPI_CLIENT_ID = "smart_farming_raspi"
|
RASPI_CLIENT_ID = "smart_farming_raspi"
|
||||||
BACKEND_CLIENT_ID = "smart_farming_server"
|
BACKEND_CLIENT_ID = "smart_farming_server"
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
test_dict = {
|
||||||
|
"PlantData": [ {"PlantID":1, "Test": 0}, {"PlantID": 2, "Test": 0}],
|
||||||
|
"ActionID": "SUIIII"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
plant_ids = []
|
||||||
|
for i in test_dict["PlantData"]:
|
||||||
|
plant_ids.append(i["PlantID"])
|
||||||
|
|
||||||
|
print(plant_ids)
|
||||||
@@ -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")
|
||||||
@@ -39,6 +39,9 @@ def on_connect(_client: mqtt.Client, _userdata, _flags, _rc, _mydatabase, _robot
|
|||||||
_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, _robot))
|
data_sensordata(client, userdata, message, _mydatabase, _robot))
|
||||||
|
|
||||||
|
_client.subscribe(Topics['ROBOT_DATA_ALL'])
|
||||||
|
_client.message_callback_add(Topics['ROBOT_DATA_ALL'], lambda client, userdata, message: data_functions.
|
||||||
|
data_sensordataall(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)
|
||||||
|
|
||||||
@@ -97,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)
|
|
||||||
|
# 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,
|
||||||
@@ -108,10 +116,14 @@ def main():
|
|||||||
_mydatabase=my_database,
|
_mydatabase=my_database,
|
||||||
_robot=robot)
|
_robot=robot)
|
||||||
mqttclient.connect(MQTT_BROKER_GLOBAL)
|
mqttclient.connect(MQTT_BROKER_GLOBAL)
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,30 +2,47 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import logging
|
import logging
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
|
|
||||||
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'))," \
|
||||||
@@ -43,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()
|
||||||
@@ -52,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))
|
||||||
@@ -62,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()
|
||||||
@@ -75,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 "
|
||||||
@@ -88,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:
|
||||||
@@ -119,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()
|
||||||
@@ -129,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")
|
||||||
@@ -138,13 +187,22 @@ 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 [row[0] for row in self.cur.fetchall()]
|
return self.cur.fetchall()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
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]
|
||||||
@@ -152,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
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ ALLSENSORDATA = {
|
|||||||
"ActionID": ""
|
"ActionID": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO When sensor is available
|
|
||||||
POSITION = {
|
POSITION = {
|
||||||
"Position": ""
|
"Position": ""
|
||||||
}
|
}
|
||||||
@@ -115,7 +114,6 @@ ALLPLANTDATA = [
|
|||||||
PLANTDATA
|
PLANTDATA
|
||||||
]
|
]
|
||||||
|
|
||||||
# TODO When sensor is available
|
|
||||||
POSITION = {
|
POSITION = {
|
||||||
"Position": "",
|
"Position": "",
|
||||||
"Timestamp": ""
|
"Timestamp": ""
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ def measure_send_data(plantID, actionID, client: mqtt.Client):
|
|||||||
"""
|
"""
|
||||||
Reads all sensors and publishes data via MQTT in form of SENSORDATA
|
Reads all sensors and publishes data via MQTT in form of SENSORDATA
|
||||||
|
|
||||||
|
***Function is only neccessary for driving without color codes***
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
plantID (_type_): plant to measure
|
plantID (_type_): plant to measure
|
||||||
actionID (_type_): current ID of driving action
|
actionID (_type_): current ID of driving action
|
||||||
@@ -48,7 +50,7 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client):
|
|||||||
Meassure and publish data via MQTT
|
Meassure and publish data via MQTT
|
||||||
Drive home to starting point
|
Drive home to starting point
|
||||||
|
|
||||||
***If color codes are working properly this function is not needed anymore***
|
***Function is only neccessary for driving without color codes***
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
plantID (_type_): plant to measure
|
plantID (_type_): plant to measure
|
||||||
@@ -58,35 +60,38 @@ def drive_plant_thread(plantID, actionID, client: mqtt.Client):
|
|||||||
|
|
||||||
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/plant_{plantID}.py')
|
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/plant_{plantID}.py')
|
||||||
|
|
||||||
|
# Error handling for drive operation
|
||||||
|
# Abort when drive operation at start when error occurs
|
||||||
if errorCode != 0:
|
if errorCode != 0:
|
||||||
if errorCode == 65280:
|
if errorCode == 65280:
|
||||||
errorMessage = "EV3 not connected"
|
errorMessage = "EV3 not connected"
|
||||||
else:
|
else:
|
||||||
errorMessage = "Motor or Sensor unplugged"
|
errorMessage = "Motor or Sensor unplugged"
|
||||||
|
|
||||||
logging.info(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
logging.error(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
||||||
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at starting position", qos=1)
|
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at starting position", qos=1)
|
||||||
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("Measuring Sensors")
|
logging.info("Measuring Sensors")
|
||||||
measure_send_data(plantID, actionID, client)
|
measure_send_data(plantID, actionID, client)
|
||||||
|
|
||||||
# TODO How to Handle Pictures and PlantID
|
# Taking Picture not fully implemented
|
||||||
logging.info("Taking Picture")
|
# logging.info("Taking Picture")
|
||||||
sendPicture(client)
|
# sendPicture(client)
|
||||||
|
|
||||||
logging.info("Robot driving home")
|
logging.info("Robot driving home")
|
||||||
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back.py {plantID}')
|
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back.py {plantID}')
|
||||||
|
|
||||||
|
# Error handling for drive operation
|
||||||
|
# Abort when drive operation at start when error occurs
|
||||||
if errorCode != 0:
|
if errorCode != 0:
|
||||||
if errorCode == 65280:
|
if errorCode == 65280:
|
||||||
errorMessage = "EV3 not connected"
|
errorMessage = "EV3 not connected"
|
||||||
else:
|
else:
|
||||||
errorMessage = "Motor or Sensor unplugged"
|
errorMessage = "Motor or Sensor unplugged"
|
||||||
|
|
||||||
logging.info(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
logging.error(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
||||||
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at plant {plantID}", qos=1)
|
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at plant {plantID}", qos=1)
|
||||||
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
||||||
return
|
return
|
||||||
@@ -111,7 +116,7 @@ def drive_plant_all_thread(plantIDs: list, actionID, client: mqtt.Client):
|
|||||||
client (mqtt.Client): current MQTT client
|
client (mqtt.Client): current MQTT client
|
||||||
"""
|
"""
|
||||||
|
|
||||||
allPlantData = {
|
allPlantData = { # Sensordata for all plants
|
||||||
"SensorData": [],
|
"SensorData": [],
|
||||||
"ActionID": ""
|
"ActionID": ""
|
||||||
}
|
}
|
||||||
@@ -119,17 +124,18 @@ def drive_plant_all_thread(plantIDs: list, actionID, client: mqtt.Client):
|
|||||||
allPlantData["ActionID"] = actionID
|
allPlantData["ActionID"] = actionID
|
||||||
|
|
||||||
for plant in plantIDs:
|
for plant in plantIDs:
|
||||||
# FIXME Only possible with color codes
|
|
||||||
logging.info(f"Robot Driving to plant {plant}")
|
logging.info(f"Robot Driving to plant {plant}")
|
||||||
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_plant.py {plant}')
|
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_plant.py {plant}')
|
||||||
|
|
||||||
|
# Error handling for drive operation
|
||||||
|
# Abort when drive operation at start when error occurs
|
||||||
if errorCode != 0:
|
if errorCode != 0:
|
||||||
if errorCode == 65280:
|
if errorCode == 65280:
|
||||||
errorMessage = "EV3 not connected"
|
errorMessage = "EV3 not connected"
|
||||||
else:
|
else:
|
||||||
errorMessage = "Motor or Sensor unplugged"
|
errorMessage = "Motor or Sensor unplugged"
|
||||||
|
|
||||||
logging.info(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
logging.error(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
||||||
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at starting position", qos=1)
|
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at starting position", qos=1)
|
||||||
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
||||||
return
|
return
|
||||||
@@ -149,27 +155,32 @@ def drive_plant_all_thread(plantIDs: list, actionID, client: mqtt.Client):
|
|||||||
allPlantData["SensorData"].append(sensordata)
|
allPlantData["SensorData"].append(sensordata)
|
||||||
|
|
||||||
|
|
||||||
# TODO How to Handle Pictures and PlantID
|
# Taking Picture not fully implemented
|
||||||
logging.info("Taking Picture")
|
# logging.info("Taking Picture")
|
||||||
sendPicture(client)
|
# sendPicture(client)
|
||||||
|
|
||||||
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_arm.py {plant}')
|
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_arm.py {plant}')
|
||||||
|
# Error handling for drive operation
|
||||||
|
# Abort when drive operation at start when error occurs
|
||||||
if errorCode != 0:
|
if errorCode != 0:
|
||||||
if errorCode == 65280:
|
if errorCode == 65280:
|
||||||
errorMessage = "EV3 not connected"
|
errorMessage = "EV3 not connected"
|
||||||
else:
|
else:
|
||||||
errorMessage = "Motor or Sensor unplugged"
|
errorMessage = "Motor or Sensor unplugged"
|
||||||
|
|
||||||
logging.info(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
logging.error(f"{errorMessage}, Drive Plant aborted, Robot at plant {plant}")
|
||||||
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at plant {plant}", qos=1)
|
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at plant {plant}", qos=1)
|
||||||
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# End of drive and measure operation
|
||||||
logging.info("All Plants measured, sending data")
|
logging.info("All Plants measured, sending data")
|
||||||
client.publish(Topics["ROBOT_DATA_ALL"], json.dumps(allPlantData, indent=4), qos=1)
|
client.publish(Topics["ROBOT_DATA_ALL"], json.dumps(allPlantData, indent=4), qos=1)
|
||||||
|
|
||||||
|
|
||||||
logging.info("Robot driving home")
|
logging.info("Robot driving home")
|
||||||
|
# Error handling for drive operation
|
||||||
|
# Abort when drive operation at start when error occurs
|
||||||
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back_straight.py')
|
errorCode = os.system(f'sshpass -p maker ssh robot@ev3dev.local python3 /home/robot/Programme/drive_back_straight.py')
|
||||||
if errorCode != 0:
|
if errorCode != 0:
|
||||||
if errorCode == 65280:
|
if errorCode == 65280:
|
||||||
@@ -177,7 +188,7 @@ def drive_plant_all_thread(plantIDs: list, actionID, client: mqtt.Client):
|
|||||||
else:
|
else:
|
||||||
errorMessage = "Motor or Sensor unplugged"
|
errorMessage = "Motor or Sensor unplugged"
|
||||||
|
|
||||||
logging.info(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
logging.error(f"{errorMessage}, Drive Plant aborted, Robot at starting position")
|
||||||
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at plant {plant}", qos=1)
|
client.publish(Topics["ROBOT_DATA_ERROR"], f"{errorMessage}, Drive Plant aborted, Robot at plant {plant}", qos=1)
|
||||||
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
client.publish(Topics["ROBOT_DATA_ROBOTREADY"], "True", qos=1)
|
||||||
return
|
return
|
||||||
@@ -196,7 +207,7 @@ def drive_plant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
|||||||
Function to drive to plant according to request
|
Function to drive to plant according to request
|
||||||
Starting Drive in Thread to not block main programm
|
Starting Drive in Thread to not block main programm
|
||||||
|
|
||||||
***If color codes are working properly this function is not needed anymore***
|
***Function is only neccessary for driving without color codes***
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
client (mqtt.Client): current mqtt client
|
client (mqtt.Client): current mqtt client
|
||||||
@@ -215,7 +226,6 @@ def drive_plant(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
|||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
# FIXME Only possible with color codes
|
|
||||||
def drive_plant_all(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
def drive_plant_all(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
||||||
"""
|
"""
|
||||||
Function to drive to plants according to request
|
Function to drive to plants according to request
|
||||||
@@ -240,7 +250,7 @@ def drive_plant_all(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
|||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
def get_position(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
def get_position(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
||||||
"""
|
"""
|
||||||
Callback function for GPS position request
|
Callback function for GPS position request
|
||||||
Function to send actual GPS position via MQTT in form of POSITION
|
Function to send actual GPS position via MQTT in form of POSITION
|
||||||
@@ -254,18 +264,18 @@ def get_position(clients: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
|||||||
"""
|
"""
|
||||||
logging.info("Received position request")
|
logging.info("Received position request")
|
||||||
|
|
||||||
# TODO Write GPS Sensor Function
|
|
||||||
position = {
|
position = {
|
||||||
"Position": ""
|
"Position": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
clients.publish(Topics["ROBOT_DATA_POSITION"], json.dumps(position, indent=4), qos=1)
|
client.publish(Topics["ROBOT_DATA_POSITION"], json.dumps(position, indent=4), qos=1)
|
||||||
|
|
||||||
|
|
||||||
def get_batteryStatus(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
def get_batteryStatus(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
||||||
"""
|
"""
|
||||||
Callback function for battery status request
|
Callback function for battery status request
|
||||||
Function to read battery status from EV3 and send via MQTT in form of BATTERY
|
Function to read battery status from EV3 and send via MQTT in form of BATTERY
|
||||||
|
Current battery level is stored in "voltage_now" file
|
||||||
Reading battery level via SSH script execution
|
Reading battery level via SSH script execution
|
||||||
Battery level shown in Volts:
|
Battery level shown in Volts:
|
||||||
8,5V -> 100%
|
8,5V -> 100%
|
||||||
@@ -282,7 +292,7 @@ def get_batteryStatus(client: mqtt.Client, userdata, message: mqtt.MQTTMessage):
|
|||||||
batteryLevel = int(os.popen('sshpass -p maker ssh robot@ev3dev.local cat /sys/devices/platform/battery/power_supply/lego-ev3-battery/voltage_now').read())
|
batteryLevel = int(os.popen('sshpass -p maker ssh robot@ev3dev.local cat /sys/devices/platform/battery/power_supply/lego-ev3-battery/voltage_now').read())
|
||||||
except:
|
except:
|
||||||
logging.error("EV3 not connected")
|
logging.error("EV3 not connected")
|
||||||
client.publish(Topics["ROBOT_DATA_ERROR"], "Robot not connected", qos=1)
|
client.publish(Topics["ROBOT_DATA_ERROR"], "EV3 not connected", qos=1)
|
||||||
return
|
return
|
||||||
|
|
||||||
batteryLevel = round(batteryLevel / 1000000, 2) # Conversion to Volt
|
batteryLevel = round(batteryLevel / 1000000, 2) # Conversion to Volt
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def on_connect(client: mqtt.Client, userdata, flags, rc):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""
|
"""
|
||||||
Initialises MQTT and Sensors
|
Initialises MQTT
|
||||||
Runs forever and controlls all robot functions
|
Runs forever and controlls all robot functions
|
||||||
"""
|
"""
|
||||||
logging.basicConfig(filename="robot.log", filemode="a", encoding="utf-8", level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s %(message)s',
|
logging.basicConfig(filename="robot.log", filemode="a", encoding="utf-8", level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s %(message)s',
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def readDHT22():
|
|||||||
|
|
||||||
def readTSL2561():
|
def readTSL2561():
|
||||||
"""
|
"""
|
||||||
Reads TSL2561 brightness in Lux and returns integer value
|
Reads TSL2561 brightness via I2C in Lux and returns integer value
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
Exception: If TSL2561 not connected properly
|
Exception: If TSL2561 not connected properly
|
||||||
@@ -67,7 +67,7 @@ def readTSL2561():
|
|||||||
|
|
||||||
def readMCP3008():
|
def readMCP3008():
|
||||||
"""
|
"""
|
||||||
Reads YL-69 via MCP3008 ADC soil moisture in percent and returns float value
|
Reads YL-69 via MCP3008 ADC and SPI soil moisture in percent and returns float value
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
Exception: If YL-69 not connected properly
|
Exception: If YL-69 not connected properly
|
||||||
@@ -99,6 +99,7 @@ def readSensors(sensorData):
|
|||||||
"""
|
"""
|
||||||
Read DHT22, TSL2561 and Humidity Sensor
|
Read DHT22, TSL2561 and Humidity Sensor
|
||||||
Dictionary is passed to ensure that values are available when errors occur
|
Dictionary is passed to ensure that values are available when errors occur
|
||||||
|
When error occurs during reading of sensor, affected values are set to 0
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sensorData (dictionary): Dictionary of type SENSORDATA
|
sensorData (dictionary): Dictionary of type SENSORDATA
|
||||||
@@ -114,7 +115,7 @@ def readSensors(sensorData):
|
|||||||
try:
|
try:
|
||||||
sensorData["AirTemperature"], sensorData["AirHumidity"] = readDHT22()
|
sensorData["AirTemperature"], sensorData["AirHumidity"] = readDHT22()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sensorData["AirHumidity"] = 0 # No value returend if error occurs -> setting dummy value
|
sensorData["AirHumidity"] = 0 # No value returend if error occurs -> setting safe values
|
||||||
sensorData["AirTemperature"] = 0
|
sensorData["AirTemperature"] = 0
|
||||||
errorMessage = str(e) + "\n" # Appending received error message to later forward all occured errors
|
errorMessage = str(e) + "\n" # Appending received error message to later forward all occured errors
|
||||||
|
|
||||||
@@ -122,17 +123,17 @@ def readSensors(sensorData):
|
|||||||
try:
|
try:
|
||||||
sensorData["Brightness"] = readTSL2561()
|
sensorData["Brightness"] = readTSL2561()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sensorData["Brightness"] = 0 # No value returend if error occurs -> setting dummy value
|
sensorData["Brightness"] = 0 # No value returend if error occurs -> setting safe value
|
||||||
errorMessage = errorMessage + str(e) + "\n" # Appending received error message to later forward all occured errors
|
errorMessage = errorMessage + str(e) + "\n" # Appending received error message to later forward all occured errors
|
||||||
|
|
||||||
# read YL-69
|
# read YL-69
|
||||||
try:
|
try:
|
||||||
sensorData["SoilMoisture"] = readMCP3008()
|
sensorData["SoilMoisture"] = readMCP3008()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sensorData["SoilMoisture"] = 0 # No value returend if error occurs -> setting dummy value
|
sensorData["SoilMoisture"] = 0 # No value returend if error occurs -> setting safe value
|
||||||
errorMessage = errorMessage + str(e) + "\n" # Appending received error message to later forward all occured errors
|
errorMessage = errorMessage + str(e) + "\n" # Appending received error message to later forward all occured errors
|
||||||
|
|
||||||
# raise combined error message
|
# raise combined error message, successfull values still available
|
||||||
if errorMessage != "":
|
if errorMessage != "":
|
||||||
raise Exception(errorMessage)
|
raise Exception(errorMessage)
|
||||||
|
|
||||||
@@ -146,15 +147,13 @@ def takePicture():
|
|||||||
try:
|
try:
|
||||||
camera = PiCamera()
|
camera = PiCamera()
|
||||||
except:
|
except:
|
||||||
# raise Exception("Camera not connected")
|
raise Exception("Camera not connected")
|
||||||
pass
|
|
||||||
|
|
||||||
camera.start_preview()
|
camera.start_preview()
|
||||||
camera.capture("picture.png")
|
camera.capture("picture.png")
|
||||||
camera.stop_preview()
|
camera.stop_preview()
|
||||||
|
|
||||||
|
|
||||||
# TODO - read position with sensor
|
|
||||||
def readPosition():
|
def readPosition():
|
||||||
"""
|
"""
|
||||||
Read and return Position
|
Read and return Position
|
||||||
|
|||||||
Reference in new issue
Block a user