Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dea00a8d1 | ||
|
|
960b05e76f | ||
|
|
94e9c4ba7a | ||
|
|
6da9bfff54 | ||
|
|
c40b69ebe2 | ||
|
|
397e6fe331 | ||
|
|
60fbc8758d | ||
|
|
fe4af52380 | ||
|
|
426f57798e | ||
|
|
9d896df965 | ||
|
|
ce6ca27490 | ||
|
|
bc3484acae | ||
|
|
1f172a8b7a | ||
|
|
6577c39a1b | ||
|
|
9a80ec1410 | ||
|
|
366b3db8f9 | ||
|
|
65516babfc | ||
|
|
a920aefa54 | ||
|
|
a34657dc1a | ||
|
|
8cda9e8c0f | ||
|
|
e9114fdfe5 | ||
|
|
a8e8ccbbe1 | ||
|
|
ffb38996e8 | ||
|
|
e248dc3e0c | ||
|
|
8831c130cd | ||
|
|
b361fad52d | ||
|
|
2ee3603229 | ||
|
|
cd6973efbb | ||
|
|
60e19a8968 | ||
|
|
a929bbebed | ||
|
|
6a55bda519 | ||
|
|
53c37e03c1 | ||
|
|
7814f64873 | ||
|
|
6692a2fcda | ||
|
|
ed730bc438 | ||
|
|
1c245e9b43 | ||
|
|
ce8d424f95 | ||
|
|
f6d346a3c1 | ||
|
|
0f5c2e3f42 | ||
|
|
2af8bfa536 | ||
|
|
be7b35f668 | ||
|
|
ea33e0b065 | ||
|
|
9709f4c0aa | ||
|
|
cb388a769d | ||
|
|
d7fc54cfd0 | ||
|
|
722898da6d | ||
|
|
0aa939db39 | ||
|
|
a9a24e5d72 | ||
|
|
450bb76d7d | ||
|
|
59ce30a828 | ||
|
|
b3d0037952 | ||
|
|
75e1391017 | ||
|
|
e5fc3f9f38 | ||
|
|
e7e40ef3b6 | ||
|
|
7c8dc41063 | ||
|
|
7411bdc628 | ||
|
|
8812ecfda1 | ||
|
|
6e4500d932 | ||
|
|
3149466a37 | ||
|
|
0218391ea0 | ||
|
|
bbc154efe2 | ||
|
|
1fd6853245 | ||
|
|
b1a8673617 | ||
|
|
0b90a6b5ae | ||
|
|
ff7cc62f6a | ||
|
|
208728662d | ||
|
|
e30f7c80c9 | ||
|
|
4d800c36fe | ||
|
|
1f66795bb1 | ||
|
|
3368c2586f | ||
|
|
ff7634e0b0 | ||
|
|
7175b68abe | ||
|
|
747053f132 | ||
|
|
8150850bb6 | ||
|
|
f55ebb6b3f | ||
|
|
7fe6850da4 | ||
|
|
4c9435dbee | ||
|
|
b5811dbbcb | ||
|
|
b46898a506 | ||
|
|
aad6de3687 | ||
|
|
d21882f036 | ||
|
|
d66e5d05b2 | ||
|
|
103eeadf0b | ||
|
|
6b0e4b04ce | ||
|
|
aec322031e | ||
|
|
0ea548ec8a | ||
|
|
44a266629f | ||
|
|
633b75176c | ||
|
|
bb16b602a7 | ||
|
|
8c53f49935 | ||
|
|
01f29ca9cd | ||
|
|
e45f46c102 | ||
|
|
551d5f4bb3 | ||
|
|
b9965ee956 | ||
|
|
f208cda1ed | ||
|
|
977f531f72 | ||
|
|
55dc2a9b9a |
@@ -9,23 +9,6 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package( OpenCV REQUIRED )
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
include_directories( ${OpenCV_INCLUDE_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Input
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Processing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ControlModule
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Interpreter
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/IntersectionHandler
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Utils
|
||||
)
|
||||
|
||||
link_directories( ${Input_SOURCE_DIRS}
|
||||
${Processing_SOURCE_DIRS}
|
||||
${ControlModule_SOURCE_DIRS}
|
||||
${Interpreter_SOURCE_DIRS}
|
||||
${IntersectionHandler_SOURCE_DIRS}
|
||||
${Utils_SOURCE_DIRS}
|
||||
)
|
||||
|
||||
add_subdirectory(Input)
|
||||
add_subdirectory(Processing)
|
||||
add_subdirectory(ControlModule)
|
||||
@@ -34,16 +17,28 @@ add_subdirectory(IntersectionHandler)
|
||||
add_subdirectory(Utils)
|
||||
add_subdirectory(Spielwiese)
|
||||
|
||||
target_include_directories(Input PRIVATE .)
|
||||
target_include_directories(Processing PRIVATE .)
|
||||
target_include_directories(ControlModule PRIVATE .)
|
||||
target_include_directories(Interpreter PRIVATE .)
|
||||
target_include_directories(IntersectionHandler PRIVATE .)
|
||||
target_include_directories(Utils PRIVATE .)
|
||||
|
||||
add_executable(lfr_image_processing lfr.cpp autonomous_mode_main.cpp)
|
||||
add_library(lfr_image_processing_lib lfr.cpp)
|
||||
|
||||
target_include_directories(lfr_image_processing PUBLIC ${OpenCV_INCLUDE_DIRS})
|
||||
target_include_directories(lfr_image_processing PUBLIC ${Input_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing PUBLIC ${Processing_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing PUBLIC ${ControlModule_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing PUBLIC ${Interpreter_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing PUBLIC ${IntersectionHandler_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing PUBLIC ${Utils_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing PUBLIC .)
|
||||
|
||||
target_include_directories(lfr_image_processing_lib PUBLIC ${OpenCV_INCLUDE_DIRS})
|
||||
target_include_directories(lfr_image_processing_lib PUBLIC ${Input_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing_lib PUBLIC ${Processing_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing_lib PUBLIC ${ControlModule_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing_lib PUBLIC ${Interpreter_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing_lib PUBLIC ${IntersectionHandler_SOURCE_DIRS})
|
||||
target_include_directories(lfr_image_processing_lib PUBLIC .)
|
||||
|
||||
target_link_libraries( lfr_image_processing ${OpenCV_LIBS} Input Processing ControlModule Interpreter IntersectionHandler Utils Threads::Threads)
|
||||
target_link_libraries( lfr_image_processing_lib ${OpenCV_LIBS} Input Processing ControlModule Interpreter IntersectionHandler Utils Threads::Threads)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
include_directories( ${Utils_SOURCE_DIRS})
|
||||
link_directories(${Utils_SOURCE_DIRS})
|
||||
|
||||
add_library(ControlModule control_module.cpp)
|
||||
set_target_properties(ControlModule PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(ControlModule PRIVATE .)
|
||||
|
||||
target_include_directories(ControlModule PRIVATE ${Utils_SOURCE_DIRS})
|
||||
target_include_directories(ControlModule PUBLIC .)
|
||||
target_link_libraries( ControlModule Utils)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
@@ -1,9 +1,122 @@
|
||||
#include "control_module.h"
|
||||
|
||||
ControlModule::ControlModule(/* args */)
|
||||
ControlModule::ControlModule(): ControlModule(0.0, 1.0, 1.0)
|
||||
{
|
||||
}
|
||||
|
||||
ControlModule::ControlModule(double maxSpeed, double rotateSpeed, double moveSideSpeed)
|
||||
{
|
||||
motors[0] = 0.0;
|
||||
motors[1] = 0.0;
|
||||
motors[2] = 0.0;
|
||||
motors[3] = 0.0;
|
||||
this->maxSpeed = maxSpeed;
|
||||
this->rotateSpeed = rotateSpeed;
|
||||
this->moveSideSpeed = moveSideSpeed;
|
||||
}
|
||||
|
||||
ControlModule::~ControlModule()
|
||||
{
|
||||
}
|
||||
|
||||
void ControlModule::adjustSpeed(){
|
||||
double factor = 0.0;
|
||||
if(maxSpeed > 1.0)
|
||||
{
|
||||
factor = 1.0;
|
||||
}
|
||||
else if(maxSpeed < -1.0)
|
||||
{
|
||||
factor = -1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
factor = maxSpeed;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
motors[i] *= factor;
|
||||
}
|
||||
};
|
||||
|
||||
void ControlModule::moveSide(int imageColumsMiddle, int contourColumsMiddle){
|
||||
double speed = moveSideSpeed * static_cast<double>(contourColumsMiddle - imageColumsMiddle)/static_cast<double>(imageColumsMiddle);
|
||||
motors[0] += speed;
|
||||
motors[1] -= speed;
|
||||
motors[2] -= speed;
|
||||
motors[3] += speed;
|
||||
}
|
||||
|
||||
void ControlModule::drive(double ratio)
|
||||
{
|
||||
motors[0] += ratio;
|
||||
motors[1] += ratio;
|
||||
motors[2] += ratio;
|
||||
motors[3] += ratio;
|
||||
}
|
||||
|
||||
void ControlModule::rotate(double ratio)
|
||||
{
|
||||
motors[0] += ratio;
|
||||
motors[1] -= ratio;
|
||||
motors[2] += ratio;
|
||||
motors[3] -= ratio;
|
||||
}
|
||||
|
||||
void ControlModule::unit(){
|
||||
double max = 1.0E-6;
|
||||
for(int i = 0; i <= sizeof(motors)/sizeof(int); i++){
|
||||
if(motors[i] > max)
|
||||
max = motors[i];
|
||||
}
|
||||
|
||||
//Avoid dividing by zero
|
||||
if (max > 0.001)
|
||||
{
|
||||
for(int i = 0; i < 4; i++){
|
||||
motors[i] /= max;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ControlModule::calcSpeeds(int imageColumsMiddle, int contourColumsMiddle, double angle){
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
double rotationRatio = ratio(angle);
|
||||
rotate(rotationRatio);
|
||||
drive(1.0-std::fabs(rotationRatio));
|
||||
unit();
|
||||
adjustSpeed();
|
||||
}
|
||||
|
||||
std::vector<double> ControlModule::readMotors()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
return std::vector<double> {motors[0], motors[1], motors[2], motors[3]};
|
||||
}
|
||||
|
||||
double ControlModule::ratio(double angle)
|
||||
{
|
||||
double minAngularSpeed = -1.0;
|
||||
double maxAngularSpeed = 1.0;
|
||||
double speedRange = maxAngularSpeed - minAngularSpeed;
|
||||
|
||||
double minAngle = -20.0;
|
||||
double maxAngle = 20.0;
|
||||
double angularRange = maxAngle - minAngle;
|
||||
|
||||
if(angle < minAngle)
|
||||
{
|
||||
angle = minAngle;
|
||||
}
|
||||
if(angle > maxAngle)
|
||||
{
|
||||
angle = maxAngle;
|
||||
}
|
||||
double progress = angle - minAngle;
|
||||
double progressPercent = progress/angularRange;
|
||||
|
||||
double speed = minAngularSpeed + progressPercent*speedRange;
|
||||
return speed;
|
||||
}
|
||||
@@ -1,11 +1,28 @@
|
||||
#pragma once
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
class ControlModule
|
||||
{
|
||||
private:
|
||||
/* data */
|
||||
mutable std::mutex mtx;
|
||||
double motors[4]; //LeftFront; RightFront; LeftBack; RightBack
|
||||
double maxSpeed;
|
||||
double rotateSpeed;
|
||||
double moveSideSpeed;
|
||||
public:
|
||||
ControlModule(/* args */);
|
||||
|
||||
ControlModule(double maxSpeed, double rotateSpeed, double moveSideSpeed);
|
||||
~ControlModule();
|
||||
void adjustSpeed();
|
||||
void moveSide(int imageColumsMiddle, int contourColumsMiddle);
|
||||
void rotate(double ratio);
|
||||
void drive(double ratio);
|
||||
void unit(); //Brings the max Value to 1.0
|
||||
double ratio(double angle);
|
||||
|
||||
void calcSpeeds(int imageColumsMiddle, int contourColumsMiddle, double angle); //Funktion to be called
|
||||
std::vector<double> readMotors();
|
||||
};
|
||||
@@ -1,12 +1,10 @@
|
||||
find_package( OpenCV REQUIRED )
|
||||
include_directories( ${OpenCV_INCLUDE_DIRS} ${Utils_SOURCE_DIRS})
|
||||
|
||||
link_directories(${Utils_SOURCE_DIRS})
|
||||
|
||||
add_library(Input input.cpp)
|
||||
set_target_properties(Input PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(Input PRIVATE .)
|
||||
|
||||
target_include_directories(Input PRIVATE ${OpenCV_INCLUDE_DIRS} ${Utils_SOURCE_DIRS})
|
||||
target_include_directories(Input PUBLIC .)
|
||||
target_link_libraries( Input ${OpenCV_LIBS} Utils)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "input.h"
|
||||
|
||||
Input::Input(int videoHeight, int videoWidth) : cap(0), videoHeight(videoHeight), videoWidth(videoWidth)
|
||||
// TODO: Wenn ihr in die Zeile den Pfad zum Testvideo statt der 0 packt, benmutzt er das Testvideo.
|
||||
Input::Input(int videoHeight, int videoWidth) : cap("C:\\Line-Following-Robot\\AutonomousMode\\Test_data\\video1.h264"), videoHeight(videoHeight), videoWidth(videoWidth)//Input::Input(int videoHeight, int videoWidth) : cap(0), videoHeight(videoHeight), videoWidth(videoWidth)
|
||||
//Input::Input(int videoHeight, int videoWidth) : cap(0), videoHeight(videoHeight), videoWidth(videoWidth)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
this->cap.set(CAP_PROP_FRAME_HEIGHT, videoHeight);
|
||||
this->cap.set(CAP_PROP_FRAME_WIDTH, videoWidth);
|
||||
}
|
||||
@@ -13,22 +16,25 @@ Input::~Input()
|
||||
|
||||
Mat Input::readFile(String filePath)
|
||||
{
|
||||
std::srand(std::time(0));
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
std::srand(static_cast<unsigned int>(std::time(0)));
|
||||
// Read all .jpg files from the specified folder
|
||||
std::string folder = filePath;
|
||||
std::vector<std::string> filenames;
|
||||
cv::String folder = filePath;
|
||||
std::vector<cv::String> filenames;
|
||||
cv::glob(folder, filenames);
|
||||
|
||||
// Random shuffle
|
||||
std::random_shuffle(filenames.begin(), filenames.end());
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
std::shuffle(filenames.begin(), filenames.end(), g);
|
||||
|
||||
Mat image = imread(filenames[0], IMREAD_COLOR);
|
||||
Mat image = imread(filePath, IMREAD_COLOR);
|
||||
|
||||
if(image.empty())
|
||||
{
|
||||
std::cout << "Could not read the image: " << filePath << std::endl;
|
||||
return Mat();
|
||||
//To do:Exception handeling
|
||||
stringstream sstream;
|
||||
sstream << "Could not read the image: " << filePath << std::endl;
|
||||
throw std::runtime_error(sstream.str());
|
||||
}
|
||||
resize(image, image, Size(this->videoWidth, this->videoHeight));
|
||||
return image;
|
||||
@@ -36,19 +42,26 @@ Mat Input::readFile(String filePath)
|
||||
|
||||
Mat Input::readWebcam()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
Mat image;
|
||||
|
||||
if(!cap.isOpened()) {
|
||||
cout << "Fehler";
|
||||
return Mat();
|
||||
stringstream sstream;
|
||||
sstream << "Video capture not opened" << std::endl;
|
||||
throw std::runtime_error(sstream.str());
|
||||
}
|
||||
|
||||
cap.read(image);
|
||||
|
||||
if(!cap.grab()) {
|
||||
stringstream sstream;
|
||||
sstream << "Could not grab frame from camera" << std::endl;
|
||||
throw std::runtime_error(sstream.str());
|
||||
}
|
||||
cap.retrieve(image);
|
||||
return image;
|
||||
}
|
||||
|
||||
void Input::freeWebcam()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
this->cap.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
@@ -15,7 +16,7 @@ class Input
|
||||
{
|
||||
private:
|
||||
VideoCapture cap;
|
||||
|
||||
mutable std::mutex mtx;
|
||||
public:
|
||||
int videoHeight;
|
||||
int videoWidth;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
include_directories( ${Utils_SOURCE_DIRS})
|
||||
link_directories(${Utils_SOURCE_DIRS})
|
||||
|
||||
add_library(Interpreter interpreter.cpp)
|
||||
set_target_properties(Interpreter PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(Interpreter PRIVATE .)
|
||||
|
||||
target_include_directories(Interpreter PRIVATE ${Utils_SOURCE_DIRS})
|
||||
target_include_directories(Interpreter PUBLIC .)
|
||||
target_link_libraries( Interpreter Utils)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
include_directories( ${Utils_SOURCE_DIRS})
|
||||
link_directories(${Utils_SOURCE_DIRS})
|
||||
|
||||
add_library(IntersectionHandler intersection_handler.cpp)
|
||||
set_target_properties(IntersectionHandler PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(IntersectionHandler PRIVATE .)
|
||||
|
||||
#target_include_directories(IntersectionHandler PRIVATE ${Utils_SOURCE_DIRS})
|
||||
target_include_directories(IntersectionHandler PRIVATE ${Utils_SOURCE_DIRS})
|
||||
target_include_directories(IntersectionHandler PUBLIC .)
|
||||
target_link_libraries( IntersectionHandler Utils)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
@@ -6,4 +6,14 @@ IntersectionHandler::IntersectionHandler(/* args */)
|
||||
|
||||
IntersectionHandler::~IntersectionHandler()
|
||||
{
|
||||
}
|
||||
|
||||
const bool IntersectionHandler::foundLane(const FrameData& data)
|
||||
{
|
||||
return data.boundingBoxes.size();
|
||||
}
|
||||
|
||||
const bool IntersectionHandler::foundIntersection(const FrameData& data)
|
||||
{
|
||||
return data.boundingBoxes.size()>1;
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
#include <utils.h>
|
||||
|
||||
class IntersectionHandler
|
||||
{
|
||||
private:
|
||||
/* data */
|
||||
public:
|
||||
IntersectionHandler(/* args */);
|
||||
|
||||
~IntersectionHandler();
|
||||
|
||||
const bool foundLane(const FrameData& data);
|
||||
const bool foundIntersection(const FrameData& data);
|
||||
};
|
||||
@@ -1,13 +1,11 @@
|
||||
find_package( OpenCV REQUIRED )
|
||||
include_directories( ${OpenCV_INCLUDE_DIRS} )
|
||||
include_directories( ${Utils_SOURCE_DIRS})
|
||||
|
||||
link_directories(${Utils_SOURCE_DIRS})
|
||||
|
||||
add_library(Processing processing.cpp)
|
||||
set_target_properties(Processing PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(Processing PRIVATE .)
|
||||
|
||||
target_include_directories(Processing PRIVATE ${Utils_INCLUDE_DIRS})
|
||||
target_include_directories(Processing PRIVATE ${OpenCV_INCLUDE_DIRS})
|
||||
target_include_directories(Processing PUBLIC .)
|
||||
target_link_libraries( Processing ${OpenCV_LIBS} Utils)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "processing.h"
|
||||
|
||||
|
||||
|
||||
|
||||
Processing::Processing(/* args */)
|
||||
{
|
||||
}
|
||||
@@ -8,35 +11,213 @@ Processing::~Processing()
|
||||
{
|
||||
}
|
||||
|
||||
static double angle( Point pt1, Point pt2, Point pt0 )
|
||||
void Processing::filterReflections(FrameData& frameData)
|
||||
{
|
||||
double dx1 = pt1.x - pt0.x;
|
||||
double dy1 = pt1.y - pt0.y;
|
||||
double dx2 = pt2.x - pt0.x;
|
||||
double dy2 = pt2.y - pt0.y;
|
||||
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
|
||||
//Try to filter reflections from the frame data.
|
||||
|
||||
std::vector<int> indicesToDelete;
|
||||
for(int i = 0; i < frameData.contours.size(); i++)
|
||||
{
|
||||
|
||||
// First approach: correct contours nearly fill their bounding box.
|
||||
// So delete all contours with an area smaller than 75% of their bounding box
|
||||
double contourArea = cv::contourArea(frameData.contours[i], false);
|
||||
double boundingBoxArea = double(frameData.boundingBoxes[i].width*frameData.boundingBoxes[i].height);
|
||||
double minRatio = 0.75;
|
||||
if(boundingBoxArea/contourArea < minRatio)
|
||||
{
|
||||
indicesToDelete.push_back(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Second approach: The contour should be nearly convex
|
||||
// So delete all contours with an area smaller than 95% of their convex hull.
|
||||
std::vector<Point> hull;
|
||||
cv::convexHull(frameData.contours[i], hull);
|
||||
double hullArea = cv::contourArea(hull, false);
|
||||
double minRatioHull = 0.95;
|
||||
if(contourArea/boundingBoxArea < minRatio)
|
||||
{
|
||||
indicesToDelete.push_back(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//reverse the vector with the indices so the order isn't messed up when deleting:
|
||||
std::reverse(indicesToDelete.begin(), indicesToDelete.end());
|
||||
for(int index : indicesToDelete)
|
||||
{
|
||||
frameData.boundingBoxes.erase(frameData.boundingBoxes.begin() + index);
|
||||
frameData.contours.erase(frameData.contours.begin() + index);
|
||||
frameData.leftEdges.erase(frameData.leftEdges.begin() + index);
|
||||
frameData.middlePoints.erase(frameData.middlePoints.begin() + index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Processing::processImage(Mat& inputPicture, int thresholdBinary, int gaussKernelSize, int thresholdCanny1, int thresholdCanny2, int apertureSizeCanny)
|
||||
bool Processing::checkIntersection(const std::vector<std::vector<Point>>& lines, const std::vector<Point>& outline)
|
||||
{
|
||||
|
||||
/* TODO:
|
||||
* Was ich mir hier vorstelle:
|
||||
* Wir haben die konturen und die hough linien
|
||||
* Konturen haben schwäche mit Reflektionen, hough linien sind unvollständig.
|
||||
* Irgendwie sollten die beiden Datensätze gemerged werden, um das "gute" aus beiden zu vereinen.
|
||||
* Evtl in richtung "Schau den Bereich der Contour (+ Toleranz?) im hough bild an. Wenn keine Linie im Bereich ist, ist die Contour eine Reflektion"
|
||||
*
|
||||
* Aktuell nehmen wir die abkürzung, die segmente rauszuwerfen, sobald keine Linie im Bild ist.
|
||||
* Funktioniert für unsere Daten auch ziemlich gut
|
||||
*/
|
||||
|
||||
if(lines.size() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Processing::calcAngles(FrameData &data, int imageColums, int imageRows, bool turnLeft)
|
||||
{
|
||||
|
||||
if(data.contours.size() > 0)
|
||||
{
|
||||
int index;
|
||||
// get the most left/right contour
|
||||
|
||||
int leftmostEdge = imageColums;
|
||||
int rightmostEdge = 0;
|
||||
|
||||
for (int i = 0; i < data.leftEdges.size(); i++)
|
||||
{
|
||||
int edge = data.leftEdges[i].x;
|
||||
|
||||
if (edge <= leftmostEdge && turnLeft)
|
||||
{
|
||||
leftmostEdge = edge;
|
||||
index = i;
|
||||
}
|
||||
else if(edge >= rightmostEdge && !turnLeft)
|
||||
{
|
||||
rightmostEdge = edge;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the Top-Left and Bottom-Left Point of the Contour
|
||||
|
||||
std::vector<cv::Point> leftMostContour = data.contours[index];
|
||||
|
||||
/*
|
||||
Not going to dive into that part to fix it.
|
||||
Trying to use centroid instead.
|
||||
int xleftBottom = imageColums;
|
||||
int xleftTop = imageColums;
|
||||
int yBottom = data.boundingBoxes[index].y;
|
||||
int yTop = data.boundingBoxes[index].height;
|
||||
|
||||
for (int i = 0; i < leftMostContour.size(); i++)
|
||||
{
|
||||
if(leftMostContour[i].y == 0 && leftMostContour[i].x < xleftBottom)
|
||||
{
|
||||
xleftBottom = leftMostContour[i].x;
|
||||
}
|
||||
else if(leftMostContour[i].y > yTop -4 && leftMostContour[i].x < xleftTop)
|
||||
{
|
||||
xleftTop = leftMostContour[i].x;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate angle
|
||||
int deltaX = abs(xleftBottom - xleftTop);
|
||||
int deltaY = yTop;
|
||||
angle = Calcs::calcAngle(deltaX, deltaY);
|
||||
*/
|
||||
|
||||
//Get the contours center using moments:
|
||||
cv::Moments moments=cv::moments(leftMostContour);
|
||||
LFRPoint contourCenter(moments.m10/moments.m00, moments.m01/moments.m00);
|
||||
LFRPoint imageCenter(double(imageColums)/2.0, double(imageRows)/2.0);
|
||||
LFRPoint focusPoint(imageCenter.x, imageCenter.y+1000.0);
|
||||
|
||||
LFRVector a(imageCenter-focusPoint);
|
||||
LFRVector b(contourCenter-focusPoint);
|
||||
|
||||
double angle = a.angle(b);
|
||||
if (b.x < 0.0)
|
||||
{
|
||||
angle *= -1.0;
|
||||
}
|
||||
|
||||
//Write to Data
|
||||
data.angle = angle;
|
||||
data.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Processing::processImage(Mat& inputPicture, int thresholdBinary, int gaussKernelSize)
|
||||
{
|
||||
//Idea here is: Processing module consists of two methods:
|
||||
// One (this) to do all kinds of stuff to the picture (grayscale conversion, threshold, gauss etc etc)
|
||||
// And one (the other one) to segment the lines.
|
||||
// No return value here as the input is passed by reference -> directly modified.
|
||||
cvtColor(inputPicture, inputPicture, COLOR_BGR2GRAY);
|
||||
threshold(inputPicture, inputPicture, thresholdBinary, 255, THRESH_BINARY);
|
||||
GaussianBlur(inputPicture, inputPicture, Size(gaussKernelSize, gaussKernelSize), 0);
|
||||
Canny(inputPicture, inputPicture, thresholdCanny1, thresholdCanny2, apertureSizeCanny);
|
||||
threshold(inputPicture, inputPicture, thresholdBinary, 255, THRESH_BINARY);
|
||||
|
||||
//Perform an opening
|
||||
Mat kernel(5,5, CV_8UC1,1);
|
||||
morphologyEx(inputPicture, inputPicture, 2, kernel);
|
||||
}
|
||||
|
||||
std::vector<Vec4i> Processing::calculateLineSegments(const Mat& inputPicture)
|
||||
FrameData Processing::calculateLineSegments(Mat& inputPicture, const cv::Rect& roi)
|
||||
{
|
||||
//See following link
|
||||
//https://stackoverflow.com/questions/45322630/how-to-detect-lines-in-opencv
|
||||
|
||||
FrameData data;
|
||||
Mat inputPictureRoi = inputPicture(roi);
|
||||
cv::findContours(inputPictureRoi, data.contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
|
||||
|
||||
vector<Vec4i> lines;
|
||||
VectorOfLines linesInVectors;
|
||||
HoughLinesP(inputPicture, lines, 1, CV_PI/360, 150, 0, 250);
|
||||
//lines = linesInVectors.findMiddleLine(lines);
|
||||
|
||||
return lines;
|
||||
std::vector<std::vector<Point>> houghLines;
|
||||
Canny(inputPicture, inputPicture, 50, 100, 3);
|
||||
HoughLinesP(inputPicture, lines, 1, CV_PI/180, 100, 30, 150);
|
||||
//Draw lines
|
||||
inputPicture = Mat::zeros(inputPicture.size().height, inputPicture.size().width, CV_8UC1);
|
||||
|
||||
for( size_t i = 0; i < lines.size(); i++ )
|
||||
{
|
||||
line( inputPicture, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(255,255,255), 3, 8 );
|
||||
std::vector<Point> line;
|
||||
line.push_back(Point(lines[i][0], lines[i][1]));
|
||||
line.push_back(Point(lines[i][2], lines[i][3]));
|
||||
houghLines.push_back(line);
|
||||
}
|
||||
|
||||
|
||||
//Delete the areas that are too small
|
||||
auto iterator = data.contours.begin();
|
||||
while(iterator != data.contours.end())
|
||||
{
|
||||
if (contourArea(*iterator) < 7000)
|
||||
{
|
||||
iterator = data.contours.erase(iterator);
|
||||
}
|
||||
else if (!checkIntersection(houghLines, *iterator))
|
||||
{
|
||||
iterator = data.contours.erase(iterator);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Check for intersection with lines:
|
||||
Rect boundingBox = boundingRect(*iterator);
|
||||
boundingBox.x += roi.x;
|
||||
boundingBox.y += roi.y;
|
||||
data.boundingBoxes.push_back(boundingBox);
|
||||
data.middlePoints.push_back(Point(boundingBox.x+boundingBox.width/2, boundingBox.y+boundingBox.height/2));
|
||||
data.leftEdges.push_back(Point(boundingBox.x, boundingBox.y+boundingBox.height/2));
|
||||
++iterator;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <utils.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
@@ -10,6 +11,7 @@ class Processing
|
||||
{
|
||||
private:
|
||||
/* data */
|
||||
bool checkIntersection(const std::vector<std::vector<Point>>& lines, const std::vector<Point>& outline);
|
||||
public:
|
||||
Processing(/* args */);
|
||||
// To do:
|
||||
@@ -19,7 +21,8 @@ public:
|
||||
// End und Anfangspunkt analysieren und Winkel und Ausrichtung der Linie extrahieren (Abstand des untersten Punktes von der Mitte)
|
||||
~Processing();
|
||||
|
||||
void processImage(Mat& inputPicture, int thresholdBinary, int gaussKernelSize, int thresholdCanny1, int thresholdCanny2, int apertureSizeCanny);
|
||||
|
||||
std::vector<Vec4i> calculateLineSegments(const Mat& inputPicture);
|
||||
void processImage(Mat& inputPicture, int thresholdBinary, int gaussKernelSize);
|
||||
void filterReflections(FrameData& frameData);
|
||||
FrameData calculateLineSegments(Mat& inputPicture, const cv::Rect& roi);
|
||||
void calcAngles(FrameData &data, int imageColums, int imageRows, bool turnLeft);
|
||||
};
|
||||
@@ -1,7 +1,8 @@
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
//#include <opencv2/core/utils/logger.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <direct.h>
|
||||
|
||||
#include <input.h>
|
||||
#include <processing.h>
|
||||
@@ -9,6 +10,9 @@
|
||||
#include <interpreter.h>
|
||||
#include <intersection_handler.h>
|
||||
|
||||
// ToDo:
|
||||
// Hab ich nicht mehr aktuell gehalten. Wenn Ihr hier arbeiten wollt, an aktuellen Stand anpassen.
|
||||
/*
|
||||
void sweep_em_all(int thresholdBinary, int videoHeight, int videoWidth, int gaussKernelSize, int thresholdCanny1, int thresholdCanny2, int apertureSizeCanny)
|
||||
{
|
||||
Input input(videoHeight, videoWidth);
|
||||
@@ -34,27 +38,97 @@ void sweep_em_all(int thresholdBinary, int videoHeight, int videoWidth, int gaus
|
||||
|
||||
void in_depth_processing_chain(int thresholdBinary, int videoHeight, int videoWidth, int gaussKernelSize, int thresholdCanny1, int thresholdCanny2, int apertureSizeCanny)
|
||||
{
|
||||
std::string outputFolder = "C:\\Users\\User\\Desktop\\temp";
|
||||
Input input(videoHeight, videoWidth);
|
||||
|
||||
Mat image = input.readFile("C:\\Users\\User\\Desktop\\Studium\\02_Master_MSY\\2. Semester Winter 22 23\\Projekt\\Line-Following-Robot\\Test_data");
|
||||
imwrite(outputFolder + "\\01_input.jpg", image);
|
||||
cvtColor(image, image, COLOR_BGR2GRAY);
|
||||
imwrite(outputFolder + "\\02_color_convert.jpg", image);
|
||||
GaussianBlur(image, image, Size(gaussKernelSize, gaussKernelSize), 0);
|
||||
imwrite(outputFolder + "\\03_gauss.jpg", image);
|
||||
threshold(image, image, thresholdBinary, 255, THRESH_BINARY);
|
||||
imwrite(outputFolder + "\\04_threshold.jpg", image);
|
||||
Canny(image, image, thresholdCanny1, thresholdCanny2, apertureSizeCanny);
|
||||
imwrite(outputFolder + "\\05_canny.jpg", image);
|
||||
cv::String outputFolder = "C:\\Users\\tim-z\\Desktop\\temp";
|
||||
cv::String inputFolder = "C:\\Users\\tim-z\\Desktop\\Studium\\02_Master MSY\\2. Semester Winter 2022 2023\\Projekt\\Repo\\Line-Following-Robot\\AutonomousMode\\Test_data";
|
||||
|
||||
}
|
||||
std::vector<cv::String> filenames;
|
||||
cv::glob(inputFolder, filenames);
|
||||
|
||||
//filenames.begin(), filenames.end()
|
||||
int i = 0;
|
||||
for(std::vector<cv::String>::iterator it = filenames.begin(); it != filenames.end(); it++)
|
||||
{
|
||||
std::string current_output = outputFolder + "\\" + to_string(i);
|
||||
std::cout << current_output << std::endl;
|
||||
const char* current_output_char = current_output.c_str();
|
||||
_mkdir(current_output_char);
|
||||
|
||||
std::string inputFile = inputFolder + "\\image" + to_string(i+1) + ".jpeg";
|
||||
Mat original_image = input.readFile(inputFile);
|
||||
imwrite(current_output + "\\00_input.jpg", original_image);
|
||||
|
||||
Point roiOrigin(0, original_image.rows*(7.5/12.0));
|
||||
Rect roi = Rect(roiOrigin.x, roiOrigin.y, original_image.cols, original_image.rows/12);
|
||||
|
||||
Mat image = original_image(roi);
|
||||
|
||||
imwrite(current_output + "\\01_roi.jpg", image);
|
||||
cvtColor(image, image, COLOR_BGR2GRAY);
|
||||
imwrite(current_output + "\\02_color_convert.jpg", image);
|
||||
GaussianBlur(image, image, Size(gaussKernelSize, gaussKernelSize), 0);
|
||||
imwrite(current_output + "\\03_gauss.jpg", image);
|
||||
threshold(image, image, thresholdBinary, 255, THRESH_BINARY);
|
||||
imwrite(current_output + "\\04_threshold.jpg", image);
|
||||
|
||||
// Opening (reduces noise)
|
||||
Mat kernel(5,5, CV_8UC1,1);
|
||||
morphologyEx(image, image, 2, kernel);
|
||||
imwrite(current_output + "\\05_opening.jpg", image);
|
||||
|
||||
//Canny(image, image, thresholdCanny1, thresholdCanny2, apertureSizeCanny);
|
||||
//imwrite(outputFolder + "\\06_canny.jpg", image);
|
||||
|
||||
vector<vector<Point> > contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
vector<Rect> vectorOfRects;
|
||||
vector<Point> vectorOfLeftEdges;
|
||||
|
||||
findContours(image,contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
|
||||
int amountOfValidRects = 0;
|
||||
for( int i = 0; i< contours.size(); i++ ) // iterate through each contour.
|
||||
{
|
||||
double a = contourArea( contours[i],false); // Find the area of contour
|
||||
if(a > 3500)
|
||||
{
|
||||
drawContours(original_image, contours, i, Scalar(0,255,255), 1, 8, hierarchy, 0, roiOrigin);
|
||||
|
||||
Rect currentBoundingRect = boundingRect(contours[i]);
|
||||
//Handle roi offset:
|
||||
currentBoundingRect.x += roiOrigin.x;
|
||||
currentBoundingRect.y += roiOrigin.y;
|
||||
vectorOfRects.push_back(currentBoundingRect);
|
||||
|
||||
rectangle(original_image, currentBoundingRect, Scalar(0,255,0));
|
||||
|
||||
// get mid-point of rect
|
||||
Point midRect = Point(currentBoundingRect.x+currentBoundingRect.width/2, currentBoundingRect.y+currentBoundingRect.height/2);
|
||||
|
||||
// Draw middle as small rect instead of circle because for whatever reasons drawing a circle doesnt work.
|
||||
Rect testRect(Point(midRect.x-2, midRect.y-2), Point(midRect.x+2, midRect.y+2));
|
||||
rectangle(original_image, testRect, Scalar(0,0,255));
|
||||
// get the left edge of rect
|
||||
// used as offset as raspicam is not
|
||||
// mounted on mid of regbot
|
||||
Point leftEdge(currentBoundingRect.x, currentBoundingRect.y+currentBoundingRect.height/2);
|
||||
vectorOfLeftEdges.push_back(leftEdge);
|
||||
|
||||
testRect = Rect(Point(leftEdge.x-2, leftEdge.y-2), Point(leftEdge.x+2, leftEdge.y+2));
|
||||
rectangle(original_image, testRect, Scalar(0,0,255));
|
||||
amountOfValidRects++;
|
||||
}
|
||||
}
|
||||
imwrite(current_output + "\\06_contours.jpg", original_image);
|
||||
i++;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
//Disable opencv logging messages
|
||||
cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_WARNING);
|
||||
//cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_WARNING);
|
||||
|
||||
const int thresholdBinary = 140;
|
||||
const int videoHeight = 720;
|
||||
@@ -65,5 +139,5 @@ int main(void)
|
||||
const int apertureSizeCanny = 3;
|
||||
|
||||
//sweep_em_all(thresholdBinary, videoHeight, videoWidth, gaussKernelSize, thresholdCanny1, thresholdCanny2, apertureSizeCanny);
|
||||
in_depth_processing_chain(thresholdBinary, videoHeight, videoWidth, gaussKernelSize, thresholdCanny1, thresholdCanny2, apertureSizeCanny);
|
||||
//in_depth_processing_chain(thresholdBinary, videoHeight, videoWidth, gaussKernelSize, thresholdCanny1, thresholdCanny2, apertureSizeCanny);
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
find_package( OpenCV REQUIRED )
|
||||
include_directories( ${OpenCV_INCLUDE_DIRS} )
|
||||
|
||||
add_library(Utils utils.cpp)
|
||||
set_target_properties(Utils PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(Utils PRIVATE .)
|
||||
|
||||
target_include_directories(Utils PRIVATE ${OpenCV_INCLUDE_DIRS})
|
||||
target_include_directories(Utils PUBLIC .)
|
||||
target_link_libraries( Utils ${OpenCV_LIBS} )
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
set (UTILS_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include(CPack)
|
||||
@@ -29,6 +29,22 @@ LFRVector::~LFRVector()
|
||||
{
|
||||
}
|
||||
|
||||
double LFRVector::angle(const LFRVector& other) const
|
||||
{
|
||||
return acos(dot(other)/(this->norm()*other.norm())) * (180.0/M_PI);
|
||||
}
|
||||
|
||||
double LFRVector::dot(const LFRVector& other) const
|
||||
{
|
||||
return x*other.x + y*other.y;
|
||||
}
|
||||
|
||||
double LFRVector::norm() const
|
||||
{
|
||||
return sqrt(x*x+y*y);
|
||||
}
|
||||
|
||||
|
||||
LFRLine::LFRLine(/* args */) : start(), dir()
|
||||
{
|
||||
}
|
||||
@@ -46,57 +62,23 @@ LFRLine::~LFRLine()
|
||||
{
|
||||
}
|
||||
|
||||
VectorOfLines::VectorOfLines()
|
||||
{
|
||||
}
|
||||
|
||||
VectorOfLines::~VectorOfLines()
|
||||
{
|
||||
}
|
||||
|
||||
double VectorOfLines::calcGradient(Point p0, Point p1)
|
||||
{
|
||||
double gradient = (p1.y - p0.y)/(p1.x - p0.x + 1e-10);
|
||||
return p1.x > p0.x ? gradient : - gradient;
|
||||
}
|
||||
|
||||
float VectorOfLines::calcZeroPoint(cv::Point x, float m)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
int Calcs::calcAngle(int deltaX, int deltaY){
|
||||
|
||||
double VectorOfLines::calcDistance(Point p0, Point p1)
|
||||
{
|
||||
return sqrt(pow(p1.y - p0.y, 2) + pow(p1.x - p0.x, 2));
|
||||
}
|
||||
int refAngle = 0;
|
||||
|
||||
vector<Vec4i> VectorOfLines::findMiddleLine(vector<Vec4i> &lines){
|
||||
Point point11;
|
||||
Point point12;
|
||||
Point point21;
|
||||
Point point22;
|
||||
vector<Vec4i> middleLines;
|
||||
for( size_t i = 0; i < (lines.size() - 1); i++ )
|
||||
{
|
||||
point11 = Point(lines[i][0], lines[i][1]);
|
||||
point12 = Point( lines[i][2], lines[i][3]);
|
||||
double gradient1 = VectorOfLines::calcGradient(point11, point12);
|
||||
if(deltaX > 10E-12){
|
||||
refAngle = (int)((atan(deltaY/deltaX) * 180.0/M_PI) + 0.5 - (refAngle<0)); //Here 0.5 (or -0.5) is added to round a float number to int right
|
||||
|
||||
//Compare every Line with the other
|
||||
for( size_t j = 0; j < (lines.size()); j++ )
|
||||
{
|
||||
if(j != i)
|
||||
{
|
||||
point21 = Point(lines[j][0], lines[j][1]);
|
||||
point22 = Point(lines[j][2], lines[j][3]);
|
||||
double gradient2 = VectorOfLines::calcGradient(point21, point22);
|
||||
if(norm(gradient1 - gradient2) < 0.15)
|
||||
{
|
||||
middleLines.push_back(Vec4i((point11.x+point21.x)/2, (point11.y+point21.y)/2, (point12.x+point22.x)/2, (point12.y+point22.y)/2));
|
||||
}
|
||||
}
|
||||
// convert from img coordinates to regbot coordinates
|
||||
refAngle = -(refAngle);
|
||||
|
||||
if (refAngle > 90)
|
||||
refAngle = refAngle - 180;
|
||||
}else{
|
||||
refAngle = -90;
|
||||
}
|
||||
|
||||
}
|
||||
return middleLines;
|
||||
return refAngle;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <cmath>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
@@ -30,6 +33,10 @@ public:
|
||||
LFRVector(const LFRPoint& pt);
|
||||
~LFRVector();
|
||||
|
||||
double angle(const LFRVector& other) const;
|
||||
double dot(const LFRVector& other) const;
|
||||
double norm() const;
|
||||
|
||||
};
|
||||
|
||||
class LFRLine
|
||||
@@ -47,17 +54,21 @@ public:
|
||||
~LFRLine();
|
||||
};
|
||||
|
||||
class VectorOfLines{
|
||||
class Calcs{
|
||||
private:
|
||||
public:
|
||||
Point startPoint;
|
||||
float gradient;
|
||||
float zeroPoint;
|
||||
VectorOfLines();
|
||||
~VectorOfLines();
|
||||
static double calcGradient(Point x, Point y);
|
||||
float calcZeroPoint(cv::Point x, float m);
|
||||
static double calcDistance(Point p0, Point p1);
|
||||
vector<Vec4i> findMiddleLine(vector<Vec4i> &lines);
|
||||
static int calcAngle(int deltaX, int deltaY);
|
||||
};
|
||||
|
||||
class FrameData
|
||||
{
|
||||
public:
|
||||
std::vector<std::vector<cv::Point>> contours;
|
||||
std::vector<cv::Rect> boundingBoxes;
|
||||
std::vector<cv::Point> leftEdges;
|
||||
std::vector<cv::Point> middlePoints;
|
||||
double angle; //Angle of the contour the robot has to follow to
|
||||
int index; //Index of the contour the robot has to follow to
|
||||
|
||||
FrameData(): contours(), boundingBoxes(), leftEdges(), middlePoints() {}
|
||||
};
|
||||
@@ -4,20 +4,64 @@
|
||||
int main(void)
|
||||
{
|
||||
//Disable opencv logging messages
|
||||
cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_WARNING);
|
||||
//cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_WARNING);
|
||||
|
||||
const int thresholdBinary = 140;
|
||||
const int videoHeight = 240;
|
||||
const int videoWidth = 320;
|
||||
const int gaussKernelSize = 21;
|
||||
const int thresholdCanny1 = 50;
|
||||
const int thresholdCanny2 = 100;
|
||||
const int apertureSizeCanny = 3;
|
||||
const int videoHeight = 720;
|
||||
const int videoWidth = 1280;
|
||||
const int gaussKernelSize = 11;
|
||||
const double maxSpeed = 0.5;
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
LFR lfr(videoHeight, videoWidth, thresholdBinary, gaussKernelSize, maxSpeed, [&](std::exception const &ex)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
std::cerr<<"camera exception:"<<ex.what()<<std::endl;
|
||||
return false;
|
||||
});
|
||||
|
||||
//To calculate the frame rate
|
||||
std::chrono::milliseconds last = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
|
||||
cv::Mat img;
|
||||
lfr.addListener([&](LFR_Result result)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (!result.rawImage.empty())
|
||||
{
|
||||
img = result.rawImage;
|
||||
//Resize to minimize latency using raspi over ssh
|
||||
//cv::resize(result.rawImage, img, cv::Size(128, 64+32));
|
||||
|
||||
//Calculate frame rate
|
||||
now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
unsigned int deltaMs = static_cast<unsigned int>((now-last).count());
|
||||
double delta = static_cast<double>(deltaMs) / 1000.0;
|
||||
double frameRate = 1.0 / static_cast<double>(delta);
|
||||
|
||||
if (result.validLane)
|
||||
{
|
||||
std::cout << "Frame rate: " << frameRate << " angle: " << result.data.angle
|
||||
<< " motor 1: " << result.motorSignals[0] << " motor 2: " << result.motorSignals[1]
|
||||
<< " motor 3: " << result.motorSignals[2] << " motor 4: " << result.motorSignals[3] <<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "No lane found." << std::endl;;
|
||||
}
|
||||
last = now;
|
||||
}
|
||||
}, &mutex);
|
||||
|
||||
LFR lfr(videoHeight, videoWidth, thresholdBinary, gaussKernelSize, thresholdCanny1, thresholdCanny2, apertureSizeCanny);
|
||||
lfr.startLoop();
|
||||
//To end the video stream, write any char in the console.
|
||||
char a;
|
||||
std::cin >> a;
|
||||
lfr.endLoop();
|
||||
}
|
||||
|
||||
for(int finished = false; finished != 'q';){
|
||||
finished = std::tolower(cv::waitKey(1));
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if(!img.empty()){
|
||||
cv::imshow("frame", img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,151 @@
|
||||
#include "lfr.h"
|
||||
|
||||
#define right false
|
||||
#define left true
|
||||
|
||||
LFR::LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize, int thresholdCanny1, int thresholdCanny2, int apertureSizeCanny)
|
||||
: iAmLooping(false), input(videoHeight, videoWidth), processing(), controlModule(), interpreter(), intersectionHandler()
|
||||
LFR::LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize, double maxSpeed, ExceptionCallback cb):
|
||||
stop(false),
|
||||
input(videoHeight, videoWidth),
|
||||
processing(),
|
||||
controlModule(maxSpeed, 1.0, 1.0),
|
||||
interpreter(),
|
||||
intersectionHandler(),
|
||||
cb(cb)
|
||||
{
|
||||
this->iAmLooping = false;
|
||||
this->thresholdBinary = thresholdBinary;
|
||||
this->gaussKernelSize = gaussKernelSize;
|
||||
this->thresholdCanny1 = thresholdCanny1;
|
||||
this->thresholdCanny2 = thresholdCanny2;
|
||||
this->apertureSizeCanny = apertureSizeCanny;
|
||||
}
|
||||
|
||||
LFR::~LFR()
|
||||
{
|
||||
if(iAmLooping)
|
||||
endLoop();
|
||||
if(thread)
|
||||
{
|
||||
this->endLoop();
|
||||
thread->join();
|
||||
}
|
||||
}
|
||||
|
||||
void LFR::loop()
|
||||
void LFR::removeListener(LFR::ListenerKey key)
|
||||
{
|
||||
namedWindow("Display window");
|
||||
while(iAmLooping)
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
auto it = std::find_if(std::begin(listeners), std::end(listeners), [&](auto const &val){
|
||||
return val.first == key;
|
||||
});
|
||||
if(it != std::end(listeners))
|
||||
{
|
||||
Mat image = input.readWebcam();
|
||||
processing.processImage(image, this->thresholdBinary, this->gaussKernelSize, this->thresholdCanny1, thresholdCanny2, this->apertureSizeCanny);
|
||||
std::vector<Vec4i> lines = processing.calculateLineSegments(image);
|
||||
for( size_t i = 0; i < lines.size(); i++ )
|
||||
{
|
||||
line( image, Point(lines[i][0], lines[i][1]),
|
||||
Point( lines[i][2], lines[i][3]), (0,0,255), 1, 8 );
|
||||
}
|
||||
imshow("Display window", image);
|
||||
char c = (char)waitKey(1);
|
||||
listeners.erase(it);
|
||||
}
|
||||
destroyWindow("Display window");
|
||||
input.freeWebcam();
|
||||
}
|
||||
|
||||
void LFR::addListener(LFR::ListenerCallback cb, LFR::ListenerKey key)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
listeners.emplace_back(key, std::move(cb));
|
||||
}
|
||||
|
||||
void LFR::setStop(bool val)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
stop = val;
|
||||
}
|
||||
|
||||
void LFR::createThread()
|
||||
{
|
||||
thread = std::make_unique<std::thread>([this](){
|
||||
while(true)
|
||||
{
|
||||
LFR_Result result;
|
||||
if(!stop && !listeners.empty())
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
Mat originalImage = input.readWebcam();
|
||||
|
||||
Point roiOrigin(0, int(originalImage.rows*(3.25/6.0)));
|
||||
Rect roi(roiOrigin.x, roiOrigin.y, originalImage.cols, originalImage.rows/6);
|
||||
Mat processedImage = originalImage;
|
||||
|
||||
processing.processImage(processedImage, this->thresholdBinary, this->gaussKernelSize);
|
||||
FrameData data = processing.calculateLineSegments(processedImage, roi);
|
||||
processing.filterReflections(data);
|
||||
processing.calcAngles(data, originalImage.cols, originalImage.rows, left);
|
||||
|
||||
result.validLane = intersectionHandler.foundLane(data);
|
||||
if (result.validLane)
|
||||
{
|
||||
controlModule.calcSpeeds(1,1, data.angle);
|
||||
processedImage = provideOutput(originalImage, processedImage, data, roi);
|
||||
}
|
||||
result.rawImage = originalImage;
|
||||
result.processedImage = processedImage;
|
||||
result.data = data;
|
||||
result.motorSignals = controlModule.readMotors();
|
||||
}
|
||||
catch(std::exception const &ex)
|
||||
{
|
||||
if(!cb(ex))
|
||||
{
|
||||
//callback returned false -> exception not handled -> exit
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
//Invoke the callback method (ListenerPair second -> ListenerCallback)
|
||||
for(auto &val : listeners)
|
||||
{
|
||||
val.second(result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LFR::startLoop()
|
||||
{
|
||||
iAmLooping = true;
|
||||
this->loopThread=thread(&LFR::loop, this);
|
||||
if(thread)
|
||||
{
|
||||
//Restart thread if it is running
|
||||
setStop(true);
|
||||
thread->join();
|
||||
setStop(false);
|
||||
}
|
||||
createThread();
|
||||
}
|
||||
|
||||
void LFR::endLoop()
|
||||
{
|
||||
iAmLooping = false;
|
||||
this->loopThread.join();
|
||||
return;
|
||||
}
|
||||
setStop(true);
|
||||
}
|
||||
|
||||
cv::Mat LFR::provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi)
|
||||
{
|
||||
for(int i = 0; i < frameData.contours.size(); i++)
|
||||
{
|
||||
drawContours(originalImage, frameData.contours, i, Scalar(0,255,255), 1, 8, noArray(), 0, Point(roi.x, roi.y));
|
||||
|
||||
rectangle(originalImage, frameData.boundingBoxes[i], Scalar(0,255,0));
|
||||
Rect center(Point(frameData.middlePoints[i].x-2, frameData.middlePoints[i].y-2), Point(frameData.middlePoints[i].x+2, frameData.middlePoints[i].y+2));
|
||||
rectangle(originalImage, center, Scalar(0,0,255));
|
||||
Rect leftRect(Point(frameData.leftEdges[i].x-2, frameData.leftEdges[i].y-2), Point(frameData.leftEdges[i].x+2, frameData.leftEdges[i].y+2));
|
||||
rectangle(originalImage, leftRect, Scalar(0,0,255));
|
||||
}
|
||||
|
||||
if(frameData.contours.size() > 0)
|
||||
{
|
||||
//Draw the Arrow for the check of the angle
|
||||
int length = 100;
|
||||
|
||||
Point contourCenter = frameData.middlePoints[frameData.index];
|
||||
Point imageCenter = Point(originalImage.size().width/2.0, originalImage.size().height/2.0);
|
||||
Point focalPoint = Point(imageCenter.x, imageCenter.y+1000.0);
|
||||
|
||||
cv::arrowedLine(originalImage, focalPoint, contourCenter, Scalar(0,0,255), 2, 8, 0, 0.03);
|
||||
cv::arrowedLine(originalImage, focalPoint, imageCenter, Scalar(0,0,255), 2, 8, 0, 0.03);
|
||||
}
|
||||
return originalImage;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
@@ -15,29 +16,54 @@
|
||||
|
||||
using namespace cv;
|
||||
|
||||
struct LFR_Result
|
||||
{
|
||||
bool validLane;
|
||||
cv::Mat rawImage;
|
||||
cv::Mat processedImage;
|
||||
FrameData data;
|
||||
std::vector<double> motorSignals;
|
||||
};
|
||||
|
||||
class LFR
|
||||
{
|
||||
public:
|
||||
using ListenerKey = void const*;
|
||||
using ExceptionCallback = std::function<bool(std::exception const &ex)>;
|
||||
using ListenerCallback = std::function<void(LFR_Result)>;
|
||||
|
||||
private:
|
||||
using ListenerPair = std::pair<ListenerKey, ListenerCallback>;
|
||||
using ListenerVector = std::vector<ListenerPair>;
|
||||
|
||||
Input input;
|
||||
Processing processing;
|
||||
ControlModule controlModule;
|
||||
Interpreter interpreter;
|
||||
IntersectionHandler intersectionHandler;
|
||||
volatile bool iAmLooping;
|
||||
void loop();
|
||||
thread loopThread;
|
||||
|
||||
int thresholdBinary;
|
||||
int gaussKernelSize;
|
||||
int thresholdCanny1;
|
||||
int thresholdCanny2;
|
||||
int apertureSizeCanny;
|
||||
|
||||
ListenerVector listeners;
|
||||
ExceptionCallback cb;
|
||||
bool stop;
|
||||
std::unique_ptr<std::thread> thread;
|
||||
mutable std::mutex mutex;
|
||||
|
||||
//void provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi);
|
||||
void createThread();
|
||||
void setStop(bool val);
|
||||
|
||||
public:
|
||||
|
||||
LFR() = delete;
|
||||
LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize, int thresholdCanny1, int thresholdCanny2, int apertureSizeCanny);
|
||||
LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize, double maxSpeed, ExceptionCallback cb);
|
||||
~LFR();
|
||||
|
||||
void startLoop();
|
||||
void endLoop();
|
||||
|
||||
void addListener(ListenerCallback cv, ListenerKey key);
|
||||
void removeListener(ListenerKey key);
|
||||
void isStopped() const noexcept;
|
||||
Mat provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
project(lfr_central VERSION 0.1.0)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
|
||||
add_subdirectory(AutonomousMode)
|
||||
add_subdirectory(Communication)
|
||||
add_subdirectory(Socket)
|
||||
|
||||
add_executable(lfr_central main.cpp lfr_state_machine.cpp lfr_states.cpp)
|
||||
|
||||
target_include_directories(lfr_central PRIVATE ${lfr_image_processing_SOURCE_DIRS})
|
||||
target_include_directories(lfr_central PRIVATE ${lfr_uart_SOURCE_DIRS})
|
||||
target_include_directories(lfr_central PRIVATE ${lfr_socket_SOURCE_DIRS})
|
||||
|
||||
|
||||
target_link_libraries( lfr_central lfr_image_processing_lib lfr_uart_lib lfr_socket_lib)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
include(CPack)
|
||||
@@ -8,9 +8,14 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_executable(lfr_uart uart_communication.cpp)
|
||||
add_executable(lfr_uart uart_communication.cpp uart_communication_main.cpp)
|
||||
add_library(lfr_uart_lib uart_communication.cpp)
|
||||
|
||||
target_link_libraries( lfr_uart Threads::Threads)
|
||||
target_link_libraries( lfr_uart_lib Threads::Threads)
|
||||
|
||||
target_include_directories(lfr_uart PUBLIC .)
|
||||
target_include_directories(lfr_uart_lib PUBLIC .)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
LFR_UART::LFR_UART() : fileDescriptor(-1) {
|
||||
this->last =std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
this->openSerialPort();
|
||||
this->configureSerialPort();
|
||||
}
|
||||
@@ -13,11 +14,13 @@ void LFR_UART::openFile(const char *fileName) {
|
||||
this->fileDescriptor = open(fileName, O_RDWR | O_NONBLOCK);
|
||||
}
|
||||
|
||||
int LFR_UART::writeDataToFile(uint8_t *buff, uint32_t bufferLength) {
|
||||
int LFR_UART::writeDataToFile(int8_t *buff, uint32_t bufferLength) {
|
||||
//std::cout << "Sending uart: " << std::bitset<8>(buff[0]) << std::endl;
|
||||
std::cout << "Sending Uart: " << std::bitset<8>(buff[0]) << ", " << std::bitset<8>(buff[1]) << ", " << std::bitset<8>(buff[2]) << ", " << std::bitset<8>(buff[3]) << " csum: " << std::bitset<8>(buff[4]) << std::endl;
|
||||
return write(this->fileDescriptor, buff, bufferLength);
|
||||
}
|
||||
|
||||
int LFR_UART::readFromFile(uint8_t *buff, uint32_t bufferLength) {
|
||||
int LFR_UART::readFromFile(int8_t *buff, uint32_t bufferLength) {
|
||||
return read(this->fileDescriptor, buff, bufferLength);
|
||||
}
|
||||
|
||||
@@ -25,37 +28,32 @@ int LFR_UART::closeFile() {
|
||||
return close(this->fileDescriptor);
|
||||
}
|
||||
|
||||
uint8_t LFR_UART::doubleToByte(double in){
|
||||
int8_t LFR_UART::doubleToByte(double in){
|
||||
/*
|
||||
* Map the range of -1.0 to 1.0 as double to the range of a byte.
|
||||
*
|
||||
* -1 -> 0
|
||||
* 0.0 -> 127
|
||||
* 1.0 -> 254
|
||||
* Not using the full range upto 254 to hit the 0.0
|
||||
* Map the range of -1.0 to 1.0 as double to the range of a byte. (int8 range)
|
||||
*/
|
||||
double minDouble = -1.0;
|
||||
double maxDouble = 1.0;
|
||||
double rangeDouble = maxDouble - minDouble;
|
||||
|
||||
double minByte = 0.0;
|
||||
double maxByte = 255.0;
|
||||
double minByte = -128.0;
|
||||
double maxByte = 127.0;
|
||||
double rangeByte = maxByte - minByte;
|
||||
|
||||
double progress = in - minDouble;
|
||||
double progressPercent = progress/rangeDouble;
|
||||
|
||||
double inputInByteRange = minByte + progressPercent*rangeByte;
|
||||
return uint8_t(inputInByteRange);
|
||||
return int8_t(inputInByteRange);
|
||||
}
|
||||
|
||||
double LFR_UART::byteToDouble(uint8_t in){
|
||||
double LFR_UART::byteToDouble(int8_t in){
|
||||
double minDouble = -1.0;
|
||||
double maxDouble = 1.0;
|
||||
double rangeDouble = maxDouble - minDouble;
|
||||
|
||||
double minByte = 0.0;
|
||||
double maxByte = 255.0;
|
||||
double minByte = -128.0;
|
||||
double maxByte = 127.0;
|
||||
double rangeByte = maxByte - minByte;
|
||||
|
||||
double progress = double(in) - minByte;
|
||||
@@ -66,27 +64,43 @@ double LFR_UART::byteToDouble(uint8_t in){
|
||||
}
|
||||
|
||||
void LFR_UART::sendTelegram(double wheel1, double wheel2, double wheel3, double wheel4){
|
||||
std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
unsigned int deltaMs = static_cast<unsigned int>((now-last).count());
|
||||
if (deltaMs < 50 && (std::fabs(wheel1)+std::fabs(wheel2)+std::fabs(wheel3)+std::fabs(wheel4)) > 0.0005)
|
||||
{
|
||||
std::cout << "Too fast" << std::endl;
|
||||
return;
|
||||
}
|
||||
last = now;
|
||||
|
||||
if(wheel1 > 1.0 || wheel2 > 1.0 || wheel3 > 1.0 || wheel4 > 1.0){
|
||||
throw CommunicatorException("Wheel value must not be greater than 1.0");
|
||||
}
|
||||
if(wheel1 < -1.0 || wheel2 < -1.0 || wheel3 < -1.0 || wheel4 < -1.0){
|
||||
throw CommunicatorException("Wheel value must not be smaller than -1.0");
|
||||
}
|
||||
// Discrepancy between the numbering of the wheels in App/Pi and the µc
|
||||
// Our - their
|
||||
// 1 - 4
|
||||
// 2 - 2
|
||||
// 3 - 3
|
||||
// 4 - 1
|
||||
int8_t wheel1B = this->doubleToByte(wheel4);
|
||||
//int8_t wheel1B = this->doubleToByte(wheel1);
|
||||
int8_t wheel2B = this->doubleToByte(wheel2);
|
||||
int8_t wheel3B = this->doubleToByte(wheel3);
|
||||
int8_t wheel4B = this->doubleToByte(wheel1);
|
||||
//int8_t wheel4B = this->doubleToByte(wheel4);
|
||||
|
||||
uint8_t wheel1B = this->doubleToByte(wheel1);
|
||||
uint8_t wheel2B = this->doubleToByte(wheel2);
|
||||
uint8_t wheel3B = this->doubleToByte(wheel3);
|
||||
uint8_t wheel4B = this->doubleToByte(wheel4);
|
||||
int8_t checksum = wheel1B^wheel2B^wheel3B^wheel4B;
|
||||
|
||||
uint8_t checksum = wheel1B^wheel2B^wheel3B^wheel4B;
|
||||
|
||||
uint8_t telegram_buffer[5] = {wheel1B, wheel2B, wheel3B, wheel4B, checksum};
|
||||
int8_t telegram_buffer[5] = {wheel1B, wheel2B, wheel3B, wheel4B, checksum};
|
||||
uint32_t telegram_length = 5;
|
||||
this->writeDataToFile(telegram_buffer, telegram_length);
|
||||
}
|
||||
|
||||
bool LFR_UART::readTelegram(double* buffer){
|
||||
uint8_t tmp_buffer[5] = {0, 0, 0, 0, 0};
|
||||
int8_t tmp_buffer[5] = {0, 0, 0, 0, 0};
|
||||
uint32_t telegram_length = 5;
|
||||
this->readFromFile(tmp_buffer, telegram_length);
|
||||
|
||||
@@ -133,20 +147,4 @@ void LFR_UART::closeSerialPort() {
|
||||
this->closeFile();
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("Starting the loopback application...\r\n");
|
||||
|
||||
LFR_UART uartCommunicator;
|
||||
uartCommunicator.sendTelegram(-1.0, -0.99, 0.99, 1.0);
|
||||
double buffer[4] = {0.0, 0.0, 0.0, 0.0};
|
||||
sleep(1);
|
||||
if(uartCommunicator.readTelegram(buffer)){
|
||||
for(int i = 0; i < 4; i++) {
|
||||
std::cout << buffer[i] << " ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout<<"ne";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,17 @@
|
||||
#include <termios.h>
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
#include <bitset>
|
||||
|
||||
|
||||
class LFR_UART
|
||||
{
|
||||
public:
|
||||
std::chrono::milliseconds last;
|
||||
int fileDescriptor;
|
||||
const char* serialPortPath = "/dev/ttyS0";
|
||||
|
||||
@@ -28,13 +33,14 @@ class LFR_UART
|
||||
void configureSerialPort();
|
||||
void closeSerialPort();
|
||||
|
||||
int writeDataToFile(uint8_t *buff, uint32_t bufferLength);
|
||||
int readFromFile(uint8_t *buff, uint32_t bufferLength);
|
||||
|
||||
int writeDataToFile(int8_t *buff, uint32_t bufferLength);
|
||||
int readFromFile(int8_t *buff, uint32_t bufferLength);
|
||||
|
||||
public:
|
||||
|
||||
uint8_t doubleToByte(double in);
|
||||
double byteToDouble(uint8_t in);
|
||||
int8_t doubleToByte(double in);
|
||||
double byteToDouble(int8_t in);
|
||||
|
||||
void sendTelegram(double wheel1, double wheel2, double wheel3, double wheel4);
|
||||
bool readTelegram(double* buffer);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "uart_communication.h"
|
||||
|
||||
/*
|
||||
* Usage: telnet [ip] [port]
|
||||
* eg.: telnet 192.158.4.1 8080
|
||||
* To quit: ctrl + +
|
||||
* q
|
||||
*/
|
||||
|
||||
int main(void) {
|
||||
printf("Starting the loopback application...\r\n");
|
||||
|
||||
LFR_UART uartCommunicator;
|
||||
uartCommunicator.sendTelegram(-1.0, -0.99, 0.99, 1.0);
|
||||
double buffer[4] = {0.0, 0.0, 0.0, 0.0};
|
||||
sleep(1);
|
||||
if(uartCommunicator.readTelegram(buffer)){
|
||||
for(int i = 0; i < 4; i++) {
|
||||
std::cout << buffer[i] << " ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout<<"ne";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
@@ -0,0 +1 @@
|
||||
LFRMobileApp
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<targetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="QUICK_BOOT_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="VIRTUAL_DEVICE_PATH" />
|
||||
<value value="C:\Users\baran\.android\avd\Pixel_2_API_R.avd" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</targetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2023-01-12T21:16:30.648959800Z" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
<option value="$PROJECT_DIR$/virtual-joystick-android-master/virtualjoystick" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable-anydpi/manuell.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable/ic_baseline_rotate_left_24.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable/ic_dashboard_black_24dp.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable/ic_home_black_24dp.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable/ic_launcher_background.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable/ic_notifications_black_24dp.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable/my_btn_toggle.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/drawable/vorwarts.xml" value="0.1" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/layout/activity_main.xml" value="0.16666666666666666" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/layout/fragment_automatik.xml" value="0.14666666666666667" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/layout/fragment_dashboard.xml" value="0.14666666666666667" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/layout/fragment_einstellungen.xml" value="0.25" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/layout/fragment_home.xml" value="0.16666666666666666" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/layout/fragment_manuell.xml" value="0.22" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/layout/fragment_notifications.xml" value="0.16666666666666666" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/menu/bottom_nav_menu.xml" value="0.16666666666666666" />
|
||||
<entry key="..\:/Line-Following-Robot/MobileApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.1" />
|
||||
<entry key="..\:/Users/baran/.gradle/caches/transforms-3/2518aadee33334bef1598ce32f2dbd8e/transformed/material-1.7.0/res/drawable-v21/material_cursor_drawable.xml" value="0.1" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 32
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.example.lfrmobileapp"
|
||||
minSdk 22
|
||||
targetSdk 32
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.5.3'
|
||||
implementation 'androidx.navigation:navigation-ui:2.5.3'
|
||||
implementation project(path: ':virtualjoystick')
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.example.lfrmobileapp;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.example.lfrmobileapp", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.example.lfrmobileapp">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.LFRMobileApp"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.example.lfrmobileapp;
|
||||
|
||||
import android.accounts.NetworkErrorException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Communication {
|
||||
public String telegram(int[] wheels, int strenght){
|
||||
String tel = "aa";
|
||||
tel += "0"; //Manuel mode
|
||||
|
||||
for(int i = 0; i < wheels.length; i++){
|
||||
if(wheels[i] == 1){
|
||||
tel += ";" + Float.toString(((float)strenght)/100) ;
|
||||
}else if(wheels[i] == -1)
|
||||
tel += ";" + Float.toString(((float)-strenght)/100);
|
||||
else{
|
||||
tel += ";0.00";
|
||||
}
|
||||
}
|
||||
tel += "zz";
|
||||
return tel;
|
||||
}
|
||||
|
||||
public String telegram(boolean startStop, ArrayList<String> list){
|
||||
String tel = "aa";
|
||||
tel += "1"; //Autonomous mode
|
||||
if(startStop){
|
||||
tel += ";1";
|
||||
}else{
|
||||
return "aa0;0.0;0.0;0.0;0.0zz";
|
||||
}
|
||||
|
||||
for (String i : list) {
|
||||
if(i.equals("Links")){
|
||||
tel += ";0";
|
||||
}else if (i.equals("Rechts")){
|
||||
tel += ";1";
|
||||
}
|
||||
}
|
||||
tel += "zz";
|
||||
return tel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.example.lfrmobileapp;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DataTransferAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||
private static final double sendRatePerSecond = 4.0;
|
||||
private boolean connected;
|
||||
private final ExtendedSocket socket;
|
||||
private BlockingQueue<String> blockingQueue;
|
||||
|
||||
public DataTransferAsyncTask(ExtendedSocket socket) {
|
||||
this.socket = socket;
|
||||
this.connected = true;
|
||||
this.blockingQueue = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
public void writeTelegram(String telegram) {
|
||||
// Schau mal hier bei Fehler
|
||||
blockingQueue.offer(telegram);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
while(!isCancelled() && connected) {
|
||||
try {
|
||||
synchronized (socket) {
|
||||
String message = blockingQueue.poll((long) (1000.0/sendRatePerSecond), TimeUnit.MILLISECONDS);
|
||||
if(message != null){
|
||||
Log.v("TAG", message + "\n");
|
||||
socket.sendMessage(message);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.example.lfrmobileapp;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
public class ExtendedSocket {
|
||||
private final Socket socket;
|
||||
|
||||
public ExtendedSocket(Socket socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public void sendMessage(String message) throws IOException {
|
||||
synchronized (socket){
|
||||
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
|
||||
outputStream.writeBytes(message);
|
||||
outputStream.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void closeSocket() throws IOException {
|
||||
this.socket.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.example.lfrmobileapp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.ui.AppBarConfiguration;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import com.example.lfrmobileapp.databinding.ActivityMainBinding;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityMainBinding binding;
|
||||
private ExtendedSocket socket;
|
||||
private DataTransferAsyncTask dataTransferAsyncTask;
|
||||
public Boolean connected = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
BottomNavigationView navView = findViewById(R.id.nav_view);
|
||||
// Passing each menu ID as a set of Ids because each
|
||||
// menu should be considered as top level destinations.
|
||||
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
|
||||
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
|
||||
.build();
|
||||
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
|
||||
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
|
||||
NavigationUI.setupWithNavController(binding.navView, navController);
|
||||
}
|
||||
|
||||
public boolean connectToWifi(String ipAddress, int port){
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
Future<Boolean> future = executor.submit(() -> {
|
||||
Socket tempSocket = new Socket();
|
||||
tempSocket.connect(new InetSocketAddress(ipAddress, port), 1000);
|
||||
try {
|
||||
socket = new ExtendedSocket(tempSocket);
|
||||
socket.sendMessage("Verbindung aufbauen");
|
||||
} catch (Exception e) {
|
||||
socket.closeSocket();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
try {
|
||||
connected = future.get();
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println("InterruptedException: " + e.getMessage());
|
||||
} catch (ExecutionException e) {
|
||||
System.err.println("ExecutionException: " + e.getCause().getMessage());
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
|
||||
if(connected){
|
||||
dataTransferAsyncTask = new DataTransferAsyncTask(socket);
|
||||
dataTransferAsyncTask.execute();
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendTelegram(String telegram)
|
||||
{
|
||||
this.dataTransferAsyncTask.writeTelegram(telegram);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.example.lfrmobileapp.ui.dashboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.example.lfrmobileapp.Communication;
|
||||
import com.example.lfrmobileapp.DataTransferAsyncTask;
|
||||
import com.example.lfrmobileapp.MainActivity;
|
||||
import com.example.lfrmobileapp.R;
|
||||
import com.example.lfrmobileapp.databinding.FragmentAutomatikBinding;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import kotlin.collections.UArraySortingKt;
|
||||
|
||||
// Autonomous Mode
|
||||
public class DashboardFragment extends Fragment {
|
||||
Communication com = new Communication();
|
||||
private FragmentAutomatikBinding binding;
|
||||
|
||||
MainActivity mainActivity;
|
||||
|
||||
//Keys for Shared Preferences
|
||||
String listKey = "listkey";
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mainActivity = (MainActivity) context;
|
||||
}
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
DashboardViewModel dashboardViewModel =
|
||||
new ViewModelProvider(this).get(DashboardViewModel.class);
|
||||
|
||||
binding = FragmentAutomatikBinding.inflate(inflater, container, false);
|
||||
View root = binding.getRoot();
|
||||
|
||||
final TextView textView = binding.textDashboard;
|
||||
dashboardViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
|
||||
|
||||
//Shared Preferences
|
||||
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPref.edit();
|
||||
|
||||
//Get Arraylist from Shared Preferences String
|
||||
ArrayList<String> autoList = stringToArraylist(sharedPref.getString(listKey, ""));
|
||||
|
||||
binding.startAutomatic.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
if (mainActivity.connected){
|
||||
if(isChecked){
|
||||
mainActivity.sendTelegram(com.telegram(true, autoList));
|
||||
}else{
|
||||
mainActivity.sendTelegram(com.telegram(false, autoList));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// on the below line we are initializing the adapter for our list view.
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(root.getContext(), android.R.layout.simple_list_item_1, autoList){
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View view =super.getView(position, convertView, parent);
|
||||
TextView textView=(TextView) view.findViewById(android.R.id.text1);
|
||||
//Set Color to black
|
||||
textView.setTextColor(Color.BLACK);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
super.notifyDataSetChanged();
|
||||
//Save Arraylist in Shared Preferences as String
|
||||
editor.putString(listKey, listToString(autoList));
|
||||
editor.apply();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// on below line we are setting adapter for our list view.
|
||||
binding.automaticList.setAdapter(adapter);
|
||||
|
||||
// on below line we are adding click listener for our button.
|
||||
binding.addLeft.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// on below line we are getting text from edit text
|
||||
String item = "Links";
|
||||
Boolean automaticOn = binding.startAutomatic.isChecked();
|
||||
// on below line we are checking if item is not empty
|
||||
if (!item.isEmpty() && !automaticOn) {
|
||||
// on below line we are adding item to our list.
|
||||
autoList.add(item);
|
||||
// on below line we are notifying adapter
|
||||
// that data in list is updated to
|
||||
// update our list view.
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// on below line we are adding click listener for our button.
|
||||
binding.addRight.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// on below line we are getting text from edit text
|
||||
String item = "Rechts";
|
||||
|
||||
Boolean automaticOn = binding.startAutomatic.isChecked();
|
||||
// on below line we are checking if item is not empty
|
||||
if (!item.isEmpty() && !automaticOn) {
|
||||
// on below line we are adding item to our list.
|
||||
autoList.add(item);
|
||||
// on below line we are notifying adapter
|
||||
// that data in list is updated to
|
||||
// update our list view.
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Deleting element in ListView
|
||||
binding.automaticList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Boolean automaticOn = binding.startAutomatic.isChecked();
|
||||
if(!automaticOn) {
|
||||
autoList.remove(position);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
public String listToString(ArrayList<String> list){
|
||||
String result = "";
|
||||
if(!list.isEmpty()) {
|
||||
for (String s : list) {
|
||||
result += s + ";";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ArrayList<String> stringToArraylist(String str){
|
||||
ArrayList<String> list = null;
|
||||
String[] stringArray = str.split(";");
|
||||
list = new ArrayList<String>(Arrays.asList(stringArray));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.example.lfrmobileapp.ui.dashboard;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
public class DashboardViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<String> mText;
|
||||
|
||||
public DashboardViewModel() {
|
||||
mText = new MutableLiveData<>();
|
||||
mText.setValue("");
|
||||
}
|
||||
|
||||
public LiveData<String> getText() {
|
||||
return mText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.example.lfrmobileapp.ui.home;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.example.lfrmobileapp.Communication;
|
||||
import com.example.lfrmobileapp.DataTransferAsyncTask;
|
||||
import com.example.lfrmobileapp.MainActivity;
|
||||
import com.example.lfrmobileapp.R;
|
||||
import com.example.lfrmobileapp.databinding.FragmentManuellBinding;
|
||||
import com.example.lfrmobileapp.ui.notifications.NotificationsFragment;
|
||||
|
||||
import io.github.controlwear.virtual.joystick.android.JoystickView;
|
||||
|
||||
public class HomeFragment extends Fragment {
|
||||
|
||||
//Keys for Shared Preferences
|
||||
String ipKey = "ipkey";
|
||||
String portKey = "portkey";
|
||||
|
||||
//Default Ip-Address and Port
|
||||
String ipAddress = "192.168.0.1";
|
||||
int port = 8000;
|
||||
|
||||
MainActivity mainActivity;
|
||||
|
||||
Communication com = new Communication();
|
||||
long lastOnMoveCall = System.currentTimeMillis();
|
||||
|
||||
|
||||
private FragmentManuellBinding binding;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mainActivity = (MainActivity) context;
|
||||
}
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
HomeViewModel homeViewModel =
|
||||
new ViewModelProvider(this).get(HomeViewModel.class);
|
||||
|
||||
binding = FragmentManuellBinding.inflate(inflater, container, false);
|
||||
View root = binding.getRoot();
|
||||
|
||||
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPref.edit();
|
||||
|
||||
//Get saved Ip-Adress and Port from Shared Preferences
|
||||
ipAddress = sharedPref.getString(ipKey, "");
|
||||
port = sharedPref.getInt(portKey, 0);
|
||||
|
||||
//Set text from Textview
|
||||
final TextView textView = binding.textHome;
|
||||
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
|
||||
|
||||
|
||||
|
||||
binding.rotateLeft.setOnTouchListener(new View.OnTouchListener(){
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (mainActivity.connected) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
binding.rotateLeft.performClick();
|
||||
mainActivity.sendTelegram(com.telegram(new int[]{0, 0, 0, 0}, 0));
|
||||
return true;
|
||||
} else {
|
||||
mainActivity.sendTelegram(com.telegram(new int[]{-1, 1, -1, 1}, 50));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
binding.rotateRight.setOnTouchListener(new View.OnTouchListener(){
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (mainActivity.connected){
|
||||
if(event.getAction() == MotionEvent.ACTION_UP){
|
||||
binding.rotateRight.performClick();
|
||||
mainActivity.sendTelegram(com.telegram(new int[]{0, 0, 0, 0}, 0));
|
||||
return true;
|
||||
}else{
|
||||
mainActivity.sendTelegram(com.telegram(new int[]{1, -1, 1, -1}, 50));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
JoystickView joystick = (JoystickView) binding.joystick;
|
||||
joystick.setOnMoveListener(new JoystickView.OnMoveListener() {
|
||||
@Override
|
||||
public void onMove(int angle, int strength) {
|
||||
boolean send = false;
|
||||
if (System.currentTimeMillis() - lastOnMoveCall > 250){
|
||||
send = true;
|
||||
lastOnMoveCall = System.currentTimeMillis();
|
||||
}
|
||||
String telegram = "";
|
||||
|
||||
homeViewModel.setText(Integer.toString(angle), Integer.toString(strength));
|
||||
//8 segments of the joystick
|
||||
if((angle >= 338 || angle < 22) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.right);
|
||||
telegram = com.telegram(new int[]{1, -1, -1, 1}, strength);
|
||||
}else if((angle >= 22 && angle < 67) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.right_forward);
|
||||
telegram = com.telegram(new int[]{1, 0, 0, 1}, strength);
|
||||
}else if((angle >=67 && angle < 112) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.forward);
|
||||
telegram = com.telegram(new int[]{1, 1, 1, 1}, strength);
|
||||
}else if((angle >=112 && angle < 157) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.left_forward);
|
||||
telegram = com.telegram(new int[]{0, 1, 1, 0}, strength);
|
||||
}else if((angle >=157 && angle < 202) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.left);
|
||||
telegram = com.telegram(new int[]{-1, 1, 1, -1}, strength);
|
||||
}else if((angle >=202 && angle < 247) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.left_back);
|
||||
telegram = com.telegram(new int[]{-1, 0, 0, -1}, strength);
|
||||
}else if((angle >=247 && angle < 292) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.back);
|
||||
telegram = com.telegram(new int[]{-1, -1, -1, -1}, strength);
|
||||
}else if((angle >=292 && angle < 337) && strength != 0){
|
||||
joystick.setBackgroundResource(R.mipmap.right_back);
|
||||
telegram = com.telegram(new int[]{0, -1, -1, 0}, strength);
|
||||
}
|
||||
else{
|
||||
joystick.setBackgroundResource(R.mipmap.blank);
|
||||
telegram = com.telegram(new int[]{0, 0, 0, 0}, strength);
|
||||
send=true;
|
||||
}
|
||||
if (send && mainActivity.connected)
|
||||
{
|
||||
mainActivity.sendTelegram(telegram);
|
||||
// Log.v("TAG", telegram + "\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
// dataTransferAsyncTask.cancel(false);
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.example.lfrmobileapp.ui.home;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
public class HomeViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<String> mText;
|
||||
|
||||
public HomeViewModel() {
|
||||
mText = new MutableLiveData<>();
|
||||
mText.setValue("Bewege den Punkt zum steuern des Roboters");
|
||||
}
|
||||
|
||||
public LiveData<String>setText(String angle, String strength){
|
||||
mText.setValue("Winkel: "+ angle + " Verstärkung: " + strength);
|
||||
return mText;
|
||||
};
|
||||
|
||||
public LiveData<String> getText() {
|
||||
return mText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.example.lfrmobileapp.ui.notifications;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.example.lfrmobileapp.Communication;
|
||||
import com.example.lfrmobileapp.DataTransferAsyncTask;
|
||||
import com.example.lfrmobileapp.MainActivity;
|
||||
import com.example.lfrmobileapp.databinding.FragmentEinstellungenBinding;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
|
||||
/*class NetworkThread extends Thread {
|
||||
String ip;
|
||||
int port;
|
||||
boolean connected;
|
||||
|
||||
NetworkThread(String ip, int port) {
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
this.connected = false;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = new Socket(ip, port);
|
||||
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
|
||||
outputStream.writeBytes("Verbindung aufbauen");
|
||||
outputStream.flush();
|
||||
socket.close();
|
||||
connected = true;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
public class NotificationsFragment extends Fragment {
|
||||
|
||||
Boolean connected = false;
|
||||
MainActivity mainActivity;
|
||||
|
||||
String ipKey = "ipkey";
|
||||
String portKey = "portkey";
|
||||
String connectionFailed = "Verbindung fehlgeschlagen.";
|
||||
String connectionSuccessful = "Verbindung erfolgreich.";
|
||||
|
||||
String ipAddress = "192.168.4.1";
|
||||
int port = 8080;
|
||||
|
||||
int duration = Toast.LENGTH_LONG;
|
||||
DataTransferAsyncTask dataTransferAsyncTask;
|
||||
|
||||
private FragmentEinstellungenBinding binding;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mainActivity = (MainActivity) context;
|
||||
}
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
NotificationsViewModel notificationsViewModel =
|
||||
new ViewModelProvider(this).get(NotificationsViewModel.class);
|
||||
|
||||
binding = FragmentEinstellungenBinding.inflate(inflater, container, false);
|
||||
View root = binding.getRoot();
|
||||
|
||||
Communication com = new Communication();
|
||||
|
||||
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPref.edit();
|
||||
|
||||
ipAddress = sharedPref.getString(ipKey, "");
|
||||
binding.ipAdress.setText(ipAddress);
|
||||
port = sharedPref.getInt(portKey, -1);
|
||||
if(port != -1){
|
||||
binding.port.setText(Integer.toString(port));
|
||||
}else{
|
||||
binding.port.setText("");
|
||||
}
|
||||
|
||||
// dataTransferAsyncTask = new DataTransferAsyncTask(ipAddress, port);
|
||||
|
||||
final Button button = binding.connect;
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
ipAddress = String.valueOf(binding.ipAdress.getText());
|
||||
port = Integer.parseInt(String.valueOf(binding.port.getText()));
|
||||
} catch (Exception e){
|
||||
System.err.println("Parse Exception: " + e.getMessage());
|
||||
}
|
||||
|
||||
connected = mainActivity.connectToWifi(ipAddress, port);
|
||||
|
||||
if(connected){
|
||||
editor.putString(ipKey, ipAddress);
|
||||
editor.putInt(portKey, port);
|
||||
editor.apply();
|
||||
Toast toast = Toast.makeText(v.getContext(), connectionSuccessful, duration);
|
||||
toast.show();
|
||||
}else{
|
||||
Toast toast = Toast.makeText(v.getContext(), connectionFailed, duration);
|
||||
toast.show();
|
||||
// dataTransferAsyncTask.cancel(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
// dataTransferAsyncTask.cancel(true);
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.example.lfrmobileapp.ui.notifications;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
public class NotificationsViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<String> mText;
|
||||
|
||||
public NotificationsViewModel() {
|
||||
mText = new MutableLiveData<>();
|
||||
mText.setValue("");
|
||||
}
|
||||
|
||||
public LiveData<String> getText() {
|
||||
return mText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#333333"
|
||||
android:alpha="0.6">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17,16l-4,-4V8.82C14.16,8.4 15,7.3 15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6c0,1.3 0.84,2.4 2,2.82V12l-4,4H3v5h5v-3.05l4,-4.2 4,4.2V21h5v-5h-4z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#333333"
|
||||
android:alpha="0.6">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#333333"
|
||||
android:alpha="0.6">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.54,5.54L13.77,7.3 12,5.54 10.23,7.3 8.46,5.54 12,2zM18.46,15.54l-1.76,-1.77L18.46,12l-1.76,-1.77 1.76,-1.77L22,12zM8.46,18.46l1.77,-1.76L12,18.46l1.77,-1.76 1.77,1.76L12,22zM5.54,8.46l1.76,1.77L5.54,12l1.76,1.77 -1.76,1.77L2,12z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,12m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#333333"
|
||||
android:alpha="0.6">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
||||
</vector>
|
||||
|
After Width: | Height: | Size: 318 B |
|
After Width: | Height: | Size: 480 B |
|
After Width: | Height: | Size: 416 B |
|
After Width: | Height: | Size: 246 B |
|
After Width: | Height: | Size: 230 B |
|
After Width: | Height: | Size: 329 B |
|
After Width: | Height: | Size: 288 B |
|
After Width: | Height: | Size: 182 B |
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
|
After Width: | Height: | Size: 398 B |
|
After Width: | Height: | Size: 647 B |
|
After Width: | Height: | Size: 445 B |
|
After Width: | Height: | Size: 279 B |
|
After Width: | Height: | Size: 586 B |
|
After Width: | Height: | Size: 928 B |
|
After Width: | Height: | Size: 750 B |
|
After Width: | Height: | Size: 475 B |
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#3C3F41"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.11,8.53L5.7,7.11C4.8,8.27 4.24,9.61 4.07,11h2.02c0.14,-0.87 0.49,-1.72 1.02,-2.47zM6.09,13L4.07,13c0.17,1.39 0.72,2.73 1.62,3.89l1.41,-1.42c-0.52,-0.75 -0.87,-1.59 -1.01,-2.47zM7.1,18.32c1.16,0.9 2.51,1.44 3.9,1.61L11,17.9c-0.87,-0.15 -1.71,-0.49 -2.46,-1.03L7.1,18.32zM13,4.07L13,1L8.45,5.55 13,10L13,6.09c2.84,0.48 5,2.94 5,5.91s-2.16,5.43 -5,5.91v2.02c3.95,-0.49 7,-3.85 7,-7.93s-3.05,-7.44 -7,-7.93z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#3C3F41"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M15.55,5.55L11,1v3.07C7.06,4.56 4,7.92 4,12s3.05,7.44 7,7.93v-2.02c-2.84,-0.48 -5,-2.94 -5,-5.91s2.16,-5.43 5,-5.91L11,10l4.55,-4.45zM19.93,11c-0.17,-1.39 -0.72,-2.73 -1.62,-3.89l-1.42,1.42c0.54,0.75 0.88,1.6 1.02,2.47h2.02zM13,17.9v2.02c1.39,-0.17 2.74,-0.71 3.9,-1.61l-1.44,-1.44c-0.75,0.54 -1.59,0.89 -2.46,1.03zM16.89,15.48l1.42,1.41c0.9,-1.16 1.45,-2.5 1.62,-3.89h-2.02c-0.14,0.87 -0.48,1.72 -1.02,2.48z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/nav_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:background="?android:attr/windowBackground"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:menu="@menu/bottom_nav_menu" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment_activity_main"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/mobile_navigation" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.dashboard.DashboardFragment">
|
||||
|
||||
<Switch
|
||||
android:id="@+id/startAutomatic"
|
||||
android:layout_width="224dp"
|
||||
android:layout_height="38dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:checked="false"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="Roboter losfahren "
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.147"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_dashboard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.946" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textTurnLeftRight"
|
||||
android:layout_width="137dp"
|
||||
android:layout_height="39dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="Abzweigung hinzufügen:"
|
||||
android:textColor="#000000"
|
||||
android:textSize="16sp"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.089"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/addLeft"
|
||||
android:layout_width="105dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="Links"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.613"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/addRight"
|
||||
android:layout_width="105dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="Rechts"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/automaticList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="470dp"
|
||||
android:layout_marginTop="110dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/startAutomatic" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.notifications.NotificationsFragment">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/ipAdress"
|
||||
android:layout_width="269dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="60dp"
|
||||
android:hint="IP-Adresse"
|
||||
android:digits="0123456789."
|
||||
android:textColor="#000000"
|
||||
android:inputType="number|numberDecimal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/port"
|
||||
android:layout_width="269dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="120dp"
|
||||
android:ems="10"
|
||||
android:hint="Port"
|
||||
android:digits="0123456789"
|
||||
android:textColor="#000000"
|
||||
android:inputType="number|numberDecimal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/connect"
|
||||
android:layout_width="269dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="180dp"
|
||||
android:text="Verbinden"
|
||||
android:background="@color/THblue"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"></Button>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.home.HomeFragment">
|
||||
|
||||
<io.github.controlwear.virtual.joystick.android.JoystickView
|
||||
android:id="@+id/joystick"
|
||||
android:layout_width="308dp"
|
||||
android:layout_height="317dp"
|
||||
android:layout_marginTop="68dp"
|
||||
android:background="@mipmap/blank"
|
||||
app:JV_buttonColor="#FFFFFF"
|
||||
app:JV_buttonSizeRatio="11%"
|
||||
app:JV_fixedCenter="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.495"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_home"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/rotateLeft"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="350dp"
|
||||
android:background="@drawable/ic_baseline_rotate_left_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.150"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"></Button>
|
||||
|
||||
<Button
|
||||
android:id="@+id/rotateRight"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="350dp"
|
||||
android:background="@drawable/ic_baseline_rotate_right_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.85"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"></Button>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/navigation_home"
|
||||
android:icon="@drawable/manuell"
|
||||
android:title="@string/title_home" />
|
||||
|
||||
<item
|
||||
android:id="@+id/navigation_dashboard"
|
||||
android:icon="@drawable/automatik"
|
||||
android:title="@string/title_dashboard" />
|
||||
|
||||
<item
|
||||
android:id="@+id/navigation_notifications"
|
||||
android:icon="@drawable/einstellungen"
|
||||
android:title="@string/title_notifications" />
|
||||
|
||||
</menu>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.2 KiB |