desktop push
This commit is contained in:
@@ -10,6 +10,7 @@ import 'package:bus_infotainment/auth/api_constants.dart';
|
||||
import 'package:bus_infotainment/auth/auth_api.dart';
|
||||
import 'package:bus_infotainment/backend/modules/announcement.dart';
|
||||
import 'package:bus_infotainment/backend/modules/commands.dart';
|
||||
import 'package:bus_infotainment/backend/modules/networking.dart';
|
||||
import 'package:bus_infotainment/backend/modules/synced_time.dart';
|
||||
import 'package:bus_infotainment/backend/modules/tracker.dart';
|
||||
import 'package:bus_infotainment/backend/modules/tube_info.dart';
|
||||
@@ -75,6 +76,8 @@ class LiveInformation {
|
||||
if (!auth.isAuthenticated()) {
|
||||
auth.loadAnonymousUser();
|
||||
}
|
||||
|
||||
networkingModule = NetworkingModule();
|
||||
}
|
||||
|
||||
Future<void> initTrackerModule() async {
|
||||
@@ -90,9 +93,12 @@ class LiveInformation {
|
||||
String? roomCode;
|
||||
String? roomDocumentID;
|
||||
bool isHost = false;
|
||||
bool inRoom = false;
|
||||
appwrite.RealtimeSubscription? _subscription;
|
||||
RealtimeKeepAliveConnection? _keepAliveConnection; // This is a workaround for a bug in the appwrite SDK
|
||||
|
||||
// Local room stuff
|
||||
ListenerReceipt<String>? _listenerReciept;
|
||||
|
||||
// Modules
|
||||
// late CommandModule commandModule; This needs to be deprecated
|
||||
@@ -101,6 +107,7 @@ class LiveInformation {
|
||||
late SyncedTimeModule syncedTimeModule;
|
||||
late TrackerModule trackerModule;
|
||||
late TubeStations tubeStations;
|
||||
late NetworkingModule networkingModule;
|
||||
|
||||
// Important variables
|
||||
BusRouteVariant? _currentRouteVariant;
|
||||
@@ -114,7 +121,7 @@ class LiveInformation {
|
||||
|
||||
|
||||
|
||||
Future<void> setRouteVariant(BusRouteVariant? routeVariant) async {
|
||||
Future<void> setRouteVariant(BusRouteVariant? routeVariant, {bool sendToServer = false}) async {
|
||||
|
||||
if (routeVariant == null) {
|
||||
_currentRouteVariant = null;
|
||||
@@ -160,6 +167,11 @@ class LiveInformation {
|
||||
print("Failed to update route on server");
|
||||
}
|
||||
|
||||
}
|
||||
if (inRoom && sendToServer) {
|
||||
|
||||
SendCommand("setroute ${routeVariant.busRoute.routeNumber} ${routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant)}");
|
||||
|
||||
}
|
||||
Continuation:
|
||||
|
||||
@@ -200,12 +212,13 @@ class LiveInformation {
|
||||
return _currentRouteVariant;
|
||||
}
|
||||
|
||||
Future<void> setRouteVariantQuery(String routeNumber, int routeVariantIndex) async {
|
||||
Future<void> setRouteVariantQuery(String routeNumber, int routeVariantIndex, {bool sendToServer = false}) async {
|
||||
BusRoute route = busSequences.routes[routeNumber]!;
|
||||
BusRouteVariant routeVariant = route.routeVariants.values.toList()[routeVariantIndex];
|
||||
|
||||
await setRouteVariant(
|
||||
routeVariant
|
||||
routeVariant,
|
||||
sendToServer: sendToServer
|
||||
);
|
||||
}
|
||||
|
||||
@@ -213,174 +226,225 @@ class LiveInformation {
|
||||
// Multi device support
|
||||
|
||||
Future<void> createRoom(String roomCode) async {
|
||||
print("Creating room with code $roomCode");
|
||||
|
||||
// Update the room code
|
||||
this.roomCode = roomCode;
|
||||
|
||||
// Enable host mode
|
||||
isHost = true;
|
||||
|
||||
// Access the database
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
// Remove any existing documents
|
||||
final existingDocuments = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode)
|
||||
]
|
||||
);
|
||||
for (var document in existingDocuments.documents) {
|
||||
await databases.deleteDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: document.$id
|
||||
{
|
||||
// Local Room
|
||||
await networkingModule.startWebSocketServer();
|
||||
inRoom = true;
|
||||
_listenerReciept = networkingModule.onMessageReceived?.addListener(
|
||||
(p0) {
|
||||
print("Received local command: $p0");
|
||||
ExecuteCommand(p0);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Create the document
|
||||
final document = await databases.createDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: appwrite.ID.unique(),
|
||||
data: {
|
||||
"SessionID": roomCode,
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
{
|
||||
// Cloud Room
|
||||
print("Creating room with code $roomCode");
|
||||
|
||||
// Listen for changes
|
||||
// { Breaks due to bug in appwrite
|
||||
// final realtime = appwrite.Realtime(client);
|
||||
//
|
||||
// if (_subscription != null) {
|
||||
// _subscription!.close();
|
||||
// }
|
||||
//
|
||||
// _subscription = realtime.subscribe(
|
||||
// ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}']
|
||||
// );
|
||||
// _subscription!.stream.listen(ServerListener);
|
||||
// }
|
||||
// Listen for changes
|
||||
if (_keepAliveConnection != null) {
|
||||
try {
|
||||
_keepAliveConnection!.close();
|
||||
} catch (e) {
|
||||
print("Failed to close connection... oh well");
|
||||
// Update the room code
|
||||
this.roomCode = roomCode;
|
||||
|
||||
// Enable host mode
|
||||
isHost = true;
|
||||
inRoom = true;
|
||||
// Access the database
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
// Remove any existing documents
|
||||
final existingDocuments = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode)
|
||||
]
|
||||
);
|
||||
for (var document in existingDocuments.documents) {
|
||||
await databases.deleteDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: document.$id
|
||||
);
|
||||
}
|
||||
|
||||
// Create the document
|
||||
final document = await databases.createDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: appwrite.ID.unique(),
|
||||
data: {
|
||||
"SessionID": roomCode,
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
|
||||
// Listen for changes
|
||||
if (_keepAliveConnection != null) {
|
||||
try {
|
||||
_keepAliveConnection!.close();
|
||||
} catch (e) {
|
||||
print("Failed to close connection... oh well");
|
||||
}
|
||||
}
|
||||
|
||||
String APPWRITE_ENDPOINT_URL = "https://cloud.appwrite.io/v1";
|
||||
String domain = APPWRITE_ENDPOINT_URL.replaceAll("https://", "").trim();
|
||||
_keepAliveConnection = RealtimeKeepAliveConnection(
|
||||
channels: ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'],
|
||||
onData: ServerListener,
|
||||
domain: domain,
|
||||
client: auth.client,
|
||||
onError: (e) {
|
||||
print("Workarround Error: $e");
|
||||
},
|
||||
);
|
||||
_keepAliveConnection!.initialize();
|
||||
|
||||
|
||||
// Update the room document ID
|
||||
roomDocumentID = document.$id;
|
||||
|
||||
print("Created room with code $roomCode");
|
||||
}
|
||||
|
||||
String APPWRITE_ENDPOINT_URL = "https://cloud.appwrite.io/v1";
|
||||
String domain = APPWRITE_ENDPOINT_URL.replaceAll("https://", "").trim();
|
||||
_keepAliveConnection = RealtimeKeepAliveConnection(
|
||||
channels: ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'],
|
||||
onData: ServerListener,
|
||||
domain: domain,
|
||||
client: auth.client,
|
||||
onError: (e) {
|
||||
print("Workarround Error: $e");
|
||||
},
|
||||
);
|
||||
_keepAliveConnection!.initialize();
|
||||
|
||||
|
||||
// Update the room document ID
|
||||
roomDocumentID = document.$id;
|
||||
|
||||
print("Created room with code $roomCode");
|
||||
}
|
||||
|
||||
Future<void> joinRoom(String roomCode) async {
|
||||
print("Joining room with code $roomCode");
|
||||
Future<void> joinRoom(String infoJson) async {
|
||||
|
||||
// Disable host mode
|
||||
isHost = false;
|
||||
try {
|
||||
{
|
||||
// sync
|
||||
String routeNumber = jsonDecode(infoJson)["sync"]["route"];
|
||||
int routeVariantIndex = jsonDecode(infoJson)["sync"]["routeVariant"];
|
||||
|
||||
// Update the room code
|
||||
this.roomCode = roomCode;
|
||||
setRouteVariantQuery(routeNumber, routeVariantIndex);
|
||||
|
||||
// Access the database
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
// Get the document
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode)
|
||||
]
|
||||
);
|
||||
|
||||
if (response.documents.isEmpty) {
|
||||
throw Exception("Room not found");
|
||||
LiveInformation().announcementModule.queueAnnouncementByRouteVariant(routeVariant: _currentRouteVariant!, sendToServer: false);
|
||||
}
|
||||
} catch (e) {
|
||||
print("Failed to sync route");
|
||||
}
|
||||
|
||||
final document = response.documents.first;
|
||||
{
|
||||
// Local Room
|
||||
|
||||
// Listen for changes
|
||||
// {
|
||||
// final realtime = appwrite.Realtime(client);
|
||||
//
|
||||
// if (_subscription != null) {
|
||||
// _subscription!.close();
|
||||
// }
|
||||
//
|
||||
// _subscription = realtime.subscribe([
|
||||
// 'databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'
|
||||
// ]);
|
||||
//
|
||||
// _subscription!.stream.listen(ServerListener);
|
||||
// }
|
||||
// Listen for changes
|
||||
if (_keepAliveConnection != null) {
|
||||
try {
|
||||
_keepAliveConnection!.close();
|
||||
} catch (e) {
|
||||
print("Failed to close connection... oh well");
|
||||
String host = jsonDecode(infoJson)["local"]["host"];
|
||||
|
||||
if (await networkingModule.connectToWebSocketServer(host)){
|
||||
print("Connected to local room at $host");
|
||||
|
||||
_listenerReciept = networkingModule.onMessageReceived?.addListener(
|
||||
(p0) {
|
||||
print("Received local command: $p0");
|
||||
ExecuteCommand(p0);
|
||||
}
|
||||
);
|
||||
inRoom = true;
|
||||
return; // We dont need to connect to the cloud room if we are connected to the local room.
|
||||
} else {
|
||||
print("Failed to connect to local room at $host");
|
||||
print("Falling back to cloud room");
|
||||
}
|
||||
}
|
||||
|
||||
String APPWRITE_ENDPOINT_URL = "https://cloud.appwrite.io/v1";
|
||||
String domain = APPWRITE_ENDPOINT_URL.replaceAll("https://", "").trim();
|
||||
_keepAliveConnection = RealtimeKeepAliveConnection(
|
||||
channels: ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'],
|
||||
onData: ServerListener,
|
||||
domain: domain,
|
||||
client: auth.client,
|
||||
onError: (e) {
|
||||
print("Workarround Error: $e");
|
||||
},
|
||||
);
|
||||
_keepAliveConnection!.initialize();
|
||||
{
|
||||
// Cloud Room
|
||||
|
||||
// Update the room document ID
|
||||
roomDocumentID = document.$id;
|
||||
String roomCode = jsonDecode(infoJson)["cloud"]["roomCode"];
|
||||
|
||||
// Get the current route
|
||||
try {
|
||||
String routeNumber = document.data["CurrentRoute"];
|
||||
int routeVariantIndex = document.data["CurrentRouteVariant"];
|
||||
print("Joining cloud room with code $roomCode");
|
||||
|
||||
await setRouteVariantQuery(routeNumber, routeVariantIndex);
|
||||
print("Set route to $routeNumber $routeVariantIndex");
|
||||
} catch (e) {
|
||||
print("Failed to set route");
|
||||
// Disable host mode
|
||||
isHost = false;
|
||||
|
||||
// Update the room code
|
||||
this.roomCode = roomCode;
|
||||
|
||||
// Access the database
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
// Get the document
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode)
|
||||
]
|
||||
);
|
||||
|
||||
if (response.documents.isEmpty) {
|
||||
throw Exception("Room not found");
|
||||
}
|
||||
|
||||
final document = response.documents.first;
|
||||
|
||||
// Listen for changes
|
||||
if (_keepAliveConnection != null) {
|
||||
try {
|
||||
_keepAliveConnection!.close();
|
||||
} catch (e) {
|
||||
print("Failed to close connection... oh well");
|
||||
}
|
||||
}
|
||||
|
||||
String APPWRITE_ENDPOINT_URL = "https://cloud.appwrite.io/v1";
|
||||
String domain = APPWRITE_ENDPOINT_URL.replaceAll("https://", "").trim();
|
||||
_keepAliveConnection = RealtimeKeepAliveConnection(
|
||||
channels: ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'],
|
||||
onData: ServerListener,
|
||||
domain: domain,
|
||||
client: auth.client,
|
||||
onError: (e) {
|
||||
print("Workarround Error: $e");
|
||||
},
|
||||
);
|
||||
_keepAliveConnection!.initialize();
|
||||
|
||||
// Update the room document ID
|
||||
roomDocumentID = document.$id;
|
||||
|
||||
// Get the current route
|
||||
try {
|
||||
String routeNumber = document.data["CurrentRoute"];
|
||||
int routeVariantIndex = document.data["CurrentRouteVariant"];
|
||||
|
||||
await setRouteVariantQuery(routeNumber, routeVariantIndex);
|
||||
print("Set route to $routeNumber $routeVariantIndex");
|
||||
} catch (e) {
|
||||
print("Failed to set route");
|
||||
}
|
||||
inRoom = true;
|
||||
print("Joined cloud room with code $roomCode");
|
||||
}
|
||||
|
||||
print("Joined room with code $roomCode");
|
||||
}
|
||||
|
||||
Future<void> leaveRoom() async {
|
||||
|
||||
if (roomCode == null) {
|
||||
throw Exception("Not in a room");
|
||||
if (!inRoom) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// Local Room
|
||||
networkingModule.stopWebSocketServer();
|
||||
inRoom = false;
|
||||
networkingModule.onMessageReceived?.removeListener(_listenerReciept!);
|
||||
}
|
||||
|
||||
{
|
||||
// Cloud Room
|
||||
if (_keepAliveConnection != null) {
|
||||
_keepAliveConnection!.close();
|
||||
_keepAliveConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
inRoom = false;
|
||||
|
||||
if (isHost) {
|
||||
// Access the database
|
||||
final client = auth.client;
|
||||
@@ -406,7 +470,7 @@ class LiveInformation {
|
||||
roomCode = null;
|
||||
roomDocumentID = null;
|
||||
isHost = false;
|
||||
|
||||
inRoom = false;
|
||||
_keepAliveConnection?.close();
|
||||
_keepAliveConnection = null;
|
||||
|
||||
@@ -414,6 +478,42 @@ class LiveInformation {
|
||||
setRouteVariant(null);
|
||||
}
|
||||
|
||||
String generateRoomInfo() {
|
||||
|
||||
// Room Info Example
|
||||
/*
|
||||
{
|
||||
"cloud": {
|
||||
"roomCode": "6633e85d0020f52f3771"
|
||||
},
|
||||
"local":
|
||||
{
|
||||
"host": "ws://192.168.0.123:8080"
|
||||
},
|
||||
"sync":
|
||||
{
|
||||
"route": "W11",
|
||||
"routeVariant": 1
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return jsonEncode({
|
||||
"cloud": {
|
||||
"roomCode": roomCode,
|
||||
},
|
||||
"local": {
|
||||
"host": "ws://${networkingModule.localIP}:8080"
|
||||
},
|
||||
if (_currentRouteVariant != null)
|
||||
"sync": {
|
||||
"route": _currentRouteVariant!.busRoute.routeNumber,
|
||||
"routeVariant": _currentRouteVariant!.busRoute.routeVariants.values.toList().indexOf(_currentRouteVariant!),
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
String? lastCommand;
|
||||
Future<void> ServerListener(appwrite.RealtimeMessage response) async {
|
||||
print("Session update");
|
||||
@@ -454,8 +554,11 @@ class LiveInformation {
|
||||
// Execute the command
|
||||
List<String> commands = response.payload["Commands"].cast<String>();
|
||||
|
||||
String? command = commands.last;
|
||||
ExecuteCommand(commands.last);
|
||||
|
||||
}
|
||||
|
||||
void ExecuteCommand(String command) {
|
||||
if (command == lastCommand) {
|
||||
return;
|
||||
}
|
||||
@@ -516,6 +619,12 @@ class LiveInformation {
|
||||
);
|
||||
|
||||
}
|
||||
} else if (commandName == "setroute") {
|
||||
print("Set route command received");
|
||||
String routeNumber = commandParts[1];
|
||||
int routeVariantIndex = int.parse(commandParts[2]);
|
||||
|
||||
setRouteVariantQuery(routeNumber, routeVariantIndex, sendToServer: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,34 +636,48 @@ class LiveInformation {
|
||||
|
||||
Future<void> SendCommand(String command) async {
|
||||
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
{
|
||||
// Local Commands
|
||||
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode!)
|
||||
]
|
||||
);
|
||||
networkingModule.sendMessage(command);
|
||||
}
|
||||
|
||||
List<String> pastCommands = [];
|
||||
|
||||
response.documents.first.data["Commands"].forEach((element) {
|
||||
pastCommands.add(element);
|
||||
});
|
||||
{
|
||||
// Cloud Commands
|
||||
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode!)
|
||||
]
|
||||
);
|
||||
|
||||
List<String> pastCommands = [];
|
||||
|
||||
response.documents.first.data["Commands"].forEach((element) {
|
||||
pastCommands.add(element);
|
||||
});
|
||||
|
||||
pastCommands.add(command);
|
||||
|
||||
final document = await databases.updateDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: roomDocumentID!,
|
||||
data: {
|
||||
"Commands": pastCommands,
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
pastCommands.add(command);
|
||||
|
||||
final document = await databases.updateDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: roomDocumentID!,
|
||||
data: {
|
||||
"Commands": pastCommands,
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
List<String> _splitCommand(String command) {
|
||||
|
||||
@@ -27,7 +27,7 @@ class AnnouncementModule extends InfoModule {
|
||||
// Files
|
||||
String _bundleLocation = "assets/ibus_recordings.zip";
|
||||
Uint8List? _bundleBytes;
|
||||
void setBundleBytes(Uint8List bytes) {
|
||||
void setBundleBytes(Uint8List? bytes) {
|
||||
_bundleBytes = bytes;
|
||||
}
|
||||
Future<Uint8List> getBundleBytes() async {
|
||||
@@ -35,7 +35,6 @@ class AnnouncementModule extends InfoModule {
|
||||
if (_bundleBytes != null) {
|
||||
return _bundleBytes!;
|
||||
} else {
|
||||
|
||||
// Try to load them from shared preferences
|
||||
try {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
@@ -47,17 +46,7 @@ class AnnouncementModule extends InfoModule {
|
||||
} catch (e) {
|
||||
throw Exception("Loading announcements from assets has been deprecated.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if (kIsWeb) {
|
||||
// throw Exception("Cannot load bundle bytes on web");
|
||||
// }
|
||||
//
|
||||
// final bytes = await rootBundle.load(_bundleLocation);
|
||||
// return bytes.buffer.asUint8List();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Queue
|
||||
@@ -165,7 +154,14 @@ class AnnouncementModule extends InfoModule {
|
||||
}
|
||||
|
||||
// Configuration
|
||||
int get defaultAnnouncementDelay => liveInformation.auth.isAuthenticated() ? 1 : 0;
|
||||
Duration get defaultAnnouncementDelay {
|
||||
if (liveInformation.inRoom) {
|
||||
return Duration(milliseconds: 500);
|
||||
} else {
|
||||
print("Not in room");
|
||||
return Duration.zero;
|
||||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
Future<void> queueAnnounceByAudioName({
|
||||
@@ -177,7 +173,9 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
if (sendToServer && _shouldSendToServer()) {
|
||||
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(Duration(seconds: defaultAnnouncementDelay));
|
||||
|
||||
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(defaultAnnouncementDelay);
|
||||
|
||||
String audioNamesString = "";
|
||||
|
||||
@@ -232,7 +230,7 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
if (sendToServer && _shouldSendToServer()) {
|
||||
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(Duration(seconds: defaultAnnouncementDelay));
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(defaultAnnouncementDelay);
|
||||
|
||||
liveInformation.SendCommand("announce info $infoIndex ${scheduledTime.millisecondsSinceEpoch}");
|
||||
queueAnnouncementByInfoIndex(
|
||||
@@ -262,7 +260,9 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
if (sendToServer && _shouldSendToServer()) {
|
||||
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(Duration(seconds: defaultAnnouncementDelay));
|
||||
print("Sending route announcement to server");
|
||||
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(defaultAnnouncementDelay);
|
||||
|
||||
String routeNumber = routeVariant.busRoute.routeNumber;
|
||||
int routeVariantIndex = routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant);
|
||||
@@ -326,7 +326,7 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
// Server check
|
||||
bool _shouldSendToServer() {
|
||||
bool condition = liveInformation.roomCode != null;
|
||||
bool condition = liveInformation.inRoom;
|
||||
|
||||
print("Should send to server? " + (condition.toString()));
|
||||
return condition;
|
||||
|
||||
196
lib/backend/modules/networking.dart
Normal file
196
lib/backend/modules/networking.dart
Normal file
@@ -0,0 +1,196 @@
|
||||
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';
|
||||
|
||||
class NetworkingModule extends InfoModule {
|
||||
// Host websocket server
|
||||
String host = "ws://0.0.0.0:8080";
|
||||
HttpServer? _server;
|
||||
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
|
||||
});
|
||||
});
|
||||
|
||||
_server = await io.serve(handler, InternetAddress.anyIPv4, 8080);
|
||||
print('WebSocket server started at ${_server?.address.address}:${_server?.port}');
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Failed to start WebSocket server: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool stopWebSocketServer() {
|
||||
if (_server == null) {
|
||||
throw Exception('WebSocket server is not running');
|
||||
}
|
||||
|
||||
try {
|
||||
for (var client in _connectedClients) {
|
||||
client.sink.close();
|
||||
}
|
||||
_connectedClients.clear();
|
||||
_server?.close(force: true);
|
||||
_server = 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 {
|
||||
_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 (_server != 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user