Skip to content
Snippets Groups Projects
Commit fce8b229 authored by Paul-Winpenny's avatar Paul-Winpenny
Browse files

Beginning to handle the loading map data from the robobins side.

parent 8c6d64dd
No related branches found
No related tags found
No related merge requests found
......@@ -15,6 +15,7 @@ namespace RobobinApp.Networking
private bool _isConnected = false;
private CancellationTokenSource _cancellationTokenSource;
private TcpClient _tcpClient;
public string Location { get; private set; } = "Unknown";
// Event to notify the UI or other parts of the app of specific messages
public event Action<string> OnMessageReceived;
......@@ -29,11 +30,7 @@ namespace RobobinApp.Networking
{
while (true) // Continuous listening loop with reconnection attempts
{
if (_isConnected)
{
Debug.WriteLine("Already connected. Stopping further listening.");
break;
}
try
{
......@@ -41,10 +38,19 @@ namespace RobobinApp.Networking
var result = await _udpClient.ReceiveAsync();
string message = Encoding.ASCII.GetString(result.Buffer);
if (message == "ROBOBIN_PRESENT")
if (message.StartsWith("ROBOBIN_PRESENT"))
{
Debug.WriteLine("Detected Robobin presence from: " + result.RemoteEndPoint);
SendConnectMessage(result.RemoteEndPoint.Address.ToString());
var parts = message.Split(new[] { '(', ',', ')' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 3)
{
Location = $"({parts[1].Trim()}, {parts[2].Trim()})";
Debug.WriteLine($"Detected Robobin presence at location: {Location} from: {result.RemoteEndPoint}");
SendConnectMessage(result.RemoteEndPoint.Address.ToString());
}
else
{
Debug.WriteLine("Invalid message format received.");
}
}
}
catch (ObjectDisposedException)
......@@ -127,12 +133,11 @@ namespace RobobinApp.Networking
catch (Exception ex)
{
Debug.WriteLine($"TCP connection failed: {ex.Message}");
_isConnected = false; // Reset connection status
await StartListening(); // Retry listening for presence broadcast
_isConnected = false;
await StartListening();
}
// If TCP connection is lost, attempt to reconnect
_cancellationTokenSource.Cancel(); // Stop the current listening task
_cancellationTokenSource.Cancel();
}
private async Task ReceiveMessages()
......
{
"name": "Lab 1",
"nodes": [
{"label": "A", "x": 50, "y": 50},
{"label": "B", "x": 150, "y": 50},
{"label": "C", "x": 250, "y": 50},
{"label": "Home", "x": 350, "y": 50},
{"label": "E", "x": 50, "y": 150},
{"label": "F", "x": 150, "y": 150},
{"label": "G", "x": 250, "y": 150},
{"label": "H", "x": 350, "y": 150},
{"label": "I", "x": 50, "y": 250},
{"label": "J", "x": 150, "y": 250},
{"label": "K", "x": 250, "y": 250},
{"label": "L", "x": 350, "y": 250},
{"label": "M", "x": 50, "y": 350},
{"label": "N", "x": 150, "y": 350},
{"label": "O", "x": 350, "y": 350}
],
"adjacency_matrix": [
[false, true, false, false, true, false, false, false, false, false, false, false, false, false, false],
[true, false, true, false, false, true, false, false, false, false, false, false, false, false, false],
[false, true, false, true, false, false, true, false, false, false, false, false, false, false, false],
[false, false, true, false, false, false, false, true, false, false, false, false, false, false, true],
[true, false, false, false, false, true, false, false, true, false, false, false, false, false, false],
[false, true, false, false, true, false, true, false, false, true, false, false, false, false, false],
[false, false, true, false, false, true, false, true, false, false, true, false, false, false, false],
[false, false, false, true, false, false, true, false, false, false, false, true, false, false, false],
[false, false, false, false, true, false, false, false, false, true, false, false, true, false, false],
[false, false, false, false, false, true, false, false, true, false, true, false, false, true, false],
[false, false, false, false, false, false, true, false, false, true, false, true, false, false, false],
[false, false, false, false, false, false, false, true, false, false, true, false, false, false, true],
[false, false, false, false, false, false, false, false, true, false, false, false, false, true, false],
[false, false, false, false, false, false, false, false, false, true, false, false, true, false, true],
[false, false, false, true, false, false, false, false, false, false, false, true, false, true, false]
]
}
import queue
import os
import json
import socket
import threading
import queue
import time
class RoboBinConnectionHandler:
def __init__(self, udp_ip="255.255.255.255", udp_port=5005, listen_port=5006):
self.queue = queue.Queue(10)
def __init__(self, udp_ip="255.255.255.255", udp_port=5005, listen_port=5006, layouts_folder="Layouts", config_file="config.json"):
self.command_queue = queue.Queue(10)
self.UDP_IP = udp_ip
self.UDP_PORT = udp_port
self.LISTEN_PORT = listen_port
self.stop_event = threading.Event()
self.stop_event = threading.Event()
self.layouts_folder = layouts_folder
self.config_file = config_file
self.default_layout = "lab1"
self.current_location = (0,0)
# Load the configuration on startup
config = self.load_config()
self.current_layout = config.get("current_layout", self.default_layout) #NAME OF LAYOUT FILE
# Load and print nodes from the current layout file
self.current_nodes = self.load_layout(self.current_layout)
# Command handlers
self.message_handlers = {
"PING": self.handle_ping,
"TIME": self.handle_time_request,
"CALLOVER" : self.handle_call_over,
"CUSTOM": self.handle_custom_message,
"CALLOVER": self.handle_call_over,
"CUSTOM": self.handle_custom_message,
"REQUEST_MAP": self.handle_request_map,
"SET_LAYOUT": self.handle_set_layout
}
self.udp_sock = None
self.tcp_socket = None #
self.udp_sock = None
self.tcp_socket = None
print("RoboBinConnectionHandler initialized.")
print("Current layout: {}".format(self.current_layout))
def broadcast_presence(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
def load_config(self):
"""
Loads the configuration from the config file.
If the file does not exist, it creates a default configuration.
"""
if os.path.exists(self.config_file):
with open(self.config_file, "r") as file:
config = json.load(file)
print(f"Loaded configuration: {config}")
config_nodes = config.get("nodes", [])
for node in config_nodes:
print(f"Node: {node}")
return config
else:
config = {"current_layout": self.default_layout}
self.save_config(config)
print("No config file found. Created default configuration.")
return config
while not self.stop_event.is_set():
message = b"ROBOBIN_PRESENT"
try:
sock.sendto(message, (self.UDP_IP, self.UDP_PORT))
print("Broadcasting: {}".format(message.decode()))
except OSError as e:
print(f"Broadcast error: {e}")
break
time.sleep(5)
def save_config(self, config):
"""
Saves the configuration to the config file.
"""
with open(self.config_file, "w") as file:
json.dump(config, file, indent=4)
print(f"Configuration saved: {config}")
sock.close()
print("Broadcasting stopped.")
def handle_set_layout(self, client_socket, new_layout):
"""
Updates the current layout in the configuration and persists it.
"""
self.current_layout = new_layout
# Update config file with the new current_layout
config = {"current_layout": self.current_layout}
self.save_config(config)
response = f"Current layout set to '{self.current_layout}'.".encode()
client_socket.sendall(response)
print(f"Current layout updated to '{self.current_layout}' and saved to config.")
def load_layout(self, layout_name):
"""
Loads the specified layout file and prints all nodes in it.
"""
layout_file = os.path.join(self.layouts_folder, f"{layout_name}.json")
if os.path.exists(layout_file):
with open(layout_file, "r") as file:
layout_data = json.load(file)
print(f"Loaded layout '{layout_name}':")
nodes = layout_data.get("nodes", [])
for node in nodes:
print(f"Node: {node['label']}, Position: ({node['x']}, {node['y']})")
return layout_data
else:
print(f"Layout file '{layout_file}' not found.")
return None
def load_current_map(self):
"""
Loads the current map JSON data based on the current_layout attribute.
If the specified layout is not found, falls back to the default layout.
"""
# Attempt to load the specified layout
map_data = self.load_layout(self.current_layout)
if map_data:
print(f"Loaded map '{self.current_layout}' successfully.")
return map_data
def handle_ping(self, client_socket):
print("Received PING from client.")
# Attempt to load the default layout if the specified layout is missing
map_data = self.load_layout(self.default_layout)
if map_data:
print(f"Specified layout not found. Loaded default layout '{self.default_layout}'.")
return map_data
# Both the specified and default layouts are missing
print("Error: Neither specified nor default layout found.")
return None
def handle_request_map(self, client_socket, _):
"""
Sends the current map layout (nodes and adjacency matrix) to the client.
"""
map_data = self.load_current_map()
if map_data:
map_json = json.dumps(map_data).encode()
client_socket.sendall(map_json)
print(f"Sent map data for layout '{self.current_layout}' to client.")
else:
error_message = b"Map not found."
client_socket.sendall(error_message)
print("Error: Map data not found, sent error message to client.")
# Other command handlers
def handle_ping(self, client_socket, _):
response = b"PONG"
print("Sending PONG to client.")
client_socket.sendall(response)
def handle_time_request(self, client_socket):
def handle_time_request(self, client_socket, _):
current_time = time.ctime().encode()
print(f"Sending current time: {current_time.decode()}")
client_socket.sendall(current_time)
def handle_custom_message(self, client_socket, message):
response = f"Received custom message: {message}".encode()
print(f"Custom handler response: {response.decode()}")
client_socket.sendall(response)
def handle_call_over(self, client_socket, message):
response = f"User has requested node: {message}".encode()
print(f"Call over handler response: {response.decode()}")
node = message
response = f"User has requested node: {node}".encode()
#TODO: Node should be added to queue, respond to user with queue position
client_socket.sendall(response)
def handle_unknown_message(self, client_socket):
def handle_unknown_message(self, client_socket, _):
response = b"I don't know this message."
print("Sending response to unknown message.")
client_socket.sendall(response)
def handle_client_connection(self, client_socket):
try:
while not self.stop_event.is_set():
client_socket.settimeout(1)
client_socket.settimeout(1)
try:
request = client_socket.recv(1024)
except socket.timeout:
......@@ -82,16 +178,10 @@ class RoboBinConnectionHandler:
print("Received from client: {}".format(message))
parts = message.split(" ", 1)
message_type = parts[0]
message_data = parts[1] if len(parts) > 1 else None
if message_type in self.message_handlers:
if message_data:
self.message_handlers[message_type](client_socket, message_data)
else:
self.message_handlers[message_type](client_socket)
else:
self.handle_unknown_message(client_socket)
command = parts[0]
data = parts[1] if len(parts) > 1 else None
handler = self.message_handlers.get(command, self.handle_unknown_message)
handler(client_socket, data)
except ConnectionResetError:
print("Client connection was forcibly closed.")
......@@ -108,14 +198,13 @@ class RoboBinConnectionHandler:
while not self.stop_event.is_set():
try:
self.udp_sock.settimeout(1) # Timeout for blocking recvfrom call
self.udp_sock.settimeout(1)
data, addr = self.udp_sock.recvfrom(1024)
if data.decode() == "CONNECT":
print("Received connection request from {}".format(addr))
self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.tcp_socket.bind(('', self.LISTEN_PORT))
self.tcp_socket.bind(('', self.LISTEN_PORT))
self.tcp_socket.listen(1)
print("Listening for TCP connection...")
......@@ -125,18 +214,37 @@ class RoboBinConnectionHandler:
threading.Thread(target=self.handle_client_connection, args=(client_socket,)).start()
except socket.timeout:
continue
except OSError as e:
print(f"UDP socket no longer open (likely due to stop event):" )
except OSError:
print("UDP socket no longer open (likely due to stop event).")
break
except Exception as e:
print(f"An error occurred while listening for connections: {e}")
# Close sockets if stop event is set
if self.udp_sock:
self.udp_sock.close()
if self.tcp_socket:
self.tcp_socket.close()
print("Listening stopped.")
def broadcast_presence(self):
"""
Broadcasts RoboBin's presence periodically over UDP.
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
while not self.stop_event.is_set():
#add location to back of message
message = b"ROBOBIN_PRESENT " + str(self.current_location).encode()
try:
sock.sendto(message, (self.UDP_IP, self.UDP_PORT))
print("Broadcasting: {}".format(message.decode()))
except OSError as e:
print(f"Broadcast error: {e}")
break
time.sleep(5)
sock.close()
print("Broadcasting stopped.")
def start(self):
self.broadcast_thread = threading.Thread(target=self.broadcast_presence)
......@@ -146,29 +254,21 @@ class RoboBinConnectionHandler:
self.listen_thread.start()
def stop(self):
print("Stopping server...")
self.stop_event.set()
# Safely close the sockets to prevent further operations on closed sockets
if self.udp_sock:
self.udp_sock.close()
if self.tcp_socket:
self.tcp_socket.close()
self.broadcast_thread.join()
self.listen_thread.join()
print("Server stopped.")
# Instantiate and start the handler
if __name__ == "__main__":
robobin_handler = RoboBinConnectionHandler()
robobin_handler.start()
print("Server started. Type 'stop' to shut down.")
# Main loop to accept CLI input
while True:
command = input("Enter command: ")
if command.strip().lower() == "stop":
robobin_handler.stop()
elif command.strip().lower() == "status":
print("Server is running.")
#printstatus() # Funciton will be added later
break
handler = RoboBinConnectionHandler()
handler.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Stopping...")
handler.stop()
print("Stopped.")
\ No newline at end of file
{
"current_layout": "lab1"
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment