to laptptop
This commit is contained in:
@@ -2,7 +2,11 @@
|
||||
// Singleton
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appwrite/appwrite.dart' as appwrite;
|
||||
import 'package:appwrite/models.dart' as models;
|
||||
import 'package:bus_infotainment/audio_cache.dart';
|
||||
import 'package:bus_infotainment/auth/api_constants.dart';
|
||||
import 'package:bus_infotainment/auth/auth_api.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||
import 'package:bus_infotainment/utils/delegates.dart';
|
||||
@@ -19,7 +23,7 @@ class LiveInformation {
|
||||
|
||||
LiveInformation._internal();
|
||||
|
||||
Future<void> LoadDatasets() async {
|
||||
Future<void> Initialize() async {
|
||||
|
||||
{
|
||||
// Load the bus sequences
|
||||
@@ -41,6 +45,16 @@ class LiveInformation {
|
||||
print("Loaded bus sequences from assets");
|
||||
}
|
||||
|
||||
if (auth.isAuthenticated()){
|
||||
print("Auth is authenticated");
|
||||
setupRealtime();
|
||||
} else {
|
||||
print("Auth is not authenticated");
|
||||
auth.onLogin.addListener((value) {
|
||||
setupRealtime();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
refreshTimer();
|
||||
@@ -54,28 +68,33 @@ class LiveInformation {
|
||||
AudioWrapper audioPlayer = AudioWrapper();
|
||||
AnnouncementCache announcementCache = AnnouncementCache();
|
||||
List<AnnouncementQueueEntry> announcementQueue = [];
|
||||
DateTime lastAnnouncement = DateTime.now();
|
||||
EventDelegate<AnnouncementQueueEntry> announcementDelegate = EventDelegate();
|
||||
String CurrentAnnouncement = "*** NO MESSAGE ***";
|
||||
String _currentAnnouncement = "*** NO MESSAGE ***";
|
||||
|
||||
String get currentAnnouncement => _currentAnnouncement;
|
||||
void set currentAnnouncement(String value) {
|
||||
_currentAnnouncement = value;
|
||||
}
|
||||
|
||||
void _handleAnnouncementQueue() async {
|
||||
print("Handling announcement queue");
|
||||
if (audioPlayer.state != AudioWrapper_State.Playing) {
|
||||
if (announcementQueue.isNotEmpty) {
|
||||
AnnouncementQueueEntry announcement = announcementQueue.first;
|
||||
announcementDelegate.trigger(announcement);
|
||||
CurrentAnnouncement = announcement.displayText;
|
||||
|
||||
_currentAnnouncement = announcement.displayText;
|
||||
|
||||
lastAnnouncement = DateTime.now();
|
||||
|
||||
|
||||
for (AudioWrapperSource source in announcement.audioSources) {
|
||||
|
||||
Duration? duration = await audioPlayer.play(source);
|
||||
if (source == announcement.audioSources.last) {
|
||||
announcementQueue.removeAt(0);
|
||||
}
|
||||
await Future.delayed(duration!);
|
||||
await Future.delayed(Duration(milliseconds: 150));
|
||||
}
|
||||
audioPlayer.stop();
|
||||
announcementQueue.removeAt(0);
|
||||
print("Popped announcement queue");
|
||||
}
|
||||
}
|
||||
@@ -119,6 +138,38 @@ class LiveInformation {
|
||||
|
||||
void queueAnnouncement(AnnouncementQueueEntry announcement) {
|
||||
announcementQueue.add(announcement);
|
||||
|
||||
// Make sure the timestamp of the announcement is after the last announcement
|
||||
// If so, dont queue it
|
||||
// If timestamp is null, then skip this check
|
||||
if (announcement.timestamp != null && announcement.timestamp!.isBefore(lastAnnouncement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!announcement.sendToServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
final databases = appwrite.Databases(auth.client);
|
||||
|
||||
if (announcement is ManualAnnouncementEntry) {
|
||||
|
||||
final document = databases.createDocument(
|
||||
documentId: appwrite.ID.unique(),
|
||||
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||
data: {
|
||||
"ManualAnnouncementIndex": manualAnnouncements.indexOf(announcement),
|
||||
"SessionID": sessionID,
|
||||
}
|
||||
);
|
||||
|
||||
print("Queued manual announcement: ${announcement.shortName}");
|
||||
|
||||
} else if (announcement is AnnouncementQueueEntry) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<ManualAnnouncementEntry> manualAnnouncements = [
|
||||
@@ -204,17 +255,206 @@ class LiveInformation {
|
||||
),
|
||||
];
|
||||
|
||||
AuthAPI auth = AuthAPI();
|
||||
String sessionID = "65de648aa7f44684ecce";
|
||||
void updateServer() async {
|
||||
|
||||
|
||||
|
||||
final databases = appwrite.Databases(auth.client);
|
||||
|
||||
// final document = databases.updateDocument(
|
||||
// databaseId: ApiConstants.INFO_DATABASE_ID,
|
||||
// collectionId: ApiConstants.INFO_COLLECTION_ID,
|
||||
// documentId: documentID,
|
||||
// data: {
|
||||
// "Display": _currentAnnouncement,
|
||||
// }
|
||||
// );
|
||||
|
||||
print("Updated server with announcement: $_currentAnnouncement");
|
||||
|
||||
}
|
||||
void pullServer() async {
|
||||
|
||||
if (auth.status == AuthStatus.UNAUTHENTICATED) {
|
||||
return;
|
||||
}
|
||||
|
||||
final databases = appwrite.Databases(auth.client);
|
||||
|
||||
// final document = await databases.getDocument(
|
||||
// databaseId: ApiConstants.INFO_DATABASE_ID,
|
||||
// collectionId: ApiConstants.INFO_COLLECTION_ID,
|
||||
// documentId: documentID,
|
||||
// );
|
||||
|
||||
// queueAnnouncement(AnnouncementQueueEntry(
|
||||
// displayText: document.data['Display'],
|
||||
// audioSources: [],
|
||||
// sendToServer: false, // Don't send this back to the server, else we'll get an infinite loop
|
||||
// ));
|
||||
|
||||
// print("Pulled announcement from server: ${document.data['Display']}");
|
||||
}
|
||||
|
||||
bool purgeRunning = false;
|
||||
Future<void> deleteAllManualQueueEntries() async {
|
||||
|
||||
if (purgeRunning) {
|
||||
return;
|
||||
}
|
||||
purgeRunning = true;
|
||||
|
||||
final databases = appwrite.Databases(auth.client);
|
||||
int offset = 0;
|
||||
const int limit = 25; // Maximum number of documents that can be fetched at once
|
||||
|
||||
while (true) {
|
||||
// Fetch a page of documents from the manual queue collection
|
||||
print("Deleting manual queue entries");
|
||||
final manual_q = await databases.listDocuments(
|
||||
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", sessionID),
|
||||
appwrite.Query.limit(limit),
|
||||
appwrite.Query.offset(offset),
|
||||
appwrite.Query.orderDesc('\$createdAt')
|
||||
]
|
||||
);
|
||||
|
||||
// If there are no documents in the fetched page, break the loop
|
||||
if (manual_q.documents.isEmpty) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Delete each document in the fetched page
|
||||
for (models.Document doc in manual_q.documents) {
|
||||
await databases.deleteDocument(
|
||||
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||
documentId: doc.$id,
|
||||
);
|
||||
}
|
||||
|
||||
// Go to the next page
|
||||
offset += limit;
|
||||
}
|
||||
|
||||
print("Deleted all manual queue entries");
|
||||
}
|
||||
|
||||
void pullQueue() async {
|
||||
|
||||
if (auth.status == AuthStatus.UNAUTHENTICATED) {
|
||||
return;
|
||||
}
|
||||
|
||||
final databases = appwrite.Databases(auth.client);
|
||||
|
||||
// Pull the manual queue
|
||||
final manual_q = await databases.listDocuments(
|
||||
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||
collectionId: ApiConstants.MANUAL_Q_COLLECTION_ID,
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", sessionID),
|
||||
appwrite.Query.limit(25),
|
||||
appwrite.Query.offset(0),
|
||||
appwrite.Query.orderDesc('\$createdAt')
|
||||
]
|
||||
);
|
||||
|
||||
List<AnnouncementQueueEntry> queue = [];
|
||||
|
||||
for (models.Document doc in manual_q.documents) {
|
||||
int index = doc.data['ManualAnnouncementIndex'];
|
||||
|
||||
ManualAnnouncementEntry announcement_clone = ManualAnnouncementEntry(
|
||||
shortName: manualAnnouncements[index].shortName,
|
||||
informationText: manualAnnouncements[index].displayText,
|
||||
audioSources: manualAnnouncements[index].audioSources,
|
||||
timestamp: DateTime.parse(doc.$createdAt),
|
||||
sendToServer: false,
|
||||
);
|
||||
|
||||
// sort the queue by timestamp, so the oldest announcements are at the front
|
||||
|
||||
|
||||
|
||||
queue.add(announcement_clone);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
for (AnnouncementQueueEntry entry in queue) {
|
||||
queueAnnouncement(entry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
appwrite.RealtimeSubscription? manual_q_subscription;
|
||||
Future<void> setupRealtime() async {
|
||||
|
||||
if (manual_q_subscription != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// await deleteAllManualQueueEntries(); //todo
|
||||
|
||||
print("Setting up realtime");
|
||||
|
||||
// Websocket
|
||||
appwrite.Realtime realtime = appwrite.Realtime(auth.client);
|
||||
|
||||
manual_q_subscription = realtime.subscribe(
|
||||
['databases.${ApiConstants.INFO_Q_DATABASE_ID}.collections.${ApiConstants.MANUAL_Q_COLLECTION_ID}.documents'],
|
||||
);
|
||||
manual_q_subscription?.stream.listen((event) {
|
||||
print("Manual queue entry added");
|
||||
|
||||
pullQueue();
|
||||
});
|
||||
|
||||
print("Subscribed to servers");
|
||||
|
||||
await Future.delayed(Duration(seconds: 90));
|
||||
|
||||
manual_q_subscription?.close();
|
||||
manual_q_subscription = null;
|
||||
setupRealtime();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AnnouncementQueueEntry {
|
||||
final String displayText;
|
||||
final List<AudioWrapperSource> audioSources;
|
||||
bool sendToServer = true;
|
||||
DateTime? timestamp;
|
||||
|
||||
AnnouncementQueueEntry({required this.displayText, required this.audioSources});
|
||||
AnnouncementQueueEntry({required this.displayText, required this.audioSources, this.sendToServer = true, this.timestamp});
|
||||
}
|
||||
|
||||
class ManualAnnouncementEntry extends AnnouncementQueueEntry {
|
||||
final String shortName;
|
||||
|
||||
ManualAnnouncementEntry({required this.shortName, required String informationText, required List<AudioWrapperSource> audioSources}) : super(displayText: informationText, audioSources: audioSources);
|
||||
ManualAnnouncementEntry({
|
||||
required this.shortName,
|
||||
required String informationText,
|
||||
required List<AudioWrapperSource> audioSources,
|
||||
DateTime? timestamp,
|
||||
bool sendToServer = true,
|
||||
}) : super(
|
||||
displayText: informationText,
|
||||
audioSources: audioSources,
|
||||
sendToServer: sendToServer,
|
||||
timestamp: timestamp,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user