#ifndef FILEPLAYER_H #define FILEPLAYER_H #include "FileReader.h" #include namespace Offline { /** * this class can be used to "play" previously recorded [so-called "offline"] files. * one can attach itself as listener and is informed whenever new sensor data is available. * one may choose whether to use full-speed playback [as many events as possible] or * live-speed playback with the same timing the values were recorded with */ class FilePlayer { private: FileReader* reader; bool realtime = false; bool enabled; std::thread thread; /** the listener to inform */ Listener* listener = nullptr; public: /** empty ctor */ FilePlayer() : reader(nullptr), listener(nullptr) { ; } /** ctor */ FilePlayer(FileReader* reader, Listener* l) : reader(reader), listener(l) { ; } /** whether to use realtime playback or "as fast as possible" */ void setRealtime(const bool rt) { this->realtime = rt; } /** set the offline-file-reader to use as data source */ void setReader(FileReader* r) { this->reader = r; } /** set the event listener to inform */ void setListener(Listener* l) { this->listener = l; } /** start playback */ void start() { // sanity check Assert::isNotNull(reader, "call FilePlayer::setReader() first"); Assert::isNotNull(listener, "call FilePlayer::setListener() first"); Assert::isFalse(reader->getEntries().empty(), "FileReader has no loaded entries for playback within the FilePlayer!"); enabled = true; thread = std::thread(&FilePlayer::loop, this); } /** stop playback */ void stop() { enabled = false; } /** wait for termination */ void join() { thread.join(); } private: /** background loop */ void loop() { // get all sensor events from the offline file const std::vector events = reader->getEntries(); // reference time (system vs. first-event) Timestamp tsRef1 = Timestamp::fromMS(events.front().ts); Timestamp tsRef2 = Timestamp::fromRunningTime(); // process every event for (const Entry& e : events) { // aborted? if (!enabled) {break;} // timestamp const Timestamp ts = Timestamp::fromMS(e.ts); // ensure events happen occur fast as they did during recording if (realtime) { const Timestamp ts1 = ts-tsRef1; const Timestamp ts2 = Timestamp::fromRunningTime() - tsRef2; const Timestamp diff = ts1-ts2; if (diff.ms() > 0) {std::this_thread::sleep_for(std::chrono::milliseconds(diff.ms()));} } // event index const size_t idx = e.idx; #warning "some sensors todo:" switch(e.type) { case Sensor::ACC: listener->onAccelerometer(ts, reader->getAccelerometer()[idx].data); break; case Sensor::BARO: listener->onBarometer(ts, reader->getBarometer()[idx].data); break; case Sensor::BEACON: break;//listener->onBe(ts, reader->getBarometer()[idx].data); break; case Sensor::COMPASS: listener->onCompass(ts, reader->getCompass()[idx].data); break; case Sensor::GPS: listener->onGPS(ts, reader->getGPS()[idx].data); break; case Sensor::GRAVITY: listener->onGravity(ts, reader->getGravity()[idx].data); break; case Sensor::GYRO: listener->onGyroscope(ts, reader->getGyroscope()[idx].data); break; case Sensor::LIN_ACC: break;//listener->on(ts, reader->getBarometer()[idx].data); break; case Sensor::WIFI: listener->onWiFi(ts, reader->getWiFiGroupedByTime()[idx].data); break; default: throw Exception("code error. found not-yet-implemented sensor"); } } // done enabled = false; } }; } #endif // FILEPLAYER_H