221 lines
6.1 KiB
Dart
221 lines
6.1 KiB
Dart
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<WebSocketChannel> _connectedClients = [];
|
|
|
|
EventDelegate<String>? onMessageReceived = EventDelegate();
|
|
|
|
NetworkingModule() {
|
|
_refresh();
|
|
refreshTimer();
|
|
}
|
|
|
|
Future<bool> 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<bool> 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<void> _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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |