Compare commits
12
Commits
a857f870a5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
556736ccf5 | ||
|
|
410e27adc3 | ||
|
|
f6f6421620 | ||
|
|
5e14633f42 | ||
|
|
1778a211bb | ||
|
|
22bcf7310f | ||
|
|
90235a73f1 | ||
|
|
2c065573a5 | ||
|
|
2a3136503a | ||
|
|
d9ad3bce0c | ||
|
|
e975585f57 | ||
|
|
9b1bcbebde |
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
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-robotText">
|
<div class="item-robotText">
|
||||||
<div *ngIf="robotStateChange$ | async" id="robotIsOnTour">Robot is on Tour</div>
|
<div *ngIf="!(robotIsReady$ | async)" id="robotIsOnTour">Robot is on Tour</div>
|
||||||
<div *ngIf="!(robotStateChange$ | async)" id="robotIsReady">Robot is Ready</div>
|
<div *ngIf="(robotIsReady$ | async)" id="robotIsReady">Robot is Ready</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -8,7 +8,8 @@ import { StoreService } from '../Service/store.service';
|
|||||||
styleUrls: ['./home.component.css']
|
styleUrls: ['./home.component.css']
|
||||||
})
|
})
|
||||||
export class HomeComponent {
|
export class HomeComponent {
|
||||||
robotStateChange$: Observable<boolean> = this.storeService.currentRobotReady;
|
// Aktueller RobotStatus wird subscribed
|
||||||
|
robotIsReady$: Observable<boolean> = this.storeService.currentRobotReady;
|
||||||
|
|
||||||
constructor(private storeService: StoreService) {
|
constructor(private storeService: StoreService) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,36 +15,43 @@ export class InputComponent implements OnInit, OnDestroy {
|
|||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public service: SpeechService,
|
public speechService: SpeechService,
|
||||||
private mqttRequestService: MqttRequestService
|
private mqttRequestService: MqttRequestService
|
||||||
) {
|
) {
|
||||||
if (!this.service.initSpeechServiceStarted) {
|
if (!this.speechService.initSpeechServiceStarted) {
|
||||||
this.service.init();
|
this.speechService.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.subscription = this.service.currentApprovalStageMessage.subscribe(msg => {
|
// subscribe auf die eingegebene Spracheingabe, gibt als response einen string
|
||||||
|
this.subscription = this.speechService.currentApprovalStageMessage.subscribe(msg => {
|
||||||
this.text = msg;
|
this.text = msg;
|
||||||
if (this.text !== '') {
|
if (this.text !== '') {
|
||||||
this.micIsClicked = false;
|
this.micIsClicked = false;
|
||||||
this.mqttRequestService.publishToINPUT(this.text);
|
this.mqttRequestService.publishToINPUT(this.text); // msg wird hier den Request Service weitergeleitet
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beim klick auf das Mikrofon, wird die Spracheingabe gestartet. Beim 2ten klick auf das
|
||||||
|
* Mikrofon wird die Spracheingabe gestoppt
|
||||||
|
*/
|
||||||
startStopService() {
|
startStopService() {
|
||||||
|
//
|
||||||
if (this.micIsClicked) {
|
if (this.micIsClicked) {
|
||||||
this.micIsClicked = false;
|
this.micIsClicked = false;
|
||||||
this.service.stop();
|
this.speechService.stop(); // Spracheingabe stoppen
|
||||||
} else {
|
} else {
|
||||||
this.micIsClicked = true;
|
this.micIsClicked = true;
|
||||||
this.service.start();
|
this.speechService.start(); //Spracheingabe starten
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.subscription.unsubscribe()
|
this.subscription.unsubscribe()
|
||||||
this.service.onDestory();
|
this.speechService.onDestory();
|
||||||
this.text = '';
|
this.text = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||||
|
|
||||||
declare var speechSynthesis: any;
|
declare var speechSynthesis: any;
|
||||||
@@ -16,6 +15,7 @@ export class OutputComponent implements OnInit, OnDestroy {
|
|||||||
constructor(private mqttRequestService: MqttRequestService) { }
|
constructor(private mqttRequestService: MqttRequestService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// subsribe auf "responseMessage", sie wird jedes mal getriggert wenn eine Spracheingabe statt findet
|
||||||
this.subscription = this.mqttRequestService.responseMessage.subscribe(msg => {
|
this.subscription = this.mqttRequestService.responseMessage.subscribe(msg => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.outputText = msg;
|
this.outputText = msg;
|
||||||
@@ -24,6 +24,11 @@ export class OutputComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode spricht das übergeben string aus --> Sprachausgabe
|
||||||
|
* @method speak
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
speak(text: string) {
|
speak(text: string) {
|
||||||
const utterance = new SpeechSynthesisUtterance(text);
|
const utterance = new SpeechSynthesisUtterance(text);
|
||||||
utterance.lang = 'en-US';
|
utterance.lang = 'en-US';
|
||||||
@@ -34,5 +39,4 @@ export class OutputComponent implements OnInit, OnDestroy {
|
|||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
this.mqttRequestService.onDestroy();
|
this.mqttRequestService.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,13 @@ declare var webkitSpeechRecognition: any;
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class SpeechService {
|
export class SpeechService {
|
||||||
|
/*
|
||||||
|
* currentApprovalStageMessage wird als observable festgelegt,
|
||||||
|
* damit man aus anderen Komponenten die änderungen mitbekommt!
|
||||||
|
*/
|
||||||
private approvalStageMessage = new BehaviorSubject('');
|
private approvalStageMessage = new BehaviorSubject('');
|
||||||
currentApprovalStageMessage = this.approvalStageMessage.asObservable();
|
currentApprovalStageMessage = this.approvalStageMessage.asObservable();
|
||||||
|
|
||||||
initSpeechServiceStarted: boolean = false;
|
initSpeechServiceStarted: boolean = false;
|
||||||
|
|
||||||
recognition = new webkitSpeechRecognition();
|
recognition = new webkitSpeechRecognition();
|
||||||
@@ -18,8 +23,6 @@ export class SpeechService {
|
|||||||
tempWords: string | undefined;
|
tempWords: string | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.recognition.pitch = 10;
|
|
||||||
this.recognition.rate = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -33,22 +36,19 @@ export class SpeechService {
|
|||||||
.map((result) => result.transcript)
|
.map((result) => result.transcript)
|
||||||
.join('');
|
.join('');
|
||||||
this.tempWords = transcript;
|
this.tempWords = transcript;
|
||||||
console.log('init:' + transcript);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.text = '';
|
this.text = '';
|
||||||
this.isStoppedSpeechRecog = false;
|
this.isStoppedSpeechRecog = false;
|
||||||
this.recognition.start();
|
this.recognition.start(); // Start der Sprachaufnahme
|
||||||
console.log("Speech recognition started")
|
|
||||||
this.recognition.addEventListener('end', () => {
|
this.recognition.addEventListener('end', () => {
|
||||||
this.wordConcat()
|
this.wordConcat()
|
||||||
this.recognition.stop();
|
this.recognition.stop(); // Ende der Sprachaufnahme
|
||||||
if (!this.isStoppedSpeechRecog) {
|
if (!this.isStoppedSpeechRecog) {
|
||||||
this.isStoppedSpeechRecog = true;
|
this.isStoppedSpeechRecog = true;
|
||||||
console.log("End speech recognition");
|
this.approvalStageMessage.next(this.text); // String was eingelesen wurde, wird mit der Methode "next(..)" an alle subsriber weitergeleitet!
|
||||||
this.approvalStageMessage.next(this.text);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -62,6 +62,7 @@ export class SpeechService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addiert die Wörter bei der Sprachaufnahme zusammen
|
||||||
wordConcat() {
|
wordConcat() {
|
||||||
this.text = this.text + ' ' + this.tempWords;
|
this.text = this.text + ' ' + this.tempWords;
|
||||||
this.tempWords = '';
|
this.tempWords = '';
|
||||||
|
|||||||
@@ -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()">
|
||||||
|
|||||||
@@ -18,10 +18,19 @@ export class PlantCardComponent {
|
|||||||
constructor(public dialog: MatDialog, private mqttRequestService: MqttRequestService) {
|
constructor(public dialog: MatDialog, private mqttRequestService: MqttRequestService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode wird in der HTML datei verwendet damit der Wert nicht undefined ist.
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
getFormattedLastRequestedTimestamp(): Observable<string> {
|
getFormattedLastRequestedTimestamp(): Observable<string> {
|
||||||
return of(this.calculateTimestamp(this.plant.Timestamp))
|
return of(this.calculateTimestamp(this.plant.Timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode dividiert den Timestamp der Pflanze mit der aktuellen Zeit. Die Zeit wird dann in Tagen, Stunden, Minuten, Sekunden angezeigt
|
||||||
|
* @method
|
||||||
|
* @return string wie z.B "1 hour ago"
|
||||||
|
*/
|
||||||
calculateTimestamp(timestamp: string): string {
|
calculateTimestamp(timestamp: string): string {
|
||||||
const datePlant = new Date(timestamp);
|
const datePlant = new Date(timestamp);
|
||||||
this.currentDate = new Date();
|
this.currentDate = new Date();
|
||||||
@@ -43,6 +52,10 @@ export class PlantCardComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode öffnet den Dialog "DeleteConfirmDialogComponent"
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
onDeletePlant() {
|
onDeletePlant() {
|
||||||
this.dialog.open(DeleteConfirmDialogComponent, {
|
this.dialog.open(DeleteConfirmDialogComponent, {
|
||||||
data: this.plant,
|
data: this.plant,
|
||||||
@@ -50,6 +63,10 @@ export class PlantCardComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode öffnet den Dialog "ConfigurePlantDialogComponent"
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
onConfigurePlant() {
|
onConfigurePlant() {
|
||||||
this.dialog.open(ConfigurePlantDialogComponent, {
|
this.dialog.open(ConfigurePlantDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
@@ -58,5 +75,4 @@ export class PlantCardComponent {
|
|||||||
width: '400px',
|
width: '400px',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,8 @@ 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
|
||||||
this.options$.subscribe(response => {
|
this.options$.subscribe(response => {
|
||||||
this.filteredOptions = this.myControl.valueChanges.pipe(
|
this.filteredOptions = this.myControl.valueChanges.pipe(
|
||||||
startWith(''),
|
startWith(''),
|
||||||
@@ -30,16 +30,29 @@ export class PlantsComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* es wird nach dem Pflanzen Namen gesucht.
|
||||||
|
* @method
|
||||||
|
* @return Pflant
|
||||||
|
*/
|
||||||
onSearchPlant(plantName: string): Observable<Plant> {
|
onSearchPlant(plantName: string): Observable<Plant> {
|
||||||
return this.storeService.getPlant(plantName);
|
return this.storeService.getPlant(plantName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode öffnet das Dialog "AddPlantDialogComponent"
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
onNewPlant() {
|
onNewPlant() {
|
||||||
this.dialog.open(AddPlantDialogComponent, {
|
this.dialog.open(AddPlantDialogComponent, {
|
||||||
width: '420px',
|
width: '420px',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* die ausgewählte Pflanze wird in "selectedPlant" geschrieben
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
onOptionSelect(value: string) {
|
onOptionSelect(value: string) {
|
||||||
this.selectedPlant = value;
|
this.selectedPlant = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -17,18 +17,32 @@ export class RobotComponent {
|
|||||||
constructor(private storeService: StoreService) {
|
constructor(private storeService: StoreService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode setzt die Farbe der LoadingBar auf Rot ("warn") oder Blau ("Primary")
|
||||||
|
* @method
|
||||||
|
* @return warn oder primary
|
||||||
|
*/
|
||||||
getLoadingBarColor(): Observable<ThemePalette> {
|
getLoadingBarColor(): Observable<ThemePalette> {
|
||||||
return this.batteryChargingStatus$.pipe(
|
return this.batteryChargingStatus$.pipe(
|
||||||
map(chargingStatus => chargingStatus < 20 ? "warn" : "primary")
|
map(chargingStatus => chargingStatus < 20 ? "warn" : "primary")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode wird in der HTML datei verwendet damit der Wert nicht undefined ist.
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
getFormattedLastRequestedBatteryState(): Observable<string> {
|
getFormattedLastRequestedBatteryState(): Observable<string> {
|
||||||
return this.requestTimestamp$.pipe(
|
return this.requestTimestamp$.pipe(
|
||||||
map(timestamp => this.formatLastRequestedBatteryState(timestamp))
|
map(timestamp => this.formatLastRequestedBatteryState(timestamp))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode dividiert den Timestamp der Pflanze mit der aktuellen Zeit. Die Zeit wird dann in Tagen, Stunden, Minuten, Sekunden angezeigt
|
||||||
|
* @method
|
||||||
|
* @return string wie z.B "1 hour ago"
|
||||||
|
*/
|
||||||
formatLastRequestedBatteryState(timestamp: string): string {
|
formatLastRequestedBatteryState(timestamp: string): string {
|
||||||
const datePlant = new Date(timestamp);
|
const datePlant = new Date(timestamp);
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
@@ -49,5 +63,4 @@ export class RobotComponent {
|
|||||||
return `${seconds} second${seconds !== 1 ? 's' : ''} ago`;
|
return `${seconds} second${seconds !== 1 ? 's' : ''} ago`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
import { ErrorDialogComponent } from 'src/app/dialog/error-dialog/error-dialog.component';
|
import { ErrorDialogComponent } from 'src/app/dialog/error-dialog/error-dialog.component';
|
||||||
import { Plant } from 'src/app/models/plant.model';
|
import { Plant } from 'src/app/models/plant.model';
|
||||||
import { PlantCount } from 'src/app/models/plantCount.model';
|
import { PlantCount } from 'src/app/models/plantCount.model';
|
||||||
import { Position } from 'src/app/models/position.model';
|
|
||||||
import { RobotBattery } from 'src/app/models/robotBattery.model';
|
import { RobotBattery } from 'src/app/models/robotBattery.model';
|
||||||
import { StoreService } from '../store.service';
|
import { StoreService } from '../store.service';
|
||||||
import { MqttSetDataService } from './mqtt-set-data.service';
|
import { MqttSetDataService } from './mqtt-set-data.service';
|
||||||
@@ -14,6 +14,7 @@ import { MqttService } from './mqtt.service';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MqttRequestService {
|
export class MqttRequestService {
|
||||||
|
// wird für die Sprachausgabe verwendet
|
||||||
private sourceMessage = new BehaviorSubject('');
|
private sourceMessage = new BehaviorSubject('');
|
||||||
responseMessage = this.sourceMessage.asObservable();
|
responseMessage = this.sourceMessage.asObservable();
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ export class MqttRequestService {
|
|||||||
constructor(private mqttService: MqttService,
|
constructor(private mqttService: MqttService,
|
||||||
private mqttSetDataService: MqttSetDataService,
|
private mqttSetDataService: MqttSetDataService,
|
||||||
private storeService: StoreService, public dialog: MatDialog) {
|
private storeService: StoreService, public dialog: MatDialog) {
|
||||||
|
|
||||||
//Subscribe to the Topics
|
//Subscribe to the Topics
|
||||||
this.mqttService.subscribeToTopic("BACKEND/DATA/SENSORDATA_ALL").subscribe(data => {
|
this.mqttService.subscribeToTopic("BACKEND/DATA/SENSORDATA_ALL").subscribe(data => {
|
||||||
if (typeof data !== "object") {
|
if (typeof data !== "object") {
|
||||||
@@ -36,11 +38,10 @@ export class MqttRequestService {
|
|||||||
console.error('Error:', err);
|
console.error('Error:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mqttService.subscribeToTopic("BACKEND/DATA/ERROR").subscribe(data => {
|
this.mqttService.subscribeToTopic("BACKEND/DATA/ERROR").subscribe(errorMsg => {
|
||||||
if (typeof data !== "object") {
|
if (typeof errorMsg !== "object") {
|
||||||
const payload = JSON.parse(data) as string;
|
|
||||||
this.dialog.open(ErrorDialogComponent, {
|
this.dialog.open(ErrorDialogComponent, {
|
||||||
data: payload,
|
data: errorMsg,
|
||||||
width: '400px',
|
width: '400px',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -65,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;
|
||||||
@@ -92,9 +80,10 @@ export class MqttRequestService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.mqttService.subscribeToTopic("BACKEND/DATA/ROBOTREADY").subscribe(data => {
|
this.mqttService.subscribeToTopic("BACKEND/DATA/ROBOTREADY").subscribe(data => {
|
||||||
if (typeof data !== "object") {
|
if (data === "False") {
|
||||||
const payload = JSON.parse(data) as boolean;
|
this.mqttSetDataService.setRobotReady(false);
|
||||||
this.mqttSetDataService.setRobotReady(payload);
|
} else if (data === "True") {
|
||||||
|
this.mqttSetDataService.setRobotReady(true);
|
||||||
}
|
}
|
||||||
}, err => {
|
}, err => {
|
||||||
this.dialog.open(ErrorDialogComponent, {
|
this.dialog.open(ErrorDialogComponent, {
|
||||||
@@ -116,29 +105,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 Plant[];
|
|
||||||
// this.mqttSetDataService.setDataAllPlants(payload);
|
|
||||||
// }
|
|
||||||
// }, 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();
|
||||||
@@ -150,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));
|
||||||
@@ -179,9 +141,15 @@ export class MqttRequestService {
|
|||||||
this.mqttService.publishToTopic('BACKEND/ACTION/PLANTCOUNT');
|
this.mqttService.publishToTopic('BACKEND/ACTION/PLANTCOUNT');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* publishToINPUT wird die Spracheingabe übermittelt, die dann mit
|
||||||
|
* mehreren if else anweisungen einen Request an das Backend sendet und
|
||||||
|
* die Sprachausgabe mit next(..) triggert
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
publishToINPUT(message: string) {
|
publishToINPUT(message: string) {
|
||||||
message = message.replace(/\s/g, "");
|
message = message.replace(/\s/g, ""); //entfernt alle leerzeichen
|
||||||
console.log(":" + message + ":");
|
console.log(":" + message + ":"); //Nachricht die übermittel wurde, wird ausgegeben auf der Konsole
|
||||||
let msgFound: boolean = false;
|
let msgFound: boolean = false;
|
||||||
|
|
||||||
if (message === "whereismyrobot") {
|
if (message === "whereismyrobot") {
|
||||||
@@ -194,10 +162,11 @@ export class MqttRequestService {
|
|||||||
this.sourceMessage.next("The data is being collected and you can view it under the 'Robot' tab");
|
this.sourceMessage.next("The data is being collected and you can view it under the 'Robot' tab");
|
||||||
} else if (message === "howaremyplants") {
|
} else if (message === "howaremyplants") {
|
||||||
msgFound = true;
|
msgFound = true;
|
||||||
// this.mqttService.publishToTopic('BACKEND/ACTION/DRIVEALL');
|
this.mqttService.publishToTopic('BACKEND/ACTION/DRIVEALL');
|
||||||
this.sourceMessage.next("Robot is driving to all plants");
|
this.sourceMessage.next("Robot is driving to all plants");
|
||||||
} else {
|
} else {
|
||||||
this.storeService.getAllPlantNames().subscribe(result =>
|
// schickt den Roboter zur eingegeben Pflanze..
|
||||||
|
this.storeService.getAllPlantNames().pipe(take(1)).subscribe(result =>
|
||||||
result.forEach((element: string) => {
|
result.forEach((element: string) => {
|
||||||
let msgPlantName = "howismyplant".concat(element.toLowerCase())
|
let msgPlantName = "howismyplant".concat(element.toLowerCase())
|
||||||
if (message === msgPlantName) {
|
if (message === msgPlantName) {
|
||||||
@@ -210,7 +179,9 @@ export class MqttRequestService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wenn kein case eintrifft, wird "Please try again" ausgegeben
|
||||||
if (!msgFound) {
|
if (!msgFound) {
|
||||||
|
msgFound = false;
|
||||||
this.sourceMessage.next("Please try again");
|
this.sourceMessage.next("Please try again");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,5 +32,4 @@ export class MqttSetDataService {
|
|||||||
setPlantCount(data: PlantCount): void {
|
setPlantCount(data: PlantCount): void {
|
||||||
this.storeService.setPlantCount(data);
|
this.storeService.setPlantCount(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,12 +6,12 @@ import { Observable } from 'rxjs';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MqttService {
|
export class MqttService {
|
||||||
|
|
||||||
private client: MqttClient;
|
private client: MqttClient;
|
||||||
|
|
||||||
|
// 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> {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { RobotBattery } from '../models/robotBattery.model';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
export class StoreService {
|
export class StoreService {
|
||||||
positionRobot: Position;
|
positionRobot: Position;
|
||||||
plantCount: PlantCount;
|
plantCount: PlantCount;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { MqttRequestService } from './Service/Mqtt/mqtt-request.service';
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
header: string;
|
header: string;
|
||||||
constructor(private mqttRequestService: MqttRequestService, private location: Location) {
|
constructor(private mqttRequestService: MqttRequestService, private location: Location) {
|
||||||
|
// Header in der Leiste zu bestimmen
|
||||||
const actualRoute = this.location.path().slice(1);
|
const actualRoute = this.location.path().slice(1);
|
||||||
const lastParam = actualRoute.charAt(0).toUpperCase() + actualRoute.slice(1);
|
const lastParam = actualRoute.charAt(0).toUpperCase() + actualRoute.slice(1);
|
||||||
if (lastParam === "") {
|
if (lastParam === "") {
|
||||||
|
|||||||
@@ -17,13 +17,11 @@
|
|||||||
Soil Moisture: <br>
|
Soil Moisture: <br>
|
||||||
<img class="icons-measurements" src="assets/images/Icons-Card/brightness_icon.png" alt="not available">
|
<img class="icons-measurements" src="assets/images/Icons-Card/brightness_icon.png" alt="not available">
|
||||||
Brightness:<br>
|
Brightness:<br>
|
||||||
<!-- <img class="icons-measurements" src="assets/images/Icons-Card/Timestamp_icon.png" alt="not available">
|
|
||||||
Timestamp: -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="item-Measurements-Values">
|
<div class="item-Measurements-Values">
|
||||||
<mat-form-field class="item-option" appearance="fill">
|
<mat-form-field class="item-option" appearance="fill">
|
||||||
<mat-label>Choose an ID</mat-label>
|
<mat-label>Choose an ID</mat-label>
|
||||||
<mat-select [(value)]="selected" (closed)="onOptionSelected(selected)">
|
<mat-select [(value)]="selected" (closed)="onOptionSelected()">
|
||||||
<mat-option *ngFor="let option of options$ | async" [value]="option">
|
<mat-option *ngFor="let option of options$ | async" [value]="option">
|
||||||
{{option}}
|
{{option}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
|||||||
@@ -12,13 +12,17 @@ import { MapPlantDialogComponent } from '../map-plant-dialog/map-plant-dialog.co
|
|||||||
templateUrl: './add-plant-dialog.component.html',
|
templateUrl: './add-plant-dialog.component.html',
|
||||||
styleUrls: ['./add-plant-dialog.component.css']
|
styleUrls: ['./add-plant-dialog.component.css']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* Mit diesem Dialog "AddPlantDialogComponent" wird eine neue Pflanze erstellt und in das Backend geschickt.
|
||||||
|
*/
|
||||||
export class AddPlantDialogComponent implements OnInit {
|
export class AddPlantDialogComponent implements OnInit {
|
||||||
selected: number;
|
selected: number;
|
||||||
options$: Observable<number[]>;
|
options$: Observable<number[]>;
|
||||||
plantNames: string[];
|
plantNames: string[];
|
||||||
plant = {} as Plant;
|
plant = {} as Plant;
|
||||||
buttonSavedisabled: boolean;
|
buttonSavedisabled: boolean;
|
||||||
number: number[];
|
|
||||||
|
|
||||||
constructor(public dialogRef: MatDialogRef<AddPlantDialogComponent>,
|
constructor(public dialogRef: MatDialogRef<AddPlantDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||||
@@ -28,16 +32,26 @@ export class AddPlantDialogComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.buttonSavedisabled = true;
|
this.buttonSavedisabled = true; //Button "Save" wird disabled
|
||||||
this.mqttRequestService.publishToPLANTCOUNT();
|
this.mqttRequestService.publishToPLANTCOUNT(); // Pflanzen anzahl wird angefordet#
|
||||||
|
|
||||||
|
// ID's bei denen noch keine Pflanze sind, werden in "option$" rein geschrieben
|
||||||
this.storeService.currentPlants.subscribe(response => {
|
this.storeService.currentPlants.subscribe(response => {
|
||||||
this.options$ = this.storeService.getIDsWithoutPlant(response);
|
this.options$ = this.storeService.getIDsWithoutPlant(response);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Alle aktuellen Pflanzen Namen werden in "plantNames" geschrieben
|
||||||
this.storeService.getAllPlantNames().subscribe(result => {
|
this.storeService.getAllPlantNames().subscribe(result => {
|
||||||
this.plantNames = result
|
this.plantNames = result
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nach dem der Button "Save" geklickt wurde, werden die Daten in die Pflanze geschrieben
|
||||||
|
* die anschließend geprüft wird auf zulässige Werte, falls unzulässige werte vorhanden sind,
|
||||||
|
* wird eine Fehlermeldung angezeigt
|
||||||
|
* @method onSaveDialog
|
||||||
|
*/
|
||||||
onSaveDialog() {
|
onSaveDialog() {
|
||||||
this.plant.PlantName = (<HTMLInputElement>document.getElementById("inputName")).value;
|
this.plant.PlantName = (<HTMLInputElement>document.getElementById("inputName")).value;
|
||||||
this.plant.AirTemperature = +(<HTMLInputElement>document.getElementById("inputAirTemperature")).value;
|
this.plant.AirTemperature = +(<HTMLInputElement>document.getElementById("inputAirTemperature")).value;
|
||||||
@@ -48,6 +62,7 @@ export class AddPlantDialogComponent implements OnInit {
|
|||||||
|
|
||||||
const plantInputString: string = this.plantInputTestFails();
|
const plantInputString: string = this.plantInputTestFails();
|
||||||
|
|
||||||
|
// bei unzulässigen Werten --> Dialog, bei zulässigen Wertden --> Pflanze wird an das Backend geschickt
|
||||||
if (plantInputString) {
|
if (plantInputString) {
|
||||||
this.dialog.open(ErrorDialogComponent, {
|
this.dialog.open(ErrorDialogComponent, {
|
||||||
data: "Error: " + plantInputString,
|
data: "Error: " + plantInputString,
|
||||||
@@ -55,11 +70,16 @@ export class AddPlantDialogComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.mqttRequestService.publishToNEWPLANT(this.plant);
|
this.mqttRequestService.publishToNEWPLANT(this.plant);
|
||||||
this.mqttRequestService.publishToGETALLDATA();
|
this.mqttRequestService.publishToGETALLDATA(); // Daten werden aus dem Backend angefragt
|
||||||
window.location.reload();
|
window.location.reload(); // Seite neu laden
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode prüft ob die Werte die eingegeben wurden, korrekt sind.
|
||||||
|
* @method
|
||||||
|
* @return Bei einem Fehler --> Fehlermeldung; wenn es passt --> string oder leeren string
|
||||||
|
*/
|
||||||
plantInputTestFails(): string {
|
plantInputTestFails(): string {
|
||||||
|
|
||||||
if (this.plantNames.indexOf(this.plant.PlantName) > -1) {
|
if (this.plantNames.indexOf(this.plant.PlantName) > -1) {
|
||||||
@@ -77,12 +97,16 @@ export class AddPlantDialogComponent implements OnInit {
|
|||||||
if (this.plant.SoilMoisture > 100) {
|
if (this.plant.SoilMoisture > 100) {
|
||||||
return "Soil Moisture is to high";
|
return "Soil Moisture is to high";
|
||||||
}
|
}
|
||||||
if (this.plant.Brightness > 100) {
|
if (this.plant.Brightness > 40000) {
|
||||||
return "Brightness is to high";
|
return "Brightness is to high";
|
||||||
}
|
}
|
||||||
return ""; //Empty string is a Boolean false!
|
return ""; //Empty string is a Boolean false!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* beim klick auf den Button, wird das Dialog "MapPlantDialogComponent" geöffnet
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
onOpenMap() {
|
onOpenMap() {
|
||||||
this.dialog.open(MapPlantDialogComponent, {
|
this.dialog.open(MapPlantDialogComponent, {
|
||||||
width: '400px',
|
width: '400px',
|
||||||
@@ -90,11 +114,11 @@ export class AddPlantDialogComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onOptionSelected(numb: number) {
|
/**
|
||||||
let num: number = numb;
|
* nach der Auswahl der ID, wird der Button "Save" enabled
|
||||||
if (num < 7 && num > 0) { //TODOO anzahl der Pflanzen IDS variable machen statt 7 evtl. eine variable
|
* @method
|
||||||
|
*/
|
||||||
|
onOptionSelected() {
|
||||||
this.buttonSavedisabled = false;
|
this.buttonSavedisabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
+19
-10
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||||
import { Plant } from 'src/app/models/plant.model';
|
import { Plant } from 'src/app/models/plant.model';
|
||||||
@@ -9,7 +9,7 @@ import { ErrorDialogComponent } from '../error-dialog/error-dialog.component';
|
|||||||
templateUrl: './configure-plant-dialog.component.html',
|
templateUrl: './configure-plant-dialog.component.html',
|
||||||
styleUrls: ['./configure-plant-dialog.component.css']
|
styleUrls: ['./configure-plant-dialog.component.css']
|
||||||
})
|
})
|
||||||
export class ConfigurePlantDialogComponent implements OnInit {
|
export class ConfigurePlantDialogComponent {
|
||||||
plant: Plant;
|
plant: Plant;
|
||||||
|
|
||||||
constructor(public dialogRef: MatDialogRef<ConfigurePlantDialogComponent>,
|
constructor(public dialogRef: MatDialogRef<ConfigurePlantDialogComponent>,
|
||||||
@@ -19,9 +19,12 @@ export class ConfigurePlantDialogComponent implements OnInit {
|
|||||||
this.plant = data.plant;
|
this.plant = data.plant;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
/**
|
||||||
}
|
* Nach dem der Button "Save" geklickt wurde, werden die Daten in die Pflanze geschrieben
|
||||||
|
* die anschließend geprüft wird auf zulässige Werte, falls unzulässige werte vorhanden sind,
|
||||||
|
* wird eine Fehlermeldung angezeigt
|
||||||
|
* @method onSaveDialog
|
||||||
|
*/
|
||||||
onSaveDialog() {
|
onSaveDialog() {
|
||||||
this.plant.AirTemperature = +(<HTMLInputElement>document.getElementById("inputAirTemperature")).value;
|
this.plant.AirTemperature = +(<HTMLInputElement>document.getElementById("inputAirTemperature")).value;
|
||||||
this.plant.AirHumidity = +(<HTMLInputElement>document.getElementById("inputAirHumidity")).value;
|
this.plant.AirHumidity = +(<HTMLInputElement>document.getElementById("inputAirHumidity")).value;
|
||||||
@@ -29,19 +32,25 @@ export class ConfigurePlantDialogComponent implements OnInit {
|
|||||||
this.plant.Brightness = +(<HTMLInputElement>document.getElementById("inputBrightness")).value;
|
this.plant.Brightness = +(<HTMLInputElement>document.getElementById("inputBrightness")).value;
|
||||||
|
|
||||||
const plantInputString: string = this.plantInputTestFails();
|
const plantInputString: string = this.plantInputTestFails();
|
||||||
|
|
||||||
|
// bei unzulässigen Werten --> Dialog, bei zulässigen Wertden --> Pflanze wird an das Backend geschickt
|
||||||
if (plantInputString) {
|
if (plantInputString) {
|
||||||
this.dialog.open(ErrorDialogComponent, {
|
this.dialog.open(ErrorDialogComponent, {
|
||||||
data: "Error: " + plantInputString,
|
data: "Error: " + plantInputString,
|
||||||
width: '400px',
|
width: '400px',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//Request to the Backend, to change the data of the plant
|
this.mqttRequestService.publishToCONFIGUREPLANT(this.plant); //Request to the Backend, to change the data of the plant
|
||||||
this.mqttRequestService.publishToCONFIGUREPLANT(this.plant);
|
this.mqttRequestService.publishToGETALLDATA(); // Daten werden aus dem Backend angefragt
|
||||||
this.mqttRequestService.publishToGETALLDATA();
|
window.location.reload(); // Seite neu laden
|
||||||
window.location.reload();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methode prüft ob die Werte die eingegeben wurden, korrekt sind.
|
||||||
|
* @method
|
||||||
|
* @return Bei einem Fehler --> Fehlermeldung; wenn es passt --> string oder leeren string
|
||||||
|
*/
|
||||||
plantInputTestFails(): string {
|
plantInputTestFails(): string {
|
||||||
if (this.plant.AirTemperature > 100) {
|
if (this.plant.AirTemperature > 100) {
|
||||||
return "Air Temperature is to high";
|
return "Air Temperature is to high";
|
||||||
@@ -52,7 +61,7 @@ export class ConfigurePlantDialogComponent implements OnInit {
|
|||||||
if (this.plant.SoilMoisture > 100) {
|
if (this.plant.SoilMoisture > 100) {
|
||||||
return "Soil Moisture is to high";
|
return "Soil Moisture is to high";
|
||||||
}
|
}
|
||||||
if (this.plant.Brightness > 100) {
|
if (this.plant.Brightness > 40000) {
|
||||||
return "Brightness is to high";
|
return "Brightness is to high";
|
||||||
}
|
}
|
||||||
return ""; //Empty string is a Boolean false!
|
return ""; //Empty string is a Boolean false!
|
||||||
|
|||||||
+5
-2
@@ -11,12 +11,15 @@ import { ConfigurePlantDialogComponent } from '../configure-plant-dialog/configu
|
|||||||
})
|
})
|
||||||
export class DeleteConfirmDialogComponent {
|
export class DeleteConfirmDialogComponent {
|
||||||
plant: Plant;
|
plant: Plant;
|
||||||
|
|
||||||
constructor(public dialogRef: MatDialogRef<ConfigurePlantDialogComponent>,
|
constructor(public dialogRef: MatDialogRef<ConfigurePlantDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: Plant, private mqttRequestService: MqttRequestService) {
|
@Inject(MAT_DIALOG_DATA) public data: Plant, private mqttRequestService: MqttRequestService) {
|
||||||
this.plant = data;
|
this.plant = data;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* beim Click auf "Delete" wird eine anfrage an das Backend geschickt um die Pflanze zu löschen.
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
onDelete() {
|
onDelete() {
|
||||||
this.mqttRequestService.publishToDELETEPLANT(this.plant.PlantID);
|
this.mqttRequestService.publishToDELETEPLANT(this.plant.PlantID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { ConfigurePlantDialogComponent } from '../configure-plant-dialog/configure-plant-dialog.component';
|
import { ConfigurePlantDialogComponent } from '../configure-plant-dialog/configure-plant-dialog.component';
|
||||||
|
|
||||||
@@ -7,19 +7,15 @@ import { ConfigurePlantDialogComponent } from '../configure-plant-dialog/configu
|
|||||||
templateUrl: './error-dialog.component.html',
|
templateUrl: './error-dialog.component.html',
|
||||||
styleUrls: ['./error-dialog.component.css']
|
styleUrls: ['./error-dialog.component.css']
|
||||||
})
|
})
|
||||||
export class ErrorDialogComponent implements OnInit {
|
export class ErrorDialogComponent {
|
||||||
|
|
||||||
text: string = "";
|
text: string = "";
|
||||||
|
|
||||||
constructor(public dialogRef: MatDialogRef<ConfigurePlantDialogComponent>,
|
constructor(public dialogRef: MatDialogRef<ConfigurePlantDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any) {
|
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||||
this.text = data;
|
this.text = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
window.location.reload();
|
window.location.reload(); // Seite neu laden
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-map-plant-dialog',
|
selector: 'app-map-plant-dialog',
|
||||||
templateUrl: './map-plant-dialog.component.html',
|
templateUrl: './map-plant-dialog.component.html',
|
||||||
styleUrls: ['./map-plant-dialog.component.css']
|
styleUrls: ['./map-plant-dialog.component.css']
|
||||||
})
|
})
|
||||||
export class MapPlantDialogComponent implements OnInit {
|
export class MapPlantDialogComponent {
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
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