From 287e537c4c8934890ac734ff3042d55515fccd34 Mon Sep 17 00:00:00 2001
From: "Paul Winpenny (plw1g21)" <plw1g21@soton.ac.uk>
Date: Fri, 15 Nov 2024 14:42:19 +0000
Subject: [PATCH] Api node now has a few publisher nodes being created, need to
 actaully test on a machine

---
 .vs/robobin/v17/.wsuo                         | Bin 0 -> 14336 bytes
 .vs/robobin/v17/DocumentLayout.json           |  23 +++++
 App/RobobinApp/RobobinApp.csproj.user         |   6 +-
 .../connection_manager.cpython-312.pyc        | Bin 0 -> 4947 bytes
 .../message_handler.cpython-312.pyc           | Bin 0 -> 2623 bytes
 .../{ => api_helpers}/connection_manager.py   |   0
 .../robobin/api_helpers/message_handler.py    |  80 ++++++++++++++++++
 ros2/src/robobin/robobin/api_node.py          |  44 ++++++++--
 ros2/src/robobin/robobin/message_handler.py   |  40 ---------
 ros2/src/robobin/robobin/motor_controller.py  |   0
 10 files changed, 144 insertions(+), 49 deletions(-)
 create mode 100644 .vs/robobin/v17/.wsuo
 create mode 100644 .vs/robobin/v17/DocumentLayout.json
 create mode 100644 ros2/src/robobin/robobin/api_helpers/__pycache__/connection_manager.cpython-312.pyc
 create mode 100644 ros2/src/robobin/robobin/api_helpers/__pycache__/message_handler.cpython-312.pyc
 rename ros2/src/robobin/robobin/{ => api_helpers}/connection_manager.py (100%)
 create mode 100644 ros2/src/robobin/robobin/api_helpers/message_handler.py
 delete mode 100644 ros2/src/robobin/robobin/message_handler.py
 create mode 100644 ros2/src/robobin/robobin/motor_controller.py

diff --git a/.vs/robobin/v17/.wsuo b/.vs/robobin/v17/.wsuo
new file mode 100644
index 0000000000000000000000000000000000000000..ba3598a9956c8e8e60cf96d02f38688a8af569fc
GIT binary patch
literal 14336
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

literal 0
HcmV?d00001

diff --git a/.vs/robobin/v17/DocumentLayout.json b/.vs/robobin/v17/DocumentLayout.json
new file mode 100644
index 00000000..218fa7fb
--- /dev/null
+++ b/.vs/robobin/v17/DocumentLayout.json
@@ -0,0 +1,23 @@
+{
+  "Version": 1,
+  "WorkspaceRootPath": "C:\\Users\\plw1g21\\source\\repos\\robobin\\",
+  "Documents": [],
+  "DocumentGroupContainers": [
+    {
+      "Orientation": 0,
+      "VerticalTabListWidth": 256,
+      "DocumentGroups": [
+        {
+          "DockedWidth": 200,
+          "SelectedChildIndex": -1,
+          "Children": [
+            {
+              "$type": "Bookmark",
+              "Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/App/RobobinApp/RobobinApp.csproj.user b/App/RobobinApp/RobobinApp.csproj.user
index 0e19fbac..c541a85b 100644
--- a/App/RobobinApp/RobobinApp.csproj.user
+++ b/App/RobobinApp/RobobinApp.csproj.user
@@ -2,9 +2,9 @@
 <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
     <IsFirstTimeProjectOpen>False</IsFirstTimeProjectOpen>
-    <ActiveDebugFramework>net8.0-windows10.0.19041.0</ActiveDebugFramework>
-    <ActiveDebugProfile>Windows Machine</ActiveDebugProfile>
-    <SelectedPlatformGroup>PhysicalDevice</SelectedPlatformGroup>
+    <ActiveDebugFramework>net8.0-android</ActiveDebugFramework>
+    <ActiveDebugProfile>Android Emulator</ActiveDebugProfile>
+    <SelectedPlatformGroup>Emulator</SelectedPlatformGroup>
     <DefaultDevice>pixel_5_-_api_34</DefaultDevice>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-android|AnyCPU'">
diff --git a/ros2/src/robobin/robobin/api_helpers/__pycache__/connection_manager.cpython-312.pyc b/ros2/src/robobin/robobin/api_helpers/__pycache__/connection_manager.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9e8339995626eb47254d11b77a06a152efb18d72
GIT binary patch
literal 4947
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

literal 0
HcmV?d00001

diff --git a/ros2/src/robobin/robobin/api_helpers/__pycache__/message_handler.cpython-312.pyc b/ros2/src/robobin/robobin/api_helpers/__pycache__/message_handler.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..185a8f39506e9b882bca056c9730a246a471baad
GIT binary patch
literal 2623
zcmb7G&2JM&6rcUFHwFjD2Te?qE=@wZRbr7eP${BFX=xKGVh|rfic+o2&XCwRyJluh
z6w6jQaLA#R+EYMKLDfTxxbP?RUa7)LBfFy>kb2?<$)OUL_RV_NIL09LNq#%?-kY~?
ze(z)bTRa{^Kp&p^cxftv(BEPck{T>_`e3nvWHgH~x`nXxNUm!Z(3)&2c4#l2Qn1-M
z3X2Uy(1L`~f{dkGXhFd;a1|@SHLL=UU=4T_M}X^VQB&ug^MtYV5}8XoI7g@*yvg?}
z;!)tYgMHs6se~+FAy^4zn{5L*NW)t+EPY$JYb#;to&p{l_G`$LQ>MbzbEi|YT)lXD
z{&OCipGjSsIrG`Y^Jh$r>*;*Pa$HRKo@GB?E1z+ecz3YzGAqQQ<a&WHkM{<`m9$ex
z=PcWGJnH7SUKdkl%3Nh6ca`c87ZX=8jF(^8(~FlFp=>dqTb)>%oLFRTf!bt|lDx|n
zse8q}l5yb4CKnmCn*?=*h;NzX^1{PP9at-PVKTqQb<4^)8PBqc@9c&>87c%AY#i84
zRO>U=)o<cYd-|$9!<C-lnpligdd6yr!D?cvl9+lCRr_P>>bLP7IDqU1kgySJKXq4N
zu_2NL&sNF`ixw)$?Nb<*U|E$>Nrh5SLWzXdBvURX&$|VWFvD9WhV8BZ?AY*Jf1A$d
zshg*nv`35(4M;TI3-9L267l%KAXx?Hn&YlIR)C$U(YUaQ+qn#JJd3&ZHL#8b{<(_N
zUYbY4lxkpW2%??hRA=Bca8?{>!F;z`1j9Z6b`w4AI=C_Z{dl!&sM0m`VD^`b)$zH?
z_}pgK>GkNJ`o8btx4kWWnC=H-yBR{U_CY@a3jkfphTRGKkf56bDF6rETpT!0Sl)Fo
zGgdR+vXM5<olVUeLC{GiGASgfhZ2u6;^1^H_fYY1p*^7ElEuVJ!)wzH*c21(?<(5m
z0O*;B9WJW(R`r33K5%<!OF#1Zg&I|K70lmsA+Iq8vLXR3^eEV8qb&;9hPoy|L?sPg
zfLjKIRG<_RZ$L}lAk+{Io=ll4Jp`w@Dz;p+{YQj2HVg*(E@*b+A?9uI{q}e?PmEr1
z@;zWpxZK0{vv;z0bJf9#%HYK2;742f)ax*bYx{Zs9Tpofud$33tYYn+T2@L*yDBKj
z?V2wH<ZVFWNZ5E~6-P_zUG#(eHgL-t)S;+}7Ak44VE6WfO01XE&_C2?hZ!r5&H1C2
z8TCmD&FxRpu*uo9ae2&`88a@78K;t_@=R3sGvSX81A{gfk2)Lj1<(EkdL`XuF(NEf
zE?wr*43{o&>C`T@a8*1!?~nA>KnzN3lDR?fv{5{Kj=DC?UxMEd^>oUN&@S-CHRjPw
zo@<3XOl^7?MD^;@U|=~OScwT#@3d&plstwr@G>vsI*iAu-@c4k{xH(REfZfnXi6Y|
z98rOBakzaQ?*2&igC!F!cN0C2psqu;1Bu!J;D=sxXa{5KvFCeH|Iq!;JDm?!e=h%6
z{^{%L=!weciOtcIzjkc)eYqa5^&Wp*e(XN!{-WAFU+JFT>^{4$*BZ^f{D(gJmmYu8
zIq~?DEq&(of)>T>7xe!cWc^{G1XH|GAxX9m9A2u=_E4Il2@tmF*KQCON801m@_i8D
zF)`1YTK37{(Jg(#zf;QW5T9Hp4Sx$P9<!_!7Z-A3z1Om?!@q*MhKlH@_|N6fPAUe5
zpDijvd5C;p^&ARNXowCI#c~U6%MmTMy<gErwqvq3x~<FFv2A^i_Nla!khRJBX>lfH
qO8!}TOn4C!*mwF;KT(hCBs~r?(LO$2&lO3M{)vo8liLWGf8amDWmx|J

literal 0
HcmV?d00001

diff --git a/ros2/src/robobin/robobin/connection_manager.py b/ros2/src/robobin/robobin/api_helpers/connection_manager.py
similarity index 100%
rename from ros2/src/robobin/robobin/connection_manager.py
rename to ros2/src/robobin/robobin/api_helpers/connection_manager.py
diff --git a/ros2/src/robobin/robobin/api_helpers/message_handler.py b/ros2/src/robobin/robobin/api_helpers/message_handler.py
new file mode 100644
index 00000000..01adeb4d
--- /dev/null
+++ b/ros2/src/robobin/robobin/api_helpers/message_handler.py
@@ -0,0 +1,80 @@
+# robobin/message_handler.py
+import time
+class MessageHandler:
+    def __init__(self, api_node, testing=False):
+        self.api_node = api_node
+        self.testing = testing
+        self.handlers = {
+            "PING": self.handle_ping,
+            "TIME": self.handle_time_request,
+            "MANUALCTRL": self.handle_manual_control
+        }
+
+    def handle_message(self, client_socket, raw_message):
+        """Parses the incoming message and routes the command to the appropriate handler."""
+        command, *args = raw_message.split(" ", 1)
+        data = args[0] if args else None
+        handler = self.handlers.get(command, self.handle_unknown_message)
+        return handler(client_socket, data)
+
+    def handle_ping(self, client_socket, _):
+        """Responds with a PONG message."""
+        response = b"PONG"
+        if self.testing:
+            print(response.decode())
+        else:
+            client_socket.sendall(response)
+
+        return None
+
+    def handle_time_request(self, client_socket, _):
+        """Sends the current server time."""
+        response = time.ctime().encode()
+        if self.testing:
+            print(response.decode())
+        else:
+            client_socket.sendall(response)
+
+        return None
+    
+    def handle_manual_control(self, client_socket, message):
+        """Handles manual control commands: W, A, S, D."""
+        # W: linear.x = 0.5, angular.z = 0
+        # A: linear.x = 0, angular.z = 0.5
+        # S: linear.x = -0.5, angular.z = 0
+        # D: linear.x = 0, angular.z = -0.5
+        
+        directions = {
+            "W": (0.5, 0),    # Move forward
+            "A": (0, 0.5),    # Turn left
+            "S": (-0.5, 0),   # Move backward
+            "D": (0, -0.5)    # Turn right
+        }
+        response_data = directions.get(message.strip().upper(), (0, 0))
+        response = f"Manual control command received: {response_data}".encode()
+        if self.testing:
+            print(response.decode())
+        else:
+            client_socket.sendall(response)
+        print("Processed manual control command:", response_data)
+        return "cmd_vel", response_data
+    
+    def handle_unknown_message(self, client_socket, _):
+        """Handles unknown commands."""
+        response = b"Unknown command"
+        if self.testing:
+            print(response.decode())
+        else:
+            client_socket.sendall(response)
+        return None
+    
+# Test class without api_node and with testing enabled
+if __name__ == "__main__":
+    message_handler = MessageHandler(None, testing=True)
+    assert message_handler.handle_message(None, "PING") is None
+    assert message_handler.handle_message(None, "TIME") is None
+    assert message_handler.handle_message(None, "MANUALCTRL W") == ("cmd_vel", (0.5, 0))
+    assert message_handler.handle_message(None, "MANUALCTRL A") == ("cmd_vel", (0, 0.5))
+    assert message_handler.handle_message(None, "MANUALCTRL S") == ("cmd_vel", (-0.5, 0))
+    assert message_handler.handle_message(None, "MANUALCTRL D") == ("cmd_vel", (0, -0.5))
+    assert message_handler.handle_message(None, "UNKNOWN") is None
\ No newline at end of file
diff --git a/ros2/src/robobin/robobin/api_node.py b/ros2/src/robobin/robobin/api_node.py
index df71bb2f..c7fa7e1f 100644
--- a/ros2/src/robobin/robobin/api_node.py
+++ b/ros2/src/robobin/robobin/api_node.py
@@ -1,19 +1,28 @@
+
+from api_helpers.message_handler import MessageHandler
+from api_helpers.connection_manager import ConnectionManager
+
+from geometry_msgs.msg import Twist
 # robobin/api_node.py
 import rclpy
 from rclpy.node import Node
-from .message_handler import MessageHandler
-from .connection_manager import ConnectionManager
 
 class ApiNode(Node):
     def __init__(self):
         super().__init__('api_node')
         self.get_logger().info("ApiNode has been started.")
 
-        # Initialize handlers
+        self.publisher_topics = {
+            "cmd_vel": self.create_publisher(Twist, '/cmd_vel', 10),
+            "nav_send": self.create_publisher(Twist, '/nav_send', 10), 
+            "map": self.create_publisher(Twist, '/map', 10) 
+        }
+        subscriber_topics = {
+            "location": "/location"
+        }
         self.message_handler = MessageHandler(self)
         self.connection_manager = ConnectionManager(self)
 
-        # Start connection manager
         self.connection_manager.start()
 
     def handle_client_connection(self, client_socket):
@@ -23,11 +32,34 @@ class ApiNode(Node):
                 data = client_socket.recv(1024).decode()
                 if not data:
                     break
-                command, *args = data.split(" ", 1)
-                self.message_handler.handle_message(client_socket, command, args[0] if args else None)
+                topic,message = self.message_handler.handle_message(client_socket, data)
+                if topic is not None:
+                    self.publish_to_topic(topic, message)
         finally:
             client_socket.close()
             self.get_logger().info("Client disconnected.")
+    def publish_to_topic(self, topic, message):
+        """Publishes the message to the specified topic."""
+        if topic in self.publisher_topics:
+            publisher = self.publisher_topics[topic]
+
+            # Check if the topic is 'cmd_vel' and format the message as a Twist message
+            if topic == "cmd_vel" and isinstance(message, tuple):
+                linear_x, angular_z = message
+                twist_msg = Twist()
+                twist_msg.linear.x = linear_x
+                twist_msg.linear.y = 0.0
+                twist_msg.linear.z = 0.0
+                twist_msg.angular.x = 0.0
+                twist_msg.angular.y = 0.0
+                twist_msg.angular.z = angular_z
+
+                publisher.publish(twist_msg)
+                self.get_logger().info(f"Published to {topic}: linear_x={linear_x}, angular_z={angular_z}")
+            else:
+                self.get_logger().warning(f"Unhandled message type for topic: {topic}")
+        else:
+            self.get_logger().warning(f"Unknown topic: {topic}")
 
     def shutdown(self):
         """Stops the connection manager."""
diff --git a/ros2/src/robobin/robobin/message_handler.py b/ros2/src/robobin/robobin/message_handler.py
deleted file mode 100644
index 46f3ffa3..00000000
--- a/ros2/src/robobin/robobin/message_handler.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# robobin/message_handler.py
-
-class MessageHandler:
-    def __init__(self, api_node):
-        self.api_node = api_node
-        self.handlers = {
-            "PING": self.handle_ping,
-            "TIME": self.handle_time_request,
-            "MANUALCTRL": self.handle_manual_control
-        }
-
-    def handle_message(self, client_socket, command, data):
-        """Routes the command to the appropriate handler."""
-        handler = self.handlers.get(command, self.handle_unknown_message)
-        handler(client_socket, data)
-
-    def handle_ping(self, client_socket, _):
-        """Responds with a PONG message."""
-        client_socket.sendall(b"PONG")
-
-    def handle_time_request(self, client_socket, _):
-        """Sends the current server time."""
-        client_socket.sendall(time.ctime().encode())
-
-    def handle_manual_control(self, client_socket, message):
-        """Handles manual control commands: W, A, S, D."""
-        directions = {
-            "W": (1, 0, 0),    # Move forward
-            "A": (0, 0, 1),    # Turn left
-            "S": (-1, 0, 0),   # Move backward
-            "D": (0, 0, -1)    # Turn right
-        }
-        response_data = directions.get(message.strip().upper(), (0, 0, 0))
-        response = f"Manual control command received: {response_data}".encode()
-        client_socket.sendall(response)
-        print("Processed manual control command:", response_data)
-
-    def handle_unknown_message(self, client_socket, _):
-        """Handles unknown commands."""
-        client_socket.sendall(b"Unknown command")
diff --git a/ros2/src/robobin/robobin/motor_controller.py b/ros2/src/robobin/robobin/motor_controller.py
new file mode 100644
index 00000000..e69de29b
-- 
GitLab