desktop push

This commit is contained in:
ImBenji
2024-05-20 09:06:38 +01:00
parent e5a8d78bf1
commit 639faddfc8
19 changed files with 2455 additions and 996 deletions

View File

@@ -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) {