360 lines
10 KiB
C++
Executable File
360 lines
10 KiB
C++
Executable File
/**
|
|
* Communication with A/V system
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
#include "av_control.h"
|
|
#include <QTimer>
|
|
#include <QList>
|
|
#include <time.h>
|
|
|
|
// TODO: on reconnect operatingmode abgleichen!
|
|
|
|
namespace AudioVideoManagement
|
|
{
|
|
|
|
AVControl::AVControl(const QString &avsServerHost, quint16 avsServerPort, QObject *parent)
|
|
: AVSocket(avsServerHost, avsServerPort, getLogger("AVControl"), getLogger("AVControl.IO"), parent)
|
|
, m_basicOperatingMode(BasicOperatingMode::PowerOn)
|
|
, m_bWaitingForSwitchContext(false)
|
|
, m_keepAliveTime(-1)
|
|
, m_keepAliveTimer(new QTimer(this))
|
|
{
|
|
m_keepAliveTimer->setSingleShot(false);
|
|
m_keepAliveTimer->setInterval(2000);
|
|
connect(m_keepAliveTimer, &QTimer::timeout,
|
|
this, &AVControl::onCheckKeepAlive);
|
|
}
|
|
|
|
AVControl::~AVControl()
|
|
{
|
|
delete m_keepAliveTimer;
|
|
}
|
|
|
|
bool AVControl::triggerAVUpdate()
|
|
{
|
|
DEBUG_LOG(m_logger) << "triggerAVUpdate ...";
|
|
// switching to init triggers a reload of the database
|
|
return setOperatingMode(ExerciseContext::defaultContext(), Component::AudioAndVideo, OperatingMode::Initialization) &&
|
|
setOperatingMode(ExerciseContext::defaultContext(), Component::AudioAndVideo, OperatingMode::PowerOn);
|
|
}
|
|
|
|
bool AVControl::setOperatingModeForContext(const ExerciseContext &context, OperatingMode om)
|
|
{
|
|
if (!m_bSuccessfullyLoggedIn)
|
|
return false;
|
|
|
|
return setOperatingMode(context, Component::AudioAndVideo, om);
|
|
}
|
|
|
|
void AVControl::onReadyRead()
|
|
{
|
|
static const QByteArray baGreeter("RCP/AVE 1.0");
|
|
while (m_avsSocket->canReadLine())
|
|
{
|
|
const QByteArray line = m_avsSocket->readLine().trimmed();
|
|
if (line.isEmpty())
|
|
continue;
|
|
DEBUG_LOG(m_loggerIO) << "From A/V: '" << line << "'";
|
|
if (line == baGreeter)
|
|
onRcpLogin();
|
|
else
|
|
{
|
|
const auto baList = parseLine(line);
|
|
bool bOk;
|
|
const Commands cmdId = static_cast<Commands>(baList.at(0).toInt(&bOk));
|
|
if (!bOk)
|
|
{
|
|
ERROR_LOG(m_logger) << "Error parsing command: '" << line << "'";
|
|
continue;
|
|
}
|
|
switch (cmdId)
|
|
{
|
|
case Commands::WrongSessionPriority:
|
|
// Try relogin
|
|
m_bSuccessfullyLoggedIn = false;
|
|
m_rcpLoginTimer->start();
|
|
break;
|
|
case Commands::Ok:
|
|
sendNextPendingCommand();
|
|
break;
|
|
case Commands::SessionDisabled:
|
|
// Try relogin
|
|
m_bSuccessfullyLoggedIn = false;
|
|
m_rcpLoginTimer->start();
|
|
break;
|
|
case Commands::SessionEnabled:
|
|
m_bSuccessfullyLoggedIn = true;
|
|
onSendKeepAlive();
|
|
trySendCommand(boost::none, "EnableFMServer");
|
|
emit successfullyLoggedIn();
|
|
break;
|
|
case Commands::Context:
|
|
if (baList.size() >= 2)
|
|
{
|
|
m_currentContext = ExerciseContext::fromByteArray(baList.at(1));
|
|
m_bWaitingForSwitchContext = false;
|
|
sendNextPendingCommand();
|
|
}
|
|
break;
|
|
case Commands::BasicOperatingMode:
|
|
if (baList.size() >= 2)
|
|
{
|
|
const BasicOperatingMode mode = static_cast<BasicOperatingMode>(baList.at(1).toInt(&bOk));
|
|
if (!bOk)
|
|
{
|
|
ERROR_LOG(m_logger) << "Error parsing mode: '" << line << "'";
|
|
break;
|
|
}
|
|
DEBUG_LOG(m_logger) << "Current " << mode;
|
|
checkBasicOperatingMode(mode);
|
|
}
|
|
else
|
|
ERROR_LOG(m_logger) << "Invalid arguments to BasicOperatingMode: '" << line << "'";
|
|
break;
|
|
case Commands::ComponentBasicOperatingMode:
|
|
break; // nothing to do
|
|
case Commands::OperatingMode:
|
|
if (baList.size() >= 3)
|
|
{
|
|
const Component comp = static_cast<Component>(baList.at(1).toInt(&bOk));
|
|
if (!bOk)
|
|
{
|
|
ERROR_LOG(m_logger) << "Error parsing component: '" << line << "'";
|
|
break;
|
|
}
|
|
const OperatingMode mode = static_cast<OperatingMode>(baList.at(2).toInt(&bOk));
|
|
if (!bOk)
|
|
{
|
|
ERROR_LOG(m_logger) << "Error parsing mode: '" << line << "'";
|
|
break;
|
|
}
|
|
emit operatingModeChanged(*m_currentContext, comp, mode);
|
|
}
|
|
else
|
|
ERROR_LOG(m_logger) << "Invalid arguments to OperatingMode: '" << line << "'";
|
|
break;
|
|
case Commands::FMServerState:
|
|
// TODO!
|
|
break;
|
|
case Commands::KeppAlive:
|
|
m_keepAliveTime = time(nullptr);
|
|
break;
|
|
case Commands::WrongCommand:
|
|
case Commands::BadArguments:
|
|
case Commands::CommandTooLong:
|
|
case Commands::CommandNotAllowed:
|
|
case Commands::CommandNotImplemented:
|
|
case Commands::CommandNotStateValid:
|
|
{
|
|
ERROR_LOG(m_logger) << "Sent wrong command to A/V";
|
|
QByteArray commands;
|
|
while (!m_commandQueueForErrorHandling.empty())
|
|
{
|
|
commands += m_commandQueueForErrorHandling.front() + "\n";
|
|
m_commandQueueForErrorHandling.pop();
|
|
}
|
|
if (!commands.isEmpty())
|
|
{
|
|
ERROR_LOG(m_logger) << "Last " << m_commandQueueForErrorHandling.size()
|
|
<< " commands: " << commands;
|
|
}
|
|
break;
|
|
}
|
|
case Commands::InternalError:
|
|
ERROR_LOG(m_logger) << "Internal A/V error: '" << line << "'";
|
|
break;
|
|
case Commands::InvalidRCPVersion:
|
|
ERROR_LOG(m_logger) << "Invalid RCP-Version: '" << line << "'";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AVControl::onSendKeepAlive()
|
|
{
|
|
if (!m_bSuccessfullyLoggedIn)
|
|
return;
|
|
if (!sendInternal("KEEPALIVE"))
|
|
return;
|
|
m_keepAliveTime = time(nullptr);
|
|
m_keepAliveTimer->start();
|
|
}
|
|
|
|
void AVControl::onCheckKeepAlive()
|
|
{
|
|
if (!m_bSuccessfullyLoggedIn)
|
|
return;
|
|
// 3 Sekunden
|
|
if (m_keepAliveTime + 3 < time(nullptr))
|
|
{
|
|
WARNING_LOG(m_logger) << "No KeepAlive from A/V since more than 3 seconds - reconnecting";
|
|
m_avsSocket->disconnectFromHost();
|
|
return;
|
|
}
|
|
onSendKeepAlive();
|
|
}
|
|
|
|
bool AVControl::trySendCommand(const boost::optional<ExerciseContext> &ctx, const QByteArray &cmd)
|
|
{
|
|
if (!m_avsSocket || !m_avsSocket->isWritable())
|
|
{
|
|
WARNING_LOG(m_logger) << "Trying to send command but not connected to AVS";
|
|
return false;
|
|
}
|
|
|
|
const bool pendingCommandsInQueue = m_commandsToSend.size() != 0; // do not use empty() here!
|
|
//DEBUG_LOG(m_logger) << "Trying to send command for context " << ctx
|
|
// << ": '" << cmd
|
|
// << "' - waiting for other commands: "
|
|
// << m_commandsToSend.size()
|
|
// << ", " << !pendingCommandsInQueue;
|
|
m_commandsToSend.push({ctx, cmd});
|
|
if (!pendingCommandsInQueue)
|
|
sendNextPendingCommand();
|
|
|
|
return true;
|
|
}
|
|
|
|
void AVControl::sendNextPendingCommand()
|
|
{
|
|
if (!m_bSuccessfullyLoggedIn ||
|
|
m_bWaitingForSwitchContext ||
|
|
m_commandsToSend.empty())
|
|
{
|
|
//DEBUG_LOG(m_logger) << "Nothing to send or not connected: "
|
|
// << m_bSuccessfullyLoggedIn << ", "
|
|
// << m_bWaitingForSwitchContext << ", "
|
|
// << m_commandsToSend.empty() << ", "
|
|
// ;
|
|
return;
|
|
}
|
|
|
|
const CommandToSend &cts = m_commandsToSend.front();
|
|
// if a context is required and does not match -> switch context first
|
|
if (cts.ctx && cts.ctx != m_currentContext)
|
|
{
|
|
if (sendInternal("SwitchContext " + cts.ctx->toByteArray()))
|
|
m_bWaitingForSwitchContext = true;
|
|
return;
|
|
}
|
|
|
|
if (sendInternal(cts.cmd))
|
|
m_commandsToSend.pop();
|
|
}
|
|
|
|
void AVControl::checkBasicOperatingMode(BasicOperatingMode mode)
|
|
{
|
|
if (!m_bSuccessfullyLoggedIn)
|
|
return;
|
|
INFO_LOG(m_logger) << " Is: " << mode
|
|
<< " - should be: " << m_basicOperatingMode;
|
|
if (mode != m_basicOperatingMode)
|
|
trySendCommand(boost::none, "SetBasicOperatingMode " + QByteArray::number(static_cast<int>(m_basicOperatingMode)));
|
|
}
|
|
|
|
bool AVControl::setOperatingMode(const ExerciseContext &context, Component component, OperatingMode mode)
|
|
{
|
|
if (!m_bSuccessfullyLoggedIn)
|
|
return false;
|
|
|
|
INFO_LOG(m_logger) << "Setting OperatingMode for " << component
|
|
<< " in context " << context
|
|
<< " to: " << mode;
|
|
trySendCommand(context, "SetOperatingMode " + QByteArray::number(static_cast<int>(component)) +
|
|
" " + QByteArray::number(static_cast<int>(mode)));
|
|
|
|
return true;
|
|
}
|
|
|
|
std::ostream &operator <<(std::ostream &stream, AVControl::BasicOperatingMode bom)
|
|
{
|
|
stream << "BasicOperatingMode ";
|
|
switch (bom)
|
|
{
|
|
case AVControl::BasicOperatingMode::Unknown:
|
|
stream << "Unknown";
|
|
break;
|
|
case AVControl::BasicOperatingMode::Reset:
|
|
stream << "Reset";
|
|
break;
|
|
case AVControl::BasicOperatingMode::Shutdown:
|
|
stream << "Shutdown";
|
|
break;
|
|
case AVControl::BasicOperatingMode::Service:
|
|
stream << "Service";
|
|
break;
|
|
case AVControl::BasicOperatingMode::PowerOn:
|
|
stream << " PowerOn";
|
|
break;
|
|
}
|
|
|
|
stream << " (" << static_cast<int>(bom) << ")";
|
|
return stream;
|
|
}
|
|
|
|
std::ostream &operator <<(std::ostream &stream, AVControl::OperatingMode om)
|
|
{
|
|
stream << "OperatingMode ";
|
|
switch (om)
|
|
{
|
|
case AVControl::OperatingMode::Unknown:
|
|
stream << "Unknown operating mode";
|
|
break;
|
|
case AVControl::OperatingMode::PowerOn:
|
|
stream << "PowerOn";
|
|
break;
|
|
case AVControl::OperatingMode::Initialization:
|
|
stream << "Initialization";
|
|
break;
|
|
case AVControl::OperatingMode::Recording:
|
|
stream << "Recording";
|
|
break;
|
|
case AVControl::OperatingMode::Pause:
|
|
stream << "Pause";
|
|
break;
|
|
case AVControl::OperatingMode::SaveData:
|
|
stream << "SaveData";
|
|
break;
|
|
case AVControl::OperatingMode::Cleanup:
|
|
stream << "Cleanup";
|
|
break;
|
|
}
|
|
stream << " (" << static_cast<int>(om) << ")";
|
|
return stream;
|
|
}
|
|
|
|
std::ostream &operator <<(std::ostream &stream, AVControl::Commands cmd)
|
|
{
|
|
stream << "Commands: " << static_cast<int>(cmd);
|
|
return stream;
|
|
}
|
|
|
|
std::ostream &operator <<(std::ostream &stream, AVControl::Component comp)
|
|
{
|
|
stream << "Component ";
|
|
switch (comp)
|
|
{
|
|
case AVControl::Component::Audio:
|
|
stream << "Audio";
|
|
break;
|
|
case AVControl::Component::Video:
|
|
stream << "Video";
|
|
break;
|
|
case AVControl::Component::AudioAndVideo:
|
|
stream << "Audio+Video";
|
|
break;
|
|
case AVControl::Component::Unknown:
|
|
break;
|
|
}
|
|
stream << " (" << static_cast<int>(comp) << ")";
|
|
|
|
return stream;
|
|
}
|
|
|
|
|
|
} // namespace AudioVideoManagement
|