import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:network_info_plus/network_info_plus.dart'; import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart' as io; import 'package:shelf_web_socket/shelf_web_socket.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:bus_infotainment/backend/modules/info_module.dart'; import 'package:bus_infotainment/utils/delegates.dart'; import 'package:http/http.dart' as http; class NetworkingModule extends InfoModule { // Host websocket server int webSocketPort = 8080; int httpPort = 8081; HttpServer? _sockerServer; HttpServer? _httpServer; WebSocketChannel? _channel; // Store connected WebSocket channels final List _connectedClients = []; EventDelegate? onMessageReceived = EventDelegate(); NetworkingModule() { _refresh(); refreshTimer(); } Future startWebSocketServer() async { try { var handler = webSocketHandler((WebSocketChannel webSocket) { _connectedClients.add(webSocket); // Add the client to the list print('Client connected: ${webSocket}'); // Log client connection webSocket.stream.listen((message) { // Handle messages from the client here print('Received message: $message'); // Forward message to all clients except the sender for (var client in _connectedClients) { if (client != webSocket) { client.sink.add(message); } } _onMessageReceived(message); }, onDone: () { _connectedClients.remove(webSocket); // Remove client on disconnect print('Client disconnected: ${webSocket}'); // Log client disconnection }); }); _sockerServer = await io.serve(handler, InternetAddress.anyIPv4, webSocketPort); print('WebSocket server started at ${_sockerServer?.address.address}:${_sockerServer?.port}'); // Start Http api server _httpServer = await io.serve((Request request) async { return Response.ok('bus infotainment server'); }, InternetAddress.anyIPv4, httpPort); return true; } catch (e) { print('Failed to start WebSocket server: $e'); return false; } } bool stopWebSocketServer() { if (_sockerServer == null) { throw Exception('WebSocket server is not running'); } try { for (var client in _connectedClients) { client.sink.close(); } _connectedClients.clear(); _sockerServer?.close(force: true); _sockerServer = null; _httpServer?.close(force: true); _httpServer = null; print('WebSocket server stopped'); return true; } catch (e) { print('Failed to stop WebSocket server: $e'); return false; } } Future connectToWebSocketServer(String url) async { try { { // Verify that the server we are connecting to is running, and is a bus infotainment server var response = await http.get(Uri.parse('http://$url:$httpPort')); if (response.statusCode != 200 || response.body != 'bus infotainment server') { print('Server at $url is not a bus infotainment server'); return false; } } _channel = await WebSocketChannel.connect(Uri.parse(url)); _channel?.stream.listen((message) { // Handle messages from the server here print('Received message from server: $message'); _onMessageReceived(message); }); print('Connected to WebSocket server at $url'); return true; } catch (e) { print('Failed to connect to WebSocket server: $e'); return false; } } bool disconnectFromWebSocketServer() { if (_channel == null) { throw Exception('No active WebSocket connection'); } try { _channel?.sink.close(); _channel = null; print('Disconnected from WebSocket server'); return true; } catch (e) { print('Failed to disconnect from WebSocket server: $e'); return false; } } bool sendMessage(String message) { // If hosting a server, send message to all clients if (_sockerServer != null) { return sendMessageToClients(message); } if (_channel == null) { throw Exception('No active WebSocket connection'); } try { _channel?.sink.add(message); print('Sent message: $message'); return true; } catch (e) { print('Failed to send message: $e'); return false; } } bool sendMessageToClients(String message) { if (_connectedClients.isEmpty) { print('No clients connected'); return false; } try { for (var client in _connectedClients) { client.sink.add(message); } print('Sent message to all clients: $message'); return true; } catch (e) { print('Failed to send message to clients: $e'); return false; } } void _onMessageReceived(String message) { // Notify all listeners that a message has been received. onMessageReceived?.trigger(message); } // Useful boilerplate String _localIP = ""; String get localIP => _localIP; Timer refreshTimer() => Timer.periodic(const Duration(seconds: 10), (timer) { if (kIsWeb) return; _refresh(); }); Future _refresh() async { print("Refreshing network info..."); { // Update the local IP address // First try NetworkInfo _localIP = (await NetworkInfo().getWifiIP()) ?? ""; // If null, try NetworkInterface // Only look for ethernet. Wifi would have been found by NetworkInfo if (_localIP.isEmpty) { for (var interface in await NetworkInterface.list()) { if (!interface.name.toLowerCase().contains("eth") || interface.name.contains(" ")) { continue; } for (var addr in interface.addresses) { print('Interface ${interface.name} has address ${addr.address}'); if (addr.type == InternetAddressType.IPv4 && !addr.isLoopback) { _localIP = addr.address; break; } } } } } } }