90void pathToRoutine(std::vector<Pose2D> path, std::vector<Move>& routine) {
92 double previousEndHeading = path[0].getHeading();
93 for (
size_t move = 1; move < ((path.size() - 1)*2); move += 2) {
95 Pose2D pointOld = path[move / 2];
96 Pose2D pointNew = path[(move / 2) + 1];
98 double newHeading = pointNew.
angleTo(pointOld);
99 double angle = newHeading - previousEndHeading;
100 previousEndHeading = newHeading;
103 double magnitude = pointNew.
distanceTo(pointOld);
142void readAndLog(
int socket, std::mutex& fieldMutex,
Field& field, std::vector<Pose2D>& path) {
143 const uint16_t BUFF_SIZE = 1024;
145 static char name_buff[50];
146 time_t now = time(0);
147 strftime(name_buff,
sizeof(name_buff),
"log/%Y%m%d_%H%M%S.log", localtime(&now));
148 std::string str_name(name_buff);
150 std::ofstream logFile(str_name);
154 char buff[BUFF_SIZE];
157 size_t bytesRead = read(socket, buff, BUFF_SIZE);
159 std::string response(buff, bytesRead);
165 std::istringstream stream(response);
167 while (stream >> tag) {
179 std::vector<Pose2D> pathOne = field.
makePath();
180 for (uint16_t i = 0; i < pathOne.size(); i++) {
181 path.push_back(pathOne[i]);
230 if (stream >> m >> cardinality) {
250 double x1, y1, x2, y2;
251 if (stream >> x1 >> y1 >> x2 >> y2) {
276 std::this_thread::sleep_for(std::chrono::milliseconds(5));
292 int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
293 sockaddr_in serverAddress{
294 .sin_family = AF_INET,
295 .sin_port = htons(
PORT),
301 bool connected =
false;
304 if(connect(clientSocket, (
struct sockaddr*)& serverAddress,
sizeof(serverAddress))) {
305 throw std::exception();
309 catch (std::exception& e) {
310 std::this_thread::sleep_for(std::chrono::milliseconds(500));
317 std::thread readThread(
readAndLog, std::ref(clientSocket), std::ref(fieldMutex), std::ref(field), std::ref(path));
322 if (message.compare(
"q") == 0) {
326 send(clientSocket, message.c_str(), message.length(), 0);
329 std::this_thread::sleep_for(std::chrono::milliseconds(20));
479void ShowFieldWindow(std::mutex* pillarsMutex, std::vector<Pose2D>& path,
Field& field, std::atomic<bool>& showNodes, std::atomic<bool>& showEdges) {
480 ImGui::Begin(
"Field");
482 ImDrawList* drawList = ImGui::GetWindowDrawList();
484 ImGui::SetWindowSize(windowSize);
485 ImVec2 windowPos = ImGui::GetWindowPos();
486 windowPos.x += windowSize.x / 100;
487 windowPos.y += windowSize.y / 100;
488 ImVec2 offset = ImVec2(windowPos.x + windowSize.x / 50, windowPos.y - windowSize.y / 50);
489 ImVec2 scalingFactor = ImVec2(windowSize.x /
MAX_X, windowSize.y /
MAX_Y);
491 pillarsMutex->lock();
495 for (
const Pillar& pillar: pillars) {
496 ShowPillarOnWindow(drawList, pillar, IM_COL32(255, 0, 0, 200), offset, scalingFactor);
501 for (
Hole hole: holes) {
503 if (hole.isFoundHole()) {
504 drawRectangle(drawList, offset, scalingFactor, hole.getOneSquareCorner(), hole.getSecondSquareCorner());
507 std::vector<Hole> subHoles = hole.getSubHolesCopy();
508 for (
Hole holer: subHoles) {
509 drawRectangle(drawList, offset, scalingFactor, holer.getOneSquareCorner(), holer.getSecondSquareCorner());
516 if (showNodes.load()) {
520 Pose2D position = node->getData();
524 DrawCircle(drawList, center, radius, IM_COL32(120, 120, 0, 200));
531 if (showEdges.load()) {
534 std::vector<Node<Pose2D> *> adjacent = field.
getGraph().
getAdj(node);
536 ImVec2 p1 =
coordsToScreen(offset, scalingFactor, node->getData());
537 ImVec2 p2 =
coordsToScreen(offset, scalingFactor, adj->getData());
538 drawList->AddLine(p1, p2, IM_COL32(100, 100, 100, 100), 2);
544 for (uint8_t i = 0; i < path.size(); i++) {
546 ImVec2 p1 =
coordsToScreen(offset, scalingFactor, path[i - 1].getX(), path[i - 1].getY());
547 ImVec2 p2 =
coordsToScreen(offset, scalingFactor, path[i].getX(), path[i].getY());
548 drawList->AddLine(p1, p2, IM_COL32(100, 100, 100, 100), 2);
551 ImVec2 center =
coordsToScreen(offset, scalingFactor, path[i].getX(), path[i].getY());
553 DrawCircle(drawList, center, radius, IM_COL32(30, 120, 220, 150));
556 pillarsMutex->unlock();
558 ImVec2 mousePos = ImGui::GetMousePos();
560 ImGui::Text(
"mouse: %d, %d", (
int)mousePos.x, (
int)mousePos.y);
561 Pose2D transformed =
ScreenToCoords(ImVec2((
int) mousePos.x, (
int) mousePos.y), offset, scalingFactor);
562 ImGui::Text(
"Pose on field: %d, %d", (
int) transformed.
getX(), (
int) transformed.
getY());
573 if (!glfwInit())
return -1;
574 GLFWwindow* window = glfwCreateWindow(1880, 900,
"Roomba Dashboard",
nullptr,
nullptr);
575 if (!window) { glfwTerminate();
return -1; }
576 glfwMakeContextCurrent(window);
582 int driveForward = 0;
584 std::atomic<bool> showNodes;
585 showNodes.store(
false);
586 std::atomic<bool> showEdges;
587 showEdges.store(
false);
592 std::mutex pillarsMutex;
593 std::vector<Pose2D> path;
599 std::thread tcpConnect(
connectTCP, std::ref(field), std::ref(pillarsMutex), std::ref(path));
602 while (!glfwWindowShouldClose(window)) {
606 ImGui_ImplOpenGL3_NewFrame();
607 ImGui_ImplGlfw_NewFrame();
615 ImGui::Begin(
"Control Panel");
619 if (ImGui::Button(
"Forward")) {
623 if (ImGui::Button(
"Backward")) {
627 if (ImGui::Button(
"Counter Clockwise")) {
631 if (ImGui::Button(
"Clockwise")) {
635 if (ImGui::Button(
"Stop")) {
639 if (ImGui::Button(
"Scan")) {
643 if (ImGui::Button(
"Auton")) {
647 if (ImGui::Button(
"Quit all")) {
651 if (ImGui::RadioButton(
"Show nodes",
false)) {
652 showNodes.store(!showNodes.load());
655 if (ImGui::RadioButton(
"Show edges",
false)) {
656 showEdges.store(!showEdges.load());
660 if (ImGui::Button(
"Generate path")) {
674 pillarsMutex.unlock();
679 if (ImGui::Button(
"send planned path")) {
684 ImGui::SliderAngle(
"Turn angle", &angleSend);
686 ImGui::InputText(
"message: ", buffer, 100);
688 if (ImGui::Button(
"send component")) {
690 for (
size_t i = 0; i < 99; i++) {
691 if (buffer[i] == 0) {
692 buffPtr = std::min((
long) 0, (
long) i - 1);
695 std::swap(buffer[i], buffer[i + 1]);
700 if (ImGui::Button(
"Send turn")) {
704 ImGui::SliderInt(
"Drive x cm", &driveForward, 0, 999);
706 if (ImGui::Button(
"Drive x")) {
710 if (ImGui::Button(
"Discretize")) {
715 if (ImGui::Button(
"Weight")) {
723 int display_w, display_h;
724 glfwGetFramebufferSize(window, &display_w, &display_h);
725 glViewport(0, 0, display_w, display_h);
726 glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
727 glClear(GL_COLOR_BUFFER_BIT);
728 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
730 glfwSwapBuffers(window);
732 std::this_thread::sleep_for(std::chrono::milliseconds(16));
740 ImGui_ImplOpenGL3_Shutdown();
741 ImGui_ImplGlfw_Shutdown();
742 ImGui::DestroyContext();
743 glfwDestroyWindow(window);
void addToQueue(const std::string &message)
ImVec2 coordsToScreen(ImVec2 offset, ImVec2 scaling, double x, double y)
std::condition_variable sendCondition
void drawBotPose(ImDrawList *drawList, const Pose2D &botPose, ImVec2 offset, ImVec2 scaling)
void drawRectangle(ImDrawList *drawList, ImVec2 offset, ImVec2 scaling, const Pose2D &p1, const Pose2D &p2)
void ShowFieldWindow(std::mutex *pillarsMutex, std::vector< Pose2D > &path, Field &field, std::atomic< bool > &showNodes, std::atomic< bool > &showEdges)
void readAndLog(int socket, std::mutex &fieldMutex, Field &field, std::vector< Pose2D > &path)
std::string parsePathIntoRoutine(const std::vector< Pose2D > &path)
void setupImGui(GLFWwindow *window)
std::atomic< bool > stopClient(false)
void ShowPillarOnWindow(ImDrawList *drawList, Pillar pillar, ImU32 color, ImVec2 offset, ImVec2 scaling)
std::queue< std::string > sendQueue
Pose2D ScreenToCoords(ImVec2 coords, ImVec2 offset, ImVec2 scaling)
void pathToRoutine(std::vector< Pose2D > path, std::vector< Move > &routine)
void sendDistanceToQueue(uint16_t distance)
void sendAngleToQueue(int16_t angle)
void connectTCP(Field &field, std::mutex &fieldMutex, std::vector< Pose2D > &path)
void DrawCircle(ImDrawList *drawList, const ImVec2 ¢er, float radius, ImU32 color)