Files
qt5-legatus-legacy/Lts/Services/AudioVideoManagement/ava_control.h
T
2026-08-13 18:33:10 +02:00

314 lines
13 KiB
C++
Executable File

/**
* @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 <LtsUtilsCore/connection.h>
#include <QString>
#include <QVector>
#include <QObject>
#include <chrono>
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<class Target, class Slot>
void setVideoProfile(const QString &dialogId, const QString &menuDef, Target *target, Slot Target:: *slot);
void setVideoProfile(const QString &dialogId, const QString &menuDef);
template<class Target, class Slot>
void setRtProfile(RtState *rtState, const QVector<RtState::RadioCircuit> &radioCircuits, Target *target, Slot Target:: *slot);
void setRtProfile(RtState *rtState, const QVector<RtState::RadioCircuit> &radioCircuits);
template<class Target, class Slot>
void changeRtProfileCircuitName(RtState *rtState, const RtState::RadioCircuit &radioCircuit, Target *target, Slot Target:: *slot);
void changeRtProfileCircuitName(RtState *rtState, const RtState::RadioCircuit &radioCircuit);
template<class Target, class Slot>
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<class Target, class Slot>
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<class Target, class Slot>
void queryTimeline(const QString &streamName, Target *target, Slot Target:: *slot, const boost::optional<AvaTimePoint> &start = {}, const boost::optional<AvaTimePoint> &end = {});
void queryTimeline(const QString &streamName, const boost::optional<AvaTimePoint> &start = {}, const boost::optional<AvaTimePoint> &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<class Target, class Slot>
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<class Target, class Slot>
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<AvaScriptEntry> &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<AvaStream> &streams);
void queryTimelineResponse(const AvaTimeline &timeline);
void getElokaListResponse(const QVector<QString> &elokaSignals);
void queryElokaResponse(const QVector<AvaEloka> &eloka);
void setRtProfileResponse(const QVector<QString> &invalidNameList);
void setAnnouncementProfileResponse(const QVector<QString> &invalidNameList);
void setVideoProfileResponse(const QVector<QString> &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<AvaTimePoint> &start = {}, const boost::optional<AvaTimePoint> &end = {});
void getElokaList(const QString &streamName, LtsUtils::IConnection *connection);
void queryEloka(AvaQueryElokaMode mode, LtsUtils::IConnection *connection);
void setRtProfile(RtState *rtState, const QVector<RtState::RadioCircuit> &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<class Target, class Slot>
void AvaControl::getStreamList(AvaStreamType streamType, AvaStreamSource streamSource, Target *target, Slot Target:: *slot)
{
getStreamList(streamType, streamSource, LtsUtils::createConnection(this, &AvaControl::getStreamListResponse, target, slot));
}
template<class Target, class Slot>
void AvaControl::queryTimeline(const QString &streamName, Target *target, Slot Target:: *slot, const boost::optional<AvaTimePoint> &start, const boost::optional<AvaTimePoint> &end)
{
queryTimeline(streamName, LtsUtils::createConnection(this, &AvaControl::queryTimelineResponse, target, slot), start, end);
}
template<class Target, class Slot>
void AvaControl::getElokaList(const QString &streamName, Target *target, Slot Target:: *slot)
{
getElokaList(streamName, LtsUtils::createConnection(this, &AvaControl::getElokaListResponse, target, slot));
}
template<class Target, class Slot>
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<class Target, class Slot>
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<class Target, class Slot>
void AvaControl::setRtProfile(RtState *rtState, const QVector<RtState::RadioCircuit> &radioCircuits, Target *target, Slot Target:: *slot)
{
setRtProfile(rtState, radioCircuits, LtsUtils::createConnection(this, &AvaControl::setRtProfileResponse, target, slot));
}
template<class Target, class Slot>
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<class Target, class Slot>
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