Files
Bus-Infotainment--IBus-/lib/backend/modules/commands.dart
2024-05-01 12:29:59 +01:00

228 lines
6.6 KiB
Dart

import 'dart:convert';
import 'package:bus_infotainment/auth/api_constants.dart';
import 'package:bus_infotainment/backend/live_information.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:appwrite/appwrite.dart' as appwrite;
import 'package:appwrite/models.dart' as models;
import 'package:uuid/uuid.dart';
import '../../auth/auth_api.dart';
import 'info_module.dart';
class CommandModule extends InfoModule {
final String sessionID;
late final String clientID;
List<CommandInfo> _commandHistory = [];
get commandHistory => _commandHistory;
EventDelegate<CommandInfo> onCommandReceived = EventDelegate();
CommandModule(this.sessionID){
// generate a random client ID
var uuid = Uuid();
clientID = uuid.v4();
if (liveInformation.auth.isAuthenticated()){
print("Auth is authenticated");
_setupListener();
} else {
print("Auth is not authenticated");
liveInformation.auth.onLogin.addListener((value) {
_setupListener();
});
}
}
// Will execute the command an event which is triggered when a response is received
Future<EventDelegate> executeCommand(String command) async {
EventDelegate<String> delegate = EventDelegate();
final client = liveInformation.auth.client;
final databases = appwrite.Databases(client);
if (liveInformation.auth.status == AuthStatus.AUTHENTICATED) {
final document = await databases.createDocument(
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
collectionId: ApiConstants.COMMANDS_COLLECTION_ID,
documentId: appwrite.ID.unique(),
data: {
"session_id": sessionID,
"command": command,
"client_id": clientID,
}
);
}
_onCommandReceived(CommandInfo(command, clientID));
return delegate;
}
Future<void> _onCommandReceived(CommandInfo commandInfo) async {
commandHistory.add(commandInfo);
onCommandReceived.trigger(commandInfo);
print("Received command: ${commandInfo.command}");
List<String> commandParts = splitCommand(commandInfo.command);
String command = commandParts[0];
List<String> args = commandParts.sublist(1);
if (command == "Response:") {
}
else if (command == "announce") {
final displayText = args[1];
if (args[0] == "manual") {
// announce manual <DisplayText> <AudioFileName>... <ScheduledTime>
List<String> audioFileNames = args.sublist(2);
try {
if (int.parse(audioFileNames.last) != null) {
audioFileNames.removeLast();
}
} catch (e) {}
DateTime scheduledTime = LiveInformation().syncedTimeModule.Now().add(Duration(seconds: 1));
try {
if (int.parse(args.last) != null) {
scheduledTime = DateTime.fromMillisecondsSinceEpoch(int.parse(args.last));
}
} catch (e) {}
liveInformation.announcementModule.queueAnnounceByAudioName(
displayText: displayText,
audioNames: audioFileNames,
scheduledTime: scheduledTime,
sendToServer: false
);
}
else if (args[0] == "info") {
int InfoIndex = int.parse(args[1]);
DateTime scheduledTime = LiveInformation().syncedTimeModule.Now();
try {
if (int.parse(args.last) != null) {
scheduledTime = DateTime.fromMillisecondsSinceEpoch(int.parse(args.last));
}
} catch (e) {}
liveInformation.announcementModule.queueAnnounementByInfoIndex(
infoIndex: InfoIndex,
scheduledTime: scheduledTime,
sendToServer: false
);
}
else if (args[0].startsWith("dest")) {
// announce destination <RouteNumber> <RouteVariantIndex> <ScheduledTime>
print("Checkpoint 1");
String routeNumber = args[1];
int routeVariantIndex = int.parse(args[2]);
DateTime scheduledTime = LiveInformation().syncedTimeModule.Now();
try {
if (int.parse(args.last) != null) {
scheduledTime = DateTime.fromMillisecondsSinceEpoch(int.parse(args.last));
}
} catch (e) {}
print("Checkpoint 2");
BusRoute route = LiveInformation().busSequences.routes[routeNumber]!;
BusRouteVariant routeVariant = route.routeVariants.values.toList()[routeVariantIndex];
print("Checkpoint 3");
liveInformation.announcementModule.queueAnnouncementByRouteVariant(
routeVariant: routeVariant,
scheduledTime: scheduledTime,
sendToServer: false
);
}
}
else if (command == "setroute") {
// setroute <RouteNumber> <RouteVariantIndex>
LiveInformation liveInformation = LiveInformation();
String routeNumber = args[0];
int routeVariantIndex = int.parse(args[1]);
BusRoute route = liveInformation.busSequences.routes[routeNumber]!;
BusRouteVariant routeVariant = route.routeVariants.values.toList()[routeVariantIndex];
liveInformation.setRouteVariant_Internal(
routeVariant
);
executeCommand("Response: v \"Client $clientID set its route to ($routeNumber to ${routeVariant.busStops.last.formattedStopName})\"");
}
}
appwrite.RealtimeSubscription? _subscription;
Future<void> _setupListener() async {
if (_subscription != null) {
return;
}
final realtime = appwrite.Realtime(LiveInformation().auth.client);
_subscription = realtime.subscribe(
['databases.${ApiConstants.INFO_Q_DATABASE_ID}.collections.${ApiConstants.COMMANDS_COLLECTION_ID}.documents']
);
_subscription!.stream.listen((event) {
print(jsonEncode(event.payload));
// Only do something if the document was created or updated
if (!(event.events.first.contains("create") || event.events.first.contains("update"))) {
return;
}
final commandInfo = CommandInfo(event.payload['command'], event.payload['client_id']);
if (commandInfo.clientID != clientID) {
_onCommandReceived(commandInfo);
}
});
print("Listening for commands");
await Future.delayed(Duration(seconds: 90));
await _subscription!.close();
_subscription = null;
_setupListener();
}
}
class CommandInfo {
final String command;
final String clientID;
CommandInfo(this.command, this.clientID);
}
List<String> splitCommand(String command) {
var regex = RegExp(r'([^\s"]+)|"([^"]*)"');
var matches = regex.allMatches(command);
return matches.map((match) => match.group(0)!.replaceAll('"', '')).toList();
}