Compare commits
101
Commits
0f06a38556
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
556736ccf5 | ||
|
|
410e27adc3 | ||
|
|
f6f6421620 | ||
|
|
5e14633f42 | ||
|
|
1778a211bb | ||
|
|
22bcf7310f | ||
|
|
90235a73f1 | ||
|
|
2c065573a5 | ||
|
|
2a3136503a | ||
|
|
d9ad3bce0c | ||
|
|
e975585f57 | ||
|
|
9b1bcbebde | ||
|
|
a857f870a5 | ||
|
|
847a424049 | ||
|
|
d06f0a46e7 | ||
|
|
9166d4040e | ||
|
|
a13b150dfe | ||
|
|
445373a668 | ||
|
|
bd1daa0489 | ||
|
|
5e21e4b2c5 | ||
|
|
a368637346 | ||
|
|
99b1bbb065 | ||
|
|
a8f365cd4f | ||
|
|
052ace21bf | ||
|
|
612e3b1b93 | ||
|
|
8fa5cae836 | ||
|
|
4cadbf9c29 | ||
|
|
409c38a96d | ||
|
|
6964f9855e | ||
|
|
a0c7bc9077 | ||
|
|
b70d445480 | ||
|
|
93b4075c35 | ||
|
|
4e60cd9ad0 | ||
|
|
062d08fe1a | ||
|
|
7452d6aae8 | ||
|
|
43fbbfcbbb | ||
|
|
0385aa7792 | ||
|
|
c35028f354 | ||
|
|
a16c313a6c | ||
|
|
35118e8c20 | ||
|
|
b8cb4f6217 | ||
|
|
48a4bbe710 | ||
|
|
0dc80b8981 | ||
|
|
1502da2b11 | ||
|
|
9f12f21ac4 | ||
|
|
56f9065fc7 | ||
|
|
a6259e5392 | ||
|
|
20bad535eb | ||
|
|
830d662e08 | ||
|
|
0a53fe171a | ||
|
|
9832c4e986 | ||
|
|
d3ee161444 | ||
|
|
1e4fb4c4b4 | ||
|
|
ae1e67f204 | ||
|
|
58bdd595d5 | ||
|
|
24268d3039 | ||
|
|
5338490503 | ||
|
|
b798e138fa | ||
|
|
0c08ab1d8b | ||
|
|
33e2a058d3 | ||
|
|
cd07f728ee | ||
|
|
c4ef41de90 | ||
|
|
8efbe4fc71 | ||
|
|
f3fab66cb5 | ||
|
|
d54bfdd101 | ||
|
|
307e9fcb7c | ||
|
|
6adc5a3426 | ||
|
|
57cadbbbf0 | ||
|
|
5c02b0a2bc | ||
|
|
14fea73572 | ||
|
|
9396c59e86 | ||
|
|
f29e567f07 | ||
|
|
bbc895c7dd | ||
|
|
6e66611e06 | ||
|
|
9b32acdc68 | ||
|
|
3d0bc3c4d9 | ||
|
|
60be6862b0 | ||
|
|
9388f765f4 | ||
|
|
a3e3da1297 | ||
|
|
d27076ba54 | ||
|
|
7f1af76a55 | ||
|
|
3bb6f8c2c7 | ||
|
|
3f1176917a | ||
|
|
c93d1c4794 | ||
|
|
48f3dc9499 | ||
|
|
70559870a5 | ||
|
|
7b9f560da5 | ||
|
|
e39b2a2753 | ||
|
|
fc545d59a1 | ||
|
|
3a4a8e11a9 | ||
|
|
a941297323 | ||
|
|
f93b7d4f55 | ||
|
|
4cfc6442a7 | ||
|
|
969524966c | ||
|
|
f5af78ffe4 | ||
|
|
8058012260 | ||
|
|
b155fcb6a4 | ||
|
|
3878020331 | ||
|
|
057dd56e2c | ||
|
|
bb09d15a56 | ||
|
|
77c3f32f6f |
@@ -114,3 +114,8 @@ dmypy.json
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
#
|
||||
~*
|
||||
Generated
+36
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="backend_database.db" uuid="a5aefbe2-75a1-4a5e-ae27-5f87ff08045a">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/software/backend/backend_database.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="backend_database" uuid="4f1f9bd8-463b-4b3b-b101-cc4b9b84e2e1">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:C:\Users\bcali\Documents\Uni\BEI6\Projektarbeit\projektarbeit_duelger_waldhauser_caliskan\software\backend\tests\backend_database.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
<libraries>
|
||||
<library>
|
||||
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.40.1/sqlite-jdbc-3.40.1.jar</url>
|
||||
</library>
|
||||
</libraries>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="test_database" uuid="a72361b0-954e-42bd-8930-b14a0b4c1023">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:C:\Users\bcali\Documents\Uni\BEI6\Projektarbeit\projektarbeit_duelger_waldhauser_caliskan\software\backend\tests\test_database.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
<libraries>
|
||||
<library>
|
||||
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.40.1/sqlite-jdbc-3.40.1.jar</url>
|
||||
</library>
|
||||
</libraries>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+15
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PublishConfigData" autoUpload="Always" serverName="backend" remoteFilesAllowedToDisappearOnAutoupload="false">
|
||||
<serverData>
|
||||
<paths name="backend">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping deploy="/home/lego/SMARTGARDENING" local="$PROJECT_DIR$" web="/" />
|
||||
</mappings>
|
||||
</serverdata>
|
||||
</paths>
|
||||
</serverData>
|
||||
<option name="myAutoUpload" value="ALWAYS" />
|
||||
</component>
|
||||
</project>
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (projektarbeit_duelger_waldhauser_caliskan)" jdkType="Python SDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/software/backend/plantdatabase.py" dialect="GenericSQL" />
|
||||
<file url="PROJECT" dialect="SQLite" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SshConfigs">
|
||||
<configs>
|
||||
<sshConfig authType="PASSWORD" host="lego-K53SV" id="40359a92-2c87-41df-943b-cfac73c7ea3d" port="22" nameFormat="DESCRIPTIVE" username="lego" useOpenSSHConfig="true" />
|
||||
</configs>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebServers">
|
||||
<option name="servers">
|
||||
<webServer id="1b3e9e3f-7394-4cbc-a32d-b95ef2abda52" name="backend">
|
||||
<fileTransfer accessType="SFTP" host="lego-K53SV" port="22" sshConfigId="40359a92-2c87-41df-943b-cfac73c7ea3d" sshConfig="lego@lego-K53SV:22 password">
|
||||
<advancedOptions>
|
||||
<advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" />
|
||||
</advancedOptions>
|
||||
</fileTransfer>
|
||||
</webServer>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"lego-education.ev3-micropython"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
"ms-python.python"
|
||||
]
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Download and Run",
|
||||
"type": "ev3devBrowser",
|
||||
"request": "launch",
|
||||
"program": "/home/robot/${workspaceRootFolderName}/main.py",
|
||||
"interactiveTerminal": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.eol": "\n",
|
||||
"debug.openDebug": "neverOpen",
|
||||
"python.linting.enabled": false,
|
||||
"python.languageServer": "None"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Import the necessary modules from the ev3dev2 library
|
||||
from ev3dev2.motor import LargeMotor, OUTPUT_A, OUTPUT_D
|
||||
from time import sleep
|
||||
|
||||
# Connect the motors to the EV3 brick
|
||||
motor_left = LargeMotor(OUTPUT_A)
|
||||
motor_right = LargeMotor(OUTPUT_D)
|
||||
|
||||
# Move the robot forward for 5 seconds
|
||||
motor_left.on(50)
|
||||
motor_right.on(50)
|
||||
sleep(5)
|
||||
motor_left.off()
|
||||
motor_right.off()
|
||||
@@ -0,0 +1,3 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"lego-education.ev3-micropython"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
"ms-python.python"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Download and Run",
|
||||
"type": "ev3devBrowser",
|
||||
"request": "launch",
|
||||
"program": "/home/robot/${workspaceRootFolderName}/main.py",
|
||||
"interactiveTerminal": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.eol": "\n",
|
||||
"debug.openDebug": "neverOpen",
|
||||
"python.linting.enabled": false,
|
||||
"python.languageServer": "None"
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env pybricks-micropython
|
||||
from pybricks.hubs import EV3Brick
|
||||
from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor,
|
||||
InfraredSensor, UltrasonicSensor, GyroSensor)
|
||||
from pybricks.parameters import Port, Stop, Direction, Button, Color
|
||||
from pybricks.tools import wait, StopWatch, DataLog
|
||||
from pybricks.robotics import DriveBase
|
||||
from pybricks.media.ev3dev import SoundFile, ImageFile
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def fun_1(ev3):
|
||||
pass
|
||||
|
||||
|
||||
def fun_2(a,b):
|
||||
return 1
|
||||
|
||||
def move_1(mo_forward_1, mo_forward_2, mo_le_ri, mo_up_down):
|
||||
wait(2000)
|
||||
#move forward
|
||||
mo_forward_1.run_target(500, 1250,wait=False)
|
||||
mo_forward_2.run_target(500, 1250)
|
||||
|
||||
#move arm
|
||||
mo_le_ri.run_target(200,-50)
|
||||
|
||||
#move sensors
|
||||
mo_up_down.run_target(500,2500)
|
||||
wait(2000)
|
||||
mo_up_down.run_target(500,0)
|
||||
|
||||
#move arm
|
||||
mo_le_ri.run_target(200,0)
|
||||
|
||||
#move backwards
|
||||
mo_forward_1.run_target(500, 0,wait=False)
|
||||
mo_forward_2.run_target(500, 0)
|
||||
|
||||
|
||||
|
||||
|
||||
def move_2():
|
||||
pass
|
||||
def move_3():
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
ev3 = EV3Brick()
|
||||
|
||||
motor_forward_1 = Motor(Port.A)
|
||||
motor_forward_2 = Motor(Port.D)
|
||||
motor_le_ri = Motor(Port.B, Direction.COUNTERCLOCKWISE, [8, 56]) #with gear for angle
|
||||
motor_up_down = Motor(Port.C)
|
||||
|
||||
#n = int(sys.argv[1])
|
||||
|
||||
#if n == 1:
|
||||
move_1(motor_forward_1,motor_forward_2,motor_le_ri,motor_up_down)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
from flask import Flask
|
||||
import time
|
||||
from sensors import readSensors
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/", methods=['GET', 'POST'])
|
||||
def hello_world():
|
||||
return "<p>Hello, World!</p>"
|
||||
|
||||
"""
|
||||
SENSOR TEST FUNCTION
|
||||
"""
|
||||
|
||||
@app.route("/measurement_test", methods=['GET'])
|
||||
def measurement_test():
|
||||
return readSensors()
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
@app.route("/temp/<int:sensor_id>", methods=['GET'])
|
||||
def get_temperature(sensor_id):
|
||||
if sensor_id == 1:
|
||||
# here we need to read our sensor values
|
||||
return "23.4"
|
||||
elif sensor_id == 2:
|
||||
return "21.0"
|
||||
else:
|
||||
return "not found"
|
||||
|
||||
|
||||
@app.route("/move/<position>", methods=['POST'])
|
||||
def post_move(position):
|
||||
if position == 'plant1':
|
||||
return f"I moved to position: {position}"
|
||||
# control motors or whatever you like
|
||||
return f"Somewhere moved, no idea where to: {position}"
|
||||
|
||||
|
||||
@app.route("/all", methods=['GET'])
|
||||
def get_all():
|
||||
# read sensor data
|
||||
# TODO
|
||||
time.sleep(10)
|
||||
# put sensor data into a dict
|
||||
ret = {
|
||||
"plant_id": 1,
|
||||
"sensordata_temp":20.5,
|
||||
"sensordata_humidity": 23.5
|
||||
}
|
||||
|
||||
# return it as json data
|
||||
return ret
|
||||
|
||||
app.run(host='192.168.0.106', port=5000)
|
||||
@@ -0,0 +1,14 @@
|
||||
# Import the necessary modules from the ev3dev2 library
|
||||
from ev3dev2.motor import LargeMotor, OUTPUT_B, OUTPUT_C
|
||||
from time import sleep
|
||||
|
||||
# Connect the motors to the EV3 brick
|
||||
motor_left = LargeMotor(OUTPUT_B)
|
||||
motor_right = LargeMotor(OUTPUT_C)
|
||||
|
||||
# Move the robot forward for 5 seconds
|
||||
motor_left.on(50)
|
||||
motor_right.on(50)
|
||||
sleep(5)
|
||||
motor_left.off()
|
||||
motor_right.off()
|
||||
@@ -0,0 +1,89 @@
|
||||
from picamera import PiCamera
|
||||
from time import sleep
|
||||
from datetime import datetime
|
||||
import RPi.GPIO as GPIO
|
||||
import board
|
||||
import adafruit_dht
|
||||
import smbus
|
||||
import time
|
||||
|
||||
#Connect Sensors
|
||||
#Camera
|
||||
#camera = PiCamera()
|
||||
|
||||
#Air Temp&Humidity
|
||||
dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False)
|
||||
|
||||
#Earth Humidity
|
||||
chanel = 21
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setup(chanel, GPIO.IN)
|
||||
|
||||
# Brightness Sensor
|
||||
bus = smbus.SMBus(1)
|
||||
bus.write_byte_data(0x39, 0x00 | 0x80, 0x03)
|
||||
bus.write_byte_data(0x39, 0x01 | 0x80, 0x02)
|
||||
|
||||
|
||||
def readSensors():
|
||||
#Connect Sensors
|
||||
#Camera
|
||||
#camera = PiCamera()
|
||||
|
||||
#Air Temp&Humidity
|
||||
dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False)
|
||||
|
||||
#Earth Humidity
|
||||
chanel = 21
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setup(chanel, GPIO.IN)
|
||||
#Camera
|
||||
#camera.start_preview()
|
||||
dt = datetime.now()
|
||||
ts = datetime.timestamp(dt)
|
||||
#name = "/home/pi/Pictures/{}.jpg".format(ts)
|
||||
#camera.capture(name)
|
||||
print("Camera on")
|
||||
#camera.stop_preview()
|
||||
|
||||
#Air Temp&Humidity
|
||||
temperature_c = dhtDevice.temperature
|
||||
humidity = dhtDevice.humidity
|
||||
print("Temp: {:.1f} C Humidity: {}% ".format(temperature_c, humidity))
|
||||
|
||||
|
||||
#Earth Humidit:
|
||||
ground_humidity = GPIO.input(chanel)
|
||||
print("Earth Humitiy: {}".format(ground_humidity))
|
||||
|
||||
|
||||
#Brightness Sensor
|
||||
# Read data back from 0x0C(12) with command register, 0x80(128), 2 bytes
|
||||
# ch0 LSB, ch0 MSB
|
||||
data = bus.read_i2c_block_data(0x39, 0x0C | 0x80, 2)
|
||||
|
||||
# Read data back from 0x0E(14) with command register, 0x80(128), 2 bytes
|
||||
# ch1 LSB, ch1 MSB
|
||||
data1 = bus.read_i2c_block_data(0x39, 0x0E | 0x80, 2)
|
||||
|
||||
# Convert the data
|
||||
ch0 = data[1] * 256 + data[0]
|
||||
ch1 = data1[1] * 256 + data1[0]
|
||||
light_intesity = ch0-ch1
|
||||
|
||||
# Output data to screen
|
||||
print("Visible Value :%d lux" %(ch0 - ch1))
|
||||
|
||||
return {"Air_Temperature": temperature_c,
|
||||
"Air_Humidity": humidity,
|
||||
"Ground Humidity": ground_humidity,
|
||||
"Light_Intensity": light_intesity
|
||||
}
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
test = readSensors()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,14 @@
|
||||
from picamera import PiCamera
|
||||
from time import sleep
|
||||
from datetime import datetime
|
||||
|
||||
camera = PiCamera()
|
||||
|
||||
camera.start_preview()
|
||||
|
||||
dt = datetime.now()
|
||||
ts = datetime.timestamp(dt)
|
||||
name = "/home/pi/Pictures/{}.jpg".format(ts)
|
||||
|
||||
camera.capture(name)
|
||||
camera.stop_preview()
|
||||
@@ -0,0 +1,29 @@
|
||||
import time
|
||||
import board
|
||||
import adafruit_dht
|
||||
|
||||
# connect Device
|
||||
dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False)
|
||||
|
||||
# you can pass DHT22 use_pulseio=False if you wouldn't like to use pulseio.
|
||||
# This may be necessary on a Linux single board computer like the Raspberry Pi,
|
||||
# but it will not work in CircuitPython.
|
||||
# dhtDevice = adafruit_dht.DHT22(board.D18, use_pulseio=False)
|
||||
|
||||
while True:
|
||||
try:
|
||||
# Print the values to the serial port
|
||||
temperature_c = dhtDevice.temperature
|
||||
humidity = dhtDevice.humidity
|
||||
print("Temp: {:.1f} C Humidity: {}% ".format(temperature_c, humidity))
|
||||
|
||||
except RuntimeError as error:
|
||||
# Errors happen fairly often, DHT's are hard to read, just keep going
|
||||
print(error.args[0])
|
||||
time.sleep(2.0)
|
||||
continue
|
||||
except Exception as error:
|
||||
dhtDevice.exit()
|
||||
raise error
|
||||
|
||||
time.sleep(2.0)
|
||||
@@ -0,0 +1,19 @@
|
||||
import RPi.GPIO as GPIO
|
||||
import time
|
||||
|
||||
chanel = 21
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setup(chanel, GPIO.IN)
|
||||
|
||||
def callback(chanel):
|
||||
if GPIO.input(chanel):
|
||||
print("No water detected")
|
||||
else:
|
||||
print("Water detected")
|
||||
|
||||
print(GPIO.input(chanel))
|
||||
|
||||
GPIO.add_event_detect(chanel, GPIO.BOTH, bouncetime=300)
|
||||
GPIO.add_event_callback(chanel, callback)
|
||||
|
||||
callback(chanel)
|
||||
@@ -0,0 +1,33 @@
|
||||
import smbus
|
||||
import time
|
||||
|
||||
# Get I2C bus
|
||||
bus = smbus.SMBus(1)
|
||||
|
||||
# TSL2561 address, 0x39(57)
|
||||
# Select control register, 0x00(00) with command register, 0x80(128)
|
||||
# 0x03(03) Power ON mode
|
||||
bus.write_byte_data(0x39, 0x00 | 0x80, 0x03)
|
||||
# TSL2561 address, 0x39(57)
|
||||
# Select timing register, 0x01(01) with command register, 0x80(128)
|
||||
# 0x02(02) Nominal integration time = 402ms
|
||||
bus.write_byte_data(0x39, 0x01 | 0x80, 0x02)
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# Read data back from 0x0C(12) with command register, 0x80(128), 2 bytes
|
||||
# ch0 LSB, ch0 MSB
|
||||
data = bus.read_i2c_block_data(0x39, 0x0C | 0x80, 2)
|
||||
|
||||
# Read data back from 0x0E(14) with command register, 0x80(128), 2 bytes
|
||||
# ch1 LSB, ch1 MSB
|
||||
data1 = bus.read_i2c_block_data(0x39, 0x0E | 0x80, 2)
|
||||
|
||||
# Convert the data
|
||||
ch0 = data[1] * 256 + data[0]
|
||||
ch1 = data1[1] * 256 + data1[0]
|
||||
|
||||
# Output data to screen
|
||||
print("Full Spectrum(IR + Visible) :%d lux" %ch0)
|
||||
print("Infrared Value :%d lux" %ch1)
|
||||
print("Visible Value :%d lux" %(ch0 - ch1))
|
||||
@@ -0,0 +1,21 @@
|
||||
from pybricks.hubs import EV3Brick
|
||||
from pybricks.ev3devices import Motor
|
||||
from pybricks.parameters import Port
|
||||
#from ev3dev2.motor import LargeMotor, OUTPUT_A, OUTPUT_B, SpeedPercent, MoveTank
|
||||
|
||||
# Initialize the EV3 brick.
|
||||
ev3 = EV3Brick()
|
||||
|
||||
# Initialize a motor at port B.
|
||||
test_motor = Motor(Port.B)
|
||||
#test = ev3dev2.motor.LargeMotor('/dev/ttyAMA0')
|
||||
#test.on(500)
|
||||
|
||||
# Run the motor up to 500 degrees per second, for 2 seconds.
|
||||
test_motor.run_time(500, 2000)
|
||||
|
||||
# Stop the motor.
|
||||
test_motor.stop()
|
||||
|
||||
# Beep to indicate that the program has finished.
|
||||
ev3.speaker.beep()
|
||||
@@ -0,0 +1,132 @@
|
||||
Adafruit-Blinka==8.15.2
|
||||
adafruit-circuitpython-busdevice==5.2.3
|
||||
adafruit-circuitpython-dht==3.7.8
|
||||
adafruit-circuitpython-requests==1.13.0
|
||||
adafruit-circuitpython-tsl2561==3.3.15
|
||||
adafruit-circuitpython-typing==1.8.3
|
||||
Adafruit-GPIO==1.0.3
|
||||
Adafruit-PlatformDetect==3.40.3
|
||||
Adafruit-PureIO==1.1.10
|
||||
altgraph==0.17.3
|
||||
arandr==0.1.10
|
||||
astroid==2.5.1
|
||||
asttokens==2.0.4
|
||||
automationhat==0.2.0
|
||||
beautifulsoup4==4.9.3
|
||||
blinker==1.4
|
||||
blinkt==0.1.2
|
||||
buttonshim==0.0.2
|
||||
Cap1xxx==0.1.3
|
||||
certifi==2022.12.7
|
||||
cffi==1.15.1
|
||||
chardet==4.0.0
|
||||
charset-normalizer==3.0.1
|
||||
click==8.1.3
|
||||
colorama==0.4.4
|
||||
colorzero==1.1
|
||||
cryptography==3.3.2
|
||||
cupshelpers==1.0
|
||||
dbus-python==1.2.16
|
||||
distro==1.5.0
|
||||
docutils==0.16
|
||||
drumhat==0.1.0
|
||||
envirophat==1.0.0
|
||||
ExplorerHAT==0.4.2
|
||||
Flask==2.2.3
|
||||
fourletterphat==0.1.0
|
||||
gpiozero==1.6.2
|
||||
html5lib==1.1
|
||||
idna==3.4
|
||||
importlib-metadata==6.0.0
|
||||
isort==5.6.4
|
||||
itsdangerous==2.1.2
|
||||
jedi==0.18.0
|
||||
Jinja2==3.1.2
|
||||
lazy-object-proxy==0.0.0
|
||||
logilab-common==1.8.1
|
||||
lxml==4.6.3
|
||||
MarkupSafe==2.1.2
|
||||
mccabe==0.6.1
|
||||
microdotphat==0.2.1
|
||||
mote==0.0.4
|
||||
motephat==0.0.3
|
||||
mypy==0.812
|
||||
mypy-extensions==0.4.3
|
||||
numpy==1.19.5
|
||||
oauthlib==3.1.0
|
||||
pantilthat==0.0.7
|
||||
parso==0.8.1
|
||||
pexpect==4.8.0
|
||||
pgzero==1.2
|
||||
phatbeat==0.1.1
|
||||
pianohat==0.1.0
|
||||
picamera==1.13
|
||||
picamera2==0.3.8
|
||||
pidng==4.0.9
|
||||
piexif==1.1.3
|
||||
piglow==1.2.5
|
||||
pigpio==1.78
|
||||
Pillow==8.1.2
|
||||
psutil==5.8.0
|
||||
pybricks==3.2.0
|
||||
pycairo==1.16.2
|
||||
pycparser==2.21
|
||||
pycups==2.0.1
|
||||
pyftdi==0.54.0
|
||||
pygame==1.9.6
|
||||
Pygments==2.7.1
|
||||
PyGObject==3.38.0
|
||||
pyinotify==0.9.6
|
||||
pyinstaller==5.8.0
|
||||
pyinstaller-hooks-contrib==2023.0
|
||||
PyJWT==1.7.1
|
||||
pylint==2.7.2
|
||||
PyOpenGL==3.1.5
|
||||
pyOpenSSL==20.0.1
|
||||
PyQt5==5.15.2
|
||||
PyQt5-sip==12.8.1
|
||||
pyserial==3.5b0
|
||||
pysmbc==1.0.23
|
||||
python-apt==2.2.1
|
||||
python-ev3dev==1.2.0
|
||||
python-ev3dev2==2.1.0.post1
|
||||
python-prctl==1.7
|
||||
pyusb==1.2.1
|
||||
rainbowhat==0.1.0
|
||||
reportlab==3.5.59
|
||||
requests==2.28.2
|
||||
requests-oauthlib==1.0.0
|
||||
responses==0.12.1
|
||||
roman==2.0.0
|
||||
rpi-ws281x==4.3.4
|
||||
RPi.GPIO==0.7.0
|
||||
RTIMULib==7.2.1
|
||||
scrollphat==0.0.7
|
||||
scrollphathd==1.2.1
|
||||
Send2Trash==1.6.0b1
|
||||
sense-hat==2.4.0
|
||||
simplejpeg==1.6.4
|
||||
simplejson==3.17.2
|
||||
six==1.16.0
|
||||
skywriter==0.0.7
|
||||
smbus==1.1.post2
|
||||
smbus-cffi==0.5.1
|
||||
sn3218==1.2.7
|
||||
soupsieve==2.2.1
|
||||
spidev==3.5
|
||||
ssh-import-id==5.10
|
||||
sysv-ipc==1.1.0
|
||||
thonny==4.0.1
|
||||
toml==0.10.1
|
||||
touchphat==0.0.1
|
||||
tsl2561==3.4.0
|
||||
twython==3.8.2
|
||||
typed-ast==1.4.2
|
||||
typing_extensions==4.5.0
|
||||
unicornhathd==0.0.4
|
||||
urllib3==1.26.14
|
||||
v4l2-python3==0.3.1
|
||||
webencodings==0.5.1
|
||||
Werkzeug==2.2.3
|
||||
wrapt==1.12.1
|
||||
zipp==3.15.0
|
||||
@@ -0,0 +1,2 @@
|
||||
python 3.8.2
|
||||
python 2.7.18
|
||||
@@ -0,0 +1,11 @@
|
||||
certifi==2022.12.7
|
||||
charset-normalizer==3.0.1
|
||||
click==8.1.3
|
||||
Flask==2.2.3
|
||||
idna==3.4
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
MarkupSafe==2.1.2
|
||||
requests==2.28.2
|
||||
urllib3==1.26.14
|
||||
adafruit-circuitpython-dht==3.7.8
|
||||
@@ -0,0 +1 @@
|
||||
print("Hello World")
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+26
-1
@@ -1,11 +1,36 @@
|
||||
# Adafruit-Blinka==8.16.1 # Raspy only, not possible on other devices -> commented out
|
||||
# adafruit-circuitpython-busdevice==5.2.4
|
||||
# adafruit-circuitpython-dht==3.7.8
|
||||
# adafruit-circuitpython-requests==1.13.1
|
||||
# adafruit-circuitpython-tsl2561==3.3.15
|
||||
# adafruit-circuitpython-typing==1.9.0
|
||||
# Adafruit-PlatformDetect==3.43.0
|
||||
# Adafruit-PureIO==1.1.10
|
||||
certifi==2022.12.7
|
||||
charset-normalizer==3.0.1
|
||||
click==8.1.3
|
||||
Flask==2.2.3
|
||||
idna==3.4
|
||||
importlib-metadata==6.1.0
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
MarkupSafe==2.1.2
|
||||
paho-mqtt==1.6.1
|
||||
# pkg_resources==0.0.0
|
||||
# pyftdi==0.54.0
|
||||
# pyserial==3.5
|
||||
# pyusb==1.2.1
|
||||
requests==2.28.2
|
||||
# rpi-ws281x==4.3.4
|
||||
# RPi.GPIO==0.7.1
|
||||
# sysv-ipc==1.1.0
|
||||
typing_extensions==4.5.0
|
||||
urllib3==1.26.14
|
||||
paho-mqtt
|
||||
Werkzeug==2.2.3
|
||||
zipp==3.15.0
|
||||
python-ev3dev2==2.1.0.post1
|
||||
pytest==7.3.1
|
||||
pip==23.1.2
|
||||
# spidev==3.6
|
||||
# picamera==1.13
|
||||
numpy==1.19.5
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
@@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,42 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,27 @@
|
||||
# Frontend
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.8.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"Frontend": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/frontend",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/custom-theme.scss",
|
||||
"src/styles.css",
|
||||
"node_modules/leaflet/dist/leaflet.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "Frontend:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "Frontend:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "Frontend:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/custom-theme.scss",
|
||||
"src/styles.css",
|
||||
"node_modules/leaflet/dist/leaflet.css"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/frontend'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
Generated
+21168
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.2.0",
|
||||
"@angular/cdk": "^13.0.0",
|
||||
"@angular/common": "^14.2.0",
|
||||
"@angular/compiler": "^14.2.0",
|
||||
"@angular/core": "^14.2.0",
|
||||
"@angular/forms": "^14.2.0",
|
||||
"@angular/material": "^13.0.0",
|
||||
"@angular/platform-browser": "^14.2.0",
|
||||
"@angular/platform-browser-dynamic": "^14.2.0",
|
||||
"@angular/router": "^14.2.0",
|
||||
"@types/googlemaps": "^3.43.3",
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"leaflet": "^1.9.3",
|
||||
"mqtt": "^4.3.7",
|
||||
"ngx-mqtt": "^15.0.0-alpha.1",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^14.2.8",
|
||||
"@angular/cli": "~14.2.8",
|
||||
"@angular/compiler-cli": "^14.2.0",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"jasmine-core": "~4.3.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"typescript": "~4.7.2"
|
||||
},
|
||||
"description": "This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.8.",
|
||||
"main": "karma.conf.js",
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#robotIsOnTour {
|
||||
color: red;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
#robotIsReady {
|
||||
color: green;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-rows: 40% 20% 7%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: Cormorant;
|
||||
}
|
||||
|
||||
.item-input {
|
||||
grid-row: 1 / 2;
|
||||
grid-column: 1/-1;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-box-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.item-box-output {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.item-output {
|
||||
grid-row: 3 / 4;
|
||||
grid-column: 1/-1;
|
||||
justify-self: start;
|
||||
align-self: start;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.item-robotText {
|
||||
grid-row: 2 / 3;
|
||||
grid-column: 1/-1;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
font-size: 1.2rem;
|
||||
padding: 0.7rem;
|
||||
border: 0.125rem;
|
||||
border-style: solid;
|
||||
border-radius: 1rem;
|
||||
border-color: silver;
|
||||
box-shadow: 0.2rem 0.2rem 0.2rem silver;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<div class="container">
|
||||
<div class="item-box-input">
|
||||
<div class="item-input">
|
||||
<app-input></app-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-box-output">
|
||||
<div class="item-output">
|
||||
<app-output></app-output>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-robotText">
|
||||
<div *ngIf="!(robotIsReady$ | async)" id="robotIsOnTour">Robot is on Tour</div>
|
||||
<div *ngIf="(robotIsReady$ | async)" id="robotIsReady">Robot is Ready</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { HomeComponent } from './home.component';
|
||||
|
||||
// describe('HomeComponent', () => {
|
||||
// let component: HomeComponent;
|
||||
// let fixture: ComponentFixture<HomeComponent>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [ HomeComponent ]
|
||||
// })
|
||||
// .compileComponents();
|
||||
|
||||
// fixture = TestBed.createComponent(HomeComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { StoreService } from '../Service/store.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.css']
|
||||
})
|
||||
export class HomeComponent {
|
||||
// Aktueller RobotStatus wird subscribed
|
||||
robotIsReady$: Observable<boolean> = this.storeService.currentRobotReady;
|
||||
|
||||
constructor(private storeService: StoreService) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
.container {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
grid-template-rows: 25% 75%;
|
||||
font-family: Cormorant;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.inputText {
|
||||
grid-row: 1 / 2;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.inputMicro {
|
||||
width: 8.3rem;
|
||||
height: 100%;
|
||||
grid-row: 2 / 3;
|
||||
justify-self: center;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 6.25rem 2.31rem;
|
||||
border: 0.125rem;
|
||||
border-style: solid;
|
||||
border-radius: 10.625rem;
|
||||
border-color: silver;
|
||||
box-shadow: 0.2rem 0.2rem 0.2rem silver;
|
||||
background-color: white;
|
||||
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
transform: scale(8);
|
||||
margin-left: 1rem;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="container">
|
||||
<div class="inputText">
|
||||
<span>{{text}}</span>
|
||||
</div>
|
||||
<div class="inputMicro">
|
||||
<button mat-menu-item (click)="startStopService()">
|
||||
<mat-icon [ngStyle]="{'color': micIsClicked ? 'blue' : 'silver' }">micro</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||
import { SpeechService } from '../speech.service';
|
||||
import { InputComponent } from './input.component';
|
||||
|
||||
describe('InputComponent', () => {
|
||||
let component: InputComponent;
|
||||
let fixture: ComponentFixture<InputComponent>;
|
||||
let speechService: SpeechService;
|
||||
let mqttRequestService: MqttRequestService;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [InputComponent],
|
||||
providers: [SpeechService, MqttRequestService],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InputComponent);
|
||||
component = fixture.componentInstance;
|
||||
speechService = TestBed.inject(SpeechService);
|
||||
mqttRequestService = TestBed.inject(MqttRequestService);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create the InputComponent', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should initialize the SpeechService if not already initialized', () => {
|
||||
spyOn(speechService, 'init');
|
||||
component.ngOnInit();
|
||||
expect(speechService.init).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update the text and publish to INPUT when receiving a message', () => {
|
||||
const message = 'Test message';
|
||||
spyOn(mqttRequestService, 'publishToINPUT');
|
||||
component.ngOnInit();
|
||||
// component.service.currentApprovalStageMessage.next(message);
|
||||
expect(component.text).toBe(message);
|
||||
expect(mqttRequestService.publishToINPUT).toHaveBeenCalledWith(message);
|
||||
});
|
||||
|
||||
it('should stop the service when micIsClicked is true', () => {
|
||||
spyOn(component.service, 'stop');
|
||||
component.micIsClicked = true;
|
||||
component.startStopService();
|
||||
expect(component.service.stop).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should start the service when micIsClicked is false', () => {
|
||||
spyOn(component.service, 'start');
|
||||
component.micIsClicked = false;
|
||||
component.startStopService();
|
||||
expect(component.service.start).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should unsubscribe from subscription and call onDestroy', () => {
|
||||
spyOn(component.subscription, 'unsubscribe');
|
||||
spyOn(component.service, 'onDestory');
|
||||
component.ngOnDestroy();
|
||||
expect(component.subscription.unsubscribe).toHaveBeenCalled();
|
||||
expect(component.service.onDestory).toHaveBeenCalled();
|
||||
expect(component.text).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs-compat';
|
||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||
import { SpeechService } from '../speech.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-input',
|
||||
templateUrl: './input.component.html',
|
||||
styleUrls: ['./input.component.css']
|
||||
})
|
||||
export class InputComponent implements OnInit, OnDestroy {
|
||||
text: string = 'Init';
|
||||
micIsClicked = false;
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
public speechService: SpeechService,
|
||||
private mqttRequestService: MqttRequestService
|
||||
) {
|
||||
if (!this.speechService.initSpeechServiceStarted) {
|
||||
this.speechService.init();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// subscribe auf die eingegebene Spracheingabe, gibt als response einen string
|
||||
this.subscription = this.speechService.currentApprovalStageMessage.subscribe(msg => {
|
||||
this.text = msg;
|
||||
if (this.text !== '') {
|
||||
this.micIsClicked = false;
|
||||
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() {
|
||||
//
|
||||
if (this.micIsClicked) {
|
||||
this.micIsClicked = false;
|
||||
this.speechService.stop(); // Spracheingabe stoppen
|
||||
} else {
|
||||
this.micIsClicked = true;
|
||||
this.speechService.start(); //Spracheingabe starten
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe()
|
||||
this.speechService.onDestory();
|
||||
this.text = '';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: 10% 90%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-family: Cormorant;
|
||||
|
||||
border: 0.125rem;
|
||||
border-style: solid;
|
||||
border-radius: 1rem;
|
||||
border-color: silver;
|
||||
box-shadow: 0.2rem 0.2rem 0.2rem silver;
|
||||
}
|
||||
|
||||
.outputIcon {
|
||||
grid-column: 1 / 2;
|
||||
justify-self: start;
|
||||
align-self: end;
|
||||
|
||||
}
|
||||
|
||||
.outputText {
|
||||
grid-column: 2 / 3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
margin-left: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="container">
|
||||
<div class="outputIcon">
|
||||
<mat-icon>volume_up</mat-icon>
|
||||
</div>
|
||||
<div class="outputText">
|
||||
<div>{{outputText}}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { OutputComponent } from './output.component';
|
||||
|
||||
// describe('OutputComponent', () => {
|
||||
// let component: OutputComponent;
|
||||
// let fixture: ComponentFixture<OutputComponent>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [ OutputComponent ]
|
||||
// })
|
||||
// .compileComponents();
|
||||
|
||||
// fixture = TestBed.createComponent(OutputComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||
|
||||
declare var speechSynthesis: any;
|
||||
|
||||
@Component({
|
||||
selector: 'app-output',
|
||||
templateUrl: './output.component.html',
|
||||
styleUrls: ['./output.component.css']
|
||||
})
|
||||
export class OutputComponent implements OnInit, OnDestroy {
|
||||
subscription: Subscription;
|
||||
outputText: string = '';
|
||||
constructor(private mqttRequestService: MqttRequestService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
// subsribe auf "responseMessage", sie wird jedes mal getriggert wenn eine Spracheingabe statt findet
|
||||
this.subscription = this.mqttRequestService.responseMessage.subscribe(msg => {
|
||||
setTimeout(() => {
|
||||
this.outputText = msg;
|
||||
this.speak(this.outputText);
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode spricht das übergeben string aus --> Sprachausgabe
|
||||
* @method speak
|
||||
* @param string
|
||||
*/
|
||||
speak(text: string) {
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.lang = 'en-US';
|
||||
speechSynthesis.speak(utterance);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
this.mqttRequestService.onDestroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// import { TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { SpeechService } from './speech.service';
|
||||
|
||||
// describe('SpeechService', () => {
|
||||
// let service: SpeechService;
|
||||
|
||||
// beforeEach(() => {
|
||||
// TestBed.configureTestingModule({});
|
||||
// service = TestBed.inject(SpeechService);
|
||||
// });
|
||||
|
||||
// it('should be created', () => {
|
||||
// expect(service).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs-compat';
|
||||
declare var webkitSpeechRecognition: any;
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SpeechService {
|
||||
/*
|
||||
* currentApprovalStageMessage wird als observable festgelegt,
|
||||
* damit man aus anderen Komponenten die änderungen mitbekommt!
|
||||
*/
|
||||
private approvalStageMessage = new BehaviorSubject('');
|
||||
currentApprovalStageMessage = this.approvalStageMessage.asObservable();
|
||||
|
||||
initSpeechServiceStarted: boolean = false;
|
||||
|
||||
recognition = new webkitSpeechRecognition();
|
||||
|
||||
isStoppedSpeechRecog = false;
|
||||
public text = '';
|
||||
tempWords: string | undefined;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
init() {
|
||||
this.initSpeechServiceStarted = true;
|
||||
this.recognition.interimResults = true;
|
||||
this.recognition.lang = 'en-US';
|
||||
|
||||
this.recognition.addEventListener('result', (e: { results: Iterable<unknown> | ArrayLike<unknown>; }) => {
|
||||
const transcript = Array.from(e.results)
|
||||
.map((result: any) => result[0])
|
||||
.map((result) => result.transcript)
|
||||
.join('');
|
||||
this.tempWords = transcript;
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
this.text = '';
|
||||
this.isStoppedSpeechRecog = false;
|
||||
this.recognition.start(); // Start der Sprachaufnahme
|
||||
this.recognition.addEventListener('end', () => {
|
||||
this.wordConcat()
|
||||
this.recognition.stop(); // Ende der Sprachaufnahme
|
||||
if (!this.isStoppedSpeechRecog) {
|
||||
this.isStoppedSpeechRecog = true;
|
||||
this.approvalStageMessage.next(this.text); // String was eingelesen wurde, wird mit der Methode "next(..)" an alle subsriber weitergeleitet!
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.recognition.stop();
|
||||
if (!this.isStoppedSpeechRecog) {
|
||||
this.isStoppedSpeechRecog = true;
|
||||
console.log("End speech recognition");
|
||||
this.approvalStageMessage.next(this.text);
|
||||
}
|
||||
}
|
||||
|
||||
// addiert die Wörter bei der Sprachaufnahme zusammen
|
||||
wordConcat() {
|
||||
this.text = this.text + ' ' + this.tempWords;
|
||||
this.tempWords = '';
|
||||
}
|
||||
|
||||
onDestory() {
|
||||
this.text = '';
|
||||
this.approvalStageMessage.next(this.text);
|
||||
this.isStoppedSpeechRecog = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
div {
|
||||
height: 70%;
|
||||
width: 80%;
|
||||
margin: 2% 9%;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<div #mapContainer></div>
|
||||
@@ -0,0 +1,23 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { MapComponent } from './map.component';
|
||||
|
||||
// describe('MapComponent', () => {
|
||||
// let component: MapComponent;
|
||||
// let fixture: ComponentFixture<MapComponent>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [MapComponent]
|
||||
// })
|
||||
// .compileComponents();
|
||||
|
||||
// fixture = TestBed.createComponent(MapComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,23 @@
|
||||
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
|
||||
import * as L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
|
||||
@Component({
|
||||
selector: 'app-map',
|
||||
templateUrl: './map.component.html',
|
||||
styleUrls: ['./map.component.css']
|
||||
})
|
||||
export class MapComponent implements AfterViewInit {
|
||||
@ViewChild('mapContainer', { static: false }) mapContainer: ElementRef;
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const map = L.map(this.mapContainer.nativeElement).setView([51.505, -0.09], 13);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
}).addTo(map);
|
||||
|
||||
L.marker([49.452785, 11.096963]).addTo(map) //Koordinaten [X,Y]
|
||||
.bindPopup('Robot')
|
||||
.openPopup();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
.IMG-Plants {
|
||||
border-radius: 0.03125rem;
|
||||
width: 9.25rem;
|
||||
height: 7.375rem;
|
||||
margin: 0rem 0.3125rem;
|
||||
}
|
||||
|
||||
.icons-measurements {
|
||||
width: 1rem;
|
||||
height: 0.75rem;
|
||||
margin: 0rem 0.3125rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-width: 40rem;
|
||||
width: 100%;
|
||||
min-height: 9rem;
|
||||
padding: 0.3125rem;
|
||||
box-shadow: 0.3125rem 0.3125rem 0.3125rem silver;
|
||||
border-radius: 1.25rem;
|
||||
border-color: silver;
|
||||
border-width: 0.0625rem;
|
||||
border-style: solid;
|
||||
margin-bottom: 1rem;
|
||||
font-family: Cormorant;
|
||||
display: grid;
|
||||
grid-template-columns: 45% 50% 5%;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.item-header-content {
|
||||
grid-column: 1/2;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: 55% 45%;
|
||||
grid-template-rows: 25% 75%;
|
||||
justify-content: start;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
grid-column: 1/3;
|
||||
grid-row: 1/2;
|
||||
justify-self: center;
|
||||
align-self: start;
|
||||
font-size: 1.875rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.item-Measurements-IconsAndNames {
|
||||
grid-column: 1/2;
|
||||
grid-row: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-Measurements-Values {
|
||||
grid-column: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-IMG {
|
||||
justify-self: center;
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
.item-delete {
|
||||
justify-self: end;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
|
||||
.delete-icon {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<div class="container">
|
||||
<div class="item-header-content">
|
||||
<div class="item-header">
|
||||
<p class="card-title">{{plant.PlantName}}</p>
|
||||
</div>
|
||||
<div class="item-Measurements-IconsAndNames">
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/air_humidity_icon.png" alt="not available">
|
||||
Air Humidity: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/air_temperature_icon.png" alt="not available">
|
||||
Air Temperature: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/soil_moisture_icon.png" alt="not available">
|
||||
Soil Moisture: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/brightness_icon.png" alt="not available">
|
||||
Brightness:<br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/Timestamp_icon.png" alt="not available">
|
||||
Timestamp:
|
||||
</div>
|
||||
<div class="item-Measurements-Values">
|
||||
<p>{{plant.AirHumidity}} %</p>
|
||||
<p>{{plant.AirTemperature}} °C</p>
|
||||
<p>{{plant.SoilMoisture}} %</p>
|
||||
<p>{{plant.Brightness}} Lux</p>
|
||||
<p>{{getFormattedLastRequestedTimestamp() | async}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-IMG">
|
||||
<img class="IMG-Plants" src="assets/images/Pflanze.png" alt="Plant is not available">
|
||||
<img class="IMG-Plants" src="assets/images/Pflanze.png" alt="Plant is not available">
|
||||
</div>
|
||||
<div class="item-delete">
|
||||
<button class="item-Configure" mat-icon-button color="primary" (click)="onConfigurePlant()">
|
||||
<mat-icon>create</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" (click)="onDeletePlant()">
|
||||
<mat-icon class="delete-icon">delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { PlantCardComponent } from './plant-card.component';
|
||||
|
||||
// describe('PlantCardComponent', () => {
|
||||
// let component: PlantCardComponent;
|
||||
// let fixture: ComponentFixture<PlantCardComponent>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [PlantCardComponent]
|
||||
// })
|
||||
// .compileComponents();
|
||||
|
||||
// fixture = TestBed.createComponent(PlantCardComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||
import { ConfigurePlantDialogComponent } from 'src/app/dialog/configure-plant-dialog/configure-plant-dialog.component';
|
||||
import { DeleteConfirmDialogComponent } from 'src/app/dialog/delete-confirm-dialog/delete-confirm-dialog.component';
|
||||
import { Plant } from 'src/app/models/plant.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-plant-card',
|
||||
templateUrl: './plant-card.component.html',
|
||||
styleUrls: ['./plant-card.component.css']
|
||||
})
|
||||
export class PlantCardComponent {
|
||||
@Input() plant: Plant;
|
||||
currentDate: Date;
|
||||
|
||||
constructor(public dialog: MatDialog, private mqttRequestService: MqttRequestService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode wird in der HTML datei verwendet damit der Wert nicht undefined ist.
|
||||
* @method
|
||||
*/
|
||||
getFormattedLastRequestedTimestamp(): Observable<string> {
|
||||
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 {
|
||||
const datePlant = new Date(timestamp);
|
||||
this.currentDate = new Date();
|
||||
let seconds = ((this.currentDate.getTime() - datePlant.getTime()) / (1000));
|
||||
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (days >= 1) {
|
||||
return `${days} day${days > 1 ? 's' : ''} ago`;
|
||||
} else if (hours >= 1) {
|
||||
return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
||||
} else if (minutes >= 1) {
|
||||
return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
||||
} else {
|
||||
seconds = Math.floor(seconds);
|
||||
return `${seconds} second${seconds !== 1 ? 's' : ''} ago`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode öffnet den Dialog "DeleteConfirmDialogComponent"
|
||||
* @method
|
||||
*/
|
||||
onDeletePlant() {
|
||||
this.dialog.open(DeleteConfirmDialogComponent, {
|
||||
data: this.plant,
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode öffnet den Dialog "ConfigurePlantDialogComponent"
|
||||
* @method
|
||||
*/
|
||||
onConfigurePlant() {
|
||||
this.dialog.open(ConfigurePlantDialogComponent, {
|
||||
data: {
|
||||
plant: this.plant
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
.container {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: 20% 80%;
|
||||
margin-top: 3.125rem;
|
||||
}
|
||||
|
||||
.buttonSearch {
|
||||
background-color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.item-Search {
|
||||
grid-row: 1 / 2;
|
||||
grid-column: 1/2;
|
||||
justify-self: end;
|
||||
align-self: center;
|
||||
margin-right: 8rem;
|
||||
}
|
||||
|
||||
.item-NewPlant {
|
||||
grid-row: 1 / 2;
|
||||
grid-column: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
margin-left: 18rem;
|
||||
}
|
||||
|
||||
.item-Cards {
|
||||
grid-row: 2 / -1;
|
||||
grid-column: 1/3;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
mat-spinner {
|
||||
margin: 7rem auto;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<div class="container" *ngIf="(plants$ |async) !== undefined; else errorLoadingData">
|
||||
<div class="item-Search">
|
||||
<form class="example-form">
|
||||
<mat-form-field class=" example-full-width" appearance="fill">
|
||||
<input type="text" placeholder="Plant Name" aria-label="Plant Name" matInput [formControl]="myControl"
|
||||
[matAutocomplete]="auto">
|
||||
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete"
|
||||
(optionSelected)='onOptionSelect($event.option.value)'>
|
||||
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
|
||||
{{option}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<button class="buttonSearch"> <mat-icon matSuffix>search</mat-icon></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="example-button-row item-NewPlant">
|
||||
<button class="buttonNew" mat-raised-button color="primary" (click)="onNewPlant()">New Plant</button>
|
||||
</div>
|
||||
<div class="item-Cards">
|
||||
<div *ngIf="selectedPlant === 'undefined'; else elseBlock">
|
||||
<app-plant-card *ngFor="let plant of (plants$ | async)" [plant]="plant"></app-plant-card>
|
||||
</div>
|
||||
<ng-template #elseBlock>
|
||||
<app-plant-card [plant]="onSearchPlant(selectedPlant) | async"></app-plant-card>
|
||||
<button mat-raised-button color="primary" (click)="onOptionDefault()">Show all
|
||||
Plants</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #errorLoadingData><mat-spinner></mat-spinner>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,23 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { PlantsComponent } from './plants.component';
|
||||
|
||||
// describe('PlantsComponent', () => {
|
||||
// let component: PlantsComponent;
|
||||
// let fixture: ComponentFixture<PlantsComponent>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [ PlantsComponent ]
|
||||
// })
|
||||
// .compileComponents();
|
||||
|
||||
// fixture = TestBed.createComponent(PlantsComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Observable, map, startWith } from 'rxjs';
|
||||
import { MqttRequestService } from '../Service/Mqtt/mqtt-request.service';
|
||||
import { StoreService } from '../Service/store.service';
|
||||
import { AddPlantDialogComponent } from '../dialog/add-plant-dialog/add-plant-dialog.component';
|
||||
import { Plant } from '../models/plant.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-plants',
|
||||
templateUrl: './plants.component.html',
|
||||
styleUrls: ['./plants.component.css']
|
||||
})
|
||||
export class PlantsComponent {
|
||||
myControl = new FormControl('');
|
||||
options$: Observable<string[]> = this.storeService.getAllPlantNames();
|
||||
selectedPlant: string = 'undefined';
|
||||
filteredOptions: Observable<string[]>;
|
||||
plants$: Observable<Plant[]> = this.storeService.currentPlants;
|
||||
|
||||
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.filteredOptions = this.myControl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => this._filter(value || '', response)),
|
||||
);
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* es wird nach dem Pflanzen Namen gesucht.
|
||||
* @method
|
||||
* @return Pflant
|
||||
*/
|
||||
onSearchPlant(plantName: string): Observable<Plant> {
|
||||
return this.storeService.getPlant(plantName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode öffnet das Dialog "AddPlantDialogComponent"
|
||||
* @method
|
||||
*/
|
||||
onNewPlant() {
|
||||
this.dialog.open(AddPlantDialogComponent, {
|
||||
width: '420px',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* die ausgewählte Pflanze wird in "selectedPlant" geschrieben
|
||||
* @method
|
||||
*/
|
||||
onOptionSelect(value: string) {
|
||||
this.selectedPlant = value;
|
||||
}
|
||||
|
||||
onOptionDefault() {
|
||||
this.selectedPlant = 'undefined';
|
||||
}
|
||||
|
||||
private _filter(value: string, options: string[]): string[] {
|
||||
const filterValue = value.toLowerCase();
|
||||
|
||||
return options?.filter(option => option.toLowerCase().includes(filterValue));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
img {
|
||||
border-radius: 0.5rem;
|
||||
width: 15.625rem;
|
||||
height: 12.5rem;
|
||||
margin: 0rem 0.3125rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
display: grid;
|
||||
grid-template-columns: 40.625rem 12.5rem;
|
||||
grid-template-rows: 12.5rem 6.25rem 6.25rem;
|
||||
width: 53.125rem;
|
||||
min-width: 46.875rem;
|
||||
font-family: Cormorant;
|
||||
box-shadow: 0.3125rem 0.3125rem 0.3125rem silver;
|
||||
border-radius: 1.25rem;
|
||||
border-color: silver;
|
||||
border-width: 0.0625rem;
|
||||
border-style: solid;
|
||||
padding: 0.625rem;
|
||||
margin: auto;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.robotImage {
|
||||
grid-row: 1 / -1;
|
||||
grid-column: 2 / 3;
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.robotAkku {
|
||||
grid-row: 1 / 2;
|
||||
grid-column: 1/2;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
display: grid;
|
||||
grid-template-columns: 15.625rem 12.5rem 12.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.akkuMsg {
|
||||
grid-column: 1/2;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.akkuProgressbar {
|
||||
grid-column: 2/3;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.akkuValue {
|
||||
grid-column: 3/4;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
margin-left: 0.3125rem;
|
||||
}
|
||||
|
||||
.robotCoordinates {
|
||||
grid-row: 2 / 3;
|
||||
grid-column: 1/2;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
display: grid;
|
||||
grid-template-columns: 15.625rem 12.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-timestamp-name {
|
||||
grid-column: 1/2;
|
||||
justify-self: start;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.item-timestamp-value {
|
||||
grid-column: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
mat-spinner {
|
||||
margin: 7rem auto;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="container"
|
||||
*ngIf="robotBatteryResponse$ !== undefined && (robotBatteryResponse$ |async) !== undefined; else errorLoadingData">
|
||||
<div class="robotImage">
|
||||
<img src="assets/images/Robot.png" alt="Plant is not available">
|
||||
</div>
|
||||
<div class="robotAkku">
|
||||
<h1 class="akkuMsg">Robot Akku:</h1>
|
||||
<mat-progress-bar class="akkuProgressbar" mode="determinate" [value]="batteryChargingStatus$ | async"
|
||||
[color]="(getLoadingBarColor() | async)"></mat-progress-bar>
|
||||
<h3 class="akkuValue">{{batteryChargingStatus$ | async}}%</h3>
|
||||
</div>
|
||||
<div class="robotCoordinates">
|
||||
<h1 class="item-timestamp-name">Timestamp:</h1>
|
||||
<h1 class="item-timestamp-value">{{getFormattedLastRequestedBatteryState()| async}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #errorLoadingData><mat-spinner></mat-spinner>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,23 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { RobotComponent } from './robot.component';
|
||||
|
||||
// describe('RobotComponent', () => {
|
||||
// let component: RobotComponent;
|
||||
// let fixture: ComponentFixture<RobotComponent>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [ RobotComponent ]
|
||||
// })
|
||||
// .compileComponents();
|
||||
|
||||
// fixture = TestBed.createComponent(RobotComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ThemePalette } from '@angular/material/core';
|
||||
import { Observable, map } from 'rxjs';
|
||||
import { StoreService } from '../Service/store.service';
|
||||
import { RobotBattery } from '../models/robotBattery.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-robot',
|
||||
templateUrl: './robot.component.html',
|
||||
styleUrls: ['./robot.component.css']
|
||||
})
|
||||
export class RobotComponent {
|
||||
robotBatteryResponse$: Observable<RobotBattery> = this.storeService.currentRobotBattery;
|
||||
batteryChargingStatus$: Observable<number> = this.robotBatteryResponse$.pipe(map(response => response.Battery));
|
||||
requestTimestamp$: Observable<string> = this.robotBatteryResponse$.pipe(map(response => response.Timestamp));
|
||||
|
||||
constructor(private storeService: StoreService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode setzt die Farbe der LoadingBar auf Rot ("warn") oder Blau ("Primary")
|
||||
* @method
|
||||
* @return warn oder primary
|
||||
*/
|
||||
getLoadingBarColor(): Observable<ThemePalette> {
|
||||
return this.batteryChargingStatus$.pipe(
|
||||
map(chargingStatus => chargingStatus < 20 ? "warn" : "primary")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode wird in der HTML datei verwendet damit der Wert nicht undefined ist.
|
||||
* @method
|
||||
*/
|
||||
getFormattedLastRequestedBatteryState(): Observable<string> {
|
||||
return this.requestTimestamp$.pipe(
|
||||
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 {
|
||||
const datePlant = new Date(timestamp);
|
||||
const currentDate = new Date();
|
||||
let seconds = ((currentDate.getTime() - datePlant.getTime()) / (1000));
|
||||
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (days >= 1) {
|
||||
return `${days} day${days > 1 ? 's' : ''} ago`;
|
||||
} else if (hours >= 1) {
|
||||
return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
||||
} else if (minutes >= 1) {
|
||||
return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
||||
} else {
|
||||
seconds = Math.floor(seconds);
|
||||
return `${seconds} second${seconds !== 1 ? 's' : ''} ago`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// import { TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { MqttRequestService } from './mqtt-request.service';
|
||||
|
||||
// describe('MqttRequestService', () => {
|
||||
// let service: MqttRequestService;
|
||||
|
||||
// beforeEach(() => {
|
||||
// TestBed.configureTestingModule({});
|
||||
// service = TestBed.inject(MqttRequestService);
|
||||
// });
|
||||
|
||||
// it('should be created', () => {
|
||||
// expect(service).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,193 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { ErrorDialogComponent } from 'src/app/dialog/error-dialog/error-dialog.component';
|
||||
import { Plant } from 'src/app/models/plant.model';
|
||||
import { PlantCount } from 'src/app/models/plantCount.model';
|
||||
import { RobotBattery } from 'src/app/models/robotBattery.model';
|
||||
import { StoreService } from '../store.service';
|
||||
import { MqttSetDataService } from './mqtt-set-data.service';
|
||||
import { MqttService } from './mqtt.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MqttRequestService {
|
||||
// wird für die Sprachausgabe verwendet
|
||||
private sourceMessage = new BehaviorSubject('');
|
||||
responseMessage = this.sourceMessage.asObservable();
|
||||
|
||||
timerSubscription: Subscription | undefined;
|
||||
|
||||
constructor(private mqttService: MqttService,
|
||||
private mqttSetDataService: MqttSetDataService,
|
||||
private storeService: StoreService, public dialog: MatDialog) {
|
||||
|
||||
//Subscribe to the Topics
|
||||
this.mqttService.subscribeToTopic("BACKEND/DATA/SENSORDATA_ALL").subscribe(data => {
|
||||
if (typeof data !== "object") {
|
||||
const payload = JSON.parse(data) as Plant[];
|
||||
this.mqttSetDataService.setDataAllPlants(payload);
|
||||
}
|
||||
}, err => {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: err,
|
||||
width: '400px',
|
||||
});
|
||||
console.error('Error:', err);
|
||||
});
|
||||
|
||||
this.mqttService.subscribeToTopic("BACKEND/DATA/ERROR").subscribe(errorMsg => {
|
||||
if (typeof errorMsg !== "object") {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: errorMsg,
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
}, err => {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: err,
|
||||
width: '400px',
|
||||
});
|
||||
console.error('Error:', err);
|
||||
});
|
||||
|
||||
this.mqttService.subscribeToTopic("BACKEND/DATA/SENSORDATA").subscribe(data => {
|
||||
if (typeof data !== "object") {
|
||||
const payload = JSON.parse(data) as Plant;
|
||||
this.mqttSetDataService.setDataPlant(payload);
|
||||
}
|
||||
}, err => {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: err,
|
||||
width: '400px',
|
||||
});
|
||||
console.error('Error:', err);
|
||||
});
|
||||
|
||||
this.mqttService.subscribeToTopic("BACKEND/DATA/BATTERY").subscribe(data => {
|
||||
if (typeof data !== "object") {
|
||||
const payload = JSON.parse(data) as RobotBattery;
|
||||
this.mqttSetDataService.setRobotBattery(payload);
|
||||
}
|
||||
}, err => {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: err,
|
||||
width: '400px',
|
||||
});
|
||||
console.error('Error:', err);
|
||||
});
|
||||
|
||||
this.mqttService.subscribeToTopic("BACKEND/DATA/ROBOTREADY").subscribe(data => {
|
||||
if (data === "False") {
|
||||
this.mqttSetDataService.setRobotReady(false);
|
||||
} else if (data === "True") {
|
||||
this.mqttSetDataService.setRobotReady(true);
|
||||
}
|
||||
}, err => {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: err,
|
||||
width: '400px',
|
||||
});
|
||||
console.error('Error:', err);
|
||||
});
|
||||
|
||||
this.mqttService.subscribeToTopic("BACKEND/DATA/PLANTCOUNT").subscribe(data => {
|
||||
if (typeof data !== "object") {
|
||||
const payload = JSON.parse(data) as PlantCount;
|
||||
this.mqttSetDataService.setPlantCount(payload);
|
||||
}
|
||||
}, err => {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: err,
|
||||
width: '400px',
|
||||
});
|
||||
console.error('Error:', err);
|
||||
});
|
||||
|
||||
//Publish to the Topic to recieve the first Data
|
||||
this.publishToPLANTCOUNT();
|
||||
this.publishToGETBATTERY();
|
||||
this.publishToROBOTREADY();
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/GETALLDATA', JSON.stringify(this.storeService.getAllPlantNames()));
|
||||
}
|
||||
|
||||
publishToGETALLDATA() {
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/GETALLDATA', JSON.stringify(this.storeService.getAllPlantNames()));
|
||||
}
|
||||
|
||||
publishToGETBATTERY() {
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/GETBATTERY');
|
||||
}
|
||||
|
||||
publishToROBOTREADY() {
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/ROBOTREADY');
|
||||
}
|
||||
|
||||
publishToNEWPLANT(plant: Plant) {
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/NEWPLANT', JSON.stringify(plant));
|
||||
}
|
||||
|
||||
publishToCONFIGUREPLANT(plant: Plant) {
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/CONFIGUREPLANT', JSON.stringify(plant));
|
||||
}
|
||||
|
||||
publishToDELETEPLANT(plantID: number) {
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/DELETEPLANT', JSON.stringify(plantID));
|
||||
}
|
||||
|
||||
publishToPLANTCOUNT() {
|
||||
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) {
|
||||
message = message.replace(/\s/g, ""); //entfernt alle leerzeichen
|
||||
console.log(":" + message + ":"); //Nachricht die übermittel wurde, wird ausgegeben auf der Konsole
|
||||
let msgFound: boolean = false;
|
||||
|
||||
if (message === "whereismyrobot") {
|
||||
msgFound = true;
|
||||
this.mqttService.publishToTopic("BACKEND/ACTION/GETPOSITION")
|
||||
this.sourceMessage.next("The data is being collected and you can view it under the 'Robot' tab");
|
||||
} else if (message === "howmuchbatterydoesmyrobothave") {
|
||||
msgFound = true;
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/GETBATTERY');
|
||||
this.sourceMessage.next("The data is being collected and you can view it under the 'Robot' tab");
|
||||
} else if (message === "howaremyplants") {
|
||||
msgFound = true;
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/DRIVEALL');
|
||||
this.sourceMessage.next("Robot is driving to all plants");
|
||||
} else {
|
||||
// schickt den Roboter zur eingegeben Pflanze..
|
||||
this.storeService.getAllPlantNames().pipe(take(1)).subscribe(result =>
|
||||
result.forEach((element: string) => {
|
||||
let msgPlantName = "howismyplant".concat(element.toLowerCase())
|
||||
if (message === msgPlantName) {
|
||||
msgFound = true;
|
||||
let PlantName = element;
|
||||
this.mqttService.publishToTopic('BACKEND/ACTION/DRIVE', JSON.stringify(PlantName));
|
||||
this.sourceMessage.next("Robot is driving to plant " + element);
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Wenn kein case eintrifft, wird "Please try again" ausgegeben
|
||||
if (!msgFound) {
|
||||
msgFound = false;
|
||||
this.sourceMessage.next("Please try again");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.sourceMessage.next('');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// import { TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { MqttSetDataService } from './mqtt-set-data.service';
|
||||
|
||||
// describe('MqttSetDataService', () => {
|
||||
// let service: MqttSetDataService;
|
||||
|
||||
// beforeEach(() => {
|
||||
// TestBed.configureTestingModule({});
|
||||
// service = TestBed.inject(MqttSetDataService);
|
||||
// });
|
||||
|
||||
// it('should be created', () => {
|
||||
// expect(service).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Plant } from 'src/app/models/plant.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 { StoreService } from '../store.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MqttSetDataService {
|
||||
|
||||
constructor(private storeService: StoreService) { }
|
||||
|
||||
setDataAllPlants(data: Plant[]): void {
|
||||
this.storeService.setDataAllPlants(data);
|
||||
}
|
||||
|
||||
setDataPlant(data: Plant): void {
|
||||
this.storeService.setDataPlant(data);
|
||||
}
|
||||
|
||||
setRobotBattery(data: RobotBattery): void {
|
||||
this.storeService.setRobotState(data);
|
||||
}
|
||||
setRobotReady(data: boolean): void {
|
||||
this.storeService.setRobotReady(data);
|
||||
}
|
||||
setRobotPosition(data: Position): void {
|
||||
this.storeService.setRobotPosition(data);
|
||||
}
|
||||
setPlantCount(data: PlantCount): void {
|
||||
this.storeService.setPlantCount(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// import { TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { MqttService } from './mqtt.service';
|
||||
|
||||
// describe('MqttService', () => {
|
||||
// let service: MqttService;
|
||||
|
||||
// beforeEach(() => {
|
||||
// TestBed.configureTestingModule({});
|
||||
// service = TestBed.inject(MqttService);
|
||||
// });
|
||||
|
||||
// it('should be created', () => {
|
||||
// expect(service).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { connect, MqttClient } from 'mqtt/dist/mqtt';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MqttService {
|
||||
private client: MqttClient;
|
||||
|
||||
// Connection zu dem MQTT Broker
|
||||
constructor() {
|
||||
this.client = connect('wss://mqtt.eclipseprojects.io:443/mqtt'); //Online Broker
|
||||
// this.client = connect('mqtt://192.168.137.197:1883', { clientId: 'kemal' }); //lokaler Broker
|
||||
}
|
||||
|
||||
public subscribeToTopic(topic: string): Observable<any> {
|
||||
return new Observable(observer => {
|
||||
this.client.subscribe(topic, (err, granted) => {
|
||||
if (err) {
|
||||
observer.error(err);
|
||||
} else {
|
||||
observer.next(granted);
|
||||
}
|
||||
});
|
||||
this.client.on('message', (t, m) => {
|
||||
if (t === topic) {
|
||||
observer.next(m.toString());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public publishToTopic(topic: string, message?: any): void {
|
||||
this.client.publish(topic, message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { StoreService } from './store.service';
|
||||
|
||||
describe('StoreService', () => {
|
||||
let service: StoreService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(StoreService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, map, of } from 'rxjs';
|
||||
import { Plant } from '../models/plant.model';
|
||||
import { PlantCount } from '../models/plantCount.model';
|
||||
import { Position } from '../models/position.model';
|
||||
import { RobotBattery } from '../models/robotBattery.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
|
||||
|
||||
export class StoreService {
|
||||
positionRobot: Position;
|
||||
plantCount: PlantCount;
|
||||
|
||||
private plants: BehaviorSubject<Plant[]> = new BehaviorSubject<Plant[]>(undefined);
|
||||
currentPlants: Observable<Plant[]> = this.plants.asObservable();
|
||||
|
||||
private robotReady = new BehaviorSubject<boolean>(undefined);
|
||||
currentRobotReady = this.robotReady.asObservable();
|
||||
|
||||
private robotBattery = new BehaviorSubject<RobotBattery>(undefined);
|
||||
currentRobotBattery = this.robotBattery.asObservable();
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
getPlant(name: string): Observable<Plant> {
|
||||
return this.currentPlants.pipe(map(response => {
|
||||
for (let plant of response) {
|
||||
if (plant.PlantName.toLocaleLowerCase() === name.toLocaleLowerCase()) {
|
||||
return plant;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}))
|
||||
}
|
||||
|
||||
getAllPlantNames(): Observable<string[]> {
|
||||
return this.currentPlants.pipe(map(response => response?.map(response => response.PlantName)));
|
||||
}
|
||||
|
||||
getIDsWithoutPlant(plants: Plant[]): Observable<number[]> {
|
||||
let listOfID: number[] = [];
|
||||
let listOfIDWithoutPlant: number[] = [];
|
||||
|
||||
if (plants.length > 0) {
|
||||
//Write ID's of the Plants in a List
|
||||
plants.forEach(plant => {
|
||||
listOfID.push(plant.PlantID);
|
||||
});
|
||||
|
||||
//Sort the List of ID's
|
||||
listOfID.sort();
|
||||
|
||||
//Write the ID's without Plants in a List
|
||||
for (let index = 0; index < this.plantCount.MaxCount; index++) { //Noch unsicher.... evtl. die länge von wo anders holen..
|
||||
if (!listOfID.includes(index + 1)) {
|
||||
listOfIDWithoutPlant.push(index + 1);
|
||||
}
|
||||
}
|
||||
return of(listOfIDWithoutPlant);
|
||||
} else {
|
||||
for (let index = 0; index < this.plantCount.MaxCount; index++) {
|
||||
listOfIDWithoutPlant.push(index + 1);
|
||||
}
|
||||
return of(listOfIDWithoutPlant);
|
||||
}
|
||||
}
|
||||
|
||||
//Set data
|
||||
setDataAllPlants(data: Plant[]): void {
|
||||
this.plants.next(data);
|
||||
}
|
||||
|
||||
setDataPlant(data: Plant): void {
|
||||
this.plants.subscribe(response => {
|
||||
response.forEach(plant => {
|
||||
if (plant.PlantID === data.PlantID) {
|
||||
plant = data;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
setRobotState(data: RobotBattery): void {
|
||||
this.robotBattery.next(data);
|
||||
}
|
||||
setRobotReady(data: boolean): void {
|
||||
this.robotReady.next(data);
|
||||
}
|
||||
setRobotPosition(data: Position): void {
|
||||
this.positionRobot = data;
|
||||
}
|
||||
setPlantCount(data: PlantCount): void {
|
||||
this.plantCount = data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
@@ -0,0 +1,44 @@
|
||||
.container {
|
||||
margin: 50px;
|
||||
}
|
||||
|
||||
.icon-menu {
|
||||
transform: scale(2);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 1.5rem;
|
||||
font-family: Cormorant;
|
||||
}
|
||||
|
||||
header {
|
||||
border: 0.125rem;
|
||||
border-style: solid;
|
||||
border-radius: 1rem;
|
||||
border-color: #89bf54;
|
||||
box-shadow: 0.2rem 0.2rem 0.2rem silver;
|
||||
padding: 4px;
|
||||
display: grid;
|
||||
grid-template-columns: 20% 60% 20%;
|
||||
font-family: Cormorant;
|
||||
|
||||
.menu-item {
|
||||
grid-column: 1/2;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.header-item {
|
||||
grid-column: 2/3;
|
||||
justify-self: center;
|
||||
align-self: end;
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.img-item {
|
||||
grid-column: 3/4;
|
||||
justify-self: end;
|
||||
width: 15rem;
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<html class="container">
|
||||
|
||||
<header>
|
||||
<div class="menu-item">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Example icon-button with a menu">
|
||||
<mat-icon class="icon-menu">menu</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item routerLink="/home" (click)="onMenuSelect('Home')">
|
||||
<mat-icon>micro</mat-icon>
|
||||
<span>Home</span>
|
||||
</button>
|
||||
<button mat-menu-item routerLink="/map" (click)="onMenuSelect('Map')">
|
||||
<mat-icon>public</mat-icon>
|
||||
<span>Map</span>
|
||||
</button>
|
||||
<button mat-menu-item routerLink="plants" (click)="onMenuSelect('Plants')">
|
||||
<mat-icon>grass</mat-icon>
|
||||
<span>Plants</span>
|
||||
</button>
|
||||
<button mat-menu-item routerLink="robot" (click)="onMenuSelect('Robot')">
|
||||
<mat-icon>android</mat-icon>
|
||||
<span>Robot</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<p class="header-item">{{header}}</p>
|
||||
<img class="img-item" src="assets/images/logo.png" alt="not available">
|
||||
</header>
|
||||
|
||||
<body>
|
||||
<router-outlet></router-outlet>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MqttRequestService } from './Service/Mqtt/mqtt-request.service';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent;
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
let mqttRequestServiceSpy: jasmine.SpyObj<MqttRequestService>;
|
||||
let locationStub: Partial<Location>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const mqttRequestServiceMock = jasmine.createSpyObj('MqttRequestService', ['someMethod']);
|
||||
|
||||
locationStub = {
|
||||
path: () => '/home'
|
||||
};
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MatMenuModule],
|
||||
declarations: [AppComponent],
|
||||
providers: [
|
||||
{ provide: MqttRequestService, useValue: mqttRequestServiceMock },
|
||||
{ provide: Location, useValue: locationStub }
|
||||
]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
mqttRequestServiceSpy = TestBed.inject(MqttRequestService) as jasmine.SpyObj<MqttRequestService>;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set header to "Home" if the route is empty', () => {
|
||||
expect(component.header).toBe('Home');
|
||||
});
|
||||
|
||||
it('should set header to the capitalized route value', () => {
|
||||
locationStub.path = () => '/about';
|
||||
const componentNew = new AppComponent(mqttRequestServiceSpy, locationStub as Location);
|
||||
|
||||
expect(componentNew.header).toBe('About');
|
||||
});
|
||||
|
||||
it('should update header when onMenuSelect is called', () => {
|
||||
component.onMenuSelect('Map');
|
||||
|
||||
expect(component.header).toBe('Map');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { MqttRequestService } from './Service/Mqtt/mqtt-request.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css'],
|
||||
})
|
||||
export class AppComponent {
|
||||
header: string;
|
||||
constructor(private mqttRequestService: MqttRequestService, private location: Location) {
|
||||
// Header in der Leiste zu bestimmen
|
||||
const actualRoute = this.location.path().slice(1);
|
||||
const lastParam = actualRoute.charAt(0).toUpperCase() + actualRoute.slice(1);
|
||||
if (lastParam === "") {
|
||||
this.onMenuSelect('Home');
|
||||
} else {
|
||||
this.onMenuSelect(lastParam);
|
||||
}
|
||||
}
|
||||
|
||||
onMenuSelect(header: string) {
|
||||
this.header = header;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { HomeComponent } from './Home/home.component';
|
||||
import { InputComponent } from './Home/input/input.component';
|
||||
import { OutputComponent } from './Home/output/output.component';
|
||||
import { MapComponent } from './Map/map.component';
|
||||
import { PlantCardComponent } from './Plants/plant-card/plant-card.component';
|
||||
import { PlantsComponent } from './Plants/plants.component';
|
||||
import { RobotComponent } from './Robot/robot.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AddPlantDialogComponent } from './dialog/add-plant-dialog/add-plant-dialog.component';
|
||||
import { ConfigurePlantDialogComponent } from './dialog/configure-plant-dialog/configure-plant-dialog.component';
|
||||
import { ErrorDialogComponent } from './dialog/error-dialog/error-dialog.component';
|
||||
import { MapPlantDialogComponent } from './dialog/map-plant-dialog/map-plant-dialog.component';
|
||||
import { DeleteConfirmDialogComponent } from './dialog/delete-confirm-dialog/delete-confirm-dialog.component';
|
||||
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{ path: '', component: HomeComponent },
|
||||
{ path: 'home', component: HomeComponent },
|
||||
{ path: 'map', component: MapComponent },
|
||||
{ path: 'plants', component: PlantsComponent },
|
||||
{ path: 'robot', component: RobotComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HomeComponent,
|
||||
MapComponent,
|
||||
InputComponent,
|
||||
OutputComponent,
|
||||
PlantsComponent,
|
||||
PlantCardComponent,
|
||||
RobotComponent,
|
||||
ConfigurePlantDialogComponent,
|
||||
AddPlantDialogComponent,
|
||||
MapPlantDialogComponent,
|
||||
ErrorDialogComponent,
|
||||
DeleteConfirmDialogComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
MatBadgeModule,
|
||||
RouterModule.forRoot(appRoutes),
|
||||
MatFormFieldModule,
|
||||
FormsModule,
|
||||
MatInputModule,
|
||||
MatCardModule,
|
||||
ScrollingModule,
|
||||
MatProgressBarModule,
|
||||
MatAutocompleteModule,
|
||||
ReactiveFormsModule,
|
||||
MatDialogModule,
|
||||
MatSelectModule,
|
||||
MatProgressSpinnerModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
exports: [ConfigurePlantDialogComponent, AddPlantDialogComponent, MapPlantDialogComponent, ErrorDialogComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
@@ -0,0 +1,78 @@
|
||||
.item-header-content {
|
||||
display: grid;
|
||||
grid-template-columns: 11.25rem 6.25rem;
|
||||
grid-template-rows: 20% 80%;
|
||||
justify-content: start;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
grid-column: 1/-1;
|
||||
grid-row: 1/2;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
font-size: 1.5625rem;
|
||||
margin-bottom: 1rem;
|
||||
color: blue;
|
||||
display: grid;
|
||||
grid-template-columns: 11.25rem 6.25rem;
|
||||
}
|
||||
|
||||
.item-header-text {
|
||||
grid-column: 1/2;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-header-button {
|
||||
grid-column: 2/3;
|
||||
justify-self: end;
|
||||
background-color: white;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.icon-language {
|
||||
color: black;
|
||||
transform: scale(1.6);
|
||||
}
|
||||
|
||||
.item-header-button:hover {
|
||||
background-color: silver;
|
||||
}
|
||||
|
||||
.item-Measurements-IconsAndNames {
|
||||
grid-column: 1/2;
|
||||
grid-row: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.item-Measurements-Values {
|
||||
grid-column: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-IMG {
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.IMG-Plants {
|
||||
border-radius: 0.5rem;
|
||||
width: 11.25rem;
|
||||
height: 9.375rem;
|
||||
margin: 0rem 0.3125rem;
|
||||
}
|
||||
|
||||
.icons-measurements {
|
||||
width: 1rem;
|
||||
height: 0.75rem;
|
||||
margin: 0rem 0.3125rem;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<div class="item-header-content">
|
||||
<div class="item-header">
|
||||
<p class="item-header-text">Add Plant:</p>
|
||||
<button mat-fab class="item-header-button" (click)="onOpenMap()"><mat-icon
|
||||
class="icon-language">language</mat-icon></button>
|
||||
</div>
|
||||
<div class="item-Measurements-IconsAndNames">
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/id_icon.png" alt="not available">
|
||||
ID: <br><br><br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/name_icon.png" alt="not available">
|
||||
Name: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/air_humidity_icon.png" alt="not available">
|
||||
Air Humidity: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/air_temperature_icon.png" alt="not available">
|
||||
Air Temperature: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/soil_moisture_icon.png" alt="not available">
|
||||
Soil Moisture: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/brightness_icon.png" alt="not available">
|
||||
Brightness:<br>
|
||||
</div>
|
||||
<div class="item-Measurements-Values">
|
||||
<mat-form-field class="item-option" appearance="fill">
|
||||
<mat-label>Choose an ID</mat-label>
|
||||
<mat-select [(value)]="selected" (closed)="onOptionSelected()">
|
||||
<mat-option *ngFor="let option of options$ | async" [value]="option">
|
||||
{{option}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<input type="text" id="inputName" maxlength="15">
|
||||
<input type="number" id="inputAirTemperature" max="100">
|
||||
<input type="number" id="inputAirHumidity" max="100">
|
||||
<input type="number" id="inputSoilMoisture" max="100">
|
||||
<input type="number" id="inputBrightness" max="100">
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close>No</button>
|
||||
<button mat-button mat-dialog-close cdkFocusInitial (click)="onSaveDialog()"
|
||||
[disabled]="buttonSavedisabled">Save</button>
|
||||
</div>
|
||||
@@ -0,0 +1,172 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
// import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
// import { of } from 'rxjs';
|
||||
// import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||
// import { StoreService } from 'src/app/Service/store.service';
|
||||
// import { Plant } from 'src/app/models/plant.model';
|
||||
// import { AddPlantDialogComponent } from './add-plant-dialog.component';
|
||||
// const plantMock1: Plant = {
|
||||
// AirTemperature: 25,
|
||||
// AirHumidity: 60,
|
||||
// SoilMoisture: 50,
|
||||
// Brightness: 80,
|
||||
// PlantID: 1,
|
||||
// Timestamp: "2023-06-05T12:00:00",
|
||||
// MeasurementID: "abcd1234",
|
||||
// PlantName: "Plant1",
|
||||
// };
|
||||
// const plantMock2: Plant = {
|
||||
// AirTemperature: 22,
|
||||
// AirHumidity: 60,
|
||||
// SoilMoisture: 50,
|
||||
// Brightness: 80,
|
||||
// PlantID: 1,
|
||||
// Timestamp: "2023-06-05T12:00:00",
|
||||
// MeasurementID: "abcd1234",
|
||||
// PlantName: "Plant2",
|
||||
// };
|
||||
// const plantMock3: Plant = {
|
||||
// AirTemperature: 21,
|
||||
// AirHumidity: 60,
|
||||
// SoilMoisture: 50,
|
||||
// Brightness: 80,
|
||||
// PlantID: 1,
|
||||
// Timestamp: "2023-06-05T12:00:00",
|
||||
// MeasurementID: "abcd1234",
|
||||
// PlantName: "Plant3",
|
||||
// };
|
||||
// describe('AddPlantDialogComponent', () => {
|
||||
// let component: AddPlantDialogComponent;
|
||||
// let fixture: ComponentFixture<AddPlantDialogComponent>;
|
||||
// let dialogRefSpyObj: jasmine.SpyObj<MatDialogRef<AddPlantDialogComponent>>;
|
||||
// let dialogSpy: jasmine.SpyObj<MatDialog>;
|
||||
// let mqttRequestServiceSpy: jasmine.SpyObj<MqttRequestService>;
|
||||
// let storeServiceSpy: jasmine.SpyObj<StoreService>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// const dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']);
|
||||
// const dialogSpyObj = jasmine.createSpyObj('MatDialog', ['open']);
|
||||
// const mqttRequestServiceSpyObj = jasmine.createSpyObj('MqttRequestService', [
|
||||
// 'publishToPLANTCOUNT',
|
||||
// 'publishToNEWPLANT',
|
||||
// 'publishToGETALLDATA',
|
||||
// ]);
|
||||
// const storeServiceSpyObj = jasmine.createSpyObj('StoreService', [
|
||||
// 'currentPlants',
|
||||
// 'getIDsWithoutPlant',
|
||||
// 'getAllPlantNames',
|
||||
// ]);
|
||||
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [AddPlantDialogComponent],
|
||||
// providers: [
|
||||
// { provide: MatDialogRef, useValue: dialogRefSpy },
|
||||
// { provide: MAT_DIALOG_DATA, useValue: {} },
|
||||
// { provide: MatDialog, useValue: dialogSpyObj },
|
||||
// { provide: MqttRequestService, useValue: mqttRequestServiceSpyObj },
|
||||
// { provide: StoreService, useValue: storeServiceSpyObj },
|
||||
// ],
|
||||
// }).compileComponents();
|
||||
// });
|
||||
|
||||
// beforeEach(() => {
|
||||
// fixture = TestBed.createComponent(AddPlantDialogComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// dialogRefSpyObj = TestBed.inject(MatDialogRef) as jasmine.SpyObj<MatDialogRef<AddPlantDialogComponent>>;
|
||||
// dialogSpy = TestBed.inject(MatDialog) as jasmine.SpyObj<MatDialog>;
|
||||
// mqttRequestServiceSpy = TestBed.inject(MqttRequestService) as jasmine.SpyObj<MqttRequestService>;
|
||||
// storeServiceSpy = TestBed.inject(StoreService) as jasmine.SpyObj<StoreService>;
|
||||
// });
|
||||
|
||||
// it('should initialize with buttonSavedisabled set to true', () => {
|
||||
// // Assert
|
||||
// expect(component.buttonSavedisabled).toBe(true);
|
||||
// });
|
||||
|
||||
// it('should call publishToPLANTCOUNT method and retrieve options', () => {
|
||||
// // Arrange
|
||||
// const currentPlantsMock = of([plantMock1, plantMock2, plantMock3]);
|
||||
// const getIDsWithoutPlantMock = jasmine.createSpy().and.returnValue(of([4, 5, 6]));
|
||||
// const getAllPlantNamesMock = jasmine.createSpy().and.returnValue(of(['Plant1', 'Plant2', 'Plant3']));
|
||||
|
||||
// // Act
|
||||
// storeServiceSpy.currentPlants = currentPlantsMock;
|
||||
// storeServiceSpy.getIDsWithoutPlant = getIDsWithoutPlantMock;
|
||||
// storeServiceSpy.getAllPlantNames = getAllPlantNamesMock;
|
||||
// fixture.detectChanges();
|
||||
|
||||
// // Assert
|
||||
// expect(mqttRequestServiceSpy.publishToPLANTCOUNT).toHaveBeenCalled();
|
||||
// expect(storeServiceSpy.getIDsWithoutPlant).toHaveBeenCalledWith([plantMock1, plantMock2, plantMock3]);
|
||||
// expect(storeServiceSpy.getAllPlantNames).toHaveBeenCalled();
|
||||
// expect(component.options$).toEqual(of([4, 5, 6]));
|
||||
// expect(component.plantNames).toEqual(['Plant1', 'Plant2', 'Plant3']);
|
||||
// });
|
||||
|
||||
// // it('should open the error dialog if plant input fails', () => {
|
||||
// // // Arrange
|
||||
// // const plantInputString = 'Plant Name is empty';
|
||||
|
||||
// // // Act
|
||||
// // component.plantInputTestFails = jasmine.createSpy().and.returnValue(plantInputString);
|
||||
// // component.onSaveDialog();
|
||||
|
||||
// // // Assert
|
||||
// // expect(dialogSpy.open).toHaveBeenCalledWith(ErrorDialogComponent, {
|
||||
// // data: 'Error: ' + plantInputString,
|
||||
// // width: '400px',
|
||||
// // });
|
||||
// // expect(mqttRequestServiceSpy.publishToNEWPLANT).not.toHaveBeenCalled();
|
||||
// // expect(mqttRequestServiceSpy.publishToGETALLDATA).not.toHaveBeenCalled();
|
||||
// // expect(window.location.reload).not.toHaveBeenCalled();
|
||||
// // });
|
||||
|
||||
// // it('should call the MQTT request service and reload the page if plant input is valid', () => {
|
||||
// // // Arrange
|
||||
// // const plantInputString = '';
|
||||
|
||||
// // // Act
|
||||
// // component.plantInputTestFails = jasmine.createSpy().and.returnValue(plantInputString);
|
||||
// // component.onSaveDialog();
|
||||
|
||||
// // // Assert
|
||||
// // expect(dialogSpy.open).not.toHaveBeenCalled();
|
||||
// // expect(mqttRequestServiceSpy.publishToNEWPLANT).toHaveBeenCalledWith(component.plant);
|
||||
// // expect(mqttRequestServiceSpy.publishToGETALLDATA).toHaveBeenCalled();
|
||||
// // expect(window.location.reload).toHaveBeenCalled();
|
||||
// // });
|
||||
|
||||
// // it('should open the map plant dialog', () => {
|
||||
// // // Act
|
||||
// // component.onOpenMap();
|
||||
|
||||
// // // Assert
|
||||
// // expect(dialogSpy.open).toHaveBeenCalledWith(MapPlantDialogComponent, {
|
||||
// // width: '400px',
|
||||
// // height: '400px',
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // it('should set buttonSavedisabled to false when option is selected within the range', () => {
|
||||
// // // Arrange
|
||||
// // component.buttonSavedisabled = true;
|
||||
|
||||
// // // Act
|
||||
// // component.onOptionSelected(5);
|
||||
|
||||
// // // Assert
|
||||
// // expect(component.buttonSavedisabled).toBeFalse();
|
||||
// // });
|
||||
|
||||
// // it('should keep buttonSavedisabled as true when option is selected outside the range', () => {
|
||||
// // // Arrange
|
||||
// // component.buttonSavedisabled = true;
|
||||
|
||||
// // // Act
|
||||
// // component.onOptionSelected(10);
|
||||
|
||||
// // // Assert
|
||||
// // expect(component.buttonSavedisabled).toBeTrue();
|
||||
// // });
|
||||
|
||||
// });
|
||||
@@ -0,0 +1,124 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Observable } from 'rxjs';
|
||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||
import { StoreService } from 'src/app/Service/store.service';
|
||||
import { Plant } from 'src/app/models/plant.model';
|
||||
import { ErrorDialogComponent } from '../error-dialog/error-dialog.component';
|
||||
import { MapPlantDialogComponent } from '../map-plant-dialog/map-plant-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-plant-dialog',
|
||||
templateUrl: './add-plant-dialog.component.html',
|
||||
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 {
|
||||
selected: number;
|
||||
options$: Observable<number[]>;
|
||||
plantNames: string[];
|
||||
plant = {} as Plant;
|
||||
buttonSavedisabled: boolean;
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<AddPlantDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
public dialog: MatDialog,
|
||||
private storeService: StoreService,
|
||||
private mqttRequestService: MqttRequestService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.buttonSavedisabled = true; //Button "Save" wird disabled
|
||||
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.options$ = this.storeService.getIDsWithoutPlant(response);
|
||||
})
|
||||
|
||||
// Alle aktuellen Pflanzen Namen werden in "plantNames" geschrieben
|
||||
this.storeService.getAllPlantNames().subscribe(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() {
|
||||
this.plant.PlantName = (<HTMLInputElement>document.getElementById("inputName")).value;
|
||||
this.plant.AirTemperature = +(<HTMLInputElement>document.getElementById("inputAirTemperature")).value;
|
||||
this.plant.AirHumidity = +(<HTMLInputElement>document.getElementById("inputAirHumidity")).value;
|
||||
this.plant.SoilMoisture = +(<HTMLInputElement>document.getElementById("inputSoilMoisture")).value;
|
||||
this.plant.Brightness = +(<HTMLInputElement>document.getElementById("inputBrightness")).value;
|
||||
this.plant.PlantID = this.selected;
|
||||
|
||||
const plantInputString: string = this.plantInputTestFails();
|
||||
|
||||
// bei unzulässigen Werten --> Dialog, bei zulässigen Wertden --> Pflanze wird an das Backend geschickt
|
||||
if (plantInputString) {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: "Error: " + plantInputString,
|
||||
width: '400px',
|
||||
});
|
||||
} else {
|
||||
this.mqttRequestService.publishToNEWPLANT(this.plant);
|
||||
this.mqttRequestService.publishToGETALLDATA(); // Daten werden aus dem Backend angefragt
|
||||
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 {
|
||||
|
||||
if (this.plantNames.indexOf(this.plant.PlantName) > -1) {
|
||||
return "Plant Name already exists";
|
||||
}
|
||||
if (this.plant.PlantName === "") {
|
||||
return "Plant Name is empty";
|
||||
}
|
||||
if (this.plant.AirTemperature > 100) {
|
||||
return "Air Temperature is to high";
|
||||
}
|
||||
if (this.plant.AirHumidity > 100) {
|
||||
return "Air Humidity is to high";
|
||||
}
|
||||
if (this.plant.SoilMoisture > 100) {
|
||||
return "Soil Moisture is to high";
|
||||
}
|
||||
if (this.plant.Brightness > 40000) {
|
||||
return "Brightness is to high";
|
||||
}
|
||||
return ""; //Empty string is a Boolean false!
|
||||
}
|
||||
|
||||
/**
|
||||
* beim klick auf den Button, wird das Dialog "MapPlantDialogComponent" geöffnet
|
||||
* @method
|
||||
*/
|
||||
onOpenMap() {
|
||||
this.dialog.open(MapPlantDialogComponent, {
|
||||
width: '400px',
|
||||
height: '400px'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* nach der Auswahl der ID, wird der Button "Save" enabled
|
||||
* @method
|
||||
*/
|
||||
onOptionSelected() {
|
||||
this.buttonSavedisabled = false;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
.item-header-content {
|
||||
display: grid;
|
||||
grid-template-columns: 11.25rem 6rem 3rem;
|
||||
grid-template-rows: 20% 80%;
|
||||
justify-content: start;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
grid-column: 1/-1;
|
||||
grid-row: 1/2;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
font-size: 1.6rem;
|
||||
margin-bottom: 1rem;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.item-Measurements-IconsAndNames {
|
||||
grid-column: 1/2;
|
||||
grid-row: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-Measurements-Values {
|
||||
grid-column: 2/3;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-Measurements-Units {
|
||||
grid-column: 3/4;
|
||||
justify-self: start;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.item-IMG {
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.IMG-Plants {
|
||||
border-radius: 0.5rem;
|
||||
width: 11.25rem;
|
||||
height: 9.375rem;
|
||||
margin: 0rem 0.3125rem;
|
||||
}
|
||||
|
||||
.icons-measurements {
|
||||
width: 1rem;
|
||||
height: 0.75rem;
|
||||
margin: 0rem 0.3125rem;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 5rem;
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<div class="item-header-content">
|
||||
<div class="item-header">
|
||||
<p>Configure Plant: {{plant.PlantName}}</p>
|
||||
</div>
|
||||
<div class="item-Measurements-IconsAndNames">
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/air_humidity_icon.png" alt="not available">
|
||||
Air Humidity: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/air_temperature_icon.png" alt="not available">
|
||||
Air Temperature: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/soil_moisture_icon.png" alt="not available">
|
||||
Soil Moisture: <br>
|
||||
<img class="icons-measurements" src="assets/images/Icons-Card/brightness_icon.png" alt="not available">
|
||||
Brightness:<br>
|
||||
<!-- <img class="icons-measurements" src="assets/images/Icons-Card/Timestamp_icon.png" alt="not available">
|
||||
Timestamp: -->
|
||||
</div>
|
||||
<div class="item-Measurements-Values">
|
||||
<input type="number" [value]=plant.AirTemperature id="inputAirTemperature">
|
||||
<input type="number" [value]=plant.AirHumidity id="inputAirHumidity">
|
||||
<input type="number" [value]=plant.SoilMoisture id="inputSoilMoisture">
|
||||
<input type="number" [value]=plant.Brightness id="inputBrightness">
|
||||
<!-- <input type="datetime" [value]=plant.Timestamp id="inputTimestamp"> -->
|
||||
</div>
|
||||
<div class="item-Measurements-Units">
|
||||
<p>%</p>
|
||||
<p>°C</p>
|
||||
<p>%</p>
|
||||
<p>Lux</p>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close>No</button>
|
||||
<button mat-button mat-dialog-close cdkFocusInitial (click)="onSaveDialog()">Save</button>
|
||||
</div>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { ConfigurePlantDialogComponent } from './configure-plant-dialog.component';
|
||||
|
||||
// describe('ConfigurePlantDialogComponent', () => {
|
||||
// let component: ConfigurePlantDialogComponent;
|
||||
// let fixture: ComponentFixture<ConfigurePlantDialogComponent>;
|
||||
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [ ConfigurePlantDialogComponent ]
|
||||
// })
|
||||
// .compileComponents();
|
||||
|
||||
// fixture = TestBed.createComponent(ConfigurePlantDialogComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MqttRequestService } from 'src/app/Service/Mqtt/mqtt-request.service';
|
||||
import { Plant } from 'src/app/models/plant.model';
|
||||
import { ErrorDialogComponent } from '../error-dialog/error-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-configure-plant-dialog',
|
||||
templateUrl: './configure-plant-dialog.component.html',
|
||||
styleUrls: ['./configure-plant-dialog.component.css']
|
||||
})
|
||||
export class ConfigurePlantDialogComponent {
|
||||
plant: Plant;
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<ConfigurePlantDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private mqttRequestService: MqttRequestService,
|
||||
public dialog: MatDialog) {
|
||||
this.plant = data.plant;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
this.plant.AirTemperature = +(<HTMLInputElement>document.getElementById("inputAirTemperature")).value;
|
||||
this.plant.AirHumidity = +(<HTMLInputElement>document.getElementById("inputAirHumidity")).value;
|
||||
this.plant.SoilMoisture = +(<HTMLInputElement>document.getElementById("inputSoilMoisture")).value;
|
||||
this.plant.Brightness = +(<HTMLInputElement>document.getElementById("inputBrightness")).value;
|
||||
|
||||
const plantInputString: string = this.plantInputTestFails();
|
||||
|
||||
// bei unzulässigen Werten --> Dialog, bei zulässigen Wertden --> Pflanze wird an das Backend geschickt
|
||||
if (plantInputString) {
|
||||
this.dialog.open(ErrorDialogComponent, {
|
||||
data: "Error: " + plantInputString,
|
||||
width: '400px',
|
||||
});
|
||||
} else {
|
||||
this.mqttRequestService.publishToCONFIGUREPLANT(this.plant); //Request to the Backend, to change the data of the plant
|
||||
this.mqttRequestService.publishToGETALLDATA(); // Daten werden aus dem Backend angefragt
|
||||
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 {
|
||||
if (this.plant.AirTemperature > 100) {
|
||||
return "Air Temperature is to high";
|
||||
}
|
||||
if (this.plant.AirHumidity > 100) {
|
||||
return "Air Humidity is to high";
|
||||
}
|
||||
if (this.plant.SoilMoisture > 100) {
|
||||
return "Soil Moisture is to high";
|
||||
}
|
||||
if (this.plant.Brightness > 40000) {
|
||||
return "Brightness is to high";
|
||||
}
|
||||
return ""; //Empty string is a Boolean false!
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
:host ::ng-deep {
|
||||
.mat-dialog-title {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.mat-button {
|
||||
background-color: ghostwhite;
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<div class="container">
|
||||
<p mat-dialog-title>Delete Plant: {{plant.PlantName}}</p>
|
||||
<p mat-dialog-content>Soll die Pflanze "{{plant.PlantName}}" tatsächlich gelöscht werden?</p>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close>No</button>
|
||||
<button mat-button mat-dialog-close cdkFocusInitial (click)="onDelete()" color="warn">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user