/** * @RDE_HEADER@ * AvaControl * $Id$ * $HeadURL$ * $Author$ * Created by Christian Fröbel, SLSE, 2017-06-26 */ #pragma once #include "iava_command_done_observer.h" #include "avm_export.h" #include "ava_types.h" #include "rt_state.h" #include "avs_command_queue.h" #include "logger.h" #include #include #include #include #include class QStringList; namespace AudioVideoManagement { class IAvaComm; class ICommand; class StreamNameMapping; namespace AvaCommands { class ChooseStream; class GetStreamList; class QueryTimeline; class GetElokaList; class QueryEloka; class SetAnnouncementProfile; class SetVideoProfile; } /** * AvaControl sits ontop of AvaComm. * For the communication with the AVS that must be exactly one AvaComm * and one AvaControl instance. * AvaControl uses a command queue and ensures that each command is sent after * the previous command finished. * The definition of when a command is finished is defined by the command itself. * Most of the commands are SimpleCommands which only wait for the OK-response. * * For those commands that have a data-response -- such as getStreamList -- * a signal is provided that will be emitted when the command finished. * * Commands being sent while no connection is established will be dropped. * When the connection gets lost, all Commands still in the queue will be dropped. * When a command fails, all remaining commands will be dropped and the connection * to the AVS will be closed and established again. * * A command must complete within a defined time frame. If it does not, * the command is considered failed with the implications described above. * * There are two ways to get responses that contain data -- such as the result of * getStreamList or queryTimeline: * 1. Connect to the corresponding signals in the usual way. The problem with this * approach arises when there are several clients using the AvaControl-instance * at the same time. Then you receive all responses no matter who * initiated the corresponding command. * 2. You can specify an optional slot when issuing the command. * This slot will be called when the command is finished. This way you only receive * responses that correspond to the command you initiated. * * @see AvaComm * @see AvaSession * @see ICommand * @author Christian Fröbel */ class AVM_COMMON_EXPORT AvaControl : public QObject, public IAvaCommandDoneObserver { Q_OBJECT public: AvaControl(IAvaComm *comm, StreamNameMapping *streamNameMapping, std::chrono::milliseconds commandTimeout); void switchContext(int exerciseId); void setUserProfile(const QString &userName, const QString &langCode, AvaAudioPrio, AvaGuiStyle); void setTimezone(QChar tz); void setVideoDialogProfile(const QString &dialogId, AvaVisibility); void setVideoDialogGeometry(const QString &dialogId, const QRect &geometry); template void setVideoProfile(const QString &dialogId, const QString &menuDef, Target *target, Slot Target:: *slot); void setVideoProfile(const QString &dialogId, const QString &menuDef); template void setRtProfile(RtState *rtState, const QVector &radioCircuits, Target *target, Slot Target:: *slot); void setRtProfile(RtState *rtState, const QVector &radioCircuits); template void changeRtProfileCircuitName(RtState *rtState, const RtState::RadioCircuit &radioCircuit, Target *target, Slot Target:: *slot); void changeRtProfileCircuitName(RtState *rtState, const RtState::RadioCircuit &radioCircuit); template void setAnnouncementProfile(RtState *rtState, const QString &menuDef, const QString &identityList, const QString &stateList, Target *target, Slot Target:: *slot); void setAnnouncementProfile(RtState *rtState, const QString &menuDef, const QString &identityList, const QString &stateList); void chooseImport(const QString &dialogId, const QString &path, int mode); void releaseImport(const QString &dialogId); /** * @param slot must be a pointer to a method of Target and must be compatible with * the getStreamListResponse()-signal. */ template void getStreamList(AvaStreamType, AvaStreamSource, Target *target, Slot Target:: *slot); void getStreamList(AvaStreamType, AvaStreamSource); void chooseStream(const QString &dialogId, const QString &streamName); void chooseStreamOverview(const QString &dialogId, const QStringList &streamNames); void releaseStream(const QString &dialogId, const QString &streamName); /** * @param slot must be a pointer to a method of Target and must be compatible with * the queryTimelineResponse()-signal. */ template void queryTimeline(const QString &streamName, Target *target, Slot Target:: *slot, const boost::optional &start = {}, const boost::optional &end = {}); void queryTimeline(const QString &streamName, const boost::optional &start = {}, const boost::optional &end = {}); void setVolume(int db); /** streamControl */ ///@{ void startStream(const QString &dialogId, const QString &streamName, AvaSpeed); void stopStream(const QString &dialogId, const QString &streamName); void pauseStream(const QString &dialogId, const QString &streamName); void setStreamSpeed(const QString &dialogId, const QString &streamName, AvaSpeed); void setAbsPosition(const QString &dialogId, const QString &streamName, AvaTimePoint pos); void positionUpdate(const QString &dialogId, const QString &streamName, std::chrono::milliseconds updateInterval); void setPint(const QString &dialogId, const QString &streamName, bool doSkip); ///@} /** ELOKA */ ///@{ /** * @param slot must be a pointer to a method of Target and must be compatible with * the getElokaListResponse()-signal. */ template void getElokaList(const QString &streamName, Target *target, Slot Target:: *slot); void getElokaList(const QString &streamName); void chooseEloka(const QString &streamName, const QString &signalName, AvaElokaMode mode); void releaseEloka(const QString &streamName); template void queryEloka(AvaQueryElokaMode mode, Target *target, Slot Target:: *slot); void queryEloka(AvaQueryElokaMode mode); ///@} /** SCRIPT */ ///@{ void setScript(const QString &dialogId, const QString &scriptName, AvaScriptSource, const QVector &streams, LtsUtils::IConnection *connection = nullptr); void startScript(const QString &dialogId, const QString &scriptName, LtsUtils::IConnection *connection = nullptr); void releaseScript(const QString &dialogId, const QString &scriptName); ///@} /** scriptControl */ ///@{ void stopScript(const QString &dialogId, const QString &scriptName); void pauseScript(const QString &dialogId, const QString &scriptName); void setAbsPositionScript(const QString &dialogId, const QString &scriptName, AvaTimePoint pos); ///@} /** StreamNameMapping */ ///@{ void defineStreamNameMapping(const QString &internalName, const QString &displayName); void resetStreamNameMapping(); ///@} void keepAlive(); /** * These overloaded done() methods will be called by the specific * commands themselves upon completion. */ ///@{ void done(ICommand *cmd) override; void done(AvaCommands::ChooseStream *cmd) override; void done(AvaCommands::GetStreamList *cmd) override; void done(AvaCommands::QueryTimeline *cmd) override; void done(AvaCommands::GetElokaList *cmd) override; void done(AvaCommands::QueryEloka *cmd) override; void done(AvaCommands::SetRtProfileBase *cmd) override; void done(AvaCommands::SetAnnouncementProfile *cmd) override; void done(AvaCommands::SetVideoProfile *cmd) override; void done(AvaCommands::SetScript *cmd) override; void done(AvaCommands::StartScript *cmd) override; /** * Called whenever a command fails due error response or other unexpected responses. * All queued commands will be dropped and the connection to AVS will * be disconnected and re-established. */ void failed(AvaCommands::SetScript *cmd); void failed(ICommand *cmd) override; ///@} void alive(ICommand *cmd) override; /** * Access to the underlying AvaComm-instance. * @return never nullptr */ IAvaComm *comm() { return m_comm; } bool isEmpty() const { return m_queue.isEmpty(); } signals: void chooseStreamResponse(const AvaStreamInfo &streamInfo, AvaStreamState); void getStreamListResponse(const QVector &streams); void queryTimelineResponse(const AvaTimeline &timeline); void getElokaListResponse(const QVector &elokaSignals); void queryElokaResponse(const QVector &eloka); void setRtProfileResponse(const QVector &invalidNameList); void setAnnouncementProfileResponse(const QVector &invalidNameList); void setVideoProfileResponse(const QVector &invalidNameList); void setScriptResponse(const AvaStreamInfo &scriptInfo, AvaStreamState ScriptState); void startScriptResponse( AvaStreamState ScriptState); /** * Signalled whenever the command queue is empty again. This can have several reasons: * - The last command finished successfully. * - A command failed and the command queue was flushed. * - This instance is deleted with a non-empty command queue which gets flushed then. */ void commandQueueEmpty(); private slots: void onConnected(); void onDisconnected(); void onTimeout(); private: void getStreamList(AvaStreamType, AvaStreamSource, LtsUtils::IConnection *connection); void queryTimeline(const QString &streamName, LtsUtils::IConnection *connection, const boost::optional &start = {}, const boost::optional &end = {}); void getElokaList(const QString &streamName, LtsUtils::IConnection *connection); void queryEloka(AvaQueryElokaMode mode, LtsUtils::IConnection *connection); void setRtProfile(RtState *rtState, const QVector &radioCircuits, LtsUtils::IConnection *connection); void changeRtProfileCircuitName(RtState *rtState, const RtState::RadioCircuit &radioCircuit, LtsUtils::IConnection *connection); void setAnnouncementProfile(RtState *rtState, const QString &menuDef, const QString &identityList, const QString &stateList, LtsUtils::IConnection *connection); void setVideoProfile(const QString &dialogId, const QString &menuDef, LtsUtils::IConnection *connection); void queue(ICommand *cmd, LtsUtils::IConnection *connection = nullptr); IAvaComm *m_comm; StreamNameMapping *m_streamNameMapping; bool m_connected{false}; AvsCommandQueue m_queue; LOGGER_T m_logger; }; template void AvaControl::getStreamList(AvaStreamType streamType, AvaStreamSource streamSource, Target *target, Slot Target:: *slot) { getStreamList(streamType, streamSource, LtsUtils::createConnection(this, &AvaControl::getStreamListResponse, target, slot)); } template void AvaControl::queryTimeline(const QString &streamName, Target *target, Slot Target:: *slot, const boost::optional &start, const boost::optional &end) { queryTimeline(streamName, LtsUtils::createConnection(this, &AvaControl::queryTimelineResponse, target, slot), start, end); } template void AvaControl::getElokaList(const QString &streamName, Target *target, Slot Target:: *slot) { getElokaList(streamName, LtsUtils::createConnection(this, &AvaControl::getElokaListResponse, target, slot)); } template void AvaControl::queryEloka(AvaQueryElokaMode mode, Target *target, Slot Target:: *slot) { Q_ASSERT(mode != AvaQueryElokaMode::Stop); queryEloka(mode, LtsUtils::createConnection(this, &AvaControl::queryElokaResponse, target, slot)); } template void AvaControl::setVideoProfile(const QString &dialogId, const QString &menuDef, Target *target, Slot Target:: *slot) { setVideoProfile(dialogId, menuDef, LtsUtils::createConnection(this, &AvaControl::setVideoProfileResponse, target, slot)); } template void AvaControl::setRtProfile(RtState *rtState, const QVector &radioCircuits, Target *target, Slot Target:: *slot) { setRtProfile(rtState, radioCircuits, LtsUtils::createConnection(this, &AvaControl::setRtProfileResponse, target, slot)); } template void AvaControl::changeRtProfileCircuitName(RtState *rtState, const RtState::RadioCircuit &radioCircuit, Target *target, Slot Target:: *slot) { changeRtProfileCircuitName(rtState, radioCircuit, LtsUtils::createConnection(this, &AvaControl::setRtProfileResponse, target, slot)); } template void AvaControl::setAnnouncementProfile(RtState *rtState, const QString &menuDef, const QString &identityList, const QString &stateList, Target *target, Slot Target:: *slot) { setAnnouncementProfile(rtState, menuDef, identityList, stateList, LtsUtils::createConnection(this, &AvaControl::setAnnouncementProfileResponse, target, slot)); } } // namespace AudioVideoManagement