paradigm shift
This commit is contained in:
@@ -16,6 +16,7 @@ import 'package:bus_infotainment/backend/modules/tube_info.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||
import 'package:bus_infotainment/utils/delegates.dart';
|
||||
import 'package:bus_infotainment/workaround/keepalive_realtime.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@@ -62,12 +63,6 @@ class LiveInformation {
|
||||
print("Loading tube stations from assets");
|
||||
tubeStations = TubeStations.fromJson(json.decode(await rootBundle.loadString("assets/datasets/tube_stations.json")));
|
||||
print("Loaded tube stations from assets");
|
||||
|
||||
|
||||
String sessionID = "test";
|
||||
|
||||
commandModule = CommandModule(sessionID);
|
||||
|
||||
}
|
||||
|
||||
// Initialise modules
|
||||
@@ -77,6 +72,9 @@ class LiveInformation {
|
||||
initTrackerModule();
|
||||
|
||||
print("Initialised LiveInformation");
|
||||
if (!auth.isAuthenticated()) {
|
||||
auth.loadAnonymousUser();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> initTrackerModule() async {
|
||||
@@ -86,10 +84,18 @@ class LiveInformation {
|
||||
}
|
||||
|
||||
// Auth
|
||||
AuthAPI auth = AuthAPI();
|
||||
AuthAPI auth = AuthAPI(
|
||||
autoLoad: false,
|
||||
);
|
||||
String? roomCode;
|
||||
String? roomDocumentID;
|
||||
bool isHost = false;
|
||||
appwrite.RealtimeSubscription? _subscription;
|
||||
RealtimeKeepAliveConnection? _keepAliveConnection; // This is a workaround for a bug in the appwrite SDK
|
||||
|
||||
|
||||
// Modules
|
||||
late CommandModule commandModule;
|
||||
// late CommandModule commandModule; This needs to be deprecated
|
||||
late BusSequences busSequences;
|
||||
late AnnouncementModule announcementModule;
|
||||
late SyncedTimeModule syncedTimeModule;
|
||||
@@ -100,7 +106,7 @@ class LiveInformation {
|
||||
BusRouteVariant? _currentRouteVariant;
|
||||
|
||||
// Events
|
||||
EventDelegate<BusRouteVariant> routeVariantDelegate = EventDelegate();
|
||||
EventDelegate<BusRouteVariant?> routeVariantDelegate = EventDelegate();
|
||||
|
||||
// Internal methods
|
||||
|
||||
@@ -108,7 +114,54 @@ class LiveInformation {
|
||||
|
||||
|
||||
|
||||
Future<void> setRouteVariant_Internal(BusRouteVariant routeVariant) async {
|
||||
Future<void> setRouteVariant(BusRouteVariant? routeVariant) async {
|
||||
|
||||
if (routeVariant == null) {
|
||||
_currentRouteVariant = null;
|
||||
|
||||
await announcementModule.queueAnnounceByAudioName(
|
||||
displayText: "*** NO MESSAGE ***",
|
||||
);
|
||||
|
||||
routeVariantDelegate.trigger(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomCode != null) {
|
||||
try {
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode!)
|
||||
]
|
||||
);
|
||||
|
||||
final document = response.documents.first;
|
||||
|
||||
// Check if the route is not the same
|
||||
if (document.data["CurrentRoute"] != routeVariant.busRoute.routeNumber || document.data["CurrentRouteVariant"] != routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant)) {
|
||||
final updatedDocument = await databases.updateDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: document.$id,
|
||||
data: {
|
||||
"CurrentRoute": routeVariant.busRoute.routeNumber,
|
||||
"CurrentRouteVariant": routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant),
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
print("Updated route on server");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Failed to update route on server");
|
||||
}
|
||||
|
||||
}
|
||||
Continuation:
|
||||
|
||||
// Set the current route variant
|
||||
_currentRouteVariant = routeVariant;
|
||||
@@ -135,6 +188,10 @@ class LiveInformation {
|
||||
|
||||
]
|
||||
);
|
||||
|
||||
// Display the route variant
|
||||
announcementModule.queueAnnouncementByRouteVariant(routeVariant: routeVariant);
|
||||
|
||||
}
|
||||
|
||||
// Public methods
|
||||
@@ -143,16 +200,329 @@ class LiveInformation {
|
||||
return _currentRouteVariant;
|
||||
}
|
||||
|
||||
Future<void> setRouteVariant(BusRouteVariant routeVariant) async {
|
||||
await commandModule.executeCommand(
|
||||
"setroute ${routeVariant.busRoute.routeNumber} ${routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant)}"
|
||||
Future<void> setRouteVariantQuery(String routeNumber, int routeVariantIndex) async {
|
||||
BusRoute route = busSequences.routes[routeNumber]!;
|
||||
BusRouteVariant routeVariant = route.routeVariants.values.toList()[routeVariantIndex];
|
||||
|
||||
await setRouteVariant(
|
||||
routeVariant
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
);
|
||||
}
|
||||
|
||||
// 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
|
||||
// { 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");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
// 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
|
||||
// {
|
||||
// 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 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");
|
||||
}
|
||||
|
||||
print("Joined room with code $roomCode");
|
||||
}
|
||||
|
||||
String? lastCommand;
|
||||
Future<void> ServerListener(appwrite.RealtimeMessage response) async {
|
||||
print("Session update");
|
||||
|
||||
// Only do something if the document was created or updated
|
||||
if (!(response.events.first.contains("create") || response.events.first.contains("update"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the user that caused the event
|
||||
|
||||
String senderID = response.payload["LastUpdater"];
|
||||
// If the sender is the same as the client, then ignore the event
|
||||
if (senderID == auth.userID) {
|
||||
print("Ignoring event");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if the commands are updated
|
||||
|
||||
try {
|
||||
// Get the new route
|
||||
String routeNumber = response.payload["CurrentRoute"];
|
||||
int routeVariantIndex = response.payload["CurrentRouteVariant"];
|
||||
|
||||
// If the route arent the same, then update the route
|
||||
if (routeNumber != _currentRouteVariant!.busRoute.routeNumber || routeVariantIndex != _currentRouteVariant!.busRoute.routeVariants.values.toList().indexOf(_currentRouteVariant!)) {
|
||||
// Set the route
|
||||
await setRouteVariantQuery(routeNumber, routeVariantIndex);
|
||||
|
||||
// announce the route
|
||||
// announcementModule.queueAnnouncementByRouteVariant(routeVariant: _currentRouteVariant!);
|
||||
}
|
||||
} catch (e) {
|
||||
print("Failed to set route");
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
List<String> commands = response.payload["Commands"].cast<String>();
|
||||
|
||||
String? command = commands.last;
|
||||
|
||||
if (command == lastCommand) {
|
||||
return;
|
||||
}
|
||||
lastCommand = command;
|
||||
|
||||
List<String> commandParts = _splitCommand(command);
|
||||
String commandName = commandParts[0];
|
||||
|
||||
if (commandName == "announce") {
|
||||
print("Announce command received");
|
||||
String mode = commandParts[1];
|
||||
|
||||
print ("Command: $command");
|
||||
|
||||
if (mode == "info") {
|
||||
print("Announce info command received");
|
||||
announcementModule.queueAnnounementByInfoIndex(
|
||||
sendToServer: false,
|
||||
infoIndex: int.parse(commandParts[2]),
|
||||
scheduledTime: DateTime.fromMillisecondsSinceEpoch(int.parse(commandParts[3])),
|
||||
);
|
||||
} else if (mode == "dest") {
|
||||
print("Announce dest command received");
|
||||
|
||||
String routeNumber = commandParts[2];
|
||||
int routeVariantIndex = int.parse(commandParts[3]);
|
||||
|
||||
announcementModule.queueAnnouncementByRouteVariant(
|
||||
sendToServer: false,
|
||||
routeVariant: busSequences.routes[routeNumber]!.routeVariants.values.toList()[routeVariantIndex],
|
||||
scheduledTime: DateTime.fromMillisecondsSinceEpoch(int.parse(commandParts[4])),
|
||||
);
|
||||
|
||||
} else if (mode == "manual") {
|
||||
print("Announce manual command received");
|
||||
|
||||
final displayText = commandParts[2];
|
||||
|
||||
List<String> audioFileNames = commandParts.sublist(3);
|
||||
try {
|
||||
if (int.parse(audioFileNames.last) != null) {
|
||||
audioFileNames.removeLast();
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
DateTime scheduledTime = LiveInformation().syncedTimeModule.Now().add(Duration(seconds: 1));
|
||||
try {
|
||||
if (int.parse(commandParts.last) != null) {
|
||||
scheduledTime = DateTime.fromMillisecondsSinceEpoch(int.parse(commandParts.last));
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
announcementModule.queueAnnounceByAudioName(
|
||||
displayText: displayText,
|
||||
audioNames: audioFileNames,
|
||||
scheduledTime: scheduledTime,
|
||||
sendToServer: false
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _extractId(String input) {
|
||||
RegExp regExp = RegExp(r'\("user:(.*)"\)');
|
||||
Match match = regExp.firstMatch(input)!;
|
||||
return match.group(1)!;
|
||||
}
|
||||
|
||||
Future<void> SendCommand(String command) async {
|
||||
|
||||
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,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
List<String> _splitCommand(String command) {
|
||||
var regex = RegExp(r'([^\s"]+)|"([^"]*)"');
|
||||
var matches = regex.allMatches(command);
|
||||
return matches.map((match) => match.group(0)!.replaceAll('"', '')).toList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -162,8 +532,6 @@ class LiveInformation {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AnnouncementQueueEntry {
|
||||
|
||||
Reference in New Issue
Block a user