Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Christian Ehrlicher, SLSE, 2016-02-29
#
IF(${CMAKE_VERSION} VERSION_LESS "3.6.0")
FIND_PACKAGE(Boost REQUIRED COMPONENTS system) # system wird nicht gebraucht, aber ohne Komponente wird das Boost::boost imported Target nicht angelegt (cmake <= 3.5.2)
ELSE()
FIND_PACKAGE(Boost REQUIRED)
ENDIF()
ADD_SUBDIRECTORY(Data)
ADD_SUBDIRECTORY(Gui)
ADD_SUBDIRECTORY(Shared)
#
# Menu Edit Gui Plugin Interface
#
ADD_INTERFACE_LIBRARY(AvmMenuEditGuiPluginInterface
${CMAKE_CURRENT_SOURCE_DIR}/avm_menu_edit_gui_plugin_interface.h
${CMAKE_CURRENT_SOURCE_DIR}/..
GuiPluginInterface TopicReplayInterface TopicEditorInterface Qt5::Core AvmShared Boost::boost
)
#
# Combat Net Radio Gui Plugin Interface
#
ADD_INTERFACE_LIBRARY(AvmCombatNetRadioGuiPluginInterface
${CMAKE_CURRENT_SOURCE_DIR}/avm_combat_net_radio_gui_plugin_interface.h
${CMAKE_CURRENT_SOURCE_DIR}/..
GuiPluginInterface Qt5::Core
)
#
# Eloka Gui Plugin Interface
#
ADD_INTERFACE_LIBRARY(AvmElokaGuiPluginInterface
${CMAKE_CURRENT_SOURCE_DIR}/avm_eloka_gui_plugin_interface.h
${CMAKE_CURRENT_SOURCE_DIR}/..
GuiPluginInterface
)
#
# MVS Gui Plugin Interface
#
ADD_INTERFACE_LIBRARY(AvmMvsVideoTeamGuiPluginInterface
${CMAKE_CURRENT_SOURCE_DIR}/avm_mvs_video_team_gui_plugin_interface.h
${CMAKE_CURRENT_SOURCE_DIR}/..
GuiPluginInterface
)
#
# Data Plugin 'Interface' - needs export for moc (signals)
#
ADD_INTERFACE_LIBRARY(AvmDataPluginInterface
${CMAKE_CURRENT_SOURCE_DIR}/avm_data_plugin_interface.h
${CMAKE_CURRENT_SOURCE_DIR}/..
DataPluginInterface svcAvmCommon Qt5::Core IsmDataPluginInterface
)
+45
View File
@@ -0,0 +1,45 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Christian Ehrlicher, SLSE, 2016-02-29
#
#
# Data Plugin
#
SET(avm_SRCS
avm_data_plugin.cpp
avm_data_plugin.h
rt_profile_observer.cpp
rt_profile_observer.h
avs_session_context.cpp
avs_session_context.h
avm_exercise_context.cpp
avm_exercise_context.h
operator_settings_observer.cpp
operator_settings_observer.h
)
NGL_ADD_LIBRARY(WS AvmDataPlugin MODULE ${avm_SRCS})
NGL_TARGET_LINK_LIBRARIES(AvmDataPlugin
NGLLogger
ngltranslator
nglsettings
LtsSessionDataDds
LtsSession
LtsSessionData
AvmDataPluginInterface
svcAvmClient
AvmShared
svcOpmClient
svcExmClient
svcIsmClient
IsmDataPluginInterface
Qt5::Core
LtsUtilsGuiStyleManager
LtsUtilsGuiConnectDialog
LtsUtilsSettings
Boost::boost
)
NGL_INSTALL_TARGET(AvmDataPlugin)
+182
View File
@@ -0,0 +1,182 @@
/**
* @RDE_HEADER@
* Data Plugin for AudioVideoSystem
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-08-17
*/
#include "avm_data_plugin.h"
#include <AudioVideoManagement/Shared/plugin_settings.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/ava_session.h>
#include <AudioVideoManagement/ava_state.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/stream_name_mapping.h>
#include <AudioVideoManagement/avs_menus.h>
#include <AudioVideoManagement/avs_dialog.h>
#include <AudioVideoManagement/vd_session.h>
#include <AudioVideoManagement/Shared/avs_app.h>
#include <AudioVideoManagement/stream_name_mapping.h>
#include <OperatorManagement/opm.hpp>
#include <QMap>
#include <QPair>
#include <QtGlobal>
#include <LtsSession/lts_session_data_dds.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <StyleManager/style_manager.h>
#include <ngltranslator/ngltranslator.h>
#include <QDebug>
using namespace AudioVideoManagement;
namespace
{
AvaGuiStyle determineGuiStyle()
{
LtsUtils::StyleManager *styleManager = LtsUtils::StyleManager::getInstance();
if (styleManager->currentStyleName() == QStringLiteral("rme.dark"))
return AvaGuiStyle::Dark;
return AvaGuiStyle::Light;
}
} // anonymous namespace
namespace AudioVideoManagement
{
DataPlugin::DataPlugin()
: m_pluginSettings(new PluginSettings)
{}
DataPlugin::~DataPlugin() = default;
std::shared_ptr<DataPluginBase::Interface> DataPlugin::createDataInterface(LtsSession::LtsSessionDataDds *session)
{
Q_ASSERT(session);
int exerciseId = session->exerciseId();
return std::make_shared<DataInterfaceImpl>(session, AvmExerciseContextHandle(&m_exerciseContext, exerciseId, session), &m_avsSessionContext);
}
DataInterfaceImpl::DataInterfaceImpl(LtsSession::LtsSessionDataDds *session, AvmExerciseContextHandle &&exerciseContextHandle, AvsSessionContext *sessionContext)
: m_exerciseContextHandle(std::move(exerciseContextHandle))
, m_session(qobject_cast<LtsSession::LtsSession *>(session))
{
Q_ASSERT(session);
Q_ASSERT(session->partitionType() == IDL::PartitionType::Exercise ||
session->partitionType() == IDL::PartitionType::Replay ||
session->partitionType() == IDL::PartitionType::Global);
Q_ASSERT(sessionContext);
if (session->exerciseId() > 0 && sessionContext->canAcquire(session->exerciseId()))
{
LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance();
m_avsSessionContextHandle = AvsSessionContextHandle(sessionContext, session->exerciseId(),
sessionData->getOperatorId(),
QString::fromLatin1(NGL::translationLocale()),
determineGuiStyle(), m_exerciseContextHandle);
}
}
DataInterfaceImpl::~DataInterfaceImpl() = default;
AvaSession *DataInterfaceImpl::avaSession()
{
return m_avsSessionContextHandle.avaSession();
}
AvaState *DataInterfaceImpl::avaState()
{
return m_avsSessionContextHandle.avaState();
}
StreamNameMapping *DataInterfaceImpl::streamNameMapping()
{
return m_exerciseContextHandle.streamNameMapping();
}
AvsMenus *DataInterfaceImpl::menus()
{
return m_avsSessionContextHandle.menus();
}
VdSession *DataInterfaceImpl::vdSession()
{
return m_avsSessionContextHandle.vdSession();
}
bool DataInterfaceImpl::vdSessionEstablished() const
{
return m_avsSessionContextHandle.vdSession()->isEstablished();
};
void DataInterfaceImpl::showCamera(QWidget *irreplacableWidget, int mountingPointId)
{
if (!m_avsSessionContextHandle)
return;
// build list of irreplacable widgets by the dependency tree of its dependencies and their dependencies' dependencies
QVector<QWidget *> irreplacableWidgets {m_session->baseLayoutWidget(), irreplacableWidget};
LtsSession::LtsSession *dependency = m_session->dependency();
int runaway = 100;
while (dependency && runaway > 0)
{
irreplacableWidgets += dependency->baseLayoutWidget();
dependency = dependency->dependency();
runaway--;
}
qWarning() << "irreplacableWidgets count" << irreplacableWidgets.count() << "from" << m_session;
for (QWidget *w : irreplacableWidgets)
qWarning() << "irre" << w;
m_avsSessionContextHandle.liveVideoApp()->show(irreplacableWidgets);
QString streamName = streamNameMapping()->internalName(mountingPointId);
m_avsSessionContextHandle.avaSession()->control()->chooseStreamOverview(AvsDialog::VIDEO_1, {streamName});
}
void DataInterfaceImpl::showAnnouncementUnit(int mountingPointId)
{
if (!m_avsSessionContextHandle)
return;
m_avsSessionContextHandle.menus()->setAnnouncementUnit(mountingPointId);
}
void DataInterfaceImpl::showOrCloseAvsApp()
{
if (!m_avsSessionContextHandle)
return;
auto liveVideoApp = m_avsSessionContextHandle.liveVideoApp();
qWarning() << "showOrCloseAvsApp" << liveVideoApp->isVisible();
if (liveVideoApp->isActive())
liveVideoApp->hide(true /* and close */);
else
liveVideoApp->show();
}
} // namespace AudioVideoManagement
+108
View File
@@ -0,0 +1,108 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoSystem
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-08-17
*/
#pragma once
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <OperatorManagement/opm_dtc.h>
#include "avm_exercise_context.h"
#include "avs_session_context.h"
#include <memory>
namespace LtsSession { class LtsSession; }
namespace AudioVideoManagement
{
class AvaSession;
class AvaState;
class PlaybackApp;
class PluginSettings;
class StreamNameMapping;
class DataPlugin final : public QObject, public DataPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "data:3:avm")
Q_INTERFACES(DataPluginBase::Plugin)
Q_OBJECT
public:
DataPlugin();
~DataPlugin();
QString pluginName() const override { return QStringLiteral("AudioVideoManagement"); }
std::shared_ptr<DataPluginBase::Interface> createDataInterface(LtsSession::LtsSessionDataDds *session) override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise || partition == IDL::PartitionType::Replay || partition == IDL::PartitionType::Global; }
private:
AvmExerciseContextManager m_exerciseContext;
AvsSessionContext m_avsSessionContext;
std::unique_ptr<PluginSettings> m_pluginSettings; //!< the singleton instance
};
class DataInterfaceImpl final : public DataInterface
{
public:
DataInterfaceImpl(LtsSession::LtsSessionDataDds *session, AvmExerciseContextHandle &&exerciseContextHandle, AvsSessionContext *sessionContext);
~DataInterfaceImpl();
std::shared_ptr<AudioVideoManagement::Interface::Dtc> getDtc() override { return m_exerciseContextHandle.dtc(); }
std::shared_ptr<AudioVideoManagement::Interface::Ftc> getFtc() override { return m_exerciseContextHandle.ftc(); }
ExerciseManagement::Interface::Dtc *getExmDtc() override { return m_exerciseContextHandle.exmDtc(); }
OperatorManagement::Interface::Dtc *getOpmDtc() override { return m_exerciseContextHandle.opmDtc(); }
/**
* @name Access to the communication session with AVS_Workstation and related parts.
* The getter-methods may only be called when hasAvsSession() returns true.
* They always return a valid pointer then.
*/
///@{
bool hasAvsSession() const override { return m_avsSessionContextHandle; }
bool vdSessionEstablished() const override;
AvaSession *avaSession() override;
AvaState *avaState() override;
AvsMenus *menus() override;
PlaybackApp *videoPlaybackApp() override { return m_avsSessionContextHandle.videoPlaybackApp(); }
PlaybackApp *audioPlaybackApp() override { return m_avsSessionContextHandle.audioPlaybackApp(); }
PlaybackApp *videoPresentationApp() override { return m_avsSessionContextHandle.videoPresentationApp(); }
PlaybackApp *audioPresentationApp() override { return m_avsSessionContextHandle.audioPresentationApp(); }
///@}
/**
* @name Access to the communication session with AVS_Export.
* The getter-methods may only be called when hasAvsSession() returns true.
* They always return a valid pointer then.
*/
///@{
VdSession *vdSession() override;
///@}
/** Only available when not in global partition; must not be called otherwise. */
StreamNameMapping *streamNameMapping() override;
/** No-op when hasAvsSession() == false. */
///{@
void showCamera(QWidget *irreplacableWidget, int mountingPointId) override;
void showAnnouncementUnit(int mountingPointId) override;
void showOrCloseAvsApp() override;
///@}
private:
AvmExerciseContextHandle m_exerciseContextHandle;
AvsSessionContextHandle m_avsSessionContextHandle;
LtsSession::LtsSession *m_session;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,167 @@
/**
* @RDE_HEADER@
* AvmExerciseContext
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-07-30
*/
#include "avm_exercise_context.h"
#include <AudioVideoManagement/stream_name_mapping.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <ExerciseManagement/exm_dtc.h>
#include <OperatorManagement/opm_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <LtsSession/lts_session_data_dds.h>
#include <ConnectDialog/connect_dialog_nt.h>
#include <QObject>
#include <QtGlobal>
#include <utility>
namespace AudioVideoManagement
{
/**
* Small composite that just holds all entities bound to the Exercise context.
*/
struct AvmExerciseContextManager::ExerciseContext
{
explicit ExerciseContext(LtsSession::LtsSessionDataDds *session)
{
static const int TIMEOUT_MS = 1000;
Q_ASSERT(session);
session->createTopicClient(dtc, ftc);
opmDtc = OperatorManagement::Interface::Dtc::create(session->ddsConnectorGlobal());
exmDtc = ExerciseManagement::Interface::Dtc::create(session->ddsConnectorGlobal());
ismDtc = InfrastructureManagement::Interface::Dtc::create(session->ddsConnectorGlobal());
if (session->exerciseId() > 0)
streamNameMapping.reset(StreamNameMapping::create(dtc.get()));
opmDtc->enableUser();
opmDtc->waitForHistoricalDataUser(TIMEOUT_MS); // TODO: FIXME: solange man hier keinen ConnectingDialogNT benutzen kann, kein Assert
exmDtc->enableExercise();
exmDtc->waitForHistoricalDataExercise(TIMEOUT_MS);
ismDtc->enableMountingPointAsset();
ismDtc->waitForHistoricalDataMountingPointAsset(TIMEOUT_MS);
dtc->enableCnrConfig();
dtc->enableTvcConfig();
dtc->enableCnrTvcProfile();
dtc->enableMenuStruct();
dtc->waitForHistoricalDataCnrConfig(TIMEOUT_MS);
dtc->waitForHistoricalDataTvcConfig(TIMEOUT_MS);
dtc->waitForHistoricalDataCnrTvcProfile(TIMEOUT_MS);
dtc->waitForHistoricalDataMenuStruct(TIMEOUT_MS);
}
std::shared_ptr<Interface::Dtc> dtc;
std::shared_ptr<Interface::Ftc> ftc;
std::shared_ptr<OperatorManagement::Interface::Dtc> opmDtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc;
std::unique_ptr<StreamNameMapping> streamNameMapping;
};
AvmExerciseContextManager::RefCountedContext::RefCountedContext(std::unique_ptr<AvmExerciseContextManager::ExerciseContext> &&context)
: context(std::move(context))
{}
AvmExerciseContextManager::RefCountedContext::RefCountedContext(AvmExerciseContextManager::RefCountedContext &&) noexcept = default;
AvmExerciseContextManager::RefCountedContext::~RefCountedContext() = default;
AvmExerciseContextManager::~AvmExerciseContextManager()
{
// cfroebel 2018-07-31: Workaround for Bug REGG-4775
// All contexts should have been destroyed by now.
// But due to the bug there is one last context for Ex 0.
// Q_ASSERT(m_activeContexts.empty()); TODO: reactivate after fix
if (m_activeContexts.empty())
return;
Q_ASSERT(m_activeContexts.size() == 1);
//Q_ASSERT(m_activeContexts.begin()->first == 0); TODO: reactivate after fix
}
AvmExerciseContextManager::ExerciseContext *AvmExerciseContextManager::acquire(int exerciseId, LtsSession::LtsSessionDataDds *session)
{
auto found = m_activeContexts.find(exerciseId);
if (found != m_activeContexts.end())
{
++found->second.refCount;
return found->second.context.get();
}
std::unique_ptr<ExerciseContext> composite(new ExerciseContext(session));
auto insertResult = m_activeContexts.insert(std::make_pair(exerciseId, RefCountedContext(std::move(composite))));
Q_ASSERT(insertResult.second);
return insertResult.first->second.context.get();
}
void AvmExerciseContextManager::release(int exerciseId, AvmExerciseContextManager::ExerciseContext *composite)
{
auto found = m_activeContexts.find(exerciseId);
Q_ASSERT(found != m_activeContexts.end());
Q_ASSERT(found->second.context.get() == composite);
--found->second.refCount;
if (found->second.refCount == 0)
m_activeContexts.erase(found);
}
AvmExerciseContextHandle::AvmExerciseContextHandle() = default;
AvmExerciseContextHandle::AvmExerciseContextHandle(AvmExerciseContextManager *context, int exerciseId, LtsSession::LtsSessionDataDds *session)
: m_context(context->acquire(exerciseId, session), [exerciseId, context](AvmExerciseContextManager::ExerciseContext *composite) { context->release(exerciseId, composite); })
{
Q_ASSERT(context);
Q_ASSERT(m_context);
}
StreamNameMapping *AvmExerciseContextHandle::streamNameMapping() const
{
Q_ASSERT(m_context);
Q_ASSERT(m_context->streamNameMapping);
return m_context->streamNameMapping.get();
}
const std::shared_ptr<Interface::Dtc> &AvmExerciseContextHandle::dtc() const
{
Q_ASSERT(m_context);
return m_context->dtc;
}
const std::shared_ptr<Interface::Ftc> &AvmExerciseContextHandle::ftc() const
{
Q_ASSERT(m_context);
return m_context->ftc;
}
OperatorManagement::Interface::Dtc *AvmExerciseContextHandle::opmDtc() const
{
Q_ASSERT(m_context);
return m_context->opmDtc.get();
}
ExerciseManagement::Interface::Dtc *AvmExerciseContextHandle::exmDtc() const
{
Q_ASSERT(m_context);
return m_context->exmDtc.get();
}
InfrastructureManagement::Interface::Dtc *AvmExerciseContextHandle::ismDtc() const
{
Q_ASSERT(m_context);
return m_context->ismDtc.get();
}
} // namespace AudioVideoManagement
@@ -0,0 +1,161 @@
/**
* @RDE_HEADER@
* AvmExerciseContext
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-07-30
*/
#pragma once
#include <map>
#include <functional>
#include <memory>
namespace LtsSession
{
class LtsSessionDataDds;
}
namespace OperatorManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
class Ftc;
}
class StreamNameMapping;
/**
* For each Exercise -- as defined by its Id -- AvmExerciseContextManager manages
* an associated ExerciseContext. The ExerciseContext just holds all necessary entities.
* The idea is that any number of PluginInterface-instances with the same exerciseId
* share the same ExerciseContext.
* For example, all PluginInterface-instances for Exercise 42 can share the *same* Dtc or Ftc.
* Thus Dtc and Ftc are part of the ExerciseContext.
*
* The lifetime of an ExerciseContext can be longer than the lifetime of a PluginInterface-instance.
* AvsSessionContext uses (references) entities from the ExerciseContext.
* This is ok as it is guaranteed that the lifetime of the ExerciseContext is at least as long
* as the lifetime of AvsSessionContext.
* Actually they have the same lifetime -- if an AvsSessionContext exists.
*
* The difference is that there can be at most one AvsSessionContext at the same time.
* But there can be at most one ExerciseContext *for each* exerciseId at the same time.
* For the global exercise (exerciseId 0) there is never an AvsSessionContext.
*
* The following picuture illustrates the lifetimes:
*
* App1 (42) --------- ---------
* App2 (42) --------- ---------
* App3 (35) =========
*
* ExerciseContext (42) --------- --------------- ---------
* ExerciseContext (35) =========
*
* AvsSessionContext (42) --------- ---------------
* AvsSessionContext (35) =========
*
* PluginInterface-instance 1 (42) ---------
* PluginInterface-instance 2 (42) ---------
* PluginInterface-instance 3 (42) ---------
* PluginInterface-instance 4 (35) =========
* PluginInterface-instance 5 (42) ---------
*
* @see AvsSessionContext
* @author Christian Fröbel
*/
class AvmExerciseContextManager
{
public:
struct ExerciseContext;
~AvmExerciseContextManager();
/**
* Don't use directly; use AvmExerciseContextHandle instead.
*/
///@{
ExerciseContext *acquire(int exerciseId, LtsSession::LtsSessionDataDds *session);
void release(int exerciseId, ExerciseContext *composite);
///@}
private:
/**
* Small internal helper class in order to keep track of the refCount.
* Here, the refCount denotes the number of PluginInterface-Instances for the same Exercise.
*/
struct RefCountedContext
{
explicit RefCountedContext(std::unique_ptr<ExerciseContext> &&context);
RefCountedContext(RefCountedContext &&) noexcept;
~RefCountedContext();
int refCount{1};
std::unique_ptr<ExerciseContext> context;
};
using ExerciseId = int;
std::map<ExerciseId, RefCountedContext> m_activeContexts;
};
/**
* Helper class to automatically call AvmExerciseContextManager::acquire()
* and AvmExerciseContextManager::release() in the ctor and dtor respectively.
* Instances can be moved but not copied.
*
* @author Christian Fröbel
*/
class AvmExerciseContextHandle
{
public:
AvmExerciseContextHandle();
AvmExerciseContextHandle(AvmExerciseContextManager *context, int exerciseId, LtsSession::LtsSessionDataDds *session);
operator bool() const { return m_context.get(); }
/** Only available when not in global Exercise */
StreamNameMapping *streamNameMapping() const;
const std::shared_ptr<Interface::Dtc> &dtc() const;
const std::shared_ptr<Interface::Ftc> &ftc() const;
OperatorManagement::Interface::Dtc *opmDtc() const;
ExerciseManagement::Interface::Dtc *exmDtc() const;
InfrastructureManagement::Interface::Dtc *ismDtc() const;
private:
using Releaser = std::function<void (AvmExerciseContextManager::ExerciseContext *)>;
std::unique_ptr<AvmExerciseContextManager::ExerciseContext, Releaser> m_context;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,193 @@
/**
* @RDE_HEADER@
* AvsSessionContext
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-08-03
*/
#include "avs_session_context.h"
#include "avm_exercise_context.h"
#include "operator_settings_observer.h"
#include "rt_profile_observer.h"
#include <AudioVideoManagement/avs_menus.h>
#include <AudioVideoManagement/ava_comm.h>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/ava_session.h>
#include <AudioVideoManagement/ava_state.h>
#include <AudioVideoManagement/vd_comm.h>
#include <AudioVideoManagement/vd_control.h>
#include <AudioVideoManagement/vd_session.h>
#include <AudioVideoManagement/avs_dialog.h>
#include <AudioVideoManagement/stream_name_mapping_to_ava.h>
#include <AudioVideoManagement/Shared/avs_app.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <AudioVideoManagement/Shared/plugin_settings.h>
#include <OperatorManagement/opm_dtc.h>
#include <OperatorManagement/opm.hpp>
#include <InfrastructureManagement/ism_dtc.h>
#include <InfrastructureManagement/ism.hpp>
#include <nglsettings/nglsettings_connections.h>
#include <LtsUtilsSettings/settings.h>
#include <QObject>
#include <QtGlobal>
namespace AudioVideoManagement
{
/// AvsSessionContext
struct AvsSessionContext::Composite
{
Composite(int exerciseId_, int operatorId, const QString &langCode, AvaGuiStyle guiStyle, const AvmExerciseContextHandle &exerciseContextHandle)
: exerciseId(exerciseId_)
, operator_(exerciseContextHandle.opmDtc()->user(operatorId))
, audioPrioObserver(exerciseId_, operatorId, exerciseContextHandle.opmDtc(), exerciseContextHandle.exmDtc())
, avaComm(NGL::Connections::hostName(NGL::Connections::eConnection::AVS_WORKSTATION), NGL::Connections::port(NGL::Connections::eConnection::AVS_WORKSTATION))
, avaControl(&avaComm, exerciseContextHandle.streamNameMapping(), PluginSettings::instance()->avaTimeout())
, avaSession(&avaControl, exerciseId, QString::fromStdString(operator_.name()), langCode, operator_.audioChannelPriority(), audioPrioObserver.timezone(), guiStyle, PluginSettings::instance()->avaKeepAliveEnabled())
, avaState(&avaSession)
, streamNameMappingToAva(&avaSession, exerciseContextHandle.streamNameMapping())
, avsMenus(&avaSession, exerciseContextHandle.dtc().get(), exerciseContextHandle.ismDtc(), avaState.rtState())
, rtProfileObserver(exerciseContextHandle.dtc().get(), &avaSession, avaState.rtState(), exerciseId_, operatorId)
, vdComm(NGL::Connections::hostName(NGL::Connections::eConnection::AVS_WORKSTATION_VD), NGL::Connections::port(NGL::Connections::eConnection::AVS_WORKSTATION_VD))
, vdControl(&vdComm, PluginSettings::instance()->vdTimeout())
, vdSession(&vdControl, exerciseId, PluginSettings::instance()->vdKeepAliveEnabled())
, liveVideoApp(AvsDialog::VIDEO_1, &avaControl)
, videoPlaybackApp(AvsDialog::VIDEO_PLAYBACK, &avaControl, PlaybackApp::AppType::Normal)
, audioPlaybackApp(AvsDialog::AUDIO_PLAYBACK, &avaControl, PlaybackApp::AppType::AlwaysHidden)
, videoPresentationApp(AvsDialog::VIDEO_PRESENTATION, &avaControl, PlaybackApp::AppType::AutoHide)
, audioPresentationApp(AvsDialog::AUDIO_PRESENTATION, &avaControl, PlaybackApp::AppType::AlwaysHidden)
{
QObject::connect(&audioPrioObserver, &OperatorSettingsObserver::audioPrioChanged, &avaSession, &AvaSession::onAudioPrioChanged);
QObject::connect(&audioPrioObserver, &OperatorSettingsObserver::timezoneChanged, &avaSession, &AvaSession::onTimezoneChanged);
avaComm.start();
vdComm.start();
}
int exerciseId;
OperatorManagement::User operator_;
OperatorSettingsObserver audioPrioObserver;
AvaComm avaComm;
AvaControl avaControl;
AvaSession avaSession;
AvaState avaState;
StreamNameMappingToAva streamNameMappingToAva;
AvsMenus avsMenus;
RtProfileObserver rtProfileObserver;
VdComm vdComm;
VdControl vdControl;
VdSession vdSession;
AvsApp liveVideoApp;
PlaybackApp videoPlaybackApp;
PlaybackApp audioPlaybackApp;
PlaybackApp videoPresentationApp;
PlaybackApp audioPresentationApp;
};
AvsSessionContext::AvsSessionContext() = default;
AvsSessionContext::~AvsSessionContext()
{
//Q_ASSERT(m_refCount == 0);
}
bool AvsSessionContext::canAcquire(int exerciseId) const
{
Q_ASSERT(exerciseId > 0);
if (!m_composite)
return true;
return exerciseId == m_composite->exerciseId;
}
AvsSessionContext::Composite *AvsSessionContext::acquire(int exerciseId, int operatorId, const QString &langCode, AvaGuiStyle guiStyle, const AvmExerciseContextHandle &exerciseContextHandle)
{
Q_ASSERT(exerciseId > 0);
if (m_refCount == 0)
m_composite.reset(new Composite(exerciseId, operatorId, langCode, guiStyle, exerciseContextHandle));
else
Q_ASSERT(exerciseId == m_composite->exerciseId);
++m_refCount;
return m_composite.get();
}
void AvsSessionContext::release(Composite *composite)
{
Q_ASSERT(composite);
Q_ASSERT(composite == m_composite.get());
--m_refCount;
if (m_refCount == 0)
m_composite.reset();
}
/// AvsSessionContextHandle
AvsSessionContextHandle::AvsSessionContextHandle(/*LtsSession::LtsSession *session, */AvsSessionContext *context, int exerciseId, int operatorId, const QString &langCode, AvaGuiStyle guiStyle, const AvmExerciseContextHandle &exerciseContextHandle)
: m_composite(context->acquire(exerciseId, operatorId, langCode, guiStyle, exerciseContextHandle), [context](AvsSessionContext::Composite *composite) { context->release(composite); })
{
Q_ASSERT(context);
Q_ASSERT(m_composite);
}
AvaSession *AvsSessionContextHandle::avaSession() const
{
Q_ASSERT(m_composite);
return &m_composite->avaSession;
}
AvaState *AvsSessionContextHandle::avaState() const
{
Q_ASSERT(m_composite);
return &m_composite->avaState;
}
AvsMenus *AvsSessionContextHandle::menus() const
{
Q_ASSERT(m_composite);
return &m_composite->avsMenus;
}
VdSession *AvsSessionContextHandle::vdSession() const
{
Q_ASSERT(m_composite);
return &m_composite->vdSession;
}
AvsApp *AvsSessionContextHandle::liveVideoApp() const
{
Q_ASSERT(m_composite);
return &m_composite->liveVideoApp;
}
PlaybackApp *AvsSessionContextHandle::videoPlaybackApp() const
{
Q_ASSERT(m_composite);
return &m_composite->videoPlaybackApp;
}
PlaybackApp *AvsSessionContextHandle::audioPlaybackApp() const
{
Q_ASSERT(m_composite);
return &m_composite->audioPlaybackApp;
}
PlaybackApp *AvsSessionContextHandle::videoPresentationApp() const
{
Q_ASSERT(m_composite);
return &m_composite->videoPresentationApp;
}
PlaybackApp *AvsSessionContextHandle::audioPresentationApp() const
{
Q_ASSERT(m_composite);
return &m_composite->audioPresentationApp;
}
} // namespace AudioVideoManagement
+134
View File
@@ -0,0 +1,134 @@
/**
* @RDE_HEADER@
* AvsSessionContext
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-08-03
*/
#pragma once
#include <AudioVideoManagement/ava_types.h>
#include <QString>
#include <memory>
#include <functional>
namespace OperatorManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
class AvaSession;
class AvaState;
class StreamNameMapping;
class AvsMenus;
class VdSession;
class AvmExerciseContextHandle;
class AvsApp;
class PlaybackApp;
/**
* AvsSessionContext ensures that there can only be one communication
* channel to AVS_Workstation and AVS_Export with a defined exercise context at
* any given time.
* AvsSessionContext must not be used with the global exercise (exerciseId 0).
*
* The application can have many users of the communication channels at the same time.
* The reason for this is that the AvmDataPlugin will be instantiated for every
* exercise (also live and replay) and possibly multiple times for an exercise.
* All these users shall share the same communication channels.
* But all parallel users must use the same exercise context.
*
* AvsSessionContext creates the communication channels (AvaSession, VdSession) when the first user
* calls acquire(). Subsequent users can get access to the same channels by also
* calling acquire() -- but the exercise context must match.
* When a user no longer needs these communication channels it calls release().
* When all users released the communication channels, they will be deleted.
* After that another user can call acquire() with a different exercise context.
*
* Use AvsSessionContextHandle to automatically acquire() and release()
* the communication channel.
*
* See AvmExerciseContext for a visualization of lifetimes of Apps, PluginInterface-instances
* and ExerciseContext.
*
* @see AvmExerciseContext
* @author Christian Fröbel
*/
class AvsSessionContext
{
public:
/**
* Small helper struct that holds all parts that make up a session.
* Only useful for AvsSessionContextHandle in order to access those parts.
*/
struct Composite;
AvsSessionContext();
~AvsSessionContext();
/**
* Check whether a AvsSessionContext with the given exerciseId can be acquired.
* Yields true when no AvsSessionContext was created yet or when the currently active
* AvsSessionContext is for the same exericse.
*/
bool canAcquire(int exerciseId) const;
/**
* Need not to be called directly; use AvsSessionContextHandle instead.
*/
///@{
Composite *acquire(int exerciseId, int operatorId, const QString &langCode, AvaGuiStyle, const AvmExerciseContextHandle &exerciseContextHandle);
void release(Composite *composite);
///@}
private:
std::unique_ptr<Composite> m_composite;
int m_refCount{0};
};
/**
* Helper class to automatically call AvsSessionContext::acquire()
* and AvsSessionContext::release() in the ctor and dtor respectively.
* Instances can be moved but not copied.
*/
class AvsSessionContextHandle
{
public:
AvsSessionContextHandle() = default;
AvsSessionContextHandle(AvsSessionContext *context, int exerciseId, int operatorId, const QString &langCode, AvaGuiStyle, const AvmExerciseContextHandle &exerciseContextHandle);
operator bool() const { return m_composite.get(); }
AvaSession *avaSession() const;
AvaState *avaState() const;
AvsMenus *menus() const;
VdSession *vdSession() const;
AvsApp *liveVideoApp() const;
PlaybackApp *videoPlaybackApp() const;
PlaybackApp *audioPlaybackApp() const;
PlaybackApp *videoPresentationApp() const;
PlaybackApp *audioPresentationApp() const;
private:
using Releaser = std::function<void (AvsSessionContext::Composite *)>;
std::unique_ptr<AvsSessionContext::Composite, Releaser> m_composite;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,175 @@
/**
* @RDE_HEADER@
* OperatorSettingsObserver
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-04-10
*/
#include "operator_settings_observer.h"
#include <LtsUtilsCore/data_modification_enum.h>
#include <LtsUtilsSettings/settings.h>
#include <ExerciseManagement/exm_dtc.h>
#include <ExerciseManagement/exm.hpp>
#include <OperatorManagement/opm_dtc.h>
#include <OperatorManagement/opm.hpp>
#include <QtGlobal>
namespace AudioVideoManagement
{
/**
* Small helper class that calculates the timezone for the current operator
* based on the operator's settings and the default timezone for the exercise.
*/
class OperatorSettingsObserver::TimezoneCalculator : public QObject
{
Q_OBJECT
public:
void setDefaultTimezone(QChar timezone)
{
Q_ASSERT(!timezone.isNull());
if (timezone == m_defaultTimezone)
return;
m_defaultTimezone = timezone;
calculateTimezone();
}
void setOperatorTimezone(QChar timezone)
{
Q_ASSERT(!timezone.isNull());
if (timezone == m_operatorTimezone)
return;
m_operatorTimezone = timezone;
calculateTimezone();
}
void unsetOperatorTimezone()
{
if (!m_operatorTimezone)
return;
m_operatorTimezone.reset();
calculateTimezone();
}
QChar timezone() const { return m_calculatedTimezone; }
signals:
void timezoneChanged(QChar newTimezone);
private:
void calculateTimezone()
{
QChar calculated = m_operatorTimezone.get_value_or(m_defaultTimezone);
if (calculated == m_calculatedTimezone)
return;
m_calculatedTimezone = calculated;
emit timezoneChanged(calculated);
}
QChar m_defaultTimezone = QLatin1Char('Z');
boost::optional<QChar> m_operatorTimezone;
QChar m_calculatedTimezone = m_defaultTimezone;
};
OperatorSettingsObserver::OperatorSettingsObserver(int exerciseId, int operatorId, OperatorManagement::Interface::Dtc *opmDtc, ExerciseManagement::Interface::Dtc *exmDtc)
: m_exerciseId(exerciseId)
, m_operatorId(operatorId)
, m_opmDtc(opmDtc)
, m_exmDtc(exmDtc)
, m_operatorSettings(LtsUtils::OperatorSettings::getInstance())
, m_timezoneCalculator(new TimezoneCalculator)
{
Q_ASSERT(operatorId > 0);
Q_ASSERT(opmDtc);
Q_ASSERT(exmDtc);
OperatorManagement::User operator_ = m_opmDtc->user(operatorId);
if (operator_.id() == operatorId)
m_currentPrio = operator_.audioChannelPriority();
ExerciseManagement::Exercise exercise = m_exmDtc->exercise(exerciseId);
if (exercise.id() == exerciseId)
m_timezoneCalculator->setDefaultTimezone(QChar::fromLatin1(exercise.defaultTimeZone()));
connect(opmDtc, &OperatorManagement::Interface::Dtc::userChanged, this, &OperatorSettingsObserver::onOperatorChanged);
connect(exmDtc, &ExerciseManagement::Interface::Dtc::exerciseChanged, this, &OperatorSettingsObserver::onExerciseChanged);
connect(m_operatorSettings, &LtsUtils::OperatorSettings::valueChanged, this, &OperatorSettingsObserver::onSettingsValueChanged);
connect(m_timezoneCalculator.get(), &TimezoneCalculator::timezoneChanged, this, &OperatorSettingsObserver::timezoneChanged); // signal -> signal
}
QChar OperatorSettingsObserver::timezone() const
{
return m_timezoneCalculator->timezone();
}
OperatorSettingsObserver::~OperatorSettingsObserver() = default;
void OperatorSettingsObserver::onOperatorChanged(const OperatorManagement::User &operator_, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
return;
case DataModification::eModified:
{
if (operator_.id() != m_operatorId)
return;
AvaAudioPrio prio = operator_.audioChannelPriority();
if (prio == m_currentPrio)
return;
m_currentPrio = prio;
emit audioPrioChanged(prio);
return;
}
case DataModification::eRemoved:
return;
default:
Q_ASSERT(false);
}
}
void OperatorSettingsObserver::onExerciseChanged(const ExerciseManagement::Exercise &exercise, DataModification modType)
{
if (exercise.id() != m_exerciseId)
return;
switch (modType)
{
case DataModification::eModified:
m_timezoneCalculator->setDefaultTimezone(QChar::fromLatin1(exercise.defaultTimeZone()));
break;
default:
break;
}
}
void OperatorSettingsObserver::onSettingsValueChanged(const QString &group, const QString &identifier, const QVariant &value)
{
static const QString timeSettingsGroup = QStringLiteral("TimeSettings");
static const QString timezoneId = QStringLiteral("TimeZone");
if (group != timeSettingsGroup)
return;
if (identifier != timezoneId)
return;
QChar timezone = value.toChar();
if (timezone.isNull())
m_timezoneCalculator->unsetOperatorTimezone();
else
m_timezoneCalculator->setOperatorTimezone(timezone);
}
} // namespace AudioVideoManagement
#include "operator_settings_observer.moc"
@@ -0,0 +1,94 @@
/**
* @RDE_HEADER@
* OperatorSettingsObserver
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-04-10
*/
#pragma once
#include <AudioVideoManagement/ava_types.h>
#include <boost/optional.hpp>
#include <QChar>
#include <QObject>
#include <memory>
namespace OperatorManagement
{
namespace Interface
{
class Dtc;
}
class User;
} // namespace OperatorManagement
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
class Exercise;
} // namespace ExerciseManagement
namespace LtsUtils
{
class OperatorSettings;
}
enum class DataModification;
namespace AudioVideoManagement
{
/**
* Small helper class to observe the following parts of the Operator's settings:
* - audio priority
* - timezone
*
* @author Christian Fröbel
*/
class OperatorSettingsObserver : public QObject
{
Q_OBJECT
public:
OperatorSettingsObserver(int exerciseId, int operatorId, OperatorManagement::Interface::Dtc *opmDtc, ExerciseManagement::Interface::Dtc *exmDtc);
~OperatorSettingsObserver();
QChar timezone() const;
signals:
void audioPrioChanged(AvaAudioPrio newPrio);
void timezoneChanged(QChar newTimezone);
private slots:
void onOperatorChanged(const OperatorManagement::User &operator_, DataModification modType);
void onExerciseChanged(const ExerciseManagement::Exercise &exercise, DataModification modType);
void onSettingsValueChanged(const QString &group, const QString &identifier, const QVariant &value);
private:
class TimezoneCalculator;
int m_exerciseId;
int m_operatorId;
OperatorManagement::Interface::Dtc *m_opmDtc;
ExerciseManagement::Interface::Dtc *m_exmDtc;
LtsUtils::OperatorSettings *m_operatorSettings;
std::unique_ptr<TimezoneCalculator> m_timezoneCalculator;
boost::optional<AvaAudioPrio> m_currentPrio;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,156 @@
/**
* @RDE_HEADER@
* RtProfileObserver
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-08-17
*/
#include "rt_profile_observer.h"
#include <AudioVideoManagement/ava_session.h>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <LtsUtilsCore/data_modification_enum.h>
#include <QMap>
namespace AudioVideoManagement
{
RtProfileObserver::RtProfileObserver(Interface::Dtc *dtc, AvaSession *avaSession, RtState *rtState, int exerciseId, int operatorId)
: m_exerciseId(exerciseId)
, m_operatorId(operatorId)
, m_dtc(dtc)
, m_avaSession(avaSession)
, m_avaControl(avaSession->control())
, m_rtState(rtState)
{
Q_ASSERT(dtc);
Q_ASSERT(avaSession);
Q_ASSERT(rtState);
connect(m_avaSession, &AvaSession::sessionEstablished, this, &RtProfileObserver::onSessionEstablished);
connect(m_dtc, &Interface::Dtc::tvcConfigChanged, this, &RtProfileObserver::onTvcConfigChanged);
connect(m_dtc, &Interface::Dtc::cnrConfigChanged, this, &RtProfileObserver::onCnrConfigChanged);
connect(m_dtc, &Interface::Dtc::cnrTvcProfileChanged, this, &RtProfileObserver::onCnrTvcProfileChanged);
m_profile = getProfile();
if (m_avaSession->isEstablished())
onSessionEstablished();
}
void RtProfileObserver::onSessionEstablished()
{
loadVoiceRadioCommunicationMenu();
}
void RtProfileObserver::loadVoiceRadioCommunicationMenu()
{
m_avaControl->setRtProfile(m_rtState, defaultRtChannels());
}
void RtProfileObserver::onCnrConfigChanged(const CnrConfig &cnrConfig, DataModification modType)
{
if (modType != DataModification::eModified)
return;
if (!isInProfile(cnrConfig))
return;
m_avaControl->changeRtProfileCircuitName(m_rtState, {RadioSystem::Cnr, cnrConfig.id(), cnrConfig.radioCircuit()});
}
void RtProfileObserver::onTvcConfigChanged(const TvcConfig &tvcConfig, DataModification modType)
{
if (modType != DataModification::eModified)
return;
if (!isInProfile(tvcConfig))
return;
m_avaControl->changeRtProfileCircuitName(m_rtState, {RadioSystem::Tvc, tvcConfig.id(), tvcConfig.radioCircuit()});
}
void RtProfileObserver::onCnrTvcProfileChanged(const CnrTvcProfile &profile, DataModification modType)
{
Q_UNUSED(modType);
if (profile.exerciseId() != m_exerciseId || profile.operatorId() != m_operatorId)
return;
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
m_profile = profile;
break;
case DataModification::eRemoved:
m_profile.reset();
break;
default:
Q_ASSERT(false);
}
loadVoiceRadioCommunicationMenu();
}
QVector<RtState::RadioCircuit> RtProfileObserver::defaultRtChannels() const
{
if (!m_profile)
return {};
const QMap<int, CnrConfig> cnrcs = m_dtc->cnrConfigs();
const QMap<int, TvcConfig> tvccs = m_dtc->tvcConfigs();
QVector<RtState::RadioCircuit> rtChannels;
rtChannels.reserve(m_profile->data().size());
for (const auto &cnrTvcProfileData : m_profile->data())
{
std::string circuitName;
if (cnrTvcProfileData.radioSystemId() == RadioSystem::Cnr)
circuitName = cnrcs.value(cnrTvcProfileData.radioCircuitId()).radioCircuit();
else
circuitName = tvccs.value(cnrTvcProfileData.radioCircuitId()).radioCircuit();
rtChannels.push_back({cnrTvcProfileData.radioSystemId(), cnrTvcProfileData.radioCircuitId(), circuitName});
}
return rtChannels;
}
boost::optional<CnrTvcProfile> RtProfileObserver::getProfile() const
{
boost::optional<CnrTvcProfile> foundProfile;
std::function<bool(const CnrTvcProfile &profile)> forUserInExercise = [this, &foundProfile] (const CnrTvcProfile &profile)
{
if (profile.exerciseId() != m_exerciseId || profile.operatorId() != m_operatorId)
return false;
foundProfile = profile;
return true;
};
m_dtc->cnrTvcProfiles(forUserInExercise);
return foundProfile;
}
bool RtProfileObserver::isInProfile(const CnrConfig &cnrConfig) const
{
return isInProfile(RadioSystem::Cnr, cnrConfig.id());
}
bool RtProfileObserver::isInProfile(const TvcConfig &tvcConfig) const
{
return isInProfile(RadioSystem::Tvc, tvcConfig.id());
}
bool RtProfileObserver::isInProfile(RadioSystem radioSystem, int32_t id) const
{
if (!m_profile)
return false;
for (const auto &cnrTvcProfileData : m_profile->data())
if (cnrTvcProfileData.radioSystemId() == radioSystem && cnrTvcProfileData.radioCircuitId() == id)
return true;
return false;
}
} //namespace AudioVideoManagement
@@ -0,0 +1,66 @@
/**
* @RDE_HEADER@
* RtProfileObserver
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-08-17
*/
#pragma once
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/rt_state.h>
#include <AudioVideoManagement/avm.hpp>
#include <QObject>
#include <QVector>
#include <boost/optional.hpp>
enum class DataModification;
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
class AvaSession;
class AvaControl;
class TvcConfig;
class CnrConfig;
class CnrTvcProfile;
class RtProfileObserver : public QObject
{
Q_OBJECT
public:
RtProfileObserver(Interface::Dtc *dtc, AvaSession *avaSession, RtState *rtState, int exerciseId, int operatorId);
private slots:
void onSessionEstablished();
void onTvcConfigChanged(const TvcConfig &tvcConfig, DataModification modType);
void onCnrConfigChanged(const CnrConfig &cnrConfig, DataModification modType);
void onCnrTvcProfileChanged(const CnrTvcProfile &profile, DataModification modType);
private:
void loadVoiceRadioCommunicationMenu();
QVector<RtState::RadioCircuit> defaultRtChannels() const;
boost::optional<CnrTvcProfile> getProfile() const;
bool isInProfile(const CnrConfig &cnrConfig) const;
bool isInProfile(const TvcConfig &tvcConfig) const;
bool isInProfile(RadioSystem radioSystem, int32_t id) const;
const int m_exerciseId;
const int m_operatorId;
boost::optional<CnrTvcProfile> m_profile;
Interface::Dtc *m_dtc;
AvaSession *m_avaSession;
AvaControl *m_avaControl;
RtState *m_rtState;
};
} //namespace AudioVideoManagement
@@ -0,0 +1,297 @@
/**
* @RDE_HEADER@
* OperatorManangement - Show all available streams
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-11-06
*/
#include "audio_settings_avail_streams_model.h"
#include <nglwidgets/ngltreeitem.h>
#include <QFont>
namespace AudioVideoManagement
{
QDataStream &operator<<(QDataStream &ds, const StreamData &sd)
{
ds << sd.topicId << sd.name << sd.bIsCnr;
return ds;
}
QDataStream &operator>>(QDataStream &ds, StreamData &sd)
{
ds >> sd.topicId >> sd.name >> sd.bIsCnr ;
return ds;
}
//
// AvailStreamsModel
//
AvailStreamsModel::AvailStreamsModel(Interface::Dtc *avmDtc,
int32_t exerciseId,
QObject *parent)
: NGLTreeModel(parent, new NGLTreeItem(QVariant()), {tr("Available")})
, m_exerciseId(exerciseId)
, m_avmDtc(avmDtc)
, m_cnrRoot(new NGLTreeItem(QVariant()))
, m_tvcRoot(new NGLTreeItem(QVariant()))
, m_changes(false)
, m_logger(getLogger("AudioSettingsAvailStreamsModel"))
{
Q_ASSERT(avmDtc);
qRegisterMetaTypeStreamOperators<StreamData>();
setMimeType(StreamData::mimeType());
StreamData data;
data.name = tr("CNR");
data.bIsCnr = true;
m_cnrRoot->setData(QVariant::fromValue(data));
m_rootItem->appendChild(m_cnrRoot);
data.name = tr("TVC");
data.bIsCnr = false;
m_tvcRoot->setData(QVariant::fromValue(data));
m_rootItem->appendChild(m_tvcRoot);
}
/*
AvailStreamsModel::~AvailStreamsModel()
{
// do not delete, is deleted through root item
//delete m_cnrRoot;
//delete m_tvcRoot;
}
*/
void AvailStreamsModel::setStreamChanged(const QVector<StreamData> &streams)
{
for (const StreamData &d : streams)
if (!m_changedStreams.contains(QPair<bool,int>(d.bIsCnr, d.topicId)))
m_changedStreams += QPair<bool,int>(d.bIsCnr, d.topicId);
emit modelDataChanged();
}
bool AvailStreamsModel::initModel()
{
connect(m_avmDtc, &AudioVideoManagement::Interface::Dtc::cnrConfigChanged,
this, &AvailStreamsModel::onCnrConfigChanged, Qt::UniqueConnection);
m_avmDtc->cnrConfigs([this](const AudioVideoManagement::CnrConfig & ch) { onCnrConfigChanged(ch, DataModification::eAdded); });
#if 0
AudioVideoManagement::CnrConfig data;
data.exerciseId(42);
data.id(1);
data.radioCircuit(QStringLiteral("test1").toStdString());
onCnrConfigChanged(data, DataModification::eAdded);
data.id(2);
data.radioCircuit(QStringLiteral("test2").toStdString());
onCnrConfigChanged(data, DataModification::eAdded);
AudioVideoManagement::TvcConfig data2;
data2.id(1);
data2.radioCircuit(QStringLiteral("test3 (tvc)").toStdString());
onTvcConfigChanged(data2, DataModification::eAdded);
#endif
connect(m_avmDtc, &AudioVideoManagement::Interface::Dtc::tvcConfigChanged,
this, &AvailStreamsModel::onTvcConfigChanged, Qt::UniqueConnection);
m_avmDtc->tvcConfigs([this](const AudioVideoManagement::TvcConfig & ch) { onTvcConfigChanged(ch, DataModification::eAdded); });
return true;
}
Qt::ItemFlags AvailStreamsModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags f = NGLTreeModel::flags(index) | Qt::ItemIsDropEnabled;
if (!index.parent().isValid())
f &= ~Qt::ItemIsDragEnabled;
return f;
}
QVariant AvailStreamsModel::data(const QModelIndex &index, int role) const
{
switch (role)
{
case Qt::DisplayRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
DEBUG_LOG(m_logger) << data.name;
return data.name;
}
case Qt::FontRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
if (!m_changedStreams.contains(QPair<bool,int>(data.bIsCnr, data.topicId)))
return {};
QFont f;
f.setBold(true);
return f;
}
default:
{
break;
}
}
return NGLTreeModel::data(index, role);
}
bool AvailStreamsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(data);
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
if (action != supportedDropActions())
return false;
return true;
}
bool AvailStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(action);
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
const QVector<NGLTreeItem *> droppedItems = getMimeDataItems(data, m_mimeType);
QVector<StreamData> streams;
streams.reserve(droppedItems.size());
for (const NGLTreeItem *item : droppedItems)
streams += item->data().value<StreamData>();
qDeleteAll(droppedItems);
emit droppedStreamNames(streams);
m_changes = true;
return true;
}
void AvailStreamsModel::onCnrConfigChanged(const AudioVideoManagement::CnrConfig &data, DataModification modType)
{
insertUpdateData(data, modType, m_cnrRoot);
}
void AvailStreamsModel::onTvcConfigChanged(const AudioVideoManagement::TvcConfig &data, DataModification modType)
{
insertUpdateData(data, modType, m_tvcRoot);
}
template <typename T>
void AvailStreamsModel::insertUpdateData(const T &config, DataModification modType, NGLTreeItem *parent)
{
DataModification action = modType;
if (modType == DataModification::eModified)
{
// If a config of a different Exercise was changed the config might have been global before.
// Thus we might need to remove this config.
if (config.exerciseId().is_set() && config.exerciseId().get() != m_exerciseId)
action = DataModification::eRemoved;
}
StreamData newData(config);
switch (action)
{
case DataModification::eAdded:
if (config.exerciseId().is_set() && config.exerciseId().get() != m_exerciseId)
break;
Q_FALLTHROUGH();
case DataModification::eModified:
{
bool bFound = false;
for (int i = 0; i < parent->childCount(); ++i)
{
NGLTreeItem *child = parent->child(i);
StreamData data = child->data().value<StreamData>();
if (data == newData)
{
bFound = true;
setItemData(indexFromItem(child), QVariant::fromValue(newData), child->icon());
emit updateEntry(newData);
break;
}
}
if (!bFound)
{
NGLTreeItem *child = new NGLTreeItem(QVariant::fromValue(newData));
insertRow(parent->childCount(), indexFromItem(parent), child);
}
break;
}
case DataModification::eRemoved:
{
for (int i = 0; i < parent->childCount(); ++i)
{
NGLTreeItem *child = parent->child(i);
StreamData data = child->data().value<StreamData>();
if (data == newData)
{
removeRows(i, 1, indexFromItem(parent));
emit removeEntry(newData);
break;
}
}
}
}
}
//
// AvailStreamsFilter
//
AvailStreamsFilter::AvailStreamsFilter(AvailStreamsModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
, m_sourceModel(sourceModel)
{
Q_ASSERT(sourceModel);
setSourceModel(m_sourceModel);
}
void AvailStreamsFilter::addAllocatedStreams(const QVector<StreamData> &streams)
{
m_allocatedStreams.insert(streams.begin(), streams.end());
invalidateFilter();
}
void AvailStreamsFilter::setAllocatedStreams(const QVector<StreamData> &streams)
{
m_allocatedStreams.clear();
m_allocatedStreams.insert(streams.begin(), streams.end());
invalidateFilter();
}
void AvailStreamsFilter::removeAllocatedStreams(const QVector<StreamData> &streams)
{
for (const StreamData &stream : streams)
m_allocatedStreams.erase(stream);
invalidateFilter();
}
bool AvailStreamsFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (!m_sourceModel || !source_parent.isValid())
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
NGLTreeItem *parent = m_sourceModel->privateData(source_parent);
if (!parent)
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
NGLTreeItem *child = parent->child(source_row);
if (!child)
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
StreamData data = child->data().value<StreamData>();
return m_allocatedStreams.count(data) == 0;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,88 @@
/**
* @RDE_HEADER@
* OperatorManangement - Show all available streams
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-11-06
*/
#pragma once
#include <nglwidgets/ngltreemodel.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
#include <QSortFilterProxyModel>
#include <QVector>
#include "audio_stream_type.h"
#include <set>
#include <utility>
namespace AudioVideoManagement
{
class AvailStreamsModel final : public NGLTreeModel
{
Q_OBJECT
public:
explicit AvailStreamsModel(AudioVideoManagement::Interface::Dtc *avmDtc, int32_t exerciseId,
QObject *parent = nullptr);
~AvailStreamsModel() = default;
bool getChangeState() {bool result = m_changes; m_changes = false; return result;}
void resetChangeState() {m_changes = false;m_changedStreams.clear();}
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
// CopyAction - data is filtered with proxy model
Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
Qt::DropActions supportedDragActions() const override { return Qt::CopyAction; }
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction, int, int, const QModelIndex &) override;
void setStreamChanged(const QVector<StreamData> &streams);
bool initModel();
signals:
void droppedStreamNames(const QVector<AudioVideoManagement::StreamData> &streamNamesDropped);
void updateEntry(const AudioVideoManagement::StreamData &stream);
void removeEntry(const AudioVideoManagement::StreamData &stream);
private slots:
void onCnrConfigChanged(const AudioVideoManagement::CnrConfig &data, DataModification modType);
void onTvcConfigChanged(const AudioVideoManagement::TvcConfig &data, DataModification modType);
private:
template <typename T>
void insertUpdateData(const T &data, DataModification modType, NGLTreeItem *parent);
int32_t m_exerciseId;
AudioVideoManagement::Interface::Dtc *m_avmDtc;
NGLTreeItem *m_cnrRoot;
NGLTreeItem *m_tvcRoot;
bool m_changes;
QVector<QPair<bool /* is cnr */, int> > m_changedStreams;
NGL::INGLLogger &m_logger;
};
class AvailStreamsFilter final : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit AvailStreamsFilter(AvailStreamsModel *sourceModel, QObject *parent);
void addAllocatedStreams(const QVector<StreamData> &streams);
void setAllocatedStreams(const QVector<StreamData> &streams);
void removeAllocatedStreams(const QVector<StreamData> &streams);
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
/** Don't use directly; will we done via constructor. */
using QSortFilterProxyModel::setSourceModel;
AvailStreamsModel *m_sourceModel;
std::set<StreamData> m_allocatedStreams;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,440 @@
/**
* @RDE_HEADER@
* OperatorManagement - Operator Edit Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include "audio_settings_dialog.h"
#include "ui_audio_settings_dialog.h"
#include "audio_settings_avail_streams_model.h"
#include "audio_settings_used_streams_model.h"
#include "audio_stream_type.h"
#include <LtsWidgets/lts_message_dialog.h>
#include <LtsSession/lts_session_data_dds.h>
#include <LtsSession/lts_session_data.h>
#include <AudioVideoManagement/avm_ftc_types.h>
namespace AudioVideoManagement
{
AudioSettingsDialog::AudioSettingsDialog(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ui(new Ui::AudioSettingsDialog)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_logger(getLogger("AudioSettingsDialog"))
{
m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setOnlineHelpFilename(QStringLiteral("Situation-Voice_Communication_Access"));
}
AudioSettingsDialog::~AudioSettingsDialog()
{
delete m_ui;
}
bool AudioSettingsDialog::init(OperatorManagement::Interface::Dtc *opmDtc,
Interface::Dtc *avmDtc,
Interface::Ftc *avmFtc,
LtsSession::LtsSessionDataDds *session)
{
Q_ASSERT(opmDtc);
Q_ASSERT(avmDtc);
Q_ASSERT(avmFtc);
Q_ASSERT(session);
setUnchangeable(true);
m_session = session;
m_avmDtc = avmDtc;
m_avmFtc = avmFtc;
////////////
// Create Model
m_modelAvailable = new AvailStreamsModel(avmDtc, m_session->exerciseId(), this);
if (!m_modelAvailable->initModel())
return false;
m_filterAvailable = new AvailStreamsFilter(m_modelAvailable, this);
m_modelAllocated = new AllocatedStreamsModel(this);
connect(m_modelAvailable, &AvailStreamsModel::droppedStreamNames, this, &AudioSettingsDialog::onDroppedInAvailable); // Drag and Drop from allocated -> available
connect(m_modelAllocated, &AllocatedStreamsModel::droppedStreamNames, this, &AudioSettingsDialog::onDroppedInAllocated); // Drag and Drop from available -> allocated
connect(m_modelAvailable, &AvailStreamsModel::updateEntry, m_modelAllocated, &AllocatedStreamsModel::updateEntry);
connect(m_modelAvailable, &AvailStreamsModel::removeEntry, this, &AudioSettingsDialog::onEntryRemoved);
// Glue model and view together
m_ui->treeViewAvailable->setModel(m_filterAvailable);
m_ui->treeViewAvailable->expandAll();
m_ui->treeViewAllocated->setModel(m_modelAllocated);
// Operator Management to get Name from operator
connect(opmDtc, &OperatorManagement::Interface::Dtc::userChanged,
this, &AudioSettingsDialog::onOperatorChanged);
auto cbOperator = [this](const OperatorManagement::User & u)
{
onOperatorChanged(u, DataModification::eAdded);
};
opmDtc->users(cbOperator);
fillOperatorBox();
connect(m_avmDtc, &Interface::Dtc::cnrTvcProfileChanged,
this, &AudioSettingsDialog::onCnrTvcProfileChanged);
auto cbRadioCircuits = [this](const CnrTvcProfile & rc)
{
onCnrTvcProfileChanged(rc, DataModification::eAdded);
};
m_avmDtc->cnrTvcProfiles(cbRadioCircuits);
connect(m_ui->pushButtonUp, &QPushButton::clicked, this, &AudioSettingsDialog::onUpButtonClicked);
connect(m_ui->pushButtonDown, &QPushButton::clicked, this, &AudioSettingsDialog::onDownButtonClicked);
connect(m_ui->operatorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AudioSettingsDialog::onSelectionOperatorChanged);
connect(m_ui->treeViewAllocated->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AudioSettingsDialog::onSelectionChangedAllocation);
// Populate our Model
m_ui->treeViewAllocated->setSelectionMode(QAbstractItemView::MultiSelection);
m_ui->treeViewAvailable->setSelectionMode(QAbstractItemView::MultiSelection);
m_ui->treeViewAllocated->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_ui->treeViewAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection);
//m_ui->treeViewAllocated->setSortingEnabled(true);
//m_ui->treeViewAllocated->sortByColumn(0,Qt::AscendingOrder );
m_ui->pushButtonUp->setEnabled(false);
m_ui->pushButtonDown->setEnabled(false);
m_avmFtc->enableAll();
int opId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
if (m_ui->operatorComboBox->currentIndex() != m_ui->operatorComboBox->findData(opId))
m_ui->operatorComboBox->setCurrentIndex(m_ui->operatorComboBox->findData(opId));
else
onSelectionOperatorChanged(0); //param is unused
setUnchangeable(false);
return true;
} // end of init
void AudioSettingsDialog::retranslate()
{
translate(m_ui, tr("Voice Communication Access"));
} // end of retranslate
void AudioSettingsDialog::onOperatorChanged(const OperatorManagement::User &data, DataModification modType)
{
// Table: opm.opm_operator
DEBUG_LOG(m_logger) << "************** onOperatorChanged ";
switch (modType)
{
case DataModification::eRemoved:
{
Q_ASSERT(m_operatorMap.contains(data.id()));
// Check, if Operator is currently open, if yes, reset any changes
if (m_ui->operatorComboBox->currentData().toInt() == data.id())
{
__unchange__();
m_modelAllocated->resetChangeState();
m_modelAvailable->resetChangeState();
m_filterAvailable->invalidate();
}
m_operatorMap.remove(data.id());
m_ui->operatorComboBox->removeItem(m_ui->operatorComboBox->findData(data.id()));
break;
}
case DataModification::eAdded:
{
Q_ASSERT(!m_operatorMap.contains(data.id()));
const QString operatorName = QString::fromStdString(data.name());
m_operatorMap.insert(data.id(), operatorName);
m_ui->operatorComboBox->addItem(operatorName, data.id());
m_ui->operatorComboBox->model()->sort(0);
break;
}
case DataModification::eModified:
{
Q_ASSERT(m_operatorMap.contains(data.id()));
const QString operatorName = QString::fromStdString(data.name());
m_operatorMap.insert(data.id(), operatorName);
int i = m_ui->operatorComboBox->findData(data.id());
Q_ASSERT(i != -1);
m_ui->operatorComboBox->setItemText(i, operatorName);
m_ui->operatorComboBox->model()->sort(0);
break;
}
}
} // end of onOperatorChanged
void AudioSettingsDialog::onCnrTvcProfileChanged(const CnrTvcProfile &data, DataModification modType)
{
if (m_session->exerciseId() == data.exerciseId())
{
if (modType == DataModification::eRemoved)
{
m_cnrTvcProfileMap.remove(data.id());
}
else
{
m_cnrTvcProfileMap.insert(data.id(), data);
fillOperatorList();
}
}
} // end of onCnrTvcProfileChanged
void AudioSettingsDialog::fillOperatorList()
{
QVector<StreamData> newStreamData;
for (const auto &rc : qAsConst(m_cnrTvcProfileMap))
{
if ((rc.exerciseId() != m_session->exerciseId())
|| (rc.operatorId() != m_ui->operatorComboBox->currentData().toInt()))
continue;
// insert entry in Allocated
newStreamData.reserve(rc.data().size());
for (const CnrTvcProfileData &profileEntry : rc.data())
newStreamData += toStreamData(profileEntry);
break;
}
m_modelAllocated->setNewStreamData(newStreamData);
m_filterAvailable->setAllocatedStreams(newStreamData);
} // end of fillOperatorList
void AudioSettingsDialog::fillOperatorBox()
{
m_ui->operatorComboBox->clear();
QMap<int, QString>::iterator i = m_operatorMap.begin();
while (i != m_operatorMap.end())
{
m_ui->operatorComboBox->addItem(i.value(), i.key());
++i;
}
m_ui->operatorComboBox->model()->sort(0);
}
void AudioSettingsDialog::onUpButtonClicked()
{
const QModelIndexList mil = m_ui->treeViewAllocated->selectionModel()->selectedIndexes();
if (mil.isEmpty())
return;
m_modelAllocated->changeOrder(mil.first(), true);
if (mil.first().row() > 0)
{
const QModelIndex newRow = m_modelAllocated->index(mil.first().row() - 1, 0);
m_ui->treeViewAllocated->setCurrentIndex(newRow);
}
slotChangesMade();
} // end of onUpButtonClicked
void AudioSettingsDialog::onDownButtonClicked()
{
const QModelIndexList mil = m_ui->treeViewAllocated->selectionModel()->selectedIndexes();
if (mil.isEmpty())
return;
m_modelAllocated->changeOrder(mil.first(), false);
const QModelIndex newRow = m_modelAllocated->index(mil.first().row() + 1, 0);
m_ui->treeViewAllocated->setCurrentIndex(newRow);
slotChangesMade();
} // end of onDownButtonClicked
void AudioSettingsDialog::onSelectionChangedAllocation(const QItemSelection &selected, const QItemSelection &deselected)
{
DEBUG_LOG(m_logger) << "onSelectionChangedAllocation selected.isEmpty()=" << selected.isEmpty();
Q_UNUSED(deselected);
QModelIndex first = m_ui->treeViewAllocated->model()->index(0, 0);
int rowCnt = m_ui->treeViewAllocated->model()->rowCount();
QModelIndex last = m_ui->treeViewAllocated->model()->index(rowCnt - 1, 0);
QModelIndexList selectedRows = m_ui->treeViewAllocated->selectionModel()->selectedIndexes();
const int cntSelectedRows = selectedRows.size();
if (cntSelectedRows > 1)
{
m_ui->pushButtonUp->setEnabled(false);
m_ui->pushButtonDown->setEnabled(false);
return;
}
if (selected.size() > 0)
{
m_ui->pushButtonUp->setEnabled(selected.first().indexes().first() != first);
m_ui->pushButtonDown->setEnabled(selected.first().indexes().first() != last);
}
} // end of onSelectionAllocationChanged
bool AudioSettingsDialog::apply()
{
if (m_modelAllocated->getStreamDataVector().isEmpty())
applyRemove();
else
applyModify();
m_modelAllocated->resetChangeState();
m_modelAvailable->resetChangeState();
m_filterAvailable->invalidate();
return true;
}
bool AudioSettingsDialog::applyRemove()
{
RemoveCnrTvcProfileRequest removeRequest;
DEBUG_LOG(m_logger) << "AudioSettingsDialog::applyRemove";
for (const auto &profile : qAsConst(m_cnrTvcProfileMap))
{
if (profile.operatorId() != m_operatorId)
continue;
removeRequest.id(profile.id());
break;
}
if (removeRequest.id() <= 0)
return true;
m_requestHelper->startSingleRequest([this, removeRequest]()
{
return m_avmFtc->removeCnrTvcProfileRequest(removeRequest, this);
});
return true;
} // end of applyRemove
bool AudioSettingsDialog::applyModify()
{
ModifyCnrTvcProfileRequest modifyRequest;
DEBUG_LOG(m_logger) << "AudioSettingsDialog::applyModify";
for (const auto &profile : qAsConst(m_cnrTvcProfileMap))
{
if (profile.operatorId() != m_operatorId)
continue;
modifyRequest.id(profile.id());
break;
}
modifyRequest.exerciseId(m_session->exerciseId());
modifyRequest.operatorId(m_operatorId);
const QVector<StreamData> allocatedList = m_modelAllocated->getStreamDataVector();
modifyRequest.data().reserve(allocatedList.size());
for (const StreamData &stream : allocatedList)
{
CnrTvcProfileData circuitData;
circuitData.radioSystemId(stream.bIsCnr ? RadioSystem::Cnr : RadioSystem::Tvc);
circuitData.radioCircuitId(stream.topicId);
modifyRequest.data().push_back(std::move(circuitData));
}
m_requestHelper->startSingleRequest([this, modifyRequest]()
{
return m_avmFtc->modifyCnrTvcProfileRequest(modifyRequest, this);
});
return true;
} // end of applyModify
void AudioSettingsDialog::onModifyCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject)
{
__unchange__();
m_requestHelper->requestReply(replyObject);
} // end of onCreateRadioCircuitsReply
//-----------------------------------------------------------------------------
void AudioSettingsDialog::onRemoveCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject)
{
__unchange__();
m_requestHelper->requestReply(replyObject);
} // end of onRemoveRadioCircuitsReply
//-----------------------------------------------------------------------------
void AudioSettingsDialog::onSelectionOperatorChanged(const int index)
{
Q_UNUSED(index);
if (hasUnsavedChanges() ||
m_modelAllocated->getChangeState() ||
m_modelAvailable->getChangeState())
{
switch (askApplyDiscardCancel())
{
case LtsWidgets::Button::Apply:
if (!apply())
return;
break;
case LtsWidgets::Button::Cancel:
{
// cfroebel 2019-06-26:
// Setting back the operator would cause this slot to be called again.
// Thus we avoid this by blocking signals.
{
QSignalBlocker blockSignals(m_ui->operatorComboBox);
m_ui->operatorComboBox->setCurrentIndex(m_ui->operatorComboBox->findData(m_operatorId)); // reset position
}
return;
}
case LtsWidgets::Button::Discard:
__unchange__(); // can't call resetChanges() by myself :(
break;
default:
break;
}
}
m_ui->pushButtonUp->setEnabled(false);
m_ui->pushButtonDown->setEnabled(false);
m_operatorId = m_ui->operatorComboBox->currentData().toInt();
DEBUG_LOG(m_logger) << "onSelectionOperatorChanged" << " operatorStr=" << m_ui->operatorComboBox->currentText() << " m_operatorId= " << m_operatorId;
fillOperatorList();
m_modelAllocated->resetChangeState();
m_modelAvailable->resetChangeState();
m_filterAvailable->invalidate();
}
void AudioSettingsDialog::onDroppedInAllocated(const QVector<StreamData> &streams)
{
slotChangesMade();
m_filterAvailable->addAllocatedStreams(streams);
m_modelAllocated->setStreamChanged(streams);
m_modelAvailable->setStreamChanged(streams);
}
void AudioSettingsDialog::onDroppedInAvailable(const QVector<StreamData> &streams)
{
slotChangesMade();
m_filterAvailable->removeAllocatedStreams(streams);
m_modelAllocated->setStreamChanged(streams);
m_modelAvailable->setStreamChanged(streams);
}
void AudioSettingsDialog::onEntryRemoved(const StreamData &stream)
{
m_modelAllocated->removeEntry(stream);
m_filterAvailable->removeAllocatedStreams({stream}); // in case the stream was allocated forget about it
}
StreamData AudioSettingsDialog::toStreamData(const CnrTvcProfileData &profileEntry) const
{
int configId = profileEntry.radioCircuitId();
StreamData streamData;
streamData.topicId = configId;
if (profileEntry.radioSystemId() == RadioSystem::Cnr)
{
streamData.name = QString::fromStdString(m_avmDtc->cnrConfig(configId).radioCircuit());
streamData.bIsCnr = true;
}
else
{
streamData.name = QString::fromStdString(m_avmDtc->tvcConfig(configId).radioCircuit());
streamData.bIsCnr = false;
}
return streamData;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,108 @@
/**
* @RDE_HEADER@
* OperatorManangement - Operator Edit Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#pragma once
#include <OperatorManagement/opm_dtc.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
#include <LtsWidgets/lts_dialog.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <QVector>
#include <QMap>
#include <QString>
namespace Ui
{
class AudioSettingsDialog;
}
namespace LtsSession
{
class LtsSessionDataDds;
}
class QItemSelection;
namespace AudioVideoManagement
{
struct StreamData;
class AvailStreamsModel;
class AvailStreamsFilter;
class AllocatedStreamsModel;
class AudioSettingsDialog : public LtsWidgets::LtsDialog
, protected AudioVideoManagement::Interface::Ftc::ModifyCnrTvcProfileReplyHandler
, protected AudioVideoManagement::Interface::Ftc::RemoveCnrTvcProfileReplyHandler
{
Q_OBJECT
public:
explicit AudioSettingsDialog(QWidget *parent = nullptr);
~AudioSettingsDialog();
bool init(OperatorManagement::Interface::Dtc *opmDtc,
AudioVideoManagement::Interface::Dtc *avmDtc,
AudioVideoManagement::Interface::Ftc *avmFtc,
LtsSession::LtsSessionDataDds *session);
void onModifyCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject) override;
void onRemoveCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject) override;
private:
enum class CircuitChoice
{
None,
Cnr,
Tvc
};
void retranslate() override;
bool apply() override final;
bool applyModify();
bool applyRemove();
void fillOperatorBox();
void fillOperatorList();
private slots:
void onOperatorChanged(const OperatorManagement::User &data, DataModification modType);
void onCnrTvcProfileChanged(const AudioVideoManagement::CnrTvcProfile &data, DataModification modType);
void onUpButtonClicked();
void onDownButtonClicked();
void onSelectionChangedAllocation(const QItemSelection &selected, const QItemSelection &deselected);
void onSelectionOperatorChanged(const int index);
void onDroppedInAllocated(const QVector<StreamData> &streams);
void onDroppedInAvailable(const QVector<StreamData> &streams);
void onEntryRemoved(const StreamData &stream);
private:
StreamData toStreamData(const CnrTvcProfileData &profileEntry) const;
AudioVideoManagement::Interface::Dtc *m_avmDtc{nullptr};
AudioVideoManagement::Interface::Ftc *m_avmFtc{nullptr};
int m_operatorId{-1}; //!< currently displayed operator
QMap<int, QString> m_operatorMap;
QMap<int, AudioVideoManagement::CnrTvcProfile> m_cnrTvcProfileMap; // all dds-Configs key = DDS-ID
AvailStreamsModel *m_modelAvailable{nullptr};
AvailStreamsFilter *m_filterAvailable{nullptr};
AllocatedStreamsModel *m_modelAllocated{nullptr};
Ui::AudioSettingsDialog *m_ui{nullptr};
LtsUtils::RequestHelper *m_requestHelper{nullptr};
LtsSession::LtsSessionDataDds *m_session{nullptr};
LOGGER_T m_logger;
};
}
@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioSettingsDialog</class>
<widget class="QWidget" name="AudioSettingsDialog">
<property name="windowTitle">
<string>Voice Communication Access</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" rowspan="5">
<widget class="QTreeView" name="treeViewAvailable">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
</widget>
</item>
<item row="1" column="1" rowspan="5">
<widget class="QTreeView" name="treeViewAllocated">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="headerCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="1" column="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>90</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pushButtonUp">
<property name="text">
<string notr="true">↑ </string>
</property>
</widget>
</item>
<item row="3" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="pushButtonDown">
<property name="text">
<string notr="true">↓</string>
</property>
</widget>
</item>
<item row="5" column="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>89</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lbxOperator">
<property name="text">
<string>Operator:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="operatorComboBox">
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>operatorComboBox</tabstop>
<tabstop>treeViewAvailable</tabstop>
<tabstop>treeViewAllocated</tabstop>
<tabstop>pushButtonUp</tabstop>
<tabstop>pushButtonDown</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,186 @@
/**
* @RDE_HEADER@
* OperatorManangement - Show all available streams
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-11-06
*/
#include "audio_settings_used_streams_model.h"
#include <nglwidgets/ngltreeitem.h>
#include <QMimeData>
#include <QFont>
namespace AudioVideoManagement
{
//
// UsedStreamsModel
//
AllocatedStreamsModel::AllocatedStreamsModel(QObject *parent)
: NGLTreeModel(parent, new NGLTreeItem(QVariant(QLatin1String("root"))), { tr("Allocated") })
, m_logger(getLogger("AudioSettingsUsedStreamsModel"))
{
setMimeType(StreamData::mimeType());
}
Qt::ItemFlags AllocatedStreamsModel::flags(const QModelIndex &index) const
{
return NGLTreeModel::flags(index) | Qt::ItemIsDropEnabled;
}
QVariant AllocatedStreamsModel::data(const QModelIndex &index, int role) const
{
switch (role)
{
case Qt::DisplayRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
DEBUG_LOG(m_logger) << data.name;
return data.name;
}
case Qt::FontRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
if (!m_changedStreams.contains(QPair<bool,int>(data.bIsCnr, data.topicId)))
return {};
QFont f;
f.setBold(true);
return f;
}
default:
{
break;
}
}
return NGLTreeModel::data(index, role);
}
void AllocatedStreamsModel::setStreamChanged(const QVector<StreamData> &streams)
{
for (const StreamData &d : streams)
if (!m_changedStreams.contains(QPair<bool,int>(d.bIsCnr, d.topicId)))
m_changedStreams += QPair<bool,int>(d.bIsCnr, d.topicId);
emit modelDataChanged();
}
bool AllocatedStreamsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(data);
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
if (action != supportedDropActions())
return false;
return true;
}
bool AllocatedStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
const QVector<NGLTreeItem *> droppedItems = getMimeDataItems(data, m_mimeType);
QVector<StreamData> streams;
streams.reserve(droppedItems.size());
for (const NGLTreeItem *item : droppedItems)
streams += item->data().value<StreamData>();
qDeleteAll(droppedItems);
emit droppedStreamNames(streams);
m_changes = true;
DEBUG_LOG(m_logger) << data->data(QStringLiteral("NGLTreeModel/dragDrop"));
return NGLTreeModel::dropMimeData(data, action, row, 0, QModelIndex());
}
void AllocatedStreamsModel::setNewStreamData(const QVector<StreamData> &newData)
{
DEBUG_LOG(m_logger) << "set new stream data, count: " << newData.size();
resetModel();
for (const StreamData &sd : newData)
m_rootItem->appendChild(new NGLTreeItem(QVariant::fromValue(sd)));
emit modelDataChanged();
}
QVector<StreamData> AllocatedStreamsModel::getStreamDataVector() const
{
QVector<StreamData> result;
result.reserve(m_rootItem->childCount());
for (int i = 0; i < m_rootItem->childCount(); i++)
{
const NGLTreeItem *item = m_rootItem->child(i);
const StreamData sd = item->data().value<StreamData>();
result.append(sd);
DEBUG_LOG(m_logger) << "result.append(sd.name):" << sd.name;
}
DEBUG_LOG(m_logger) << "getStreamDataVector() size: " << result.size();
return result;
}
void AllocatedStreamsModel::changeOrder(const QModelIndex &idx, bool up)
{
DEBUG_LOG(m_logger) << "changeOrder "
<< " index = " << idx.row()
<< " up = " << up
<< " m_rootItem->childCount()= " << m_rootItem->childCount();
if (up)
{
moveRow(idx.parent(), idx.row(),
idx.parent(), idx.row() - 1);
}
else
{
moveRow(idx.parent(), idx.row(),
idx.parent(), idx.row() + 1);
}
emit modelDataChanged();
}
// Wird ueber connect im SettingsDialog vom AvailableModel::updateEntry aufgerufen
void AllocatedStreamsModel::updateEntry(const StreamData &stream)
{
for (int i = 0; i < m_rootItem->childCount(); i++)
{
NGLTreeItem *child = m_rootItem->child(i);
StreamData data = child->data().value<StreamData>();
if (data == stream)
{
setItemData(indexFromItem(child), QVariant::fromValue(stream), child->icon());
break;
}
}
emit modelDataChanged();
}
// Wird ueber connect im SettingsDialog vom AvailableModel::removeEntry aufgerufen
void AllocatedStreamsModel::removeEntry(const StreamData &stream)
{
for (int i = 0; i < m_rootItem->childCount(); i++)
{
NGLTreeItem *child = m_rootItem->child(i);
StreamData data = child->data().value<StreamData>();
if (data == stream)
{
removeRows(i, 1, indexFromItem(m_rootItem));
break;
}
}
emit modelDataChanged();
}
} // namespace AudioVideoManagement
@@ -0,0 +1,54 @@
/**
* @RDE_HEADER@
* OperatorManangement - Show all available streams
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-11-06
*/
#pragma once
#include <nglwidgets/ngltreemodel.h>
#include <AudioVideoManagement/logger.h>
#include "audio_stream_type.h"
namespace AudioVideoManagement
{
class AllocatedStreamsModel final : public NGLTreeModel
{
Q_OBJECT
public:
explicit AllocatedStreamsModel(QObject *parent = nullptr);
bool getChangeState() {bool result = m_changes; m_changes = false; return result;}
void resetChangeState() {m_changes = false;m_changedStreams.clear();}
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::DropActions supportedDropActions() const override { return Qt::CopyAction; }
Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
void setStreamChanged(const QVector<StreamData> &streams);
void setNewStreamData(const QVector<StreamData> &newData);
QVector<StreamData> getStreamDataVector() const;
void changeOrder(const QModelIndex &idx, bool up);
void updateEntry(const StreamData &stream);
void removeEntry(const StreamData &stream);
signals:
void droppedStreamNames(const QVector<AudioVideoManagement::StreamData> &streamNamesDropped);
private:
bool m_changes{false};
QVector<QPair<bool /* is cnr */, int> > m_changedStreams;
NGL::INGLLogger &m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,56 @@
/**
* @RDE_HEADER@
* OperatorManangement - Show all available streams
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-11-06
*/
#pragma once
#include <AudioVideoManagement/avm.hpp>
#include <QString>
#include <QObject>
#include <boost/operators.hpp>
#include <utility>
namespace AudioVideoManagement
{
/**
* Helper struct used by AvailStreamsModel, AllocatedStreamsModel.
*
* Comparision operators only consider bIsCnr and topicId, i.e. name is irrelevant.
*/
struct StreamData : boost::totally_ordered<StreamData>
{
StreamData() = default;
explicit StreamData(const TvcConfig &tvcConfig)
: name(QString::fromStdString(tvcConfig.radioCircuit()))
, topicId(tvcConfig.id())
, bIsCnr(false)
{}
explicit StreamData(const CnrConfig &cnrConfig)
: name(QString::fromStdString(cnrConfig.radioCircuit()))
, topicId(cnrConfig.id())
, bIsCnr(true)
{}
QString name;
int topicId = 0;
bool bIsCnr = false;
static QString mimeType() { return QStringLiteral("AVM/StreamData"); }
bool operator<(const StreamData &other) const { return asPair() < other.asPair(); }
bool operator==(const StreamData &other) const { return asPair() == other.asPair(); }
private:
std::pair<bool, int> asPair() const { return {bIsCnr, topicId}; }
};
} // namespace AudioVideoManagement
Q_DECLARE_METATYPE(AudioVideoManagement::StreamData);
@@ -0,0 +1,86 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Patric Karwat, SLSE, 2016-10-05
#
SET(avm_menu_edit_gui_SRCS
avm_menu_edit_gui_plugin.cpp
avm_menu_edit_gui_plugin.h
avm_menu_edit_view.cpp
avm_menu_edit_view.h
avm_menu_edit.cpp
avm_menu_edit.h
avm_menu_view_model.cpp
avm_menu_view_model.h
avm_available_streams_model.cpp
avm_available_streams_model.h
avm_choose_menu.cpp
avm_choose_menu.h
avm_new_menu_struct.cpp
avm_new_menu_struct.h
avm_used_streams_model.cpp
avm_used_streams_model.h
avm_playback_menu.cpp
avm_playback_menu.h
avm_playback_menu_dialog.cpp
avm_playback_menu_dialog.h
TopicEditor/topic_editor.cpp
TopicEditor/topic_editor.h
TopicEditor/avm_sequence_model.cpp
TopicEditor/avm_sequence_model.h
interval_model.cpp
interval_model.h
topic_replay.cpp
topic_replay.h
AudioSettings/audio_settings_dialog.cpp
AudioSettings/audio_settings_dialog.h
AudioSettings/audio_stream_type.h
AudioSettings/audio_settings_avail_streams_model.cpp
AudioSettings/audio_settings_avail_streams_model.h
AudioSettings/audio_settings_used_streams_model.cpp
AudioSettings/audio_settings_used_streams_model.h
)
NGL_WRAP_UI(avm_menu_edit_gui_SRCS
avm_menu_edit_view.ui
avm_menu_edit.ui
avm_choose_menu.ui
avm_new_menu_struct.ui
avm_playback_menu.ui
avm_playback_menu_dialog.ui
TopicEditor/topic_editor.ui
AudioSettings/audio_settings_dialog.ui
)
NGL_ADD_LIBRARY(WS avmMenuEditGuiPlugin MODULE ${avm_menu_edit_gui_SRCS})
NGL_TARGET_LINK_LIBRARIES(avmMenuEditGuiPlugin
AvmDataPluginInterface
AvmMenuEditGuiPluginInterface
AvmShared
FlmDataPluginInterface
IsmDataPluginInterface
LtsDataView
LtsPrinting
LtsScriptTimeline
LtsSession
LtsSessionData
LtsUtilsGuiDds
LtsUtilsCore
LtsWidgets
LtsUtilsSettings
nglsettings
MrkCommon
MrkDataPluginInterface
NGLLogger
ngltools
nglwidgets
OpmDataPluginInterface
Qt5::Widgets
TopicEditorBase
Boost::boost
)
NGL_INSTALL_TARGET(avmMenuEditGuiPlugin)
@@ -0,0 +1,260 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - AVS Sequence Model
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2017-08-21
*/
#include "avm_sequence_model.h"
#include <QJsonObject>
#include <QColor>
#include <chrono>
namespace AudioVideoManagement
{
AvmSequenceModel::AvmSequenceModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<AvsSequence>({QLatin1String("Order"),
tr("Start"),
tr("End"),
tr("Duration [s]"),
tr("Stream"),
tr("Speed"),
tr("Volume"),
tr("Description")},
parent)
, m_logger(getLogger("AvmSequenceModel"))
{}
void AvmSequenceModel::setTimezoneHoursOffset(int timezoneHoursOffset)
{
m_timezoneHoursOffset = timezoneHoursOffset;
emit dataChanged(index(0, Start), index(rowCount()-1, End), {Qt::DisplayRole});
}
void AvmSequenceModel::moveUp(const QModelIndex &idx)
{
// should better be done within moveRows
int row = idx.row();
auto it = std::find_if(m_highlightedRows.begin(), m_highlightedRows.end(), [row](int currentRow)
{
return (row == currentRow);
});
if (it != m_highlightedRows.end())
(*it)--;
moveRows(idx.parent(), idx.row(), 1, idx.parent(), idx.row() - 1);
}
void AvmSequenceModel::moveDown(const QModelIndex &idx)
{
// should better be done within moveRows
int row = idx.row();
auto it = std::find_if(m_highlightedRows.begin(), m_highlightedRows.end(), [row](int currentRow)
{
return (row == currentRow);
});
if (it != m_highlightedRows.end())
(*it)++;
moveRows(idx.parent(), idx.row(), 1, idx.parent(), idx.row() + 1);
}
bool AvmSequenceModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
{
Q_UNUSED(count)
if(sourceRow > m_vecData.size() -1 || sourceRow < 0 ||
destinationChild > m_vecData.size() -1 || destinationChild < 0)
return false;
int moveTo = destinationChild;
if (destinationChild > sourceRow)
moveTo++;
beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, moveTo);
m_vecData.move(sourceRow, destinationChild);
endMoveRows();
return true;
}
static inline std::chrono::milliseconds fromJsonValue(const QJsonValue &val)
{
qint64 d = static_cast<qint64>(val.toDouble());
return std::chrono::milliseconds(d);
}
bool AvmSequenceModel::initModel(const QJsonArray &data)
{
m_highlightedRows.clear();
clear();
AvsSequence seq;
for (const auto &ele : data)
{
const QJsonObject &seqObj = ele.toObject();
if (seqObj.isEmpty())
continue;
seq.internalStreamName = seqObj.value(QStringLiteral("stream")).toString();
seq.speed = seqObj.value(QStringLiteral("speed")).toInt();
seq.volume = seqObj.value(QStringLiteral("volume")).toInt();
seq.start = AvaTimePoint(std::chrono::duration_cast<AvaDuration>(fromJsonValue(seqObj.value(QStringLiteral("start")))));
seq.duration = std::chrono::duration_cast<AvaDuration>(fromJsonValue(seqObj.value(QStringLiteral("duration"))));
seq.pint = seqObj.value(QStringLiteral("pint")).toBool();
seq.description = seqObj.value(QStringLiteral("description")).toString();
insertRow(seq);
}
return true;
}
QJsonArray AvmSequenceModel::getSequencesAsJsonArray() const
{
QJsonArray ret;
if (m_vecData.size() <= 0)
return ret;
for (int i = 0; i < m_vecData.size(); ++i)
{
const AvsSequence &seq = m_vecData.at(i);
QJsonObject seqJson;
seqJson.insert(QLatin1String("pos"), i);
seqJson.insert(QLatin1String("description"), seq.description);
seqJson.insert(QLatin1String("start"), static_cast<qint64>(std::chrono::milliseconds(seq.start.time_since_epoch()).count()));
seqJson.insert(QLatin1String("duration"), static_cast<qint64>(std::chrono::milliseconds(seq.duration).count()));
seqJson.insert(QLatin1String("speed"), static_cast<int>(seq.speed));
seqJson.insert(QLatin1String("stream"), seq.internalStreamName);
seqJson.insert(QLatin1String("volume"), static_cast<int>(seq.volume));
seqJson.insert(QLatin1String("pint"), seq.pint);
ret.append(seqJson);
}
return ret;
}
QString AvmSequenceModel::dateTimeToStringx(const QDateTime &dt) const
{
return QDateTime::fromMSecsSinceEpoch(dt.toMSecsSinceEpoch() + (m_timezoneHoursOffset * 3600 * 1000)).toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
}
QString AvmSequenceModel::msecsSinceEpochToStringx(qint64 msecs) const
{
return dateTimeToStringx(QDateTime::fromMSecsSinceEpoch(msecs));
}
QVariant AvmSequenceModel::data(const QModelIndex &idx, int role) const
{
if (role != Qt::DisplayRole && role != Qt::BackgroundRole)
return QVariant();
if (!indexIsValid(idx))
return QVariant();
if (role == Qt::DisplayRole)
{
const AvsSequence &seq = m_vecData.at(idx.row());
switch (idx.column())
{
case Order:
return idx.row() + 1;
case Start:
return msecsSinceEpochToStringx(std::chrono::milliseconds(seq.start.time_since_epoch()).count());
case End:
return msecsSinceEpochToStringx(std::chrono::milliseconds((seq.start+seq.duration).time_since_epoch()).count());
case Duration:
{
qint64 duration = std::chrono::milliseconds(seq.duration).count();
qint64 durationDisplay = duration / seq.speed * 100;
qint64 secs = durationDisplay / 1000;
qint64 msecs = (durationDisplay % 1000);
return QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0'));
}
case Description:
return seq.description;
case Streams:
return seq.internalStreamName;
case Volume:
return QVariant(seq.volume);
case Speed:
return QVariant(seq.speed);
}
}
else if (role == Qt::BackgroundRole)
{
if (m_highlightedRows.contains(idx.row()))
return QColor(Qt::green);
}
return QVariant();
}
void AvmSequenceModel::setHighlightedRow(int highlighted, bool reset)
{
QVector<int> oldSelected = m_highlightedRows;
if (highlighted < 0)
m_highlightedRows.clear();
else if (reset)
m_highlightedRows = { highlighted };
else
m_highlightedRows += highlighted;
if (m_highlightedRows == oldSelected)
return; // no row data changed, nothing to do
QVector<int> changedRows;
changedRows.reserve(oldSelected.size() + m_highlightedRows.size());
if (m_highlightedRows.isEmpty() || reset)
for (int row : qAsConst(oldSelected))
changedRows += row;
for (int row : qAsConst(m_highlightedRows))
changedRows += row;
for (int row : qAsConst(changedRows))
emit dataChanged(index(row, 0), index(row, Description), { Qt::BackgroundRole });
}
void AvmSequenceProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<AvmSequenceModel*>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
bool AvmSequenceProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel ||
source_left.column() != source_right.column() ||
!source_left.isValid() ||
!source_right.isValid())
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
const auto dataRight = m_sourceModel->m_vecData.at(source_right.row());
switch (source_left.column())
{
case AvmSequenceModel::Start:
return std::chrono::milliseconds(dataLeft.start.time_since_epoch()).count() < std::chrono::milliseconds(dataRight.start.time_since_epoch()).count();
case AvmSequenceModel::End:
return std::chrono::milliseconds((dataLeft.start+dataLeft.duration).time_since_epoch()).count() < std::chrono::milliseconds((dataRight.start+dataLeft.duration).time_since_epoch()).count();
case AvmSequenceModel::Order:
case AvmSequenceModel::Speed:
case AvmSequenceModel::Streams:
case AvmSequenceModel::Volume:
case AvmSequenceModel::Description:
break;
}
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
}
} // namespace AudioVideoManagement
@@ -0,0 +1,86 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - AVS Sequence Model
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2017-08-21
*/
#pragma once
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/ava_types.h>
#include <QJsonArray>
namespace AudioVideoManagement
{
class AvsSequence
{
public:
QString internalStreamName;
AvaSpeed speed{100};
AvaVolume volume{0};
AvaTimePoint start;
AvaDuration duration;
bool pint = false;
QString description;
};
class AvmSequenceModel final : public LtsWidgets::LtsAbstractTableModel<AvsSequence>
{
Q_OBJECT
public:
enum Columns
{
Order = 0,
Start,
End,
Duration,
Streams,
Speed,
Volume,
Description
};
explicit AvmSequenceModel(QObject *parent = nullptr);
void moveUp(const QModelIndex &idx);
void moveDown(const QModelIndex &idx);
bool initModel(const QJsonArray &data);
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
QJsonArray getSequencesAsJsonArray() const;
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
QVector<int> highlightedRows() const { return m_highlightedRows; }
void setHighlightedRow(int highlighted, bool reset = true);
friend class AvmSequenceProxyModel;
public slots:
void setTimezoneHoursOffset(int timezoneHoursOffset);
private:
QString dateTimeToStringx(const QDateTime &dt) const;
QString msecsSinceEpochToStringx(qint64 msecs) const;
QVector<int> m_highlightedRows;
int m_timezoneHoursOffset = 0;
LOGGER_T m_logger;
};
class AvmSequenceProxyModel final : public LtsWidgets::LtsColumnFilterProxyModel
{
Q_OBJECT
public:
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
void setSourceModel(QAbstractItemModel *sourceModel) override;
protected:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
private:
AvmSequenceModel *m_sourceModel = nullptr;
};
}
@@ -0,0 +1,684 @@
/**
* @RDE_HEADER@
* Topic Editor for the Situation Topic in the Script Editor
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-04-04
*/
#include "topic_editor.h"
#include "ui_topic_editor.h"
#include <AudioVideoManagement/av_helper.h>
#include <AudioVideoManagement/ava_comm.h>
#include <AudioVideoManagement/ava_session.h>
#include <AudioVideoManagement/avs_dialog.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <LtsSession/lts_session_data.h>
#include <TopicEditorBase/topic_previewer.h>
#include <boost/optional.hpp>
#include <cmath>
#include <QJsonArray>
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
#include <QScopeGuard>
#endif
static constexpr int s_defaultOffset = 10 * 1000; // 10s
namespace AudioVideoManagement
{
QString dateTimeToStringz(const QDateTime &dt)
{
return dt.toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
}
QString msecsSinceEpochToStringz(qint64 msecs)
{
return dateTimeToStringz(QDateTime::fromMSecsSinceEpoch(msecs));
}
qint64 timePointToMsecs(const AvaTimePoint &atp)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(atp.time_since_epoch()).count();
}
AvaDuration msecsToAvaDuration(qint64 msecs)
{
return std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(msecs));
}
AvaTimePoint msecsToAvaTimePoint(qint64 msecs)
{
return AvaTimePoint(msecsToAvaDuration(msecs));
}
TopicEditor::TopicEditor(LtsSession::LtsSession *session, const TopicEditorBase::TopicData &data, QWidget *parent)
: TopicEditorBase::TopicEditor(parent)
, m_session(session)
, m_avmDataIfc(nullptr)
, m_model(new AvmSequenceModel(this))
, m_playbackMenu(nullptr)
, m_ui(new Ui::AVTopicEditor)
, m_logger(getLogger("TopicEditor"))
{
Q_ASSERT(session);
m_ui->setupUi(this);
m_ui->tvSequences->hideColumn(AvmSequenceModel::Order);
init(m_ui->widget);
m_isAudio = (data.topicType == TopicEditorBase::TopicType::Audio);
if (m_isAudio)
setOnlineHelpFilename(QStringLiteral("Script_Editor-Add_Audio_Topic"));
else
setOnlineHelpFilename(QStringLiteral("Script_Editor-Add_Video_Topic"));
setTopicData(data);
m_topicData.properties[QStringLiteral("IsAudio")] = bool(m_topicData.topicType == TopicEditorBase::TopicType::Audio);
m_ui->widget->previewer()->setTopicData(m_topicData);
QJsonArray arr = getTopicVDataAsArray(QLatin1String("Sequences"));
m_model->initModel(arr);
m_ui->tvSequences->resizeColumnsToContents();
refreshDurationText();
m_proxyModel = new AvmSequenceProxyModel(this);
m_proxyModel->setSourceModel(m_model);
m_ui->tvSequences->setModel(m_proxyModel);
connect(m_ui->tvSequences, SIGNAL(doubleClicked(QModelIndex)),
m_ui->tvSequences->selectionModel(), SLOT(select(QModelIndex, QItemSelectionModel::Toggle)));
connect(m_ui->tvSequences->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &TopicEditor::onEnaDisa);
connect(m_ui->pbDelete, &QToolButton::clicked,
this, &TopicEditor::onDeleteSequences);
connect(m_ui->pbUp, &QToolButton::clicked,
this, &TopicEditor::onSequenceUp);
connect(m_ui->pbDown, &QToolButton::clicked,
this, &TopicEditor::onSequenceDown);
connect(m_ui->pbPreview, &QPushButton::clicked, this, [this]()
{
m_playbackMenu->setStreamTime(m_ui->dtStart->msecsSinceEpoch());
});
if (!m_session->getForeignDataInterface_NEW<AudioVideoManagement::DataInterface>(m_avmDataIfc))
{
playbackDlgNotAvailable(tr("The audio/video interface is not available in this session."));
return;
}
PlaybackApp *app = m_isAudio ? m_avmDataIfc->audioPlaybackApp() : m_avmDataIfc->videoPlaybackApp();
if (app == nullptr)
{
playbackDlgNotAvailable(tr("The audio/video interface is currently not available."));
return;
}
if (app->inUse())
{
playbackDlgNotAvailable(tr("The audio/video interface is already in use by another dialog."));
return;
}
// AFTER THIS POINT IT IS GUARANTEED TO HAVE A PLAYBACK DIALOG!
m_ui->pbApply->setEnabled(false);
m_ui->pbDelete->setEnabled(false);
m_ui->pbUp->setEnabled(false);
m_ui->pbDown->setEnabled(false);
// playback menu
boost::optional<QString> stream = boost::none; // will be used as stream preset
if (m_topicData.properties.contains(QStringLiteral("stream")))
stream = m_topicData.properties.value(QStringLiteral("stream")).toString();
// get preset start time from topic (e.g. from evaluation topic)
qint64 startMsecs = 0;
boost::optional<AvaTimePoint> start = boost::none; // will be used as current time preset
{
const QJsonValue jsonValue(getTopicDataValue(QStringLiteral("start")));
if (jsonValue.isDouble())
{
startMsecs = static_cast<qint64>(jsonValue.toDouble());
start = boost::optional<AvaTimePoint>(toTimePoint(startMsecs));
}
}
// get preset end time from topic (e.g. from evaluation topic)
qint64 endMsecs = 0;
{
const QJsonValue jsonValue(getTopicDataValue(QStringLiteral("end")));
if (jsonValue.isDouble())
endMsecs = static_cast<qint64>(jsonValue.toDouble());
}
m_playbackMenu = new AvmPlaybackMenu(this);
m_playbackMenu->init(
m_session, m_avmDataIfc->avaSession(), PlaybackAppHandle(app), m_avmDataIfc->streamNameMapping(), m_isAudio, true /* is inside topic editor */,
stream, start
);
m_ui->playbackParent->layout()->addWidget(m_playbackMenu);
//m_playbackDlg->hideStreamSelection();
connect(m_playbackMenu, &AvmPlaybackMenu::setTopicButtonStates,
this, &TopicEditor::onSetTopicButtonStates);
connect(m_playbackMenu, &AvmPlaybackMenu::markerSelected,
this, &TopicEditor::onMarkerSelected);
connect(m_playbackMenu, &AvmPlaybackMenu::signalCurrentStreamChanged, this, [this](const QString &displayName)
{
m_ui->lblStream->setText(displayName);
});
connect(m_playbackMenu, &AvmPlaybackMenu::signalSetStart, this, [this](qint64 start)
{
m_ui->dtStart->setMsecsSinceEpoch(start);
if (m_ui->dtEnd->msecsSinceEpoch() < start)
m_ui->dtEnd->setMsecsSinceEpoch(start + s_defaultOffset);
});
connect(m_playbackMenu, &AvmPlaybackMenu::signalSetEnd, this, [this](qint64 end)
{
m_ui->dtEnd->setMsecsSinceEpoch(end);
if (m_ui->dtStart->msecsSinceEpoch() > end)
m_ui->dtStart->setMsecsSinceEpoch(end - s_defaultOffset);
});
m_playbackMenu->show();
// NOTE: don't confuse the time *inside* the script timeline with a time since epoch!! following code lines do not make sense:
// // initial values
if (m_topicData.startTime > -1)
{
m_ui->dtStart->setMsecsSinceEpoch(static_cast<qint64>(m_topicData.startTime) * 1000);
if (m_topicData.duration > 0)
m_ui->dtEnd->setMsecsSinceEpoch(static_cast<qint64>(m_topicData.startTime + m_topicData.duration) * 1000);
} // if
// the playback dialog part of the topic editor
connect(m_ui->tvSequences, &QTableView::doubleClicked,
this, &TopicEditor::onEditSequence);
connect(m_ui->dtStart, &LtsWidgets::LtsMSecDateTimeEdit::dateTimeChanged,
this, &TopicEditor::onTimeChanged);
connect(m_ui->dtEnd, &LtsWidgets::LtsMSecDateTimeEdit::dateTimeChanged,
this, &TopicEditor::onTimeChanged);
connect(m_ui->pbAdd, &QToolButton::clicked,
this, &TopicEditor::onAddSequence);
connect(m_ui->pbApply, &QToolButton::clicked,
this, &TopicEditor::onApplySequence);
connect(m_ui->pbEditSequence, &QPushButton::clicked,
this, &TopicEditor::onEditSequence);
// if start and end times were set (e.g. by evaluation topic), then auto set them as start & end time for the new sequence
if (startMsecs > 0 && endMsecs > 0)
{
m_ui->dtStart->setMsecsSinceEpoch(startMsecs);
m_ui->dtEnd->setMsecsSinceEpoch(endMsecs);
}
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
m_ui->dtStart, &LtsWidgets::LtsMSecDateTimeEdit::setTimezoneHoursOffset);
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
m_ui->dtEnd, &LtsWidgets::LtsMSecDateTimeEdit::setTimezoneHoursOffset);
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
m_model, &AvmSequenceModel::setTimezoneHoursOffset);
int offset = m_playbackMenu->currentOffset();
m_model->setTimezoneHoursOffset(offset); // as the emit happens before the commits!
m_ui->dtStart->setTimezoneHoursOffset(offset);
m_ui->dtEnd->setTimezoneHoursOffset(offset);
}
TopicEditor::~TopicEditor()
{
delete m_ui;
delete m_model;
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE
void TopicEditor::updateInternalData()
{
if (hasTopicDataProperties())
{
m_topicStartTime = getTopicDataValue(QStringLiteral("start")).toDouble();
m_topicStreamName = getTopicDataValue(QStringLiteral("stream")).toString();
QJsonArray arr = getTopicVDataAsArray(QLatin1String("Sequences"));
m_model->initModel(arr);
m_ui->tvSequences->resizeColumnsToContents();
setDurationText(calcTrackLength(arr));
}
m_ui->pbApply->setEnabled(false);
m_ui->pbDelete->setEnabled(false);
}
void TopicEditor::retranslate()
{
// translate(m_ui, trackEntryType() == TopicType::Audio ? tr("Audio Topic Editor") : tr("Video Topic Editor") );
translate(m_ui, m_topicData.topicType == TopicEditorBase::TopicType::Audio ? tr("Audio Topic Editor") : tr("Video Topic Editor") );
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setText(tr("Apply to topic", "Auf Topic übernehmen"));
}
AvsSequence TopicEditor::createSequence(const AvaTimePoint &start, const AvaTimePoint &end) const
{
AvsSequence seq;
seq.start = start;
seq.duration = end - start; // not sure if AVS needs duration or end
seq.description = m_ui->leDescription->text();
if (m_playbackMenu != nullptr)
{
seq.speed = m_playbackMenu->getSpeed();
seq.volume = m_playbackMenu->getVolume();
seq.internalStreamName = m_playbackMenu->getInternalStreamName();
seq.pint = m_playbackMenu->getSkipPause();
}
return seq;
}
AvsSequence TopicEditor::createCurrentSequence() const
{
return createSequence(AvaTimePoint(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(m_ui->dtStart->msecsSinceEpoch()))),
AvaTimePoint(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(m_ui->dtEnd->msecsSinceEpoch()))));
}
void TopicEditor::refreshDurationText()
{
setDurationText(calcTrackLength(m_model->getSequencesAsJsonArray()));
}
void TopicEditor::setDurationText(qint64 length)
{
if (length > s_durationLimit)
m_ui->lblDuration->setText(tr("Playback too long!"));
else
m_ui->lblDuration->setText(QTime::fromMSecsSinceStartOfDay(length).toString(QStringLiteral("HH:mm:ss.zzz")));
}
qint64 TopicEditor::calcTrackLength(const QJsonArray &arr) const
{
qint64 length = 0;
for (const auto &ele : arr)
{
const QJsonObject &seqObj = ele.toObject();
if (seqObj.isEmpty())
continue;
// length += std::ceil(seqObj.value(QLatin1String("duration")).toDouble() * (std::fabs(seqObj.value(QLatin1String("speed")).toDouble(100)) / 100)); Original
length += std::ceil(seqObj.value(QLatin1String("duration")).toDouble() / (std::fabs(seqObj.value(QLatin1String("speed")).toDouble(100))) * 100 );
}
return length;
}
void TopicEditor::playbackDlgNotAvailable(const QString &msg)
{
new LtsWidgets::LtsMessageDialogNew(this, msg);
m_ui->groupBox->hide();
m_ui->line->hide();
// left side:
m_ui->pbEditSequence->hide();
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE SLOTS
bool TopicEditor::apply2()
{
const QJsonArray &arr = m_model->getSequencesAsJsonArray();
qint64 duration = calcTrackLength(arr);
if (duration > s_durationLimit)
{
new LtsWidgets::LtsMessageDialogNew(this, tr("This topic has exceeded the maximum length of %1 seconds.").arg(s_durationLimit));
return false;
}
setDurationText(duration);
if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("AVM.AllowIncompleteTopic")) != QVariant(true)) // don't execute code block if AllowIncompleteTopic is set !
{
if (duration <= 0)
{
new LtsWidgets::LtsMessageDialogNew(
this, tr("The total playback length is less than 1 second long. <br>"
"Please verify that you have one or more sequences in the sequences list and that these sequences are of at least 1 second length."),
tr("Playback length too short")
);
return false;
}
qint64 secs = duration / 1000; // strip msecs ...
qint64 msecs = duration - (secs * 1000); // ... and set to zero !
qWarning() << "secs" << secs << "msecs" << msecs;
// a ratio between 500 missing mseconds of a video and 500 msecs of no video output seems balanced
if (msecs >= 500)
++secs;
qWarning() << "secs now" << secs;
m_topicData.duration = secs;
}
else // AllowIncompleteTopic is set! fake it!
m_topicData.duration = 10;
setTopicDataProperty(QStringLiteral("Sequences"), m_model->getSequencesAsJsonArray());
setTopicDataProperty(QStringLiteral("ScriptName"), QString(QStringLiteral("S") + QString::number(m_topicData.id)));
setTopicDataProperty(QStringLiteral("IsAudio"), bool(m_topicData.topicType == TopicEditorBase::TopicType::Audio));
setDescription(m_ui->widget->description());
m_ui->widget->previewer()->setTopicData(m_topicData);
return true;
}
void TopicEditor::onEditSequence()
{
if (!m_playbackMenu)
return;
DEBUG_LOG(m_logger) << "slotEditSequence ";
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
if (selRows.size() != 1)
{
m_model->setHighlightedRow(-1);
return;
}
int row = selRows.at(0).row();
m_model->setHighlightedRow(row);
const AvsSequence selectedSeq = m_model->getRowData(row);
DEBUG_LOG(m_logger) << "selectedSeq.internalStreamName=" << selectedSeq.internalStreamName;
m_playbackMenu->setStreamName(selectedSeq.internalStreamName);
m_ui->dtStart->setMsecsSinceEpoch(std::chrono::milliseconds(selectedSeq.start.time_since_epoch()).count());
m_ui->dtEnd->setMsecsSinceEpoch(std::chrono::milliseconds((selectedSeq.start + selectedSeq.duration).time_since_epoch()).count());
m_ui->leDescription->setText(selectedSeq.description);
m_playbackMenu->setStreamByName(selectedSeq.internalStreamName);
m_playbackMenu->setSpeed(selectedSeq.speed);
m_playbackMenu->setVolume(selectedSeq.volume);
m_playbackMenu->setSkipPause(selectedSeq.pint);
m_playbackMenu->setSavedPos(m_ui->dtStart->msecsSinceEpoch()); // plays that timepoint as soon as the stream was selected and QueryTimeline was successful not working.. : m_playbackDlg->setStreamTime(m_ui->dteStart->msecsSinceEpoch());
m_ui->pbApply->setEnabled(true);
}
void TopicEditor::onTimeChanged()
{
/* user gets a warning on 'add' if times don't add up, otherwise there is too much movement in the end-time dte! */
// LtsWidgets::LtsMSecDateTimeEdit *dtSender = qobject_cast<LtsWidgets::LtsMSecDateTimeEdit*>(sender());
// if (!dtSender)
// return;
// if (dtSender == m_ui->dtEnd)
// {
// if (m_ui->dtEnd->msecsSinceEpoch() < m_ui->dtStart->msecsSinceEpoch())
// {
// qint64 duration = static_cast<qint64>(m_trackEntry->m_length > 0 ? m_trackEntry->m_length : s_defaultOffset) * 1000;
// m_ui->dteStart->setMsecsSinceEpoch(m_ui->dteEnd->msecsSinceEpoch() - duration);
// } // if
// } // if
// else // start is sender
// {
// if (m_ui->dteEnd->msecsSinceEpoch() < m_ui->dteStart->msecsSinceEpoch())
// {
// qint64 duration = static_cast<qint64>(m_trackEntry->m_length > 0 ? m_trackEntry->m_length : s_defaultOffset) * 1000;
// m_ui->dteEnd->setMsecsSinceEpoch(m_ui->dteStart->msecsSinceEpoch() + duration);
// } // if
// } // else
}
void TopicEditor::onApplySequence()
{
const auto seq = createCurrentSequence();
if (seq.start.time_since_epoch().count() == 0 || seq.duration.count() == 0)
return;
const auto rows = m_model->highlightedRows();
if (rows.count() != 1)
{
new LtsWidgets::LtsMessageDialogNew(this, tr("Please select a sequence from the sequence list first, then click 'Edit' to edit a sequence in the editor."));
return;
}
m_model->setRowData(rows.first(), seq);
refreshDurationText();
slotChangesMade();
}
void TopicEditor::onAddSequence()
{
qint64 start = m_ui->dtStart->msecsSinceEpoch();
qint64 end = m_ui->dtEnd->msecsSinceEpoch();
if (start == 0 || end == 0 || end < start)
{
new LtsWidgets::LtsMessageDialogNew(this, tr("Please check if start and end times are set and the end time is not lower than the start time."),
tr("The start and/or end time you entered is invalid"));
return;
}
AvsSequence seq = createCurrentSequence();
if (seq.pint) // if empty parts should be skipped, the duration has to be calculated (as there can be more than 1 skipped / empty part in a sequence)
{
// async!
AvaTimePoint theStart = msecsToAvaTimePoint(m_ui->dtStart->msecsSinceEpoch());
AvaTimePoint theEnd = msecsToAvaTimePoint(m_ui->dtEnd->msecsSinceEpoch());
QueryTimeline *qt = m_playbackMenu->queryTimeline(theStart, theEnd);
if (qt == nullptr)
{
new LtsWidgets::LtsMessageDialogNew(this, tr("Currently unable to request sequences from the audio / video system.<br />Please try again later."));
return;
}
setOverlay(new QWidget());
connect(qt, &QueryTimeline::signalTimelineComplete,
this, [this, theStart, theEnd](const AvaTimeline &timeline)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
auto unchangableReset = qScopeGuard([this] { unsetOverlay(); });
#else
std::shared_ptr<int> unchangeableReset(nullptr, [this](int *) { unsetOverlay(); });
#endif
static const QString msgTitle(tr("Sequences needed to be determined for audio / video clips with skipped silence"));
const QVector<AvaTimelinePart> &parts = timeline.parts();
if (parts.isEmpty())
{
new LtsWidgets::LtsMessageDialogNew(this, tr("No sequences could be found inside the specified range. <br />Please select another range that contains audio / video."),
msgTitle);
return;
}
QStringList partsStrings;
for (const AvaTimelinePart &part : parts)
{
AvaTimePoint start = part.first;
AvaTimePoint end = part.second;
if (theStart > start)
start = theStart;
if (theEnd < end)
end = theEnd;
qint64 duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
qint64 secs = duration / 1000;
qint64 msecs = (duration % 1000);
QString durationString(QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0')));
partsStrings += QStringLiteral("<span>%1 - %2 (%3 %4)</span>").arg(msecsSinceEpochToStringz(std::chrono::milliseconds(start.time_since_epoch()).count()),
msecsSinceEpochToStringz(std::chrono::milliseconds(end.time_since_epoch()).count()),
durationString,
tr("seconds"));
}
// Patrick said the message is not needed
//#define AskUserSequenceList
#ifdef AskUserSequenceList
auto msg = new LtsWidgets::LtsMessageDialogNew(this, tr("The following sequences were determined: <br /> <br /> %1 <br /> <br />Do you want to add the listed sequences to the sequences list?").arg(partsStrings.join(QStringLiteral("<br />"))),
msgTitle, LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel, { { LtsWidgets::Button::Yes, tr("Add") } });
connect(msg, &LtsWidgets::LtsMessageDialogNew::yes,
this, [this, parts, theStart, theEnd]()
#endif
{
m_model->setHighlightedRow(-1); // initial highlight reset
for (const AvaTimelinePart &part : parts)
{
AvaTimePoint start = part.first;
AvaTimePoint end = part.second;
if (theStart > start)
start = theStart;
if (theEnd < end)
end = theEnd;
m_model->insertRow(createSequence(start, end));
int row = m_model->rowCount() - 1;
QModelIndex idx = m_model->index(row, 0);
if (!idx.isValid())
{
new LtsWidgets::LtsMessageDialogNew(this, tr("One or more sequences could not be added to the sequences list. <br />Please try again later."),
msgTitle);
return;
}
m_model->setHighlightedRow(row, false /* add, don't reset after adding 1 of n rows */);
m_ui->tvSequences->setCurrentIndex(idx);
}
refreshDurationText();
m_ui->pbApply->setEnabled(true); // new seq now editable
slotChangesMade();
}
#ifdef AskUserSequenceList
);
#endif
});
return;
} // if (seq.pint)
if (seq.start.time_since_epoch().count() == 0 || seq.duration.count() == 0)
{
new LtsWidgets::LtsMessageDialogNew(this, tr("Start and end times are not valid. Please check if your input values are valid."));
return;
}
m_model->insertRow(seq);
int row = m_model->rowCount() - 1;
QModelIndex idx = m_model->index(row, 0);
if (!idx.isValid())
return;
m_model->setHighlightedRow(row);
m_ui->tvSequences->setCurrentIndex(idx);
refreshDurationText();
m_ui->pbApply->setEnabled(true); // new seq now editable
slotChangesMade();
}
void TopicEditor::onDeleteSequences()
{
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
QVector<int> rowIds;
rowIds.reserve(selRows.size());
for (const QModelIndex &idx : selRows)
rowIds += idx.row();
std::sort(rowIds.begin(), rowIds.end(), std::greater<int>());
const auto rows = m_model->highlightedRows();
int curEditedRow = -1;
if (rows.count() == 1)
curEditedRow = rows.first();
for (int row : qAsConst(rowIds))
{
m_model->removeRow(row);
if (curEditedRow == row)
{
curEditedRow = -1;
m_model->setHighlightedRow(curEditedRow);
m_ui->pbApply->setEnabled(false);
onEnaDisa();
}
if (curEditedRow > row)
--curEditedRow;
}
m_model->setHighlightedRow(curEditedRow);
refreshDurationText();
slotChangesMade();
}
void TopicEditor::onSequenceUp()
{
m_model->moveUp(m_ui->tvSequences->currentIndex());
slotChangesMade();
}
void TopicEditor::onSequenceDown()
{
m_model->moveDown(m_ui->tvSequences->currentIndex());
slotChangesMade();
}
void TopicEditor::onSetTopicButtonStates(bool enable)
{
if (enable && m_topicStartTime)
m_ui->dtStart->setMsecsSinceEpoch(m_topicStartTime);
}
void TopicEditor::onEnaDisa()
{
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
const bool hasSelection = (selRows.size() != 0);
const bool hasSingleSelection = (selRows.size() == 1);
m_ui->pbEditSequence->setEnabled(hasSingleSelection);
m_ui->pbDelete->setEnabled(hasSelection);
m_ui->pbDown->setEnabled(hasSingleSelection);
m_ui->pbUp->setEnabled(hasSingleSelection);
}
void TopicEditor::onMarkerSelected(const QString &description)
{
m_ui->leDescription->setText(description);
}
} // AudioVideoManagement FileManagement
@@ -0,0 +1,80 @@
/**
* @RDE_HEADER@
* Topic Editor for the Situation Topic in the Script Editor
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-04-04
*/
#pragma once
#include "avm_sequence_model.h"
#include "../avm_playback_menu.h"
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/logger.h>
#include <LtsSession/lts_session.h>
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <TopicEditorBase/topic_editor_base.h>
namespace Ui { class AVTopicEditor; }
namespace AudioVideoManagement
{
class TopicEditor : public TopicEditorBase::TopicEditor
{
Q_OBJECT
public:
explicit TopicEditor(LtsSession::LtsSession *session, const TopicEditorBase::TopicData &data, QWidget *parent = nullptr);
~TopicEditor();
private:
void updateInternalData() override final;
void retranslate() override final;
AvsSequence createSequence(const AvaTimePoint &start, const AvaTimePoint &end) const;
AvsSequence createCurrentSequence() const;
void refreshDurationText();
void setDurationText(qint64 length);
qint64 calcTrackLength(const QJsonArray &arr) const;
void playbackDlgNotAvailable(const QString &msg);
private slots:
bool apply2() override final;
void onEditSequence();
void onTimeChanged();
void onApplySequence();
void onAddSequence();
void onDeleteSequences();
void onSequenceUp();
void onSequenceDown();
void onSetTopicButtonStates(bool enable);
void onEnaDisa();
void onMarkerSelected(const QString &description);
private:
const int s_durationLimit{86400000};
LtsSession::LtsSession *m_session{nullptr};
std::shared_ptr<AudioVideoManagement::DataInterface> m_avmDataIfc{nullptr};
AvmSequenceModel *m_model{nullptr};
AvmSequenceProxyModel *m_proxyModel{nullptr};
AvmPlaybackMenu *m_playbackMenu{nullptr};
bool m_isAudio{false};
qint64 m_topicStartTime{0};
QString m_topicStreamName;
Ui::AVTopicEditor *m_ui{nullptr};
NGL::INGLLogger &m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,457 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AVTopicEditor</class>
<widget class="QWidget" name="AVTopicEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1150</width>
<height>577</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Script Editor</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1" rowspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lbxSequences">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string>Sequences:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbxTopicDuration">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string>Topic duration:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblDuration">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string notr="true">0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="3">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="3">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="3">
<widget class="QToolButton" name="pbDown">
<property name="toolTip">
<string>Sequence Down</string>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/sort-down.svg</normaloff>:/style/sort-down.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QToolButton" name="pbUp">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/sort-up.svg</normaloff>:/style/sort-up.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
</widget>
</item>
<item row="5" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="pbDelete">
<property name="toolTip">
<string>Delete selected sequence from list</string>
</property>
<property name="text">
<string>Delete sequence</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/delete.svg</normaloff>:/style/delete.svg</iconset>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QPushButton" name="pbEditSequence">
<property name="toolTip">
<string>Edit selected sequence from list in the right side of the layout</string>
</property>
<property name="text">
<string>Edit sequence</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/document-edit.svg</normaloff>:/style/document-edit.svg</iconset>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="4" colspan="3">
<widget class="QTableView" name="tvSequences">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="TopicEditorBase::TopicPreviewWidget" name="widget" native="true"/>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="2" rowspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Edit / Create sequence</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0,0,0">
<item>
<widget class="QWidget" name="playbackParent" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="lbxStrean">
<property name="text">
<string>Stream:</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="lbxEndTime">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string>End Time:</string>
</property>
</widget>
</item>
<item row="1" column="8">
<widget class="QPushButton" name="pbPreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Set as current time</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="lbxDescription">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbxStartTime">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string>Start Time:</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtEnd"/>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLabel" name="lblStream">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item row="1" column="7">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="5" colspan="4">
<widget class="QLineEdit" name="leDescription"/>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtStart"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pbApply">
<property name="text">
<string>Apply changes to sequence in list</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/dialog-ok-apply.svg</normaloff>:/style/dialog-ok-apply.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAdd">
<property name="text">
<string>Append as new sequence to list</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/document-new.svg</normaloff>:/style/document-new.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>TopicEditorBase::TopicPreviewWidget</class>
<extends>QWidget</extends>
<header>TopicEditorBase/topic_preview_widget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsMSecDateTimeEdit</class>
<extends>QDateTimeEdit</extends>
<header>LtsWidgets/lts_msec_date_time_edit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tvSequences</tabstop>
<tabstop>pbUp</tabstop>
<tabstop>pbDown</tabstop>
<tabstop>pbDelete</tabstop>
<tabstop>pbEditSequence</tabstop>
<tabstop>leDescription</tabstop>
<tabstop>pbPreview</tabstop>
<tabstop>pbApply</tabstop>
<tabstop>pbAdd</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,323 @@
/**
* @RDE_HEADER@
* Radio TreeModel
* $Id$
* $HeadURL$
* $Author$
*/
#include "avm_available_streams_model.h"
#include <AudioVideoManagement/stream_name_mapping.h>
#include <InfrastructureManagement/ism.hpp>
#include <nglwidgets/ngltreeitem.h>
#include <QSet>
#include <QMimeData>
#include <QCollator>
namespace
{
bool isMountingPointOfInterest(const InfrastructureManagement::StructureElement &se, bool isAudio)
{
if (se.structureElementTypeId() != InfrastructureManagement::StructureElementType::MountingPoint)
return false;
// Cameras are relevant for both, audio and video
// The problem for audio here is that we cannot know yet whether later
// there will be a camera mounted that is capable of audio.
if (se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::IndoorCamera ||
se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::OutdoorCamera)
return true;
if (isAudio && se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::AnnouncementUnit)
return true;
return false;
}
} // anonymous namespace
namespace AudioVideoManagement
{
const QString AvailableStreamsModel::s_MimeType = QStringLiteral("AvailStreamsModel/DragDrop");
struct sorter
{
bool operator()(const StreamModelData &i, const StreamModelData &j)
{
QCollator col;
col.setNumericMode(true);
return col.compare(j.name, i.name) > 0 ;
}
} sortStuff;
AvailableStreamsModel::AvailableStreamsModel(const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
LtsSession::LtsSession *session,
StreamNameMapping *streamNameMapping,
QObject *parent,
bool isAudio)
: NGLTreeModel(parent, new NGLTreeItem(QVariant(), QIcon()), {tr("Available Streams")})
, m_isAudio(isAudio)
, m_avmDtc(avmDtc)
, m_ismDtc(ismDtc)
, m_session(session)
, m_streamNameMapping(streamNameMapping)
, m_logger(getLogger("AvailableStreamsModel"))
{
Q_ASSERT(ismDtc);
Q_ASSERT(streamNameMapping);
qRegisterMetaTypeStreamOperators<StreamModelData>("StreamModelData");
qRegisterMetaType<StreamModelData>("StreamModelData");
setMimeType(s_MimeType);
connect(m_ismDtc.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged,
this, [ this]()
{
initModel();
});
connect(m_streamNameMapping, &StreamNameMapping::changed,
this, [ this]()
{
initModel();
});
if (!m_isAudio)
{
connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::videoTeamChanged,
this, [ this]()
{
initModel();
});
}
initModel();
}
QVariant AvailableStreamsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
NGLTreeItem *item = privateData(index);
if (!item->data().canConvert<StreamModelData>())
return QVariant();
const StreamModelData availStream = item->data().value<StreamModelData>();
switch (role)
{
case Qt::DisplayRole:
{
switch (index.column())
{
case 0 :
return availStream.name;
default:
return QStringLiteral("??");
}
break;
}
case Qt::DecorationRole:
{
switch (index.column())
{
case 0:
{
static const QIcon folder(QStringLiteral("icon:evaluation_folder.svg"));
static const QIcon camera(QStringLiteral("icon:camera_control.svg"));
return availStream.isGroup ? folder : camera;
}
default:
break;
}
break;
}
default:
break;
} // switch
return NGLTreeModel::data(index, role);
}
Qt::ItemFlags AvailableStreamsModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = NGLTreeModel::flags(index);
NGLTreeItem *item = privateData(index);
if (item == m_rootItem)
return defaultFlags;
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
}
QMimeData *AvailableStreamsModel::mimeData(const QModelIndexList &indexes) const
{
// Use display role (textual name of stream) for identification when dropping mimeData
QMimeData *pData = NGLTreeModel::mimeData(indexes);
pData->setText(data(indexes[0]).toString());
return pData;
}
bool AvailableStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
INFO_LOG(m_logger) << "dropMimeData: action = " << action
<< ", row = " << row
<< ", parent: valid = " << parent.isValid();
if (action == Qt::IgnoreAction)
return true;
if (!data->hasFormat(s_MimeType))
return false;
if ((action != Qt::MoveAction) && (action != Qt::CopyAction))
return false;
return NGLTreeModel::dropMimeData(data, action, row, column, parent);
}
void AvailableStreamsModel::initModel()
{
beginResetModel();
INFO_LOG(m_logger) << "initModel";
// build tree
// Ignore NOT installed streams
m_rootItem->removeAllChildren();
// group to bundle streams
StreamModelData availStream;
availStream.id = 0;
availStream.name = tr("New group");
availStream.parentId = -1;
availStream.isGroup = true;
QVariant data;
data.setValue(availStream);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
std::vector<StreamModelData> toAddIndoor;
std::vector<StreamModelData> toAddOutdoor;
std::vector<StreamModelData> toAddMvs;
const auto &mountedAssets = m_ismDtc->mountingPointAssets();
const auto &buildings = m_ismDtc->mainStructureElements();
for (const auto &building : buildings)
{
for (const auto &mountingPoint : building.elements())
{
if (!isMountingPointOfInterest(mountingPoint, m_isAudio))
continue;
//check for mounted
bool isMounted = false;
for (const auto &mountedAsset : mountedAssets)
{
if (mountedAsset.structureElementId() == mountingPoint.id())
{
isMounted = true;
break;
}
}
if (!isMounted)
continue;
StreamModelData item;
item.name = m_streamNameMapping->displayName(mountingPoint.id());
item.mountingPointId = mountingPoint.id();
item.isGroup = false;
// What about id and parentId?
if (mountingPoint.mountingPointTypeId() == InfrastructureManagement::MountingPointType::IndoorCamera)
toAddIndoor.push_back(item);
else
toAddOutdoor.push_back(item);
// m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
}
//add MVS in video list
if (!m_isAudio)
{
const auto &mvsStreams = m_avmDtc->videoTeams();
for (const auto &videoTeam : mvsStreams)
{
if (videoTeam.exerciseId() != m_session->exerciseId())
continue;
DEBUG_LOG(m_logger) << "add new videoteam" << videoTeam;
StreamModelData item;
item.name = QString::fromStdString(videoTeam.streamName().c_str());
item.mountingPointId = -1 * videoTeam.id(); // negative id for mvs
item.isGroup = false;
// data.setValue(item);
toAddMvs.push_back(item);
//m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
}
//sort
// using object as comp
std::sort(toAddOutdoor.begin(), toAddOutdoor.end(), sortStuff);
for (const auto &item : toAddOutdoor)
{
data.setValue(item);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
std::sort(toAddIndoor.begin(), toAddIndoor.end(), sortStuff);
for (const auto &item : toAddIndoor)
{
data.setValue(item);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
std::sort(toAddMvs.begin(), toAddMvs.end(), sortStuff);
for (const auto &item : toAddMvs)
{
data.setValue(item);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
endResetModel();
}
StreamModelData StreamModelData::createFromServiceItem(const AvMenuEntry &entry, StreamNameMapping *streamNameMapping, const QMap<int, VideoTeam> &videoTeamList)
{
StreamModelData data;
data.id = entry.id();
data.parentId = entry.parentId();
data.isGroup = entry.isGroup();
if (entry.isGroup())
data.name = QString::fromStdString(entry.name().get());
else
{
if (entry.mountingPointId().is_set() && (entry.mountingPointId().get() > 0))
{
data.mountingPointId = entry.mountingPointId().get();
data.name = streamNameMapping->displayName(entry.mountingPointId().get());
}
else
{
data.mountingPointId = entry.mountingPointId().get();
data.name = QString::fromStdString(videoTeamList.value(entry.mountingPointId().get() * -1).streamName());
}
}
return data;
}
} // Namespace AudioVideoManagement
@@ -0,0 +1,113 @@
/**
* @RDE_HEADER@
* Radio TreeModel
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm.hpp>
#include <AudioVideoManagement/avm_dtc.h>
#include <LtsSession/lts_session.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <nglwidgets/ngltreemodel.h>
#include <QDataStream>
class NGLTreeItem;
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class StreamNameMapping;
/**
* Used by AvailableStreamsModel and UsedStreamsModel.
*/
struct StreamModelData
{
qint64 id = 0;
qint64 parentId = 0;
bool isGroup = false;
QString name;
qint32 mountingPointId = 0;
static StreamModelData createFromServiceItem(const AvMenuEntry &entry, StreamNameMapping *streamNameMapping, const QMap<int, VideoTeam> &videoTeamList);
friend QDataStream &operator<<(QDataStream &out,
const StreamModelData &myObj)
{
out << myObj.id
<< myObj.parentId
<< myObj.isGroup
<< myObj.name
<< myObj.mountingPointId;
return out;
}
friend QDataStream &operator>>(QDataStream &in,
StreamModelData &myObj)
{
in >> myObj.id
>> myObj.parentId
>> myObj.isGroup
>> myObj.name
>> myObj.mountingPointId;
return in;
}
};
/**
* Used by MenuEdit.
*
* @see UsedStreamsModel
*/
class AvailableStreamsModel : public NGLTreeModel
{
Q_OBJECT
public:
static const QString s_MimeType;
AvailableStreamsModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
LtsSession::LtsSession *session,
StreamNameMapping *streamNameMapping,
QObject *parent,
bool isAudio);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
Qt::DropActions supportedDragActions() const override { return Qt::CopyAction; }
private:
void initModel();
const bool m_isAudio;
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc;
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
LtsSession::LtsSession *m_session;
StreamNameMapping *m_streamNameMapping;
NGL::INGLLogger &m_logger;
};
} // namespace AudioVideoManagement
Q_DECLARE_METATYPE(AudioVideoManagement::StreamModelData)
@@ -0,0 +1,128 @@
/**
* @RDE_HEADER@
* AudioVideoManagement - Menu Struct Edit Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-10-17
*/
#include "avm_choose_menu.h"
#include "ui_avm_choose_menu.h"
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/avm.hpp>
#include <AudioVideoManagement/avs_menus.h>
#include <LtsUtilsCore/data_modification_enum.h>
#include <LtsSession/lts_session.h>
namespace AudioVideoManagement
{
ChooseMenu::ChooseMenu(Interface::Dtc *dtc, AvsMenus *avsMenus, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_dtc(dtc)
, m_avsMenus(avsMenus)
, m_ui(new Ui::ChooseMenuStruct)
, m_logger(getLogger("ChooseMenu"))
{
Q_ASSERT(dtc);
Q_ASSERT(avsMenus);
m_ui->setupUi(this);
connect(dtc, &Interface::Dtc::menuStructChanged, this, &ChooseMenu::onMenuStructChanged);
m_dtc->menuStructs([this](const MenuStruct & menu) { onMenuStructChanged(menu, DataModification::eAdded);});
selectCurrent();
connect(m_ui->cbAudio, &QComboBox::currentTextChanged, this, &ChooseMenu::slotChangesMade);
connect(m_ui->cbVideo, &QComboBox::currentTextChanged, this, &ChooseMenu::slotChangesMade);
setOnlineHelpFilename(QStringLiteral("Situation-Choose_AV_Menu"));
}
//-----------------------------------------------------------------------------
ChooseMenu::~ChooseMenu()
{
delete m_ui;
}
void ChooseMenu::retranslate()
{
translate(m_ui, tr("Choose AV Menu"));
}
//-----------------------------------------------------------------------------
void ChooseMenu::onMenuStructChanged(const MenuStruct &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
{
if (data.isAudio())
m_ui->cbAudio->addItem(QString::fromStdString(data.name()), data.id());
else
m_ui->cbVideo->addItem(QString::fromStdString(data.name()), data.id());
break;
}
case DataModification::eRemoved:
{
if (data.isAudio())
{
int index = m_ui->cbAudio->findData(data.id());
m_ui->cbAudio->removeItem(index);
}
else
{
int index = m_ui->cbVideo->findData(data.id());
m_ui->cbVideo->removeItem(index);
}
break;
}
case DataModification::eModified:
break;
}
}
//-----------------------------------------------------------------------------
bool ChooseMenu::apply()
{
int menuId = m_ui->cbAudio->currentData().toInt();
INFO_LOG(m_logger) << "MenuId Audio: " << menuId;
m_avsMenus->setAnnouncementMenu(menuId);
menuId = m_ui->cbVideo->currentData().toInt();
INFO_LOG(m_logger) << "MenuId Video: " << menuId;
m_avsMenus->setVideoMenu(menuId);
return true;
}
void ChooseMenu::selectCurrent()
{
selectMenu(m_avsMenus->currentAnnouncementMenu(), m_ui->cbAudio);
selectMenu(m_avsMenus->currentVideoMenu(), m_ui->cbVideo);
}
void ChooseMenu::selectMenu(int menuId, QComboBox *cb)
{
Q_ASSERT(cb);
if (menuId < 0)
return;
int index = cb->findData(menuId);
if (index < 0)
return;
cb->setCurrentIndex(index);
}
//-----------------------------------------------------------------------------
} // namespace AudioVideoManagement
@@ -0,0 +1,62 @@
/**
* @RDE_HEADER@
* AudioVideoManagement - Menu Struct Edit Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-10-17
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
enum class DataModification;
namespace Ui
{
class ChooseMenuStruct;
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
class MenuStruct;
class AvsMenus;
class ChooseMenu : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
ChooseMenu(Interface::Dtc *dtc, AvsMenus *avsMenus, QWidget *parent = nullptr);
~ChooseMenu() override;
private slots:
void onMenuStructChanged(const MenuStruct &data, DataModification modType);
private:
void retranslate() override;
bool apply() override final;
void selectCurrent();
void selectMenu(int menuId, QComboBox *cb);
Interface::Dtc *m_dtc;
AvsMenus *m_avsMenus;
Ui::ChooseMenuStruct *m_ui;
NGL::INGLLogger &m_logger;
};
} //namespace AudioVideoManagement
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChooseMenuStruct</class>
<widget class="QWidget" name="ChooseMenuStruct">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>596</width>
<height>102</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Choose Menu</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Choose Audio Menu:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="cbAudio"/>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="LtsWidgets::LtsComboBox" name="cbVideo"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Choose Video Menu:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,159 @@
/**
* @RDE_HEADER@
* AudioVideoManagement - Static Meteo Data Edit Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-10-17
*/
#include "avm_menu_edit.h"
#include "ui_avm_menu_edit.h"
#include <AudioVideoManagement/avm_dtc.h>
namespace AudioVideoManagement
{
MenuEdit::MenuEdit(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ftc(nullptr)
, m_ui(new Ui::MenuEdit)
, m_availStreamsModel(nullptr)
, m_usedStreamsModel(nullptr)
, m_isAudio(false)
, m_logger(getLogger("AudioVideoManagementEdit"))
, m_requestHelper(new LtsUtils::RequestHelper(this))
{
m_ui->setupUi(this);
} // end of ctor()
//-----------------------------------------------------------------------------
MenuEdit::~MenuEdit()
{
delete m_ui;
} // end of dtor()
//-----------------------------------------------------------------------------
void MenuEdit::retranslate()
{
if (m_isAudio)
translate(m_ui, tr("Edit Audio Menu: %1").arg(m_name));
else
translate(m_ui, tr("Edit Video Menu: %1").arg(m_name));
} // end of retranslate()
//-----------------------------------------------------------------------------
bool MenuEdit::init(LtsSession::LtsSession *session,
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
const MenuStruct &ms)
{
DEBUG_LOG(m_logger) << "MenuEdit::init()";
if (!ftc || !dtc)
{
ERROR_LOG(m_logger) << "MenuEdit::init() Invalid shared_ptr!";
return false;
}
m_ftc = ftc;
m_isAudio = ms.isAudio();
m_name = QString::fromStdString(ms.name());
m_availStreamsModel = new AvailableStreamsModel(dtc, ismDtc, session, streamNameMapping, this, m_isAudio);
m_ui->availTreeView->setModel(m_availStreamsModel);
m_usedStreamsModel = new UsedStreamsModel(dtc, ismDtc, ms, streamNameMapping, this);
m_ui->menuTreeView->setModel(m_usedStreamsModel);
m_ui->menuTreeView->expandAll();
connect(m_ui->menuTreeView, &QTreeView::clicked,
this, &MenuEdit::onStreamSelected);
connect(m_usedStreamsModel, &UsedStreamsModel::modelDataChanged,
this, &MenuEdit::slotChangesMade);
connect(m_usedStreamsModel, &UsedStreamsModel::modelDataChanged,
this, &MenuEdit::onModelDataChanged);
m_buttonBox->setButtonVisible(LtsWidgets::DialogButton::Remove);
connect(m_buttonBox->getButton(LtsWidgets::DialogButton::Remove), &QPushButton::clicked,
this, &MenuEdit::onRemove);
onStreamSelected(QModelIndex());
// Z. Zt. keine Aufloesung nach Add/Edit verfuegbar
if (m_isAudio)
setOnlineHelpFilename(QStringLiteral("Situation-Audio_Menu"));
else
setOnlineHelpFilename(QStringLiteral("Situation-Video_Menu"));
if (ms.id() <= 0)
m_buttonBox->setButtonVisible(LtsWidgets::DialogButton::Apply, false);
return true;
} // end of init()
//-----------------------------------------------------------------------------
bool MenuEdit::apply()
{
MenuStruct ms = m_usedStreamsModel->getMenuStruct();
ms.isAudio(m_isAudio);
bool ret = m_requestHelper->startSingleRequest([this, ms]()
{
// Send modified data via FTC call
return m_ftc->modifyMenuStructRequest(ms, this);
});
return ret;
} // end of accept()
//-----------------------------------------------------------------------------
void MenuEdit::onRemove()
{
if (0 == m_ui->menuTreeView->selectionModel()->selectedIndexes().size())
{
QModelIndex index = m_ui->menuTreeView->currentIndex();
if (!index.isValid())
return;
slotChangesMade();
DEBUG_LOG(m_logger) << "index row: " << index.row();
m_usedStreamsModel->removeRow(index.row(), index.parent()); // TODO: muss hier nicht auch etwas aus dem dds geloescht werden? (mw)
}
else
{
while (m_ui->menuTreeView->selectionModel()->selectedIndexes().size() > 0)
{
QModelIndex index = m_ui->menuTreeView->selectionModel()->selectedIndexes().front();
if (!index.isValid())
return;
slotChangesMade();
DEBUG_LOG(m_logger) << "index row: " << index.row();
m_usedStreamsModel->removeRow(index.row(), index.parent()); // TODO: muss hier nicht auch etwas aus dem dds geloescht werden? (mw)
}
}
}
void MenuEdit::onModelDataChanged()
{
INFO_LOG(m_logger) << "onModelDataChanged";
m_ui->menuTreeView->expandAll();
}
void MenuEdit::onStreamSelected(const QModelIndex &idx)
{
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, idx.isValid());
}
} // namespace AudioVideoManagement
@@ -0,0 +1,84 @@
/**
* @RDE_HEADER@
* AudioVideoManagement - Menu Struct Edit Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-10-17
*/
#pragma once
#include "avm_available_streams_model.h"
#include "avm_used_streams_model.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <Services/AudioVideoManagement/avm_ftc.h>
#include <AudioVideoManagement/logger.h>
#include <LtsUtilsGuiDds/request_helper.h>
namespace Ui
{
class MenuEdit;
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class StreamNameMapping;
class MenuEdit : public LtsWidgets::LtsDialog,
protected AudioVideoManagement::Interface::Ftc::ModifyMenuStructReplyHandler
{
Q_OBJECT
public:
explicit MenuEdit(QWidget *parent = nullptr);
~MenuEdit();
bool init(LtsSession::LtsSession *session,
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
const MenuStruct &ms);
//virtual functions from AudioVideoManagement::Interface::Ftc::ModifyMenuStructReplyHandler
void onModifyMenuStructReply(const Core::FunctionReplyObject &replyObject) override { m_requestHelper->requestReply(replyObject); }
public slots:
void onRemove();
void onModelDataChanged();
void onStreamSelected(const QModelIndex &idx);
private:
void retranslate() override;
bool apply() override final;
std::shared_ptr<AudioVideoManagement::Interface::Ftc> m_ftc;
Ui::MenuEdit *m_ui;
AvailableStreamsModel *m_availStreamsModel;
UsedStreamsModel *m_usedStreamsModel;
bool m_isAudio;
QString m_name;
NGL::INGLLogger &m_logger;
LtsUtils::RequestHelper *m_requestHelper;
AudioVideoManagement::MenuStruct m_lastDdsMenuStruct; ///< last dds menu struct
AudioVideoManagement::MenuStruct m_modifiedMenuStruct; ///< modified menu struct
};
} //namespace AudioVideoManagement
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MenuEdit</class>
<widget class="QWidget" name="MenuEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>403</width>
<height>256</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Menu Edit</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="availTreeView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="menuTreeView">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,530 @@
/**
* @RDE_HEADER@
* Gui Plugin for AudioVideoManagement
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-05
*/
#include "avm_menu_edit_view.h"
#include "avm_menu_edit_gui_plugin.h"
#include "avm_menu_edit.h"
#include "avm_choose_menu.h"
#include "avm_playback_menu_dialog.h"
#include "topic_replay.h"
#include "TopicEditor/topic_editor.h"
#include "AudioSettings/audio_settings_dialog.h"
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsScriptTimeline/track_entry.h>
#include <AudioVideoManagement/Shared/stream_list_result.h>
#include <LtsUtilsSettings/settings.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <Services/AudioVideoManagement/ava_session.h>
#include <Services/AudioVideoManagement/ava_control.h>
#include <Services/AudioVideoManagement/ava_types.h>
#include <Services/AudioVideoManagement/avm_operator_rights.h>
#include <Services/AudioVideoManagement/avs_dialog.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <QFileDialog>
namespace AudioVideoManagement
{
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
{
return std::make_shared<InterfaceImpl>(session);
}
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
: GuiPluginBase::Interface(session, getLogger("Plugins.Gui.Avm.MenuEdit"))
{
Q_ASSERT(session);
Q_ASSERT(session->partitionType() == IDL::PartitionType::Exercise || session->partitionType() == IDL::PartitionType::Replay);
}
bool InterfaceImpl::fetchInterfaces()
{
if (!m_session->getForeignDataInterface_NEW(m_avmDataIfc))
return false;
// That's not how it works! Use getDtc instead
m_dtc = m_avmDataIfc->getDtc();
m_ftc = m_avmDataIfc->getFtc();
// only load on demand!!
// if (!m_session->getDtc<InfrastructureManagement::DataInterface>(m_ismDtc))
// return false;
return true;
}
void InterfaceImpl::clearInterfaces()
{
//m_ismDtc = nullptr;
m_ftc = nullptr;
m_dtc = nullptr;
m_avmDataIfc = nullptr;
}
QString InterfaceImpl::menuGroup() const
{
return tr("Audio / Video");
}
QString InterfaceImpl::playbackMenuGroup() const // Restpunkt - Fenster sollen gleichen Tab Parent Namen haben, wie im Menue einsortiert
{
return tr("Playbacks");
}
static inline QString s_avmPlaybackDialogIdentifierVideo() { return QStringLiteral("AvmPlaybackDialogVideo"); }
static inline QString s_avmPlaybackDialogIdentifierAudio() { return QStringLiteral("AvmPlaybackDialogAudio"); }
static inline QString s_avmVideoFullScreen() { return QStringLiteral("AvmVideoFullscreen"); }
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> actions;
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
const bool enableAudioVideoManagement = ps->value(QStringLiteral("avm"),
QStringLiteral("enableAudioVideoManagement")).toBool();
const bool enableVideoImport = ps->value(QStringLiteral("avm"),
QStringLiteral("enableVideoImport")).toBool();
const bool enableAudioVideoPlaybacks = ps->value(QStringLiteral("avm"),
QStringLiteral("enableAudioVideoPlaybacks")).toBool();
if (m_session->isExercisePartition() && enableAudioVideoManagement)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Video Menu");
plData.identifier = QLatin1String("avmMenuVideoView");
plData.menuIcon = QIcon(QLatin1String("icon:video_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvMenuEdit;
plData.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowMenuEditVideoView(parent);
};
actions.append(plData);
}
if (m_session->isExercisePartition() && enableAudioVideoManagement)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Audio Menu");
plData.identifier = QLatin1String("avmMenuAudioView");
plData.menuIcon = QIcon(QLatin1String("icon:announcement_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvMenuEdit;
plData.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowMenuEditAudioView(parent);
};
actions.append(plData);
}
// TBC Rechte !!
if (m_session && m_session->app() == LtsAppDescriptor::App::Situation &&
m_session->isExercisePartition() && enableAudioVideoManagement)
{
LtsWidgets::PluginData plData1;
plData1.menuGroup = menuGroup();
plData1.menuText = tr("Voice Communication Access");
plData1.identifier = QLatin1String("_OpmShowAudioSettings");
plData1.menuIcon = QIcon(QLatin1String("icon:voice_communication_access.svg"));
plData1.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData1.operatorRight = OperatorRights::AudioVideoManagement::AvVoiceCommunicationAccess;
plData1.createWidgetFunc = [this](QWidget * parent)
{
return onShowAudioSettingsDialog(parent);
};
// TODO: apply user settings with single-action
actions.append(plData1);
}
// The following actions are only available when we have access to AVS.
if (!m_avmDataIfc->hasAvsSession())
{
DEBUG_LOG(m_logger) << "No avs actions for this session, as it is missing an avs session";
return actions;
}
if (enableAudioVideoManagement)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Choose AV Menu");
plData.identifier = QLatin1String("avmChooseMenu");
plData.menuIcon = QIcon(QLatin1String("icon:choose_audio_video_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvMenuShow;
plData.exercisePart = LtsWidgets::ExercisePart::eExecution;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowChooseMenu(parent);
};
actions.append(plData);
}
if (enableAudioVideoPlaybacks)
{
LtsWidgets::PluginData plData;
plData.menuGroup = playbackMenuGroup(); // Restpunkt - Fenster sollen gleichen Tab Parent Namen haben, wie im Menue einsortiert
plData.menuText = tr("Video Playback");
plData.identifier = s_avmPlaybackDialogIdentifierVideo();
plData.menuIcon = QIcon(QLatin1String("icon:playback_video.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvPlayback;
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowVideoPlayback(parent);
};
actions.append(plData);
plData.menuText = tr("Audio Playback");
plData.identifier = s_avmPlaybackDialogIdentifierAudio();
plData.menuIcon = QIcon(QLatin1String("icon:playback_audio.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvPlayback;
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowAudioPlayback(parent);
};
actions.append(plData);
if (m_session->isExercisePartition())
{
plData.menuGroup = menuGroup();
plData.menuText = tr("Video Fullscreen");
plData.identifier = s_avmVideoFullScreen();
plData.menuIcon = QIcon(QLatin1String("icon:video_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvPlayback;
plData.exercisePart = LtsWidgets::ExercisePart::eExecution;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowVideoFullscreen(parent);
};
actions.append(plData);
#ifdef LTS_DEBUG
LtsWidgets::PluginData plDataD;
plDataD.menuGroup = QStringLiteral("[Dev]");
plDataD.menuText = QStringLiteral("Video Fullscreen");
plDataD.identifier = s_avmVideoFullScreen() + QStringLiteral("DEV");
plDataD.createWidgetFunc = [this](QWidget * parent)
{
return onShowVideoFullscreen(parent);
};
actions.append(plDataD);
#endif
}
}
if (m_session->isExercisePartition() && enableVideoImport)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Video Import");
plData.identifier = QLatin1String("avmVideoImport");
plData.menuIcon = QIcon(QLatin1String("icon:import_video.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvImportVideo;
plData.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData.createWidgetFunc = nullptr;
plData.directFunc = [this](const LtsWidgets::PluginData &) { showImportDialog(); };
actions.append(plData);
}
return actions;
}
//Topic
TopicEditorBase::TopicEditor *InterfaceImpl::createTopicEditor(const TopicEditorBase::TopicData &data)
{
return new TopicEditor(m_session, data);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowMenuEditVideoView(QWidget *parent)
{
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc;
if (!m_session->getDtc<InfrastructureManagement::DataInterface>(ismDtc))
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The Infrastructure Management is not available!"), tr("Cannot open Menu Edit"));
return nullptr;
}
MenuEditView *dialog = new MenuEditView(menuGroup(), m_session, parent);
bool isAudio = false;
dialog->init(m_dtc, m_ftc, ismDtc, m_avmDataIfc->streamNameMapping(), isAudio);
return dialog;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowMenuEditAudioView(QWidget *parent)
{
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc;
if (!m_session->getDtc<InfrastructureManagement::DataInterface>(ismDtc))
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The Infrastructure Management is not available!"), tr("Cannot open Menu Edit"));
return nullptr;
}
MenuEditView *dialog = new MenuEditView(menuGroup(), m_session, parent);
bool isAudio = true;
dialog->init(m_dtc, m_ftc, ismDtc, m_avmDataIfc->streamNameMapping(), isAudio);
return dialog;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowChooseMenu(QWidget *parent)
{
return new ChooseMenu(m_dtc.get(), m_avmDataIfc->menus(), parent);
}
bool InterfaceImpl::startAudioVideoPlayback(const QString &streamName, qint64 startTime_ms, bool isAudio)
{
auto dlg = createPlaybackDialog(isAudio, nullptr);
if (dlg == nullptr)
return false;
const AvaTimePoint startTime(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(startTime_ms)));
const QString &menuGroupString(playbackMenuGroup());
const QString &identifier(isAudio ? s_avmPlaybackDialogIdentifierAudio() : s_avmPlaybackDialogIdentifierVideo());
dlg->avmPlaybackMenu()->setPlaybackPreset(streamName, startTime, true);
if (m_session->widgetExists(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab))
{
//dlg->avmPlaybackMenu()->startPlayback(true);
return (m_session->focusTab(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab));
}
/* NOTE: no need to trigger a startPlayback here, as the playback stuff is not initialized yet (only after
* the timeline was received via signals/slots, so there is time to set some presets beforehand).
* After init was (automatically) called, the presets gets automatically played (but only if any preset is set).*/
// did not exist before:
return m_session->addWidget(dlg, menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab);
}
bool InterfaceImpl::startMarkerAudioVideoPlayback(int markerId, bool isAudio)
{
auto dlg = createPlaybackDialog(isAudio, nullptr, false);
if (dlg == nullptr)
return false;
const QString menuGroupString(playbackMenuGroup());
const QString identifier(isAudio ? s_avmPlaybackDialogIdentifierAudio() : s_avmPlaybackDialogIdentifierVideo());
dlg->avmPlaybackMenu()->setMarkerPreset(markerId, true);
if (m_session->widgetExists(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab))
{
//dlg->avmPlaybackMenu()->startPlayback(true);
return (m_session->focusTab(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab));
}
/* NOTE: no need to trigger a startPlayback here, as the playback stuff is not initialized yet (only after
* the timeline was received via signals/slots, so there is time to set some presets beforehand).
* After init was (automatically) called, the presets gets automatically played (but only if any preset is set).*/
// did not exist before:
return m_session->addWidget(dlg, menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab);
}
StreamListResult *InterfaceImpl::getAsyncStreamList(AvaStreamType streamType, AvaStreamSource streamSource)
{
if (!m_avmDataIfc->hasAvsSession())
{
ERROR_LOG(m_logger) << "No AvsSession available";
return nullptr;
}
auto *streamListResult = new StreamListResult();
m_avaSession = m_avmDataIfc->avaSession();
auto fn = [this, streamListResult, streamType, streamSource]()
{
m_avaSession->control()->getStreamList(streamType, streamSource, streamListResult, &StreamListResult::onStreamListReceived);
};
if (m_avaSession->isEstablished())
fn();
else
connect(m_avaSession, &AvaSession::sessionEstablished,
this, fn);
return streamListResult;
}
StreamClipsResult *InterfaceImpl::getAsyncStreamClips(const QString &streamName, const boost::optional<AvaTimePoint> &start, const boost::optional<AvaTimePoint> &end)
{
if (!m_avmDataIfc->hasAvsSession())
{
ERROR_LOG(m_logger) << "No AvsSession available";
return nullptr;
}
m_avaSession = m_avmDataIfc->avaSession();
auto *streamClipsResult = new StreamClipsResult();
auto fn = [this, streamClipsResult, streamName, start, end]()
{
m_avaSession->control()->queryTimeline(streamName, streamClipsResult, &StreamClipsResult::onClipsListReceived, start, end);
};
if (m_avaSession->isEstablished())
fn();
else
connect(m_avaSession, &AvaSession::sessionEstablished,
this, fn);
return streamClipsResult;
}
TopicReplay::Interface *InterfaceImpl::createInterface(TopicEditorBase::TopicType topicType)
{
Q_ASSERT(topicType == TopicEditorBase::TopicType::Audio || topicType == TopicEditorBase::TopicType::Video);
bool isAudio = topicType == TopicEditorBase::TopicType::Audio;
return new TopicReplay(m_avmDataIfc.get(), isAudio);
}
AvmPlaybackMenuDialog *InterfaceImpl::createPlaybackDialog(bool isAudio, QWidget *parent, bool setInitialSearchContentMarkers)
{
PlaybackApp *app = nullptr;
QPointer<AvmPlaybackMenuDialog> &dialogPtr = (isAudio ? m_avmPlaybackMenuDialogAudio : m_avmPlaybackMenuDialogVideo);
if (dialogPtr)
return dialogPtr;
if (isAudio)
app = m_avmDataIfc->audioPlaybackApp();
else
app = m_avmDataIfc->videoPlaybackApp();
if (!m_avmDataIfc->hasAvsSession())
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The audio/video interface is not available in this session."));
return nullptr;
}
if (app->inUse())
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The audio/video interface is already in use by another dialog."));
return nullptr;
}
dialogPtr = new AvmPlaybackMenuDialog(isAudio, parent);//m_session, m_avmDataIfc->avaSession(), PlaybackAppHandle(app), m_avmDataIfc->streamNameMapping(), isAudio, parent);
if (!dialogPtr->avmPlaybackMenu()->init(m_session, m_avmDataIfc->avaSession(), PlaybackAppHandle(app),
m_avmDataIfc->streamNameMapping(), isAudio, false, {}, {}, setInitialSearchContentMarkers))
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The audio/video interface could not be initialized. Please try again later."));
dialogPtr->deleteLater();
return nullptr;
}
AvmPlaybackMenu *apm = dialogPtr->avmPlaybackMenu();
connect(apm, &AvmPlaybackMenu::signalSetOverlay,
this, [dialogPtr](QWidget * widget)
{
dialogPtr->setOverlay(widget);
});
connect(apm, &AvmPlaybackMenu::signalUnsetOverlay,
this, [dialogPtr]()
{
dialogPtr->unsetOverlay();
});
return dialogPtr;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowVideoPlayback(QWidget *parent)
{
return createPlaybackDialog(false, parent);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowAudioPlayback(QWidget *parent)
{
return createPlaybackDialog(true, parent);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowAudioSettingsDialog(QWidget *parent)
{
AudioSettingsDialog *dialogAudio = new AudioSettingsDialog(parent);
return LtsWidgets::initDialog(dialogAudio, m_avmDataIfc->getOpmDtc(), m_dtc.get(), m_ftc.get(), m_session);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowVideoFullscreen(QWidget *parent)
{
Q_UNUSED(parent);
m_avmDataIfc->showOrCloseAvsApp();
return nullptr;
}
void InterfaceImpl::showImportDialog()
{
const QString &dialogId = AvsDialog::IMPORT;
if (m_importDialogVisible)
{
m_avmDataIfc->avaSession()->control()->setVideoDialogProfile(dialogId, AvaVisibility::Hidden);
m_avmDataIfc->avaSession()->control()->releaseImport(dialogId);
m_importDialogVisible = false;
return;
}
QWidget *parent = nullptr;
QString path = QFileDialog::getExistingDirectory(parent, tr("Choose directory"),
QStringLiteral("/mnt"),
QFileDialog::ShowDirsOnly | QFileDialog::ReadOnly);
if (path.size())
{
m_avmDataIfc->avaSession()->control()->setVideoDialogProfile(dialogId, AvaVisibility::Visible);
m_avmDataIfc->avaSession()->control()->chooseImport(dialogId, path, 1);
m_importDialogVisible = true;
}
else
m_avmDataIfc->avaSession()->control()->releaseImport(dialogId);
}
} // namespace AudioVideoManagement
@@ -0,0 +1,109 @@
/**
* @RDE_HEADER@
* Gui Plugin for AudioVideoManagement
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-05
*/
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/ava_session.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <Plugins/AudioVideoManagement/avm_menu_edit_gui_plugin_interface.h>
#include <memory>
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
class Ftc;
}
class AvaControl;
class DataInterface;
class AvmPlaybackMenuDialog;
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:avm.mne")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("AudioVideoManagementMenuEditGuiPlugin"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise || partition == IDL::PartitionType::Replay; }
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface, public ForeignMenuEditGuiInterface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
~InterfaceImpl() = default;
bool fetchInterfaces() override;
void clearInterfaces() override;
QString menuGroup() const override;
QString playbackMenuGroup() const;
QVector<LtsWidgets::PluginData> getActions() override;
//ScriptEditor
TopicEditorBase::TopicEditor *createTopicEditor(const TopicEditorBase::TopicData &data) override;
bool startAudioVideoPlayback(const QString &streamName, qint64 startTime_ms, bool isAudio) override final;
bool startMarkerAudioVideoPlayback(int markerId, bool isAudio) override;
StreamListResult* getAsyncStreamList(AvaStreamType streamType = AvaStreamType::Video, AvaStreamSource streamSource = AvaStreamSource::Server) override;
StreamClipsResult* getAsyncStreamClips(const QString &streamName, const boost::optional<AvaTimePoint> &start = {}, const boost::optional<AvaTimePoint> &end = {}) override;
TopicReplay::Interface *createInterface(TopicEditorBase::TopicType topicType) override;
private:
class TopicReplayImpl;
LtsWidgets::LtsDialog *onShowMenuEditVideoView(QWidget *parent);
LtsWidgets::LtsDialog *onShowMenuEditAudioView(QWidget *parent);
LtsWidgets::LtsDialog *onShowChooseMenu(QWidget *parent);
AvmPlaybackMenuDialog *createPlaybackDialog(bool isAudio, QWidget *parent, bool setInitialSearchContentMarkers = true);
LtsWidgets::LtsDialog *onShowVideoPlayback(QWidget *parent);
LtsWidgets::LtsDialog *onShowAudioPlayback(QWidget *parent);
LtsWidgets::LtsDialog *onShowAudioSettingsDialog(QWidget *parent);
LtsWidgets::LtsDialog *onShowVideoFullscreen(QWidget *parent);
void showImportDialog();
AvaSession *m_avaSession;
std::shared_ptr<AudioVideoManagement::DataInterface> m_avmDataIfc;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_dtc;
std::shared_ptr<AudioVideoManagement::Interface::Ftc> m_ftc;
//std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
bool m_importDialogVisible{false};
QPointer<AvmPlaybackMenuDialog> m_avmPlaybackMenuDialogAudio;
QPointer<AvmPlaybackMenuDialog> m_avmPlaybackMenuDialogVideo;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,252 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - Menu Edit Overview Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-05
*/
#include "avm_menu_edit_view.h"
#include "ui_avm_menu_edit_view.h"
#include "avm_menu_edit.h"
#include "avm_new_menu_struct.h"
#include <LtsSession/lts_session_manager.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
#include <LtsPrinting/printing.h>
#include <QTreeView>
namespace AudioVideoManagement
{
MenuEditView::MenuEditView(const QString &menuGroup, LtsSession::LtsSession *session, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_menuGroup(menuGroup)
, m_model(nullptr)
, m_isAudio(false)
, m_dtc(nullptr)
, m_ftc(nullptr)
, m_session(session)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_ui(new Ui::MenuEditView)
, m_logger(getLogger("AudioVideoManagementDialog"))
{
m_ui->setupUi(this);
m_ui->widget->setSelectionMode(QAbstractItemView::SingleSelection);
// connects fuer Widgetaenderungen
connect(m_ui->widget, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &MenuEditView::onSelectionChanged);
connect(m_ui->widget, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &MenuEditView::onEditMenuStruct);
// Edit-Button erzeugen
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &MenuEditView::onEditMenuStruct);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &MenuEditView::onRemoveMenuStruct);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &MenuEditView::onAddMenuStruct);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, false);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &MenuEditView::onPrint);
} // end of ctor()
//-----------------------------------------------------------------------------
MenuEditView::~MenuEditView()
{
delete m_ui;
} // end of dtor()
//-----------------------------------------------------------------------------
void MenuEditView::retranslate()
{
if (m_isAudio)
translate(m_ui, tr("Audio Menu"));
else
translate(m_ui, tr("Video Menu"));
} // end of retranslate()
//-----------------------------------------------------------------------------
bool MenuEditView::init(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
bool isAudio)
{
Q_ASSERT(dtc);
Q_ASSERT(ftc);
Q_ASSERT(ismDtc);
Q_ASSERT(streamNameMapping);
m_dtc = dtc;
m_ftc = ftc;
m_ismDtc = ismDtc;
m_streamNameMapping = streamNameMapping;
m_model = new MenuViewModel(this);
m_model->initModel(m_dtc, isAudio);
m_ui->widget->setModel(m_model);
m_isAudio = isAudio;
#ifndef LTS_DEBUG
m_ui->widget->hideColumn(0);
#endif
m_ui->widget->setSortIndicator(1, Qt::AscendingOrder);
// Save/Load from Library Button
m_buttonBox->connectLibraryButton(this,
&MenuEditView::onImportButtonClicked,
&MenuEditView::onExportButtonClicked);
if (!m_session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui))
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Library, false);
if (m_isAudio)
setOnlineHelpFilename(QStringLiteral("Situation-Audio_Menu"));
else
setOnlineHelpFilename(QStringLiteral("Situation-Video_Menu"));
return true;
} // end of init()
//-----------------------------------------------------------------------------
void MenuEditView::onSelectionChanged()
{
const QModelIndex idx = m_ui->widget->currentIndex();
int row = idx.row();
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, (row >= 0));
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, (row >= 0));
}
void MenuEditView::onAddMenuStruct()
{
NewMenuStruct *newDialog = new NewMenuStruct(this);
connect(newDialog, &NewMenuStruct::newName, this, [this](const QString & name)
{
MenuStruct ms;
ms.name(name.toStdString());
ms.isAudio(m_isAudio);
MenuEdit *dialog = new MenuEdit(this);
dialog->init(m_session, m_dtc, m_ftc, m_ismDtc, m_streamNameMapping, ms);
addDialog(dialog, m_menuGroup, QLatin1String("avmNewMenu") + name, LtsWidgets::LayoutArea::eDialogTab);
});
newDialog->show();
connect(newDialog, &NewMenuStruct::accepted, newDialog, &NewMenuStruct::deleteLater);
} // end of onAddMenuStruct()
//-----------------------------------------------------------------------------
void MenuEditView::onEditMenuStruct()
{
const QModelIndex idx = m_ui->widget->currentIndex();
int row = idx.row();
if (row < 0)
return;
AudioVideoManagement::MenuStruct ms = m_model->getRowData(row);
ms.isAudio(m_isAudio);
DEBUG_LOG(m_logger) << "---onEditMenuStruct () "
<< " id: " << ms.id()
<< " name: " << ms.name();
MenuEdit *dialog = new MenuEdit(this);
dialog->init(m_session, m_dtc, m_ftc, m_ismDtc, m_streamNameMapping, ms);
addDialog(dialog, m_menuGroup, QLatin1String("avmMenuEdit") + QString::number(ms.id()), LtsWidgets::LayoutArea::eDialogTab);
}
//-----------------------------------------------------------------------------
void MenuEditView::onRemoveMenuStruct()
{
const QModelIndex idx = m_ui->widget->currentIndex();
if (!idx.isValid())
return;
const AudioVideoManagement::MenuStruct ms = m_model->getRowData(idx.row());
const Core::RemoveElementRequest req(ms.id());
m_requestHelper->startSingleRequest([req, this]()
{
return m_ftc->removeMenuStructRequest(req, this);
});
if (m_model->rowCount() == 0)
{
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, false);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
}
}
//-----------------------------------------------------------------------------
void MenuEditView::onPrint()
{
LtsPrinting::printView(m_session, this, m_ui->widget->treeView());
}
void MenuEditView::tableColumnOrganiserSetting()
{
//LtsColumnOrganiser
m_ui->widget->setColumnsOrganiserEnabled(identifier(), true);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// library
void MenuEditView::onExportButtonClicked()
{
const QModelIndex idx = m_ui->widget->currentIndex();
int row = idx.row();
if (row < 0)
return;
QByteArray byteArray = m_model->modelToJson(row);
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_session->exerciseId(), byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void MenuEditView::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << " onImportButtonClicked() ";
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_session->exerciseId(), FileManagement::toString(FileManagement::XmlType::AVMMenuEditView));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMMenuEditView)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
const QVector<AudioVideoManagement::MenuStruct> menuStructs = m_model->jsonToMenuStructs(content);
for (const AudioVideoManagement::MenuStruct &request : menuStructs)
{
m_requestHelper->startSingleRequest([this, request]()
{
return m_ftc->modifyMenuStructRequest(request, this);
});
}
}
INFO_LOG(m_logger) << "--- onImportButtonClicked() ";
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,90 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - Menu Edit Overview Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-05
*/
#pragma once
#include "avm_menu_view_model.h"
#include <Services/AudioVideoManagement/avm_dtc.h>
#include <Services/AudioVideoManagement/avm_ftc.h>
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
namespace Ui
{
class MenuEditView;
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class StreamNameMapping;
class MenuEditView : public LtsWidgets::LtsDialog,
protected AudioVideoManagement::Interface::Ftc::ModifyMenuStructReplyHandler,
protected AudioVideoManagement::Interface::Ftc::RemoveMenuStructReplyHandler
{
Q_OBJECT
public:
explicit MenuEditView(const QString &menuGroup, LtsSession::LtsSession *session, QWidget *parent = nullptr);
~MenuEditView();
bool init(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
bool isAudio);
private:
void retranslate() override;
void onSelectionChanged();
void onAddMenuStruct();
void onEditMenuStruct();
void onRemoveMenuStruct();
void onModifyMenuStructReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
void onRemoveMenuStructReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
void onPrint();
void tableColumnOrganiserSetting() override;
private slots:
void onExportButtonClicked();
void onImportButtonClicked();
private:
QString m_menuGroup;
MenuViewModel *m_model;
bool m_isAudio;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_dtc;
std::shared_ptr<AudioVideoManagement::Interface::Ftc> m_ftc;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
StreamNameMapping *m_streamNameMapping{nullptr};
LtsSession::LtsSession *m_session;
LtsUtils::RequestHelper *m_requestHelper;
Ui::MenuEditView *m_ui;
LOGGER_T m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MenuEditView</class>
<widget class="QWidget" name="MenuEditView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>455</width>
<height>341</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Menu Edit View</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="LtsDataView::LtsDataView" name="widget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,157 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - Weather Station Overview Dialog Model
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10.05
*/
#include "avm_menu_view_model.h"
#include <FileManagement/flm_translation.h>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
namespace AudioVideoManagement
{
//---------------------------------
// MenuViewModel
//---------------------------------
MenuViewModel::MenuViewModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<AudioVideoManagement::MenuStruct>({tr("Id"),
tr("Name")},
parent)
, m_isAudio(false)
, m_dtc(nullptr)
, m_logger(getLogger("MenuViewModel"))
{}
bool MenuViewModel::initModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc, bool isAudio)
{
m_isAudio = isAudio;
m_dtc = dtc;
connect(m_dtc.get(), &AudioVideoManagement::Interface::Dtc::menuStructChanged,
this, &MenuViewModel::onDataChanged);
// insert Model
m_dtc->menuStructs([this](const AudioVideoManagement::MenuStruct &ms) { insertRow(ms); });
return true;
}
QVariant MenuViewModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole || !indexIsValid(index))
return QVariant();
const AudioVideoManagement::MenuStruct &ms = m_vecData.at(index.row());
switch (index.column())
{
case 0:
return ms.id();
case 1:
return QString::fromStdString(ms.name());
default:
break;
}
return QVariant();
}
bool MenuViewModel::isFiltered(const MenuStruct &data, DataModification modType) const
{
Q_UNUSED(modType);
if (data.isAudio() != m_isAudio)
return true;
if (data.isDefault())
return true;
return false;
}
QByteArray MenuViewModel::modelToJson(int row) const
{
if (row < 0 || row >= m_vecData.size())
return QByteArray("{}");
QJsonObject obj;
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::AVMMenuEditView);
DEBUG_LOG(m_logger) << "row =" << ": " << row;
const AudioVideoManagement::MenuStruct &menuStruct = m_vecData.at(row);
obj[QStringLiteral("id")] = 0;
obj[QStringLiteral("isAudio")] = menuStruct.isAudio();
obj[QStringLiteral("isDefault")] = menuStruct.isDefault();
obj[QStringLiteral("name")] = QString::fromStdString(menuStruct.name());
//menuStruct.menuEntries():
QJsonArray arrData;
for (const AudioVideoManagement::AvMenuEntry &avMenuEntry : menuStruct.menuEntries())
{
QJsonObject innerData;
innerData[QStringLiteral("id")] = avMenuEntry.id();
innerData[QStringLiteral("isGroup")] = avMenuEntry.isGroup();
if (avMenuEntry.mountingPointId().is_set())
innerData[QStringLiteral("mountingPointId")] = avMenuEntry.mountingPointId().get();
if (avMenuEntry.name().is_set())
innerData[QStringLiteral("name")] = QString::fromStdString(avMenuEntry.name().get());
innerData[QStringLiteral("parentId")] = avMenuEntry.parentId();
innerData[QStringLiteral("rowId")] = avMenuEntry.rowId();
arrData += innerData;
}
obj[QStringLiteral("menuEntries")] = arrData;
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
QVector<AudioVideoManagement::MenuStruct> MenuViewModel::jsonToMenuStructs(const QByteArray &content) const
{
// already checked that content is not empty, parseable and contains proper XmlType
QVector<AudioVideoManagement::MenuStruct> result;
AudioVideoManagement::MenuStruct menuStruct;
const QJsonDocument doc(QJsonDocument::fromJson(content));
const QJsonObject obj(doc.object());
menuStruct.id(0);
menuStruct.isAudio(obj[QStringLiteral("isAudio")].toBool());
menuStruct.isDefault(obj[QStringLiteral("isDefault")].toBool());
menuStruct.name(obj[QStringLiteral("name")].toString().toStdString());
// Start menuStruct.menuEntries():
const QJsonArray arrEntry(obj.value(QStringLiteral("menuEntries")).toArray());
menuStruct.menuEntries().resize(arrEntry.size());
INFO_LOG(m_logger) << "arrEntry.size()=" << arrEntry.size();
for (int j = 0; j < arrEntry.size(); ++j)
{
const QJsonObject objEntry(arrEntry.at(j).toObject());
AudioVideoManagement::AvMenuEntry avMenuEntry;
avMenuEntry.id(-10000000 + objEntry[QStringLiteral("id")].toInt());
avMenuEntry.isGroup(objEntry[QStringLiteral("isGroup")].toBool());
avMenuEntry.mountingPointId(objEntry[QStringLiteral("mountingPointId")].toInt());
if (objEntry.contains(QStringLiteral("name")))
avMenuEntry.name().set(objEntry[QStringLiteral("name")].toString().toStdString());
if (objEntry[QStringLiteral("isGroup")].toBool())
avMenuEntry.parentId(0);
else
avMenuEntry.parentId(-10000000 + objEntry[QStringLiteral("parentId")].toInt());
avMenuEntry.rowId(objEntry[QStringLiteral("rowId")].toInt());
menuStruct.menuEntries()[j] = avMenuEntry;
}
// End menuStruct.menuEntries():
result.append(menuStruct);
return result;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,41 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - Menu Edit Dialog Model
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10.05
*/
#pragma once
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
#include <LtsWidgets/lts_abstract_table_model.h>
namespace AudioVideoManagement
{
// --------------------------------------------------------
// MenuViewModel
// --------------------------------------------------------
class MenuViewModel final : public LtsWidgets::LtsAbstractTableModel<AudioVideoManagement::MenuStruct>
{
Q_OBJECT
public:
explicit MenuViewModel(QObject *parent = nullptr);
bool initModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc, bool isAudio);
QVariant data(const QModelIndex &, int) const override final;
QByteArray modelToJson(int row) const;
QVector<AudioVideoManagement::MenuStruct> jsonToMenuStructs(const QByteArray &content) const;
protected:
bool isFiltered(const MenuStruct &data, DataModification modType) const override final;
private:
bool m_isAudio;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_dtc;
LOGGER_T m_logger;
};
} //namespace AudioVideoManagement
@@ -0,0 +1,46 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - New Name Frameless Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2016-11-21
*/
#include "avm_new_menu_struct.h"
#include "ui_avm_new_menu_struct.h"
#include <LtsSession/lts_session_manager.h>
namespace AudioVideoManagement
{
NewMenuStruct::NewMenuStruct(QWidget *parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_ui(new Ui::NewMenuStruct)
, m_logger(getLogger("NewMenu"))
{
m_ui->setupUi(this);
m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Ok)->hide();
m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Apply)->hide();
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this, &LtsWidgets::LtsFramelessDialog::accept); // TODO: undo workaround !
m_ui->ltsDialogButtonBox->connectButton(LtsWidgets::DialogButton::Add, this, &NewMenuStruct::onSetNewName);
}
NewMenuStruct::~NewMenuStruct()
{
delete m_ui;
}
//SLOTS
void NewMenuStruct::onSetNewName()
{
if (m_ui->leName->text().isEmpty())
return;
emit newName(m_ui->leName->text());
close();
}
} //namespace ParticipantManagement
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* HMI Plugin for AudioVideoManagement - New Name Frameless Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2016-11-21
*/
#pragma once
#include <AudioVideoManagement/logger.h>
#include <LtsWidgets/lts_frameless_dialog.h>
namespace Ui
{
class NewMenuStruct;
}
namespace AudioVideoManagement
{
class NewMenuStruct : public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
explicit NewMenuStruct(QWidget *parent = nullptr);
~NewMenuStruct();
private Q_SLOTS:
void onSetNewName();
Q_SIGNALS:
void newName(const QString &);
private:
Ui::NewMenuStruct *m_ui;
LOGGER_T m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewMenuStruct</class>
<widget class="QWidget" name="NewMenuStruct">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>596</width>
<height>118</height>
</rect>
</property>
<property name="windowTitle">
<string>Create New Menu</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>New Menu:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leName"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="LtsWidgets::LtsDialogButtonBox" name="ltsDialogButtonBox"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsDialogButtonBox</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_dialog_button_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,223 @@
/**
* @RDE_HEADER@
* AvmPlaybackMenu
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-06-26
*/
#pragma once
#include "ui_avm_playback_menu.h"
#include "interval_model.h"
#include <LtsSession/lts_session.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <AudioVideoManagement/logger.h>
#include <MarkerManagement/mrk_ftc.h>
#include <QString>
#include <boost/optional.hpp>
#include <memory>
namespace MarkerManagement
{
class MarkerManagementDialogModel;
class MarkerSortFilterProxyModel;
class ForeignDataInterface;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace AudioVideoManagement
{
class AvaSession;
class AvaControl;
class AvsSequence;
class StreamNameMapping;
class AvmPlaybackMenu;
class QueryTimeline : public QObject
{
Q_OBJECT
private:
friend class AvmPlaybackMenu;
QueryTimeline(const AvaStream &stream, const AvaTimePoint &start, const AvaTimePoint &end, AvaControl *control);
AvaControl *m_avaCtrl { nullptr };
private slots:
//void slotTimelineComplete(const AvaTimeline &timeline);
signals:
void signalTimelineComplete(const AvaTimeline &timeline);
};
class AvmPlaybackMenu : public QWidget,
protected MarkerManagement::Interface::Ftc::MarkerModifyReplyHandler
{
Q_OBJECT
public:
explicit AvmPlaybackMenu(QWidget *parent = nullptr);
~AvmPlaybackMenu();
bool init(LtsSession::LtsSession *session, AvaSession *avaSession, PlaybackAppHandle &&appHandle,
StreamNameMapping* streamNameMapping, bool isAudio, bool insideTopicEditor,
const boost::optional<QString> &streamName = boost::none,
const boost::optional<AvaTimePoint> &startTime = boost::none, bool setInitialSearchContentMarkers = true);
void setPlaybackPreset(const QString &streamName, const AvaTimePoint &startTime, bool autostartPresetAfterInit);
void setMarkerPreset(int markerId, bool autostartPresetAfterInit);
QueryTimeline *queryTimeline(const AvaTimePoint &start, const AvaTimePoint &end);
void onMarkerModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void retranslate();
//void setStreamTime(AvaTimePoint t);
void setStreamTime(qint64 time);
void hideStreamSelection();
qint64 getCurrentTime() const;
AvaSpeed getSpeed() const;
void setSpeed(AvaSpeed newSpeed);
int getVolume() const;
void setVolume(int newVolume);
void setStreamByName(const QString &name);
//QString selectedStreamName() const;
QString getInternalStreamName() const;
void setStreamName(const QString &internalStreamName);
bool getSkipPause() const;
void setSkipPause(bool bSkip);
void startPlayback(bool usePresets = false);
void setSavedPos(qint64 msecs);
int currentOffset() const { return m_currentOffset; }
void selectMarker(const MarkerManagement::Marker &marker);
void selectMarker(int markerId);
signals:
void signalSetOverlay(QWidget *widget);
void signalUnsetOverlay();
void setTopicButtonStates(bool enable);
void markerSelected(const QString &description);
// both for topic editor
void signalSetStart(qint64 start);
void signalSetEnd(qint64 end);
void signalCurrentStreamChanged(const QString &displayName);
void signalTimezoneChanged(int timezoneHoursOffset);
public slots:
void onStreamSelectionChanged(const QVector<AvaStream> &selectedStreams);
private slots:
void onSessionEstablished();
void onDisconnected();
void onStreamListReceived(const QVector<AvaStream> &streams);
void onTimelineComplete(const AvaTimeline &timeline);
void onStreamPositionReceived(const QString &dialogId, const QString &streamName, AvaTimePoint);
void onVolumeReceived(AvaVolume db);
void onCommandQueueEmpty();
void onUiTimeChanged(AvaTimePoint t);
void onIntervalActivated(const QModelIndex &index);
void onMarkerClicked(const QModelIndex &index);
void onSetMarkerClicked();
void onStartClicked();
void onPauseClicked();
void onStopClicked();
void onSkipPauseClicked();
void onAllStreams();
void onIntervalBackwardClicked();
void onIntervalForwardClicked();
void onSpeedChanged();
void onVolumeChanged(int percent);
void onCurrentIntervalChanged(bool hasPreviousInterval, bool hasNextInterval, const QModelIndex &rowIndex);
void onStreamSelected();
void onMarkerStreamSelected();
void onStreamNameMappingChanged(/*int32_t mountingPointId, const QString &internalName, const QString &displayName*/);
void onAddStreamClip();
void onAddStreamMarker();
void onRemoveSelectedStreams();
void onTabChanged(int index);
void onPauseFromPlaybackApp();
void onResumeFromPlaybackApp();
private:
enum class ButtonStates
{
AllOff, //!< initial state when no stream is selected or when disconnected
Started,
Paused,
Stopped
};
void processPresets();
void preselectStream(const QString &fallbackInternalStreamName);
AvaSpeed currentSpeed() const;
void setButtonStates(ButtonStates);
void enableAll(bool enabled = true);
void disableAll() { enableAll(false); }
bool trySelectedStreams();
void addStream(QComboBox *cb);
void applyAvsPos();
void applyAvsVolume();
//void onStreamListReceived(const QVector<AvaStream> &streams);
PlaybackAppHandle m_playbackApp;
bool m_isAudio = false;
StreamNameMapping *m_streamNameMapping = nullptr;
boost::optional<QString> m_presetStreamName;
boost::optional<int> m_markerIdPreset;
boost::optional<AvaTimePoint> m_presetStartTime;
class TimelineBuilder;
std::unique_ptr<TimelineBuilder> m_timelineBuilder;
std::unique_ptr<Ui::Playback> m_ui;
class TimeControl;
std::unique_ptr<TimeControl> m_timeControl;
boost::optional<QVector<AvaStream>> m_selectedStreams;
IntervalModel m_intervalModel;
boost::optional<AvaTimePoint> m_savedPos;
std::unique_ptr<MarkerManagement::MarkerManagementDialogModel> m_markerModel;
std::unique_ptr<MarkerManagement::MarkerSortFilterProxyModel> m_markerProxy;
AvaControl *m_avaCtrl = nullptr;
LtsSession::LtsSession *m_session = nullptr;
bool m_skipEmpty = true;
ButtonStates m_currentButtonState;
std::shared_ptr<MarkerManagement::ForeignDataInterface> m_mrkDataIfc;
boost::optional<AvaTimePoint> m_lastAvsPos;
boost::optional<AvaVolume> m_lastAvsVolume;
std::unique_ptr<LtsUtils::RequestHelper> m_requestHelper;
QVector<AvaStream> m_streams;
boost::optional<AvaStream> m_currentStream;
bool m_autostartOnce { false };
int m_maxSelectedStreams = 12;
bool m_queryTimelineActive { false };
int m_currentOffset = 0;
bool m_videoScreenSelectionDone { false };
bool m_timelineIsComplete { false };
ButtonStates m_buttonStateBeforePlaybackAppPause { ButtonStates::AllOff };
LOGGER_T m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,697 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Playback</class>
<widget class="QWidget" name="Playback">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>810</width>
<height>791</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Playback Menu</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,5,1,0,0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="lbAvsConnectionStatus">
<property name="text">
<string>AVS Connection status:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblAvsConnectionStatus">
<property name="text">
<string notr="true">unknown</string>
</property>
</widget>
</item>
<item>
<spacer name="spcAvsConnectionStatus">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Time zone:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbTimeZone">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Audio / Video clips</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,999">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="lblCbStreams">
<property name="text">
<string>From stream:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAddStreamClip">
<property name="text">
<string>Add stream</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="cbStreams">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pbDebug">
<property name="text">
<string notr="true">[debug]</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="tavIntervals">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Markers</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="LtsDataView::LtsDataView" name="tvMarker" native="true">
<property name="showColumnCount" stdset="0">
<bool>false</bool>
</property>
<property name="toolButtonsVisible" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select stream from marker:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="cbMarkerStreams">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAddStreamMarker">
<property name="text">
<string>Add stream</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAllStreams">
<property name="text">
<string>Show all streams</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="multiStreamWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_13">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Selected streams:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="lwStreams"/>
</item>
<item>
<widget class="QPushButton" name="pbRemoveSelectedStream">
<property name="text">
<string>Remove selected</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Current Time:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbSetMarker">
<property name="text">
<string>Create Marker</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtCurrentTime">
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="topicEditorEssentialsWidget" native="true">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Set time as..:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAsStart">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAsEnd">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>End</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="lblStartSeq">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slSequenceSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::NoTicks</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblEndSeq">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QToolButton" name="pbIntervalBackward">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Back</string>
</property>
<property name="text">
<string>Back</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_previous_track.svg</normaloff>:/style/avm_previous_track.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbStart">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Play</string>
</property>
<property name="text">
<string>Start</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_start.svg</normaloff>:/style/avm_start.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbPause">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Pause</string>
</property>
<property name="text">
<string>Pause</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_pause.svg</normaloff>:/style/avm_pause.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbStop">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Stop</string>
</property>
<property name="text">
<string>Stop</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_stop.svg</normaloff>:/style/avm_stop.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbIntervalForward">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Forward</string>
</property>
<property name="text">
<string>Forward</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_next_track.svg</normaloff>:/style/avm_next_track.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lbSpeed">
<property name="text">
<string>Speed [%]:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbSpeedValue">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slSpeed">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="spacerSpeedVolume">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lbVolume">
<property name="text">
<string>Volume [db]:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbVolumeValue">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slVolume">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QToolButton" name="pbSkipPause">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Skip/Show Pause</string>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_pint.svg</normaloff>:/style/avm_pint.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsMSecDateTimeEdit</class>
<extends>QDateTimeEdit</extends>
<header>LtsWidgets/lts_msec_date_time_edit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tavIntervals</tabstop>
<tabstop>cbMarkerStreams</tabstop>
<tabstop>pbAddStreamMarker</tabstop>
<tabstop>pbAllStreams</tabstop>
<tabstop>lwStreams</tabstop>
<tabstop>pbRemoveSelectedStream</tabstop>
<tabstop>pbSetMarker</tabstop>
<tabstop>pbAsStart</tabstop>
<tabstop>pbAsEnd</tabstop>
<tabstop>slSequenceSlider</tabstop>
<tabstop>pbIntervalBackward</tabstop>
<tabstop>pbStart</tabstop>
<tabstop>pbPause</tabstop>
<tabstop>pbStop</tabstop>
<tabstop>pbIntervalForward</tabstop>
<tabstop>slSpeed</tabstop>
<tabstop>slVolume</tabstop>
<tabstop>pbSkipPause</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,47 @@
/**
* @RDE_HEADER@
* AvmPlaybackMenu Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, NLE, 2020-03-16
*/
#include "avm_playback_menu_dialog.h"
namespace AudioVideoManagement
{
AvmPlaybackMenuDialog::AvmPlaybackMenuDialog(bool isAudio, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_isAudio(isAudio)
, m_ui(new Ui::AvmPlaybackMenuDialog)
{
m_ui->setupUi(this);
if (m_isAudio)
setOnlineHelpFilename(QStringLiteral("Situation-Playback_Audio"));
else
setOnlineHelpFilename(QStringLiteral("Situation-Playback_Video"));
}
AvmPlaybackMenuDialog::~AvmPlaybackMenuDialog()
{
delete m_ui;
}
AudioVideoManagement::AvmPlaybackMenu *AvmPlaybackMenuDialog::avmPlaybackMenu()
{
return m_ui->widget;
}
void AvmPlaybackMenuDialog::retranslate()
{
translate(m_ui, m_isAudio ? tr("Audio Playback") : tr("Video Playback"));
m_ui->widget->retranslate();
}
} // namespace AudioVideoManagement
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* AvmPlaybackMenu Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, NLE, 2020-03-16
*/
#pragma once
#include "ui_avm_playback_menu_dialog.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <AudioVideoManagement/logger.h>
#include <boost/optional.hpp>
namespace Ui {
class AvmPlaybackMenuDialog;
}
namespace AudioVideoManagement
{
class AvmPlaybackMenuDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
AvmPlaybackMenuDialog(bool isAudio, QWidget *parent);
AudioVideoManagement::AvmPlaybackMenu *avmPlaybackMenu();
~AvmPlaybackMenuDialog();
void retranslate() override;
private:
bool m_isAudio;
Ui::AvmPlaybackMenuDialog *m_ui;
//LOGGER_T m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AvmPlaybackMenuDialog</class>
<widget class="QWidget" name="AvmPlaybackMenuDialog">
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="AudioVideoManagement::AvmPlaybackMenu" name="widget" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AudioVideoManagement::AvmPlaybackMenu</class>
<extends>QWidget</extends>
<header>avm_playback_menu.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,686 @@
/**
* @RDE_HEADER@
* Radio TreeModel
* $Id$
* $HeadURL$
* $Author$
*/
#include "avm_used_streams_model.h"
#include "avm_available_streams_model.h"
#include <AudioVideoManagement/stream_name_mapping.h>
#include <nglwidgets/ngltreeitem.h>
#include <QMimeData>
#include <QSet>
namespace AudioVideoManagement
{
const QString UsedStreamsModel::s_MimeTypeOI = QStringLiteral("UsedStreamsModel/OrderItem");
UsedStreamsModel::UsedStreamsModel(const std::shared_ptr<Interface::Dtc> avmDtc, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc,
const MenuStruct &ms,
StreamNameMapping *streamNameMapping,
QObject *parent)
: NGLTreeModel(parent, nullptr, {tr("Streams")})
, m_avmDtc(avmDtc)
, m_ismDtc(ismDtc)
, m_dummyId(-10000000)
, m_menuStruct(ms)
, m_streamNameMapping(streamNameMapping)
, m_logger(getLogger("UsedStreamsModel"))
{
Q_ASSERT(streamNameMapping);
initModel();
}
QVariant UsedStreamsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
NGLTreeItem *item = privateData(index);
if (!item->data().canConvert<StreamModelData>())
return QVariant();
const StreamModelData availStream = item->data().value<StreamModelData>();
switch (role)
{
case Qt::DisplayRole:
case Qt::EditRole:
{
switch (index.column())
{
case 0:
return availStream.name;
default:
return QStringLiteral("??");
}
break;
}
case Qt::DecorationRole:
{
switch (index.column())
{
case 0:
{
static const QIcon folder(QStringLiteral("icon:evaluation_folder.svg"));
static const QIcon camera(QStringLiteral("icon:camera_control.svg"));
return availStream.isGroup ? folder : camera;
}
default:
break;
}
break;
}
default:
break;
} // switch
return NGLTreeModel::data(index, role);
}
Qt::ItemFlags UsedStreamsModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = NGLTreeModel::flags(index);
if (index.isValid())
{
defaultFlags ^= Qt::ItemIsDropEnabled;
NGLTreeItem *item = privateData(index);
if (item->data().canConvert<StreamModelData>())
{
StreamModelData menuEntry = item->data().value<StreamModelData>();
defaultFlags |= Qt::ItemIsDragEnabled;
if (menuEntry.isGroup)
{
defaultFlags |= Qt::ItemIsEditable;
defaultFlags |= Qt::ItemIsDropEnabled;
}
}
}
else
defaultFlags |= Qt::ItemIsDropEnabled;
return defaultFlags;
}
QMimeData *UsedStreamsModel::mimeData(const QModelIndexList &indexes) const
{
QString type = AvailableStreamsModel::s_MimeType;
for (const QModelIndex &mi : indexes)
{
if (mimeType(mi) == s_MimeTypeOI)
{
type = s_MimeTypeOI;
break;
}
}
return getMimeData(indexes, type);
}
bool UsedStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(column);
if (action == Qt::IgnoreAction)
return true;
if (!checkDrop(data, index(row, column), parent, action))
return false;
INFO_LOG(m_logger) << "dropMimeData: action = " << action
<< ", row = " << row
<< ", parent: valid = " << parent.isValid();
//DEBUG_LOG(m_logger) << "row: " << row << ", column: " << column << ", parent: " << parent.row();
// insert or append (or replace ?) rows :
int beginRow;
QModelIndex parentR;
if (row != -1)
{
// insert rows:
parentR = parent;
beginRow = row;
}
else if (parent.isValid())
{
// parent is current item:
// if(canHaveChildren(parent)) :
parentR = parent;
// beginRow = 0; prepend?
beginRow = rowCount(parentR); // append to last child
}
else
{
// no parent, so append row: (take the toplevel parent)
parentR = QModelIndex();
beginRow = rowCount(parentR);
}
QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType;
bool ret = insertMimeData(data, mimeType, parentR, beginRow);
if (ret)
{
//fixup data
fixUpData(m_rootItem);
}
return ret;
}
bool UsedStreamsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) const
{
if (!NGLTreeModel::canDropMimeData(data, action, row, column, parent))
return false;
if (!data->hasFormat(AvailableStreamsModel::s_MimeType) && !data->hasFormat(s_MimeTypeOI))
return false;
if ((action != Qt::MoveAction) && (action != Qt::CopyAction))
return false;
if (!checkDrop(data, parent, parent, action))
return false;
if (!parent.isValid())
return true;
NGLTreeItem *item = privateData(parent);
QVariant v = item->data();
StreamModelData ms = v.value<StreamModelData>();
if (!ms.isGroup)
return false;
QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType;
QByteArray encodedData = data->data(mimeType);
QDataStream stream(&encodedData, QIODevice::ReadOnly);
qint64 idParent, idItem, mountId;
bool group;
QVariant itemData;
stream >> idParent >> idItem >> group >> itemData >> mountId;
ms = itemData.value<StreamModelData>();
INFO_LOG(m_logger) << "Insert:"
<< "\n ms.name=" << ms.name
<< "\n ms.isGroup=" << ms.isGroup
<< "\n ms.parentId=" << ms.parentId
<< "\n ms.mountingPointId=" << ms.mountingPointId;
if (ms.isGroup) // do not drop group on group
return false;
return true;
}
QStringList UsedStreamsModel::mimeTypes() const
{
return { AvailableStreamsModel::s_MimeType, s_MimeTypeOI };
}
QString UsedStreamsModel::mimeType(const QModelIndex &index) const
{
if (index.isValid())
{
NGLTreeItem *item = privateData(index);
QVariant v = item->data();
StreamModelData ms = v.value<StreamModelData>();
if (ms.parentId == -1)
return s_MimeTypeOI;
return AvailableStreamsModel::s_MimeType;
}
return s_MimeTypeOI;
}
static void getItems(const NGLTreeItem *item,
int rowId,
int parentId,
std::vector<AudioVideoManagement::AvMenuEntry> &contToAdd,
int &newId)
{
if (item->data().canConvert<StreamModelData>())
{
StreamModelData s = item->data().value<StreamModelData>();
AvMenuEntry me;
me.id(s.id ? s.id : newId++);
me.parentId(parentId);
me.isGroup(s.isGroup);
if (s.isGroup)
me.name().set(s.name.toStdString());
else
me.mountingPointId(s.mountingPointId);
me.rowId(rowId);
contToAdd.push_back(me);
if (s.isGroup)
{
for (int row = 0; row < item->childCount(); ++row)
getItems(item->child(row), row + 1, me.id(), contToAdd, newId);
}
}
}
MenuStruct UsedStreamsModel::getMenuStruct() const
{
MenuStruct ms;
ms.id(m_menuStruct.id());
ms.name(m_menuStruct.name());
int newId = -10000000;
for (int row = 0; row < m_rootItem->childCount(); ++row)
getItems(m_rootItem->child(row), row + 1, ms.id(), ms.menuEntries(), newId);
return ms;
}
void UsedStreamsModel::insertChildren(NGLTreeItem *parent, int parentId, const std::vector<AudioVideoManagement::AvMenuEntry> &entries)
{
QVariant data;
QMap<int, int> rowIds; // no QSet, must be sorted!
for (const AudioVideoManagement::AvMenuEntry &me : entries)
{
if (me.parentId() != parentId)
continue;
data.setValue(StreamModelData::createFromServiceItem(me, m_streamNameMapping, m_avmDtc->videoTeams()));
NGLTreeItem *treeItem = new NGLTreeItem(data);
// find the correct row to insert the data in respect to rowId
auto hierIt = rowIds.insert(me.rowId(), me.rowId());
const int rowToInsert = std::distance(rowIds.begin(), hierIt);
parent->insertChild(rowToInsert, treeItem);
if (me.isGroup())
{
DEBUG_LOG(m_logger) << "insertChildren"
<< me.mountingPointId()
<< me.name();
insertChildren(treeItem, me.id(), entries);
}
}
}
NGLTreeItem *UsedStreamsModel::getItemToCheck(const QModelIndex &index, const QModelIndex &parentIndex) const
{
NGLTreeItem *item = privateData(index);
QVariant v = item->data();
StreamModelData ms = v.value<StreamModelData>();
NGLTreeItem *itemParent = privateData(parentIndex);
const QVariant &variantParent = itemParent->data();
NGLTreeItem *itemToCheck = itemParent;
const StreamModelData &msParent = variantParent.value<StreamModelData>();
if (!ms.isGroup && msParent.isGroup)
itemToCheck = itemParent;
if (!ms.isGroup && !msParent.isGroup)
itemToCheck = m_rootItem;
return itemToCheck;
}
bool UsedStreamsModel::checkSingleItem(NGLTreeItem *target, StreamModelData &toCheck) const
{
bool retValue = true;
if (!target)
{
ERROR_LOG(m_logger) << "Nullptr exception";
return false;
}
else
{
StreamModelData msTarget = target->data().value<StreamModelData>();
if (msTarget.id == toCheck.parentId)
DEBUG_LOG(m_logger) << "Drag&drag in same folder.";
else if (msTarget.isGroup && toCheck.isGroup)
{
DEBUG_LOG(m_logger) << "Trying to drop folder into folder";
retValue = false;
}
else
{
//Check for duplicates:msSource
const QVector<NGLTreeItem *> &children = target->children();
for (const NGLTreeItem *childData : children)
{
const QVariant &variantCheck = childData->data();
const StreamModelData &msChild = variantCheck.value<StreamModelData>();
if ((msChild.name == toCheck.name) && (msChild.id != toCheck.id)) //
{
retValue = false;
break;
}
}
}
}
return retValue;
}
bool UsedStreamsModel::checkDrop(const QMimeData *data, const QModelIndex &index, const QModelIndex &parentIndex, Qt::DropAction action) const
{
/*
* WHY ? You can use the Stream in different Menus aren't you?
* You just need check if its in it CURRENT Dropped parent
* First check if we drop to a folder or directly
* If it's a folder DONT Check the parrent
* We still need to allow reordering
*/
//check alle verschiedenen
NGLTreeItem *itemToCheck = getItemToCheck(index, parentIndex);
//const QVariant &variantCheck = itemToChek->data();
// const StreamModelData &data = data->
QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType;
QByteArray encodedData = data->data(mimeType);
QList<StreamModelData> items;
QDataStream stream(&encodedData, QIODevice::ReadOnly);
qint64 idParent, idItem;
bool containsIcon;
QVariant itemData;
QIcon tmp;
stream >> containsIcon; //Contains icons
while (!stream.atEnd())
{
stream >> idParent >> idItem >> itemData;
if (containsIcon)
stream >> tmp;
items.push_back(itemData.value<StreamModelData>());
}
//Size check
int countDropped = 0; //count to be dropped
int countAgainst = 0; //count in folder
const QVector<NGLTreeItem *> &children = itemToCheck->children();
for (const auto &item : children)
{
const QVariant &variantCheck = item->data();
const StreamModelData &msCheck = variantCheck.value<StreamModelData>();
if (msCheck.isGroup)
continue;
++countAgainst; // prefer pre-increment operator
}
QList<qint64> movedFolders;
bool onlyOneFolder = false;
for (StreamModelData &item : items)
{
StreamModelData msToCheck = itemToCheck->data().value<StreamModelData>();
if (movedFolders.size() == 0)
{
//first item in list, at parentID of current item
movedFolders.push_back(item.parentId);
}
if ((item.isGroup) && movedFolders.contains(item.parentId))
movedFolders.push_back(item.id);
//different sub folder than top level, but not in case of copying from left to right view (Why is ParentId in those cases the ID of the folder
else if ((movedFolders.at(0) != item.parentId) && (movedFolders.contains(item.parentId)) && (action == Qt::MoveAction))
{
DEBUG_LOG(m_logger) << "Ignoring items in sub folder while moving.";
continue;
}
//Check if only items from the same folder (including the folder are moved) (Not checked when copying from left to right view)
else if (!movedFolders.contains(item.parentId) && (action == Qt::MoveAction))
{
DEBUG_LOG(m_logger) << "Only allow items from same folder to be moved.";
return false;
}
if (!checkSingleItem(itemToCheck, item))
{
DEBUG_LOG(m_logger) << "Not able to add item with name: " << item.name;
return false;
}
if (item.isGroup)
{
if (!onlyOneFolder)
{
onlyOneFolder = true;
continue;
}
else
{
DEBUG_LOG(m_logger) << "More than one folder selected.";
return false;
}
}
if ((msToCheck.id == item.parentId) && (Qt::MoveAction == action)) //Moving item in same folder, dont count them
continue;
++countDropped; // prefer pre-increment operator
}
if (countDropped + countAgainst > 12)
{
DEBUG_LOG(m_logger) << "Trying to add to many items to folder";
return false;
}
return true;
}
QSet<int> UsedStreamsModel::fillTakenCameras(NGLTreeItem *getItem) const
{
QSet<int> ret;
const QVector<NGLTreeItem *> &children = getItem->children();
for (const auto &item : qAsConst(children))
{
if (item->childCount())
ret += fillTakenCameras(item);
else
{
const QVariant &variantCheck = item->data();
const StreamModelData &msCheck = variantCheck.value<StreamModelData>();
if (msCheck.isGroup)
continue;
ret.insert(msCheck.mountingPointId);
}
}
return ret;
}
bool UsedStreamsModel::initModel()
{
beginResetModel();
StreamModelData ms;
ms.parentId = -1;
ms.id = -2;
QVariant data;
data.setValue(ms);
setRootItem(new NGLTreeItem(data));
insertChildren(m_rootItem, m_menuStruct.id(), m_menuStruct.menuEntries());
connect(m_ismDtc.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged,
this, &UsedStreamsModel::onDataChanged);
connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::videoTeamChanged,
this, &UsedStreamsModel::onDataChangedMvs);
// connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::streamNameChanged,
// this, &UsedStreamsModel::onStreamNameDataChanged);
connect(m_streamNameMapping, &StreamNameMapping::changed,
this, &UsedStreamsModel::onStreamNameDataChanged);
endResetModel();
return true;
}
NGLTreeItem *UsedStreamsModel::createItem(const QVariant &data, const QIcon &icon) const
{
StreamModelData s = data.value<StreamModelData>();
// need to be adjusted later on /Yeah BUT WHY Did you never DONE it , well we will never know
s.id = 0;
s.parentId = 0;
QVariant newData;
newData.setValue(s);
return new NGLTreeItem(newData, icon);
}
bool UsedStreamsModel::setData(const QModelIndex &idx, const QVariant &value, int role)
{
Q_UNUSED(role);
if (idx.isValid())
{
QVariant data;
NGLTreeItem *ti = privateData(idx);
StreamModelData ms = ti->data().value<StreamModelData>();
if (idx.column() == 0)
ms.name = value.toString();
data.setValue(ms);
ti->setData(data);
emit dataChanged(index(idx.row(), 0), index(idx.row(), columnCount(QModelIndex()) - 1));
return true;
}
return false;
}
void UsedStreamsModel::onDataChanged(const InfrastructureManagement::MainStructureElement &data,
const DataModification modType)
{
beginResetModel();
INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount()
<< " data.elements().size()=" << data.elements().size();
switch (modType)
{
case DataModification::eRemoved:
break;
case DataModification::eModified:
case DataModification::eAdded:
for (const InfrastructureManagement::StructureElement &se : data.elements())
{
INFO_LOG(m_logger) << "se.id()" << se.id()
<< " se.displayName()" << se.displayName()
<< " m_rootItem->childCount()" << m_rootItem->childCount();
const QString newName = QString::fromStdString(se.displayName());
updateStreamNames(se.id(), newName, m_rootItem);
}
}
endResetModel();
emit modelDataChanged();
}
void UsedStreamsModel::onDataChangedMvs(const VideoTeam &data, const DataModification modType)
{
beginResetModel();
INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount();
switch (modType)
{
case DataModification::eRemoved:
break;
case DataModification::eModified:
case DataModification::eAdded:
INFO_LOG(m_logger) << "se.id()" << data.id()
<< " se.displayName()" << data.streamName()
<< " m_rootItem->childCount()" << m_rootItem->childCount();
const QString newName = QString::fromStdString(data.streamName());
updateStreamNames(-1 * data.id(), newName, m_rootItem);
}
endResetModel();
emit modelDataChanged();
}
void UsedStreamsModel::onStreamNameDataChanged(int32_t mountingPointId, const QString &internalName, const QString &displayName)
{
Q_UNUSED(internalName)
beginResetModel();
INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount();
INFO_LOG(m_logger) << "se.id()" << mountingPointId
<< " se.displayName()" << displayName
<< " m_rootItem->childCount()" << m_rootItem->childCount();
updateStreamNames(mountingPointId, displayName, m_rootItem);
endResetModel();
emit modelDataChanged();
}
void UsedStreamsModel::updateStreamNames(const int mountingPointId, const QString &newName, NGLTreeItem *item)
{
INFO_LOG(m_logger) << "updateStreamNames mountingPointId=" << mountingPointId
<< " newName=" << newName;
int childCount = item->childCount();
for (int i = 0; i < childCount; ++i)
{
NGLTreeItem *child = item->child(i);
//////////////////////
if (!child->data().canConvert<StreamModelData>())
ERROR_LOG(m_logger) << "Error convert item";
StreamModelData usedStream = child->data().value<StreamModelData>();
INFO_LOG(m_logger) << "i=" << i
<< " usedStream.mountingPointId=" << usedStream.mountingPointId
<< " usedStream.name=" << usedStream.name
<< " mountingPointId=" << mountingPointId
<< " newName=" << newName;
if (mountingPointId == usedStream.mountingPointId)
{
INFO_LOG(m_logger) << "Set new DisplayName id=" << usedStream.id
<< " mountingPointId=" << usedStream.mountingPointId
<< " name=" << usedStream.name
<< " new Name =" << newName;
usedStream.name = newName;
QVariant dataVar;
dataVar.setValue(usedStream);
child->setData(dataVar);
break;
}
//////////////////////
// Recursively call the function for each child node.
updateStreamNames(mountingPointId, newName, item->child(i));
}
}
void UsedStreamsModel::fixUpData(NGLTreeItem *item)
{
//ERROR_LOG(m_logger) << "FIXUP ";
const QVector<NGLTreeItem *> &children = item->children();
for (NGLTreeItem *child : children)
{
const StreamModelData parentStream = item->data().value<StreamModelData>();
StreamModelData childStream = child->data().value<StreamModelData>();
if (childStream.isGroup && childStream.id == 0)
childStream.id = m_dummyId++;
if (!childStream.isGroup && childStream.id == 0)
childStream.id = m_dummyId++;
childStream.parentId = parentStream.id;
QVariant data;
data.setValue(childStream);
child->setData(data);
if (child->childCount() > 0)
fixUpData(child);
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,84 @@
/**
* @RDE_HEADER@
* Radio TreeModel
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm.hpp>
#include <AudioVideoManagement/avm_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <nglwidgets/ngltreemodel.h>
#include "avm_available_streams_model.h"
namespace AudioVideoManagement
{
class MenuStruct;
class StreamNameMapping;
/**
* Used by MenuEdit.
*
* @see AvailableStreamsModel.
*/
class UsedStreamsModel final : public NGLTreeModel
{
Q_OBJECT
public:
UsedStreamsModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc,
const MenuStruct &ms,
StreamNameMapping *streamNameMapping,
QObject *parent);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &idx, const QVariant &value,
int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) const override;
QStringList mimeTypes() const override;
Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }
MenuStruct getMenuStruct() const;
bool initModel();
protected:
NGLTreeItem *createItem(const QVariant &data, const QIcon &icon) const override;
private slots:
void onDataChanged(const InfrastructureManagement::MainStructureElement &data,
const DataModification modType);
void onDataChangedMvs(const AudioVideoManagement::VideoTeam &data,
const DataModification modType);
void onStreamNameDataChanged(int32_t mountingPointId, const QString &internalName, const QString &displayName);
private:
void updateStreamNames(const int mountingPointId, const QString &newName, NGLTreeItem *item);
void fixUpData(NGLTreeItem *item);
static const QString s_MimeTypeOI;
QString mimeType(const QModelIndex &index) const;
void insertNGLTreeItemChild(NGLTreeItem *item, NGLTreeItem *parent, bool asCopy);
void insertChildren(NGLTreeItem *parent, int parentId, const std::vector<AudioVideoManagement::AvMenuEntry> &entries);
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc;
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
NGLTreeItem *getItemToCheck(const QModelIndex &index, const QModelIndex &parentIndex) const;
bool checkSingleItem(NGLTreeItem * target, StreamModelData & toCheck) const;
bool checkDrop(const QMimeData *data, const QModelIndex &index, const QModelIndex &parentIndex, Qt::DropAction action) const;
QSet<int> fillTakenCameras(NGLTreeItem *getItem) const;
int m_dummyId;
MenuStruct m_menuStruct;
StreamNameMapping *m_streamNameMapping;
NGL::INGLLogger &m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,184 @@
/**
* @RDE_HEADER@
* IntervalModel
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-07-19
*/
#include "interval_model.h"
#include <AudioVideoManagement/av_helper.h>
#include <QVariant>
#include <QString>
#include <QColor>
#include <QModelIndex>
#include <QDateTime>
namespace AudioVideoManagement
{
IntervalModel::IntervalModel()
: LtsAbstractTableModel({tr("Start"), tr("End"), tr("Duration [s]")}, nullptr)
{
}
QString IntervalModel::dateTimeToStringy(const QDateTime &dt) const
{
return QDateTime::fromMSecsSinceEpoch(dt.toMSecsSinceEpoch() + (m_timezoneHoursOffset * 3600 * 1000)).toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
}
QString IntervalModel::msecsSinceEpochToStringy(qint64 msecs) const
{
return dateTimeToStringy(QDateTime::fromMSecsSinceEpoch(msecs));
}
void IntervalModel::setTimezoneHoursOffset(int timezoneHoursOffset)
{
m_timezoneHoursOffset = timezoneHoursOffset;
emit dataChanged({}, {}, {Qt::DisplayRole}); // TODO: only affected columns !!
}
QVariant IntervalModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
const AvaTimelinePart &interval = m_vecData.at(index.row());
switch (role)
{
case Qt::DisplayRole:
{
switch (index.column())
{
case Start:
return msecsSinceEpochToStringy(toMSecSinceEpoch(interval.first));
case End:
return msecsSinceEpochToStringy(toMSecSinceEpoch(interval.second));
case Duration:
{
qint64 duration = (toMSecSinceEpoch(interval.second) - toMSecSinceEpoch(interval.first));
qint64 secs = duration / 1000;
qint64 msecs = (duration % 1000);
return QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0'));
}
}
Q_ASSERT(false);
break;
}
case Qt::BackgroundRole:
{
if (m_currentInterval == (2 * index.row()))
return QVariant(QColor(Qt::green));
return QVariant();
}
default:
break;
}
return QVariant();
}
void IntervalModel::reset()
{
m_currentPos.reset();
m_currentInterval.reset();
setData({});
emit currentIntervalChanged(false, false, QModelIndex());
}
/**
* forceSignal == true is used in a reconnect scenario.
* Then, we call this method with pos == m_currentPos but
* rely on the currentIntervalChanged signal in order
* to update the button states (backward and forward interval).
*/
void IntervalModel::setCurrentPosition(AvaTimePoint pos, bool forceSignal)
{
if (m_vecData.isEmpty()) // can happen after a reconnect
return;
HalfIndex newInterval = intervalForPos(pos);
bool intervalChanged = newInterval != m_currentInterval;
m_currentPos = pos;
m_currentInterval = newInterval;
if (!intervalChanged && !forceSignal)
return;
bool hasPrev = newInterval >= 0;
bool hasNext = newInterval < 2 * (m_vecData.size() - 1);
QModelIndex rowIndex;
if (newInterval % 2 == 0)
rowIndex = index(newInterval / 2, 0);
emit currentIntervalChanged(hasPrev, hasNext, rowIndex);
QModelIndex topLeft = index(0, 0);
QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1);
emit dataChanged(topLeft, bottomRight, {Qt::BackgroundRole});
}
const AvaTimelinePart &IntervalModel::previousInterval() const
{
Q_ASSERT(m_currentInterval);
Q_ASSERT(*m_currentInterval != -1);
bool withinInterval = *m_currentInterval % 2 == 0;
int intervalIndex = *m_currentInterval / 2;
if (!withinInterval)
return m_vecData.at(intervalIndex);
// Do something like in a CD player:
// If we're a "good" time within the song, go to the beginning of the song.
// Otherwise (if we're right at the beginning), go to the previous song.
if (intervalIndex == 0)
return m_vecData.at(intervalIndex);
const AvaTimelinePart &interval = m_vecData.at(intervalIndex);
if (*m_currentPos > interval.first + std::chrono::seconds(2))
return interval;
else
return m_vecData.at(intervalIndex - 1);
}
const AvaTimelinePart &IntervalModel::nextInterval() const
{
Q_ASSERT(m_currentInterval);
int intervalIndex = (*m_currentInterval + 2) / 2;
return m_vecData.at(intervalIndex);
}
IntervalModel::HalfIndex IntervalModel::intervalForPos(AvaTimePoint pos) const
{
Q_ASSERT(!m_vecData.isEmpty());
for (int i = 0; i < m_vecData.size(); ++i)
{
const AvaTimelinePart &interval = m_vecData.at(i);
if (pos < interval.first)
{
// position is between current and previous interval.
return 2 * i - 1;
}
if (pos >= interval.first && pos <= interval.second)
{
// found containing interval
return 2 * i;
}
}
// position is after last interval
return 2 * m_vecData.size() - 1;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,94 @@
/**
* @RDE_HEADER@
* IntervalModel
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, SLSE, 2017-07-19
*/
#pragma once
#include <AudioVideoManagement/ava_types.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <boost/optional.hpp>
namespace AudioVideoManagement
{
/**
* Model for the Timeline so it can be shown in a TabView.
* This is a poor man's version as the timeline is displayed in a table.
* Later it will probably be replaced by something more usable.
* @author Christian Fröbel
*/
class IntervalModel : public LtsWidgets::LtsAbstractTableModel<AvaTimelinePart>
{
Q_OBJECT
public:
enum Columns
{
Start = 0,
End,
Duration,
};
IntervalModel();
QVariant data(const QModelIndex &index, int role) const override;
void reset();
bool isEmpty() const { return m_vecData.isEmpty(); }
void setCurrentPosition(AvaTimePoint pos, bool forceSignal = false);
boost::optional<AvaTimePoint> currentPosition() const { return m_currentPos; }
/**
* If the current position is within an interval:
* - and the position is at least 2s into the interval, return that
* - otherwise return the previous interval (unless there is none).
* If the current position is between two intervals return the one before
* the current position.
*/
const AvaTimelinePart &previousInterval() const;
const AvaTimelinePart &nextInterval() const;
signals:
/**
* Emitted if the current interval changed either due to
* setCurrentPosition() or reset().
* There must not always be a current interval as the current position
* can also be between two intervals.
* @param rowIndex might be invalid if the current position is between intervals.
*/
void currentIntervalChanged(bool hasPreviousInterval, bool hasNextInterval, const QModelIndex &rowIndex);
public slots:
void setTimezoneHoursOffset(int timezoneHoursOffset);
private:
QString dateTimeToStringy(const QDateTime &dt) const;
QString msecsSinceEpochToStringy(qint64 msecs) const;
/**
* A HalfIndex denotes the current position within or between intervals.
* The counting of indices is done as follows:
* -1 0 1 2 3 4 5 6 7
* ----- ----- ----- ----- # intervals 0...3
* Examples:
* within interval with index i => halfIndex = 2*i
* before interval with index i => halfIndex = 2*i-1
* after interval with index i => halfIndex = 2*i+1
*/
using HalfIndex = int;
HalfIndex intervalForPos(AvaTimePoint pos) const;
boost::optional<AvaTimePoint> m_currentPos;
boost::optional<HalfIndex> m_currentInterval;
int m_timezoneHoursOffset = 0;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,476 @@
/**
* @RDE_HEADER@
* TopicReplay
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-06-26
*/
#include "topic_replay.h"
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/avm_data_plugin_interface.h>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/ava_session.h>
#include <AudioVideoManagement/vd_control.h>
#include <AudioVideoManagement/vd_session.h>
#include <TopicEditorBase/topic_editor_base.h>
#include <LtsUtilsCore/json_object.h>
#include <nglsettings/nglsettings_settings.h>
#include <QWidget>
#include <QFileInfo>
#include <QJsonDocument>
#include <QDir>
#include <QtGlobal>
#include <QRect>
#include <QDebug>
#include <vector>
#include <chrono>
namespace AudioVideoManagement
{
class TopicReplay::ExportScript : public QObject
{
Q_OBJECT
public:
ExportScript(const QString &topLevelExportDir, TopicEditorBase::ExportNotifier *exportStep);
public slots:
void avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success);
private:
QString audioVideoPlaylistErstellen(const QString &dir, AvaStreamType streamType);
bool writeFile(const QByteArray &data, const QString &fileName);
QString m_topLevelExportDir;
TopicEditorBase::ExportNotifier *m_exportStep;
};
//======================================================================================================================
TopicReplay::TopicReplay(DataInterface *dataIfc, bool isAudio)
: m_isAudio(isAudio)
, m_dataIfc(dataIfc)
, m_logger(getLogger("TopicReplay"))
{
DEBUG_LOG(m_logger) << "TopicReplay::TopicReplay()";
Q_ASSERT(dataIfc);
}
TopicReplay::~TopicReplay()
{
DEBUG_LOG(m_logger) << "TopicReplay::~TopicReplay()";
if (m_isPlaying)
playbackStop();
}
QWidget *TopicReplay::createWidget()
{
DEBUG_LOG(m_logger) << "TopicReplay::createWidget()";
Q_ASSERT(!m_screenPlacementHelper);
if (m_isAudio)
return nullptr;
m_screenPlacementHelper.reset(new ScreenPlacementHelper());
connect(m_screenPlacementHelper.data(), &ScreenPlacementHelper::widgetPlaced, this, &TopicReplay::onWidgetPlaced);
return m_screenPlacementHelper.data();
}
void TopicReplay::slotSetScriptResponse(const AudioVideoManagement::AvaStreamInfo &streamInfo, AudioVideoManagement::AvaStreamState streamState)
{
if (streamState == AudioVideoManagement::AvaStreamState::NotPossible)
ERROR_LOG(m_logger) << "Script could not be set";
else
ERROR_LOG(m_logger) << "Script set!";
ERROR_LOG(m_logger) << "streamInfo: start" << streamInfo.duration.count();
ERROR_LOG(m_logger) << "streamState: " << static_cast<int>(streamState);
}
bool TopicReplay::playbackInit(const QJsonObject &attributes)
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackInit()";
DEBUG_LOG(m_logger) << QJsonDocument(attributes).toJson();
if (m_screenPlacementHelper && !m_isAudio)
m_screenPlacementHelper->show();
m_playbackAttributes = attributes;
// test avs before using it after the
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "no ava control!";
return false;
}
AvaTopicData topicData(m_playbackAttributes);
if (!topicData.isValid)
{
ERROR_LOG(m_logger) << "topicData is invalid";
return false;
}
m_scriptName = topicData.scriptName();
Q_ASSERT(topicData.isAudio() == m_isAudio);
PlaybackApp *app = m_isAudio ? m_dataIfc->audioPresentationApp() : m_dataIfc->videoPresentationApp();
if (app == nullptr || app->inUse())
{
ERROR_LOG(m_logger) << "app invalid or in use!";
return false;
}
/* init anyway as the geometry changes whenever..
* if (!m_isAudio && !m_dialogGeometry)
{
//ERROR_LOG(m_logger) << "is video topic but no screen allocated for playback!";
ERROR_LOG(m_logger) << "is video topic but no screen allocated for playback!";
return false; // video topic but no screen allocated for playback
}*/
if (!m_dialogGeometry)
{
if (m_isAudio)
m_appHandle = PlaybackAppHandle(app); // don't give geo for audio, otherwise avs is advised to create a window for audio, but a window is not needed for audio
else
m_appHandle = PlaybackAppHandle(app, QRect(0, 0, 0, 0)); // prevent the PlaybackApp to ask MMM for a selection by providing an invalid geo (puts the window at the last known position)
}
else
m_appHandle = PlaybackAppHandle(app, *m_dialogGeometry);
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app after creating appHandle!";
return false;
}
NGL::Settings s({});
bool isLocal = s.isDatabaseConnectionLocal();
DEBUG_LOG(m_logger) << "isLocal? " << isLocal;
AvaScriptSource source;
if (isLocal)
{
if (m_isAudio)
source = AvaScriptSource::LocalAudio;
else
source = AvaScriptSource::LocalVideo;
}
else // Server
{
if (m_isAudio)
source = AvaScriptSource::ServerAudio;
else
source = AvaScriptSource::ServerVideo;
}
DEBUG_LOG(m_logger) << "source: " << static_cast<int>(source);
ctrl->setScript(m_appHandle.dialogId(), m_scriptName,
source, topicData.streamList(),
LtsUtils::createConnection(ctrl, &AudioVideoManagement::AvaControl::setScriptResponse,
this, &TopicReplay::slotSetScriptResponse));
return true;
}
bool TopicReplay::playbackStart(int secondsOffset)
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackStart()";
if (m_screenPlacementHelper && !m_isAudio)
m_screenPlacementHelper->show();
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "ctrl == nullptr";
return false;
}
AvaTimePoint p{AvaDuration{std::chrono::seconds(secondsOffset)}};
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app!";
return false;
}
ctrl->setAbsPositionScript(m_appHandle.dialogId(), m_scriptName, p);
ctrl->startScript(m_appHandle.dialogId(), m_scriptName);
m_isPlaying = true;
DEBUG_LOG(m_logger) << "m_isPlaying = true";
return true;
}
bool TopicReplay::playbackPause()
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackPause()";
if (m_screenPlacementHelper)
m_screenPlacementHelper->hide();
if (!m_isPlaying)
return true;
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "no ctrl!";
return false;
}
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app!";
return false;
}
ctrl->pauseScript(m_appHandle.dialogId(), m_scriptName);
return true;
}
bool TopicReplay::playbackStop()
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackStop()";
if (m_screenPlacementHelper)
m_screenPlacementHelper->hide();
if (!m_isPlaying)
return true;
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "no ctrl!";
return false;
}
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app!";
return false;
}
ctrl->stopScript(m_appHandle.dialogId(), m_scriptName);
ctrl->releaseScript(m_appHandle.dialogId(), m_scriptName);
m_isPlaying = false;
return true;
}
bool TopicReplay::exportTopic(const QJsonObject &attributes,
const QString &folderPath,
TopicEditorBase::ExportNotifier *exportNotifier)
{
Q_ASSERT(!m_exportScript);
VdControl *ctrl = getVdControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "ctrl missing!";
exportNotifier->setExportFailed();
return false;
}
m_exportScript.reset(new ExportScript(folderPath, exportNotifier));
AvaTopicData topicData(attributes);
m_scriptName = topicData.scriptName();
if (!topicData.isValid)
{
ERROR_LOG(m_logger) << "topicData is invalid";
exportNotifier->setExportFailed();
return false;
}
QFileInfo fileInfo(QDir(folderPath).filePath(topicData.scriptName()));
Q_ASSERT(topicData.isAudio() == m_isAudio);
ctrl->vdSetScript(m_scriptName,
m_isAudio ? AvaStreamType::Audio : AvaStreamType::Video,
0, topicData.streamList(), fileInfo.baseName(), fileInfo.absolutePath(),
this, &TopicReplay::avScriptFinished);
return true;
}
void TopicReplay::onWidgetPlaced(const QRect &geometry)
{
/*DEBUG_LOG*/WARNING_LOG(m_logger) << "TopicReplay::onWidgetPlaced() - geo: " << geometry.x() << "/" << geometry.y()
<< " m_isAudio=" << m_isAudio;
m_dialogGeometry = geometry;
if (m_isAudio)
return;
PlaybackApp *app = m_isAudio ? m_dataIfc->audioPresentationApp() : m_dataIfc->videoPresentationApp();
if (app == nullptr)
{
ERROR_LOG(m_logger) << "app missing!";
return;
}
if (!app->inUse()) // playbackInit needs to create an AppHandle before we can use it, otherwise we would aquire the app at this point an playbackInit has no chance to work with the app because we aquired it here (NOTE: aquiring an app blocks it from being modified, etc)
{
ERROR_LOG(m_logger) << "app not in use - waiting for playbackInit to aquire it";
return;
}
DEBUG_LOG(m_logger) << "setGeometry";
app->setGeometry(*m_dialogGeometry);
}
AvaControl *TopicReplay::getAvaControl()
{
if (!m_dataIfc->hasAvsSession())
return nullptr;
AvaSession *avaSession = m_dataIfc->avaSession();
if (!avaSession->isEstablished())
return nullptr;
return avaSession->control();
}
VdControl *TopicReplay::getVdControl()
{
if (!m_dataIfc->hasAvsSession())
return nullptr;
VdSession *vdSession = m_dataIfc->vdSession();
if (!vdSession->isEstablished())
return nullptr;
return vdSession->control();
}
void TopicReplay::avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success)
{
VdControl *ctrl = getVdControl();
ctrl->vdReleaseScript(m_scriptName);
m_exportScript->avScriptFinished(exportDir, streamType, success);
}
//======================================================================================================================
TopicReplay::ExportScript::ExportScript(const QString &topLevelExportDir, TopicEditorBase::ExportNotifier *exportStep)
: m_topLevelExportDir(topLevelExportDir)
, m_exportStep(exportStep)
{
connect(exportStep, &QObject::destroyed, this, [this]()
{
if (static_cast<TopicEditorBase::ExportNotifier *>(sender()) != m_exportStep)
return;
m_exportStep = nullptr;
});
Q_ASSERT(exportStep);
}
void TopicReplay::ExportScript::avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success)
{
QString absPathToPlaylist = audioVideoPlaylistErstellen(exportDir, streamType);
QDir toplevelExportDir(m_topLevelExportDir);
QString relativePathToPlaylist = toplevelExportDir.relativeFilePath(absPathToPlaylist);
if (m_exportStep == nullptr)
return;
if (success)
m_exportStep->setExportSuccessful(relativePathToPlaylist);
else
m_exportStep->setExportFailed();
}
QString TopicReplay::ExportScript::audioVideoPlaylistErstellen(const QString &dir, AvaStreamType streamType)
{
QDir avDir(dir);
const QFileInfoList dateienList = avDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
QByteArray dataForFile;
QString data;
dataForFile.append("[playlist]\n");
int numberOfEntries = 0;
int i = 1;
const QString tmpl1(QStringLiteral("File %1=%2\n"));
const QString tmpl2(QStringLiteral("# File %1=%2\n"));
for (const QFileInfo &fileInfo : dateienList)
{
if (streamType == AvaStreamType::Video && fileInfo.suffix() == QLatin1String(".wav"))
{
data += tmpl2.arg(QString::number(i), fileInfo.fileName());
i--;
}
else
{
data += tmpl1.arg(QString::number(i), fileInfo.fileName());
numberOfEntries++;
}
i++;
}
QString numberOfEntriesString = QStringLiteral("NumberOfEntries=") + QString::number(numberOfEntries) + QStringLiteral("\n");
dataForFile.append(numberOfEntriesString.toUtf8());
dataForFile.append(data.toUtf8());
QString fileName;
switch (streamType)
{
case AvaStreamType::Audio:
fileName = avDir.filePath(QStringLiteral("audio.pls"));
break;
case AvaStreamType::Video:
fileName = avDir.filePath(QStringLiteral("video.pls"));
break;
}
writeFile(dataForFile, fileName);
return fileName;
}
bool TopicReplay::ExportScript::writeFile(const QByteArray &data, const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
return false;
if (file.write(data) == -1)
{
file.close();
return false;
}
file.close();
return true;
}
} // namespace AudioVideoManagement
#include "topic_replay.moc"
@@ -0,0 +1,130 @@
/**
* @RDE_HEADER@
* TopicReplay
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Fröbel, NLSE, 2018-06-26
*/
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <PluginInterfaces/topic_replay_interface.h>
#include <QString>
#include <QRect>
#include <QVector>
#include <QWidget>
#include <boost/optional.hpp>
#include <memory>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/ava_session.h>
namespace AudioVideoManagement
{
enum class AvaStreamType;
/**
* Helper class used to determine where the AVS dialog for presentation shall be placed.
* An instance of this class will be returned by TopicReplay::createWidget().
* The ScriptPlayer will then place (and resize) this widget accordingly.
*/
class ScreenPlacementHelper : public QWidget
{
Q_OBJECT
public:
ScreenPlacementHelper(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle(QStringLiteral("Topic Replay Screen Placement Helper"));
// NOTE: the widget has to be visible in order to receive resizeEvents !!
// make widget transparent so that it's not disturbing
setWindowFlags(Qt::FramelessWindowHint/* | Qt::Popup*/);
setAttribute(Qt::WA_TranslucentBackground);
activateWindow(); // force it into a window
show();
}
protected:
void moveEvent(QMoveEvent *) override
{
emitNewGeometry();
}
void resizeEvent(QResizeEvent *) override
{
emitNewGeometry();
}
void emitNewGeometry()
{
//QRect globalGeometry(mapToGlobal(geometry().topLeft()), size());
emit widgetPlaced(geometry());//globalGeometry);
}
signals:
void widgetPlaced(const QRect &geometry);
};
class DataInterface;
class AvaControl;
class VdControl;
struct AvaScriptEntry;
class TopicReplay final : public QObject, public ::TopicReplay::Interface
{
Q_OBJECT
public:
TopicReplay(DataInterface *dataIfc, bool isAudio);
~TopicReplay();
/**
* @return nullptr for audio topics.
*/
QWidget *createWidget() override;
bool playbackInit(const QJsonObject &attributes) override;
bool playbackStart(int secondsOffset) override;
bool playbackPause() override;
bool playbackStop() override;
bool exportTopic(const QJsonObject &attributes,
const QString &folderPath,
TopicEditorBase::ExportNotifier *pExportNotifier) override;
private slots:
void onWidgetPlaced(const QRect &geometry);
void avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success);
void slotSetScriptResponse(const AvaStreamInfo &, AvaStreamState);
private:
AvaControl* getAvaControl();
VdControl* getVdControl();
//class ScreenPlacementHelper;
class ExportScript;
const bool m_isAudio;
PlaybackAppHandle m_appHandle;
DataInterface *m_dataIfc;
QString m_scriptName;
bool m_isPlaying{false};
QScopedPointer<ScreenPlacementHelper> m_screenPlacementHelper;
boost::optional<QRect> m_dialogGeometry;
QScopedPointer<ExportScript> m_exportScript;
LOGGER_T m_logger;
QJsonObject m_playbackAttributes;
};
} // namespace AudioVideoManagement
+11
View File
@@ -0,0 +1,11 @@
#
# $Id$
# $HeadURL$
# $Author$
#
ADD_SUBDIRECTORY(AVMenuEdit)
ADD_SUBDIRECTORY(CombatNetRadio)
ADD_SUBDIRECTORY(Eloka)
ADD_SUBDIRECTORY(TrainingVoiceCommunication)
ADD_SUBDIRECTORY(MobileVideoSystem)
@@ -0,0 +1,101 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Hannes Baeuerle, SLSE, 2017-08-18
#
SET(avm_combat_net_radio_gui_SRCS
CombatNetRadioConfiguration/avm_cnr_json_helper.h
CombatNetRadioConfiguration/avm_cnr_json_helper.cpp
CombatNetRadioConfiguration/avm_combat_net_radio_dialog.h
CombatNetRadioConfiguration/avm_combat_net_radio_dialog.cpp
CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h
CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.cpp
CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.h
CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.cpp
CombatNetRadioConfiguration/avm_load_combat_net_radio_dialog.h
CombatNetRadioConfiguration/avm_load_combat_net_radio_dialog.cpp
CombatNetRadioConfiguration/avm_load_combat_net_radio_dialog_model.h
CombatNetRadioConfiguration/avm_load_combat_net_radio_dialog_model.cpp
FrequencyPlanning/avm_edit_frequency_planning_dialog.h
FrequencyPlanning/avm_edit_frequency_planning_dialog.cpp
FrequencyPlanning/avm_edit_frequency_planning_frequency_data_model.h
FrequencyPlanning/avm_edit_frequency_planning_frequency_data_model.cpp
FrequencyPlanning/avm_edit_frequency_planning_resource_dialog.h
FrequencyPlanning/avm_edit_frequency_planning_resource_dialog.cpp
FrequencyPlanning/avm_frequency_planning_widget.h
FrequencyPlanning/avm_frequency_planning_widget.cpp
FrequencyPlanning/avm_edit_frequency_planning_set_resource_dialog.h
FrequencyPlanning/avm_edit_frequency_planning_set_resource_dialog.cpp
FrequencyPlanning/avm_edit_frequency_planning_resource_data_model.h
FrequencyPlanning/avm_edit_frequency_planning_resource_data_model.cpp
FrequencyPlanning/avm_init_frequency_planning_dialog.h
FrequencyPlanning/avm_init_frequency_planning_dialog.cpp
FrequencyPlanning/avm_frequency_planning_dialog.h
FrequencyPlanning/avm_frequency_planning_dialog.cpp
FrequencyPlanning/avm_frequency_planning_dialog_model.h
FrequencyPlanning/avm_frequency_planning_dialog_model.cpp
FrequencyPlanning/avm_frequency_planning_helper.h
FrequencyPlanning/avm_frequency_planning_helper.cpp
avm_combat_net_radio_gui_plugin.h
avm_combat_net_radio_gui_plugin.cpp
FrequencyPlanning/avm_radio_specifica_widget.cpp
FrequencyPlanning/avm_radio_specifica_widget.h
FrequencyPlanning/avm_radio_specifica_hf_widget.cpp
FrequencyPlanning/avm_radio_specifica_hf_widget.h
FrequencyPlanning/avm_radio_specifica_uhf_widget.cpp
FrequencyPlanning/avm_radio_specifica_uhf_widget.h
FrequencyPlanning/avm_radio_specifica_vhf_widget.cpp
FrequencyPlanning/avm_radio_specifica_vhf_widget.h
FrequencyPlanning/avm_load_frequency_planning_dialog.cpp
FrequencyPlanning/avm_load_frequency_planning_dialog.h
FrequencyPlanning/avm_load_frequency_planning_dialog_model.cpp
FrequencyPlanning/avm_load_frequency_planning_dialog_model.h
)
NGL_WRAP_UI(avm_combat_net_radio_gui_SRCS
CombatNetRadioConfiguration/avm_combat_net_radio_dialog.ui
CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.ui
CombatNetRadioConfiguration/avm_load_combat_net_radio_dialog.ui
FrequencyPlanning/avm_edit_frequency_planning_dialog.ui
FrequencyPlanning/avm_edit_frequency_planning_resource_dialog.ui
FrequencyPlanning/avm_frequency_planning_widget.ui
FrequencyPlanning/avm_edit_frequency_planning_set_resource_dialog.ui
FrequencyPlanning/avm_init_frequency_planning_dialog.ui
FrequencyPlanning/avm_frequency_planning_dialog.ui
FrequencyPlanning/avm_radio_specifica_hf_widget.ui
FrequencyPlanning/avm_radio_specifica_uhf_widget.ui
FrequencyPlanning/avm_radio_specifica_vhf_widget.ui
FrequencyPlanning/avm_load_frequency_planning_dialog.ui
)
NGL_ADD_LIBRARY(WS avmCombatNetRadioGuiPlugin MODULE ${avm_combat_net_radio_gui_SRCS})
NGL_TARGET_LINK_LIBRARIES(avmCombatNetRadioGuiPlugin
svcCnrHelper
LtsPrinting
nglwidgets
NGLLogger
ngltools
LtsSession
LtsSessionData
AvmDataPluginInterface
ExmDataPluginInterface
PpmDataPluginInterface
FlmDataPluginInterface
svcAvmCommon
AvmShared
svcExmClient
Qt5::Widgets
LtsWidgets
LtsDataView
LtsUtilsGuiDds
LtsUtilsSettings
)
NGL_INSTALL_TARGET(avmCombatNetRadioGuiPlugin)
@@ -0,0 +1,98 @@
/**
* @RDE_HEADER@
* Helper funtcions for converting CnrConfigs
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#include "avm_cnr_json_helper.h"
#include <AudioVideoManagement/avm_dtc.h>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <FileManagement/flm_translation.h>
namespace AudioVideoManagement
{
QByteArray cnrConfigVectorToJson(const QVector<CnrConfig> &configVector, int32_t exerciseId)
{
const bool isGlobal = exerciseId == 0;
QJsonObject obj;
QJsonArray arr;
for (const CnrConfig &config : qAsConst(configVector))
{
if((isGlobal && config.exerciseId().is_set())
|| (!isGlobal && !config.exerciseId().is_set()))
continue;
arr += cnrConfigToJson(config);
}
obj.insert(JsonKeys::XML_TYPE(), FileManagement::toString(FileManagement::XmlType::AVMCombatNetRadio));
obj.insert(JsonKeys::CNR_CONFIGURATION(), arr);
const QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
QJsonObject cnrConfigToJson(const CnrConfig &config)
{
QJsonObject cnrObj;
using namespace JsonKeys;
cnrObj.insert(CALL_SIGN(), QString::fromStdString(config.callSign()));
cnrObj.insert(CNR_CHANNEL_ID(), config.cnrChannelId());
cnrObj.insert(IS_GLOBAL(), !config.exerciseId().is_set());
cnrObj.insert(RADIO_CIRCUIT(), QString::fromStdString(config.radioCircuit()));
cnrObj.insert(IS_ACTIVE(), config.isActive());
cnrObj.insert(RECORDING(), config.recording());
return cnrObj;
}
template<typename T>
T jsonToCnr(const QJsonObject &cnrJson)
{
T cnrConfigRequest;
using namespace JsonKeys ;
cnrConfigRequest.callSign(cnrJson.value(CALL_SIGN()).toString().toStdString());
cnrConfigRequest.cnrChannelId(cnrJson.value(CNR_CHANNEL_ID()).toInt());
cnrConfigRequest.id(0);
cnrConfigRequest.radioCircuit(cnrJson.value(RADIO_CIRCUIT()).toString().toStdString());
cnrConfigRequest.recording(cnrJson.value(RECORDING()).toBool());
return cnrConfigRequest;
}
QVector<CnrConfig> jsonToCnrConfigVector(const QByteArray &data)
{
using namespace JsonKeys;
const QJsonDocument doc(QJsonDocument::fromJson(data));
const QJsonObject obj(doc.object());
const QJsonArray arr(obj.value(CNR_CONFIGURATION()).toArray());
QVector<CnrConfig> result;
result.reserve(arr.size());
for (const auto &elem : arr)
{
const QJsonObject obj(elem.toObject());
result.append(jsonToCnr<CnrConfig>(obj));
}
return result;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* Helper funtcions for converting CnrConfigs
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#pragma once
#include <AudioVideoManagement/avm_dtc.h>
namespace AudioVideoManagement
{
namespace JsonKeys
{
inline QString CALL_SIGN() { return QLatin1String("callSign"); }
inline QString CNR_CHANNEL_ID() { return QLatin1String("cnrChannelId"); }
inline QString CNR_CONFIGURATION() { return QLatin1String("cnrConfiguration"); }
inline QString IS_ACTIVE() { return QLatin1String("isActive"); }
inline QString IS_GLOBAL() { return QLatin1String("isGlobal"); }
inline QString RADIO_CIRCUIT() { return QLatin1String("radioCircuit"); }
inline QString RECORDING() { return QLatin1String("recording"); }
inline QString XML_TYPE() { return QLatin1String("XmlType"); }
} // namespace JsonKeys
QByteArray cnrConfigVectorToJson(const QVector<CnrConfig> &configVector, int32_t exerciseId);
QJsonObject cnrConfigToJson(const CnrConfig &config);
QVector<CnrConfig> jsonToCnrConfigVector(const QByteArray &data);
template <typename T> // CnrConfig or ModifyCnrConfigRequest
T jsonToCnr(const QJsonObject &cnrJson);
} // namespace AudioVideoManagement
@@ -0,0 +1,259 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialog shows all CNR configurations and offers options for creation and editing
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#include "avm_combat_net_radio_dialog.h"
#include "ui_avm_combat_net_radio_dialog.h"
#include "avm_combat_net_radio_dialog_model.h"
#include "avm_edit_combat_net_radio_dialog.h"
#include "avm_cnr_json_helper.h"
#include "avm_load_combat_net_radio_dialog.h"
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsSession/lts_session.h>
#include <LtsPrinting/printing.h>
#include <QTreeView>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
namespace AudioVideoManagement
{
CombatNetRadioDialog::CombatNetRadioDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_session(session)
, m_ui(new Ui::CombatNetRadioDialog)
, m_menuGroup(menuGroup)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_logger(getLogger("CombatNetRadioDialog"))
{
m_ui->setupUi(this);
setUnchangeable(true);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &CombatNetRadioDialog::onPrint);
setOnlineHelpFilename(QStringLiteral("Situation-Combat_Net_Radio_Configuration"));
}
CombatNetRadioDialog::~CombatNetRadioDialog() = default;
void CombatNetRadioDialog::retranslate()
{
translate(m_ui.get(), tr("Configure Combat Net Radio"));
}
void CombatNetRadioDialog::tableColumnOrganiserSetting()
{
//LtsColumnOrganiser
m_ui->dataView->setColumnsOrganiserEnabled(identifier(), true);
}
bool CombatNetRadioDialog::init(const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
ExerciseManagement::Interface::Dtc *exmDtc)
{
if (!(avmDtc->enableCnrChannel() &&
avmDtc->enableCnrConfig() &&
ppmDtc->enableParty() &&
ppmDtc->enableParticipant() &&
ppmDtc->enableType() &&
avmFtc->enableModifyCnrConfig() &&
avmFtc->enableRemoveCnrConfig()))
{
ERROR_LOG(m_logger) << "Could not enable topics";
return false;
}
m_model.reset(new CombatNetRadioDialogModel(m_session->exerciseId(), this));
m_filter.reset(new CombatNetRadioDialogFilter(m_session->exerciseId(), m_model.get()));
m_avmDtc = avmDtc;
m_avmFtc = avmFtc;
m_ppmDtc = ppmDtc;
m_exmDtc = exmDtc;
m_avmDtc->waitForHistoricalDataCnrChannel(3000);
m_ppmDtc->waitForHistoricalDataParty(3000);
if (!m_model->init(avmDtc, ppmDtc))
{
m_filter.reset();
m_model.reset();
ERROR_LOG(m_logger) << "Could not init model";
return false;
}
m_ui->dataView->setModel(m_filter.get());
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &CombatNetRadioDialog::onAddClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &CombatNetRadioDialog::onEditClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &CombatNetRadioDialog::onRemoveClicked);
connect(m_ui->dataView, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &CombatNetRadioDialog::onSelectionChanged);
onSelectionChanged();
connect(m_ui->dataView, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &CombatNetRadioDialog::onEditClicked);
// Save/Load from Library Button
m_buttonBox->connectLibraryButton(this,
&CombatNetRadioDialog::onImportButtonClicked,
&CombatNetRadioDialog::onExportButtonClicked);
if (!m_session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui))
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Library, false);
return true;
}
void CombatNetRadioDialog::onRemoveCnrConfigReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void CombatNetRadioDialog::onModifyCnrConfigReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void CombatNetRadioDialog::onAddClicked()
{
EditCombatNetRadioDialog *dialog = new EditCombatNetRadioDialog(m_session->exerciseId(), m_session, m_ppmDtc, m_avmDtc, m_avmFtc, m_exmDtc, this);
addDialog(dialog, m_menuGroup, QStringLiteral("NewCNRConfig"), LtsWidgets::LayoutArea::eDialogTab);
}
void CombatNetRadioDialog::onEditClicked()
{
// avoid double click on table:
if (!m_buttonBox->isButtonEnabled(LtsWidgets::DialogButton::Edit))
return;
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
CnrConfig config = m_model->data(currentIndex, Qt::UserRole).value<CnrConfig>();
EditCombatNetRadioDialog *dialog = new EditCombatNetRadioDialog(config,
m_session->exerciseId(),
m_session,
m_ppmDtc,
m_avmDtc,
m_avmFtc,
m_exmDtc,
this);
addDialog(dialog, m_menuGroup, QStringLiteral("EditCNRConfig%1").arg(config.id()), LtsWidgets::LayoutArea::eDialogTab);
}
void CombatNetRadioDialog::onRemoveClicked()
{
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
Core::RemoveElementRequest request(m_model->data(currentIndex, Qt::UserRole).value<CnrConfig>().id());
m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->removeCnrConfigRequest(request, this);
});
}
void CombatNetRadioDialog::onSelectionChanged()
{
bool rowSelected = m_ui->dataView->selectionModel()->selectedRows().size() > 0;
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, rowSelected);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, rowSelected);
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
CnrConfig config = m_model->data(currentIndex, Qt::UserRole).value<CnrConfig>();
bool enabled = rowSelected && (config.exerciseId().is_set() || m_session->partitionType() == IDL::PartitionType::Global);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, enabled);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, enabled);
}
void CombatNetRadioDialog::onPrint()
{
LtsPrinting::printView(m_session, this, m_ui->dataView->treeView());
}
void CombatNetRadioDialog::onExportButtonClicked()
{
QByteArray byteArray = m_filter->modelToJson();
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_session->exerciseId(), byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void CombatNetRadioDialog::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << " onImportButtonClicked() ";
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_session->exerciseId(), FileManagement::toString(FileManagement::XmlType::AVMCombatNetRadio));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMCombatNetRadio)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
LoadCombatNetRadioDialog loadingDialog(content, m_session) ;
loadingDialog.exec();
if (loadingDialog.isApplied())
{
const QVector<Core::RemoveElementRequest> removeRequests = loadingDialog.removeRequests();
for (const auto &removeRequest : removeRequests)
{
m_requestHelper->addRequest([this, removeRequest]()
{
return m_avmFtc->removeCnrConfigRequest(removeRequest, this);
});
}
const QVector<ModifyCnrConfigRequest> modifyRequests = loadingDialog.modifyRequests();
for (const auto &modifyRequest : modifyRequests)
{
m_requestHelper->addRequest([this, modifyRequest]()
{
return m_avmFtc->modifyCnrConfigRequest(modifyRequest, this);
});
}
if (modifyRequests.size() || removeRequests.size())
(void) m_requestHelper->startRequests();
}
}
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,107 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialog shows all CNR configurations and offers options for creation and editing
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <memory>
namespace Ui
{
class CombatNetRadioDialog;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
class CombatNetRadioDialogModel;
class CombatNetRadioDialogFilter;
class CombatNetRadioDialog
: public LtsWidgets::LtsDialog
, protected Interface::Ftc::RemoveCnrConfigReplyHandler
, protected Interface::Ftc::ModifyCnrConfigReplyHandler
{
Q_OBJECT
public:
explicit CombatNetRadioDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent = nullptr);
~CombatNetRadioDialog();
bool init(const std::shared_ptr<Interface::Dtc> &avmDtc, const std::shared_ptr<Interface::Ftc> &avmFtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, ExerciseManagement::Interface::Dtc *exmDtc);
void onRemoveCnrConfigReply(const Core::FunctionReplyObject &replyObject) override final;
void onModifyCnrConfigReply(const Core::FunctionReplyObject &replyObject) override final;
private Q_SLOTS:
void onAddClicked();
void onEditClicked();
void onRemoveClicked();
void onSelectionChanged();
void onPrint();
void onExportButtonClicked();
void onImportButtonClicked();
private: // methods
void retranslate() override;
void tableColumnOrganiserSetting() override;
private: // attributes
LtsSession::LtsSession *m_session;
std::unique_ptr<CombatNetRadioDialogModel> m_model;
std::unique_ptr<CombatNetRadioDialogFilter> m_filter;
std::unique_ptr<Ui::CombatNetRadioDialog> m_ui;
QString m_menuGroup;
std::unique_ptr<LtsUtils::RequestHelper> m_requestHelper;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<Interface::Ftc> m_avmFtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
ExerciseManagement::Interface::Dtc *m_exmDtc{nullptr};
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
LOGGER_T m_logger;
}; // class CombatNetRadioDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CombatNetRadioDialog</class>
<widget class="QWidget" name="CombatNetRadioDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>262</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="LtsDataView::LtsDataView" name="dataView" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,159 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialogModel provices data for CombatNetRadioDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#include "avm_combat_net_radio_dialog_model.h"
#include <ParticipantManagement/ppm_translation.h>
#include <LtsSession/lts_session_data.h>
#include <AudioVideoManagement/avm_ftc.h>
#include "avm_cnr_json_helper.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
namespace AudioVideoManagement
{
CombatNetRadioDialogModel::CombatNetRadioDialogModel(int32_t exerciseId, QObject *parent)
: LtsAbstractTableModel<CnrConfig>(
{
tr("Channel Number"),
tr("Radio Circuit"),
tr("Code Word"),
tr("Party"),
tr("Unit"),
tr("Global")
}, parent)
, m_exerciseId(exerciseId)
, m_logger(getLogger("CombatNetRadioDialogModel"))
{
}
QVariant CombatNetRadioDialogModel::data(const QModelIndex &index, int role) const
{
if ((role != Qt::BackgroundRole &&role != Qt::DisplayRole && role != Qt::UserRole) || !indexIsValid(index))
return {};
const CnrConfig &config = m_vecData.at(index.row());
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case CHANNEL:
INFO_LOG(m_logger)<<"CombatNetRadioDialogModel::data role =" << role
<< " row = " << index.row();
return QString::fromStdString(m_cnrChannels.value(config.cnrChannelId()).name());
case RADIO_CIRCUIT:
return QString::fromStdString(config.radioCircuit());
case CALL_SIGN:
return QString::fromStdString(config.callSign());
case PARTY:
if(config.exerciseId().is_set() || config.partyId() > 0)
return ParticipantManagement::trPartyName(m_parties.value(config.partyId()).name());
else
return QStringLiteral("-");
case UNIT:
{
if (!config.unitId().is_set())
return {};
const int unitId = config.unitId().get();
const auto unit = m_units.value(config.unitId().get());
if (unit.id() != unitId)
return {};
INFO_LOG(m_logger) << " config.unitId()=" << unitId
<< " Str=" << unit.name();
QString unitStr;
if (unit.designator().size() > 0)
unitStr = QString::fromStdString(unit.designator()) + QLatin1Char('/');
unitStr.append(QString::fromStdString(unit.tacticalIdentifier()));
INFO_LOG(m_logger) << "unitStr = " << unitStr;
return unitStr;
}
case GLOBAL:
return config.exerciseId().is_set() ? tr("No") : tr("Yes");
}
}
else if (role == Qt::BackgroundRole)
{
if (config.exerciseId().is_set() && config.exerciseId().get() == m_exerciseId)
return {};
if (!config.exerciseId().is_set() && m_exerciseId <= 0)
return {};
return QColor(Qt::gray);
}
else if (role == Qt::UserRole)
return QVariant::fromValue(config);
return {};
}
bool CombatNetRadioDialogModel::init(const std::shared_ptr<Interface::Dtc> &avmDtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc)
{
m_avmDtc = avmDtc;
m_cnrChannels = avmDtc->cnrChannels();
if (!ppmDtc->visibleParties(m_parties, LtsSessionData::LtsSessionData::getInstance()->getVisibleParties(m_exerciseId)))
{
ERROR_LOG(m_logger) << "Could not retrieve visible Parties!";
return false;
}
m_units = ppmDtc->units();
connect(m_avmDtc.get(), &Interface::Dtc::cnrConfigChanged, this, &CombatNetRadioDialogModel::onDataChanged);
m_avmDtc->cnrConfigs([this](const CnrConfig &c) { insertRow(c); });
return true;
}
bool CombatNetRadioDialogModel::onlyGlobals()
{
for(const CnrConfig &config : qAsConst(m_vecData))
{
if(config.exerciseId().is_set() && config.exerciseId().get() == m_exerciseId)
return false;
}
return true;
}
QByteArray CombatNetRadioDialogFilter::modelToJson() const
{
QVector<CnrConfig> configs;
configs.reserve(rowCount());
for (int row = 0; row < rowCount(); ++row)
{
int sourceRow = mapToSource(index(row, 0)).row();
configs.append(m_sourceModel->getRowData(sourceRow));
}
return cnrConfigVectorToJson(configs, m_exerciseId);
}
CombatNetRadioDialogFilter::CombatNetRadioDialogFilter(int32_t exerciseId, CombatNetRadioDialogModel *sourceModel)
: m_exerciseId(exerciseId)
, m_sourceModel(sourceModel)
{
Q_ASSERT(sourceModel);
setSourceModel(sourceModel);
}
bool CombatNetRadioDialogFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
CnrConfig cnrConfig = m_sourceModel->getRowData(sourceRow);
if (cnrConfig.exerciseId().is_set() && cnrConfig.exerciseId().get() != m_exerciseId)
return false;
return LtsColumnFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
} // namespace AudioVideoManagement
@@ -0,0 +1,85 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialogModel provices data for CombatNetRadioDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#pragma once
// Todo:
#include <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <memory>
#include <cstdint>
namespace AudioVideoManagement
{
class CombatNetRadioDialogModel final
: public LtsWidgets::LtsAbstractTableModel<CnrConfig>
{
Q_OBJECT
public:
enum Columns
{
CHANNEL,
RADIO_CIRCUIT,
CALL_SIGN,
PARTY,
UNIT,
GLOBAL
};
explicit CombatNetRadioDialogModel(int32_t exerciseId, QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
bool init(const std::shared_ptr<Interface::Dtc> &avmDtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc);
bool onlyGlobals();
private:
std::shared_ptr<Interface::Dtc> m_avmDtc;
int32_t m_exerciseId;
QMap<int32_t, CnrChannel> m_cnrChannels;
QMap<int32_t, ParticipantManagement::Party> m_parties;
QMap<int32_t, ParticipantManagement::Participant> m_units;
LOGGER_T m_logger;
};
/**
* Responsible for filtering the CnrConfigs in a way that only those get displayed
* that are either global or local in the given exercise.
*/
class CombatNetRadioDialogFilter : public LtsWidgets::LtsColumnFilterProxyModel
{
public:
explicit CombatNetRadioDialogFilter(int32_t exerciseId, CombatNetRadioDialogModel *sourceModel);
QByteArray modelToJson() const;
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
/** Don't call directly, use the constructor instead. */
using LtsWidgets::LtsColumnFilterProxyModel::setSourceModel;
int32_t m_exerciseId;
CombatNetRadioDialogModel *m_sourceModel;
};
} // namespace AudioVideoManagement
Q_DECLARE_METATYPE(AudioVideoManagement::CnrConfig)
Q_DECLARE_METATYPE(AudioVideoManagement::FrequencyPlanningData)
@@ -0,0 +1,612 @@
/**
* @RDE_HEADER@
* The EditCombatNetRadioDialog allows to set data for a CNR Config
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#include "avm_edit_combat_net_radio_dialog.h"
#include "ui_avm_edit_combat_net_radio_dialog.h"
#include <AudioVideoManagement/av_helper.h>
#include <AudioVideoManagement/Shared/project_settings.h>
#include <ParticipantManagement/ppm_translation.h>
#include <LtsSession/lts_session_data.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
#include <ExerciseManagement/exm_dtc.h>
#include <QJsonObject>
#include <QJsonDocument>
#include <QStandardItemModel>
#include <QTimer>
namespace AudioVideoManagement
{
namespace
{
CnrConfig toConfig(const ModifyCnrConfigRequest &modifyRequest)
{
return {modifyRequest.id(), modifyRequest.cnrChannelId(), modifyRequest.exerciseId(), modifyRequest.radioCircuit(),
modifyRequest.partyId(), modifyRequest.unitId(), modifyRequest.recording(), modifyRequest.callSign(), false};
}
const QString &NO_CHANNEL()
{
static const QString noChannel = QStringLiteral("---");
return noChannel;
}
} // anonymous namespace
EditCombatNetRadioDialog::EditCombatNetRadioDialog(int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent)
: EditCombatNetRadioDialog(boost::none, exerciseId, session, ppmDtc, avmDtc, avmFtc, exmDtc, parent)
{
m_ui->chkRecording->setChecked(true);
m_isGlobal = session->partitionType() == IDL::PartitionType::Global;
if(m_isGlobal)
setUiValuesForGlobal();
initCbChannelsForCreate();
setOnlineHelpFilename(QStringLiteral("Situation-Combat_Net_Radio_Configuration"));
}
EditCombatNetRadioDialog::EditCombatNetRadioDialog(const CnrConfig &cnrConfig,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent)
: EditCombatNetRadioDialog(cnrConfig.id(), exerciseId, session, ppmDtc, avmDtc, avmFtc, exmDtc, parent)
{
m_channelId = cnrConfig.cnrChannelId();
m_isGlobal = session->partitionType() == IDL::PartitionType::Global;
setUiValues(cnrConfig);
setOnlineHelpFilename(QStringLiteral("Situation-Combat_Net_Radio_Configuration"));
}
EditCombatNetRadioDialog::EditCombatNetRadioDialog(const boost::optional<int32_t> &id,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ui(new Ui::EditCombatNetRadioDialog)
, m_id(id)
, m_exerciseId(exerciseId)
, m_ppmDtc(ppmDtc)
, m_avmDtc(avmDtc)
, m_avmFtc(avmFtc)
, m_exmDtc(exmDtc)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_logger(getLogger("EditCombatNetRadioDialog"))
{
Q_ASSERT(ppmDtc);
Q_ASSERT(avmDtc);
Q_ASSERT(avmFtc);
Q_ASSERT(exmDtc);
m_ui->setupUi(this);
if (ProjectSettings::isAlwaysRecording())
{
m_ui->chkRecording->setChecked(true);
m_ui->chkRecording->setEnabled(false);
m_ui->chkRecording->hide();
m_ui->lbRecording->hide();
}
init(session);
}
EditCombatNetRadioDialog::~EditCombatNetRadioDialog() = default;
void EditCombatNetRadioDialog::retranslate()
{
translate(m_ui.get(), QString(), !m_id.is_initialized(), tr("Combat Net Radio"));
}
void EditCombatNetRadioDialog::setUiValues(const CnrConfig &cnrConfig)
{
if(m_isGlobal)
setUiValuesForGlobal();
m_ui->chkGlobal->setChecked(m_isGlobal);
initCbChannels(cnrConfig.cnrChannelId());
bool disableParty = (m_avmDtc->cnrChannel(cnrConfig.cnrChannelId()).frequencyBand() == FrequencyBand_def::REL) || m_isGlobal;
if (!disableParty)
{
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(cnrConfig.partyId()));
int unitId = cnrConfig.unitId().is_set() ? cnrConfig.unitId().get() : 0;
m_ui->cbUnit->setCurrentIndex(m_ui->cbUnit->findData(unitId));
}
m_ui->leCallSign->setText(QString::fromStdString(cnrConfig.callSign()));
m_ui->chkRecording->setChecked(cnrConfig.recording());
m_ui->leRadioCircuit->setText(QString::fromStdString(cnrConfig.radioCircuit()));
onPartyIndexChanged(cnrConfig.partyId());
}
void EditCombatNetRadioDialog::setUiValuesForGlobal()
{
m_ui->chkGlobal->setChecked(true);
m_ui->cbUnit->setEnabled(false);
m_ui->lbUnit->setEnabled(false);
// m_ui->cbParty->addItem(QStringLiteral("-"));
// m_ui->cbParty->setCurrentText(QStringLiteral("-"));
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(1)); // Fixed on O/C as Ulli requested...
m_ui->cbParty->setEnabled(false);
m_ui->lbParty->setEnabled(false);
}
ModifyCnrConfigRequest EditCombatNetRadioDialog::createRequest()
{
ModifyCnrConfigRequest request;
request.id(m_id.get_value_or(0));
request.callSign(m_ui->leCallSign->text().toStdString());
request.cnrChannelId(m_ui->cbChannel->currentData().toInt());
request.radioCircuit(m_ui->leRadioCircuit->text().toStdString());
request.recording(m_ui->chkRecording->isChecked());
request.partyId(m_ui->cbParty->currentData().toInt());
if (!m_isGlobal)
request.exerciseId(m_exerciseId);
if (m_ui->cbUnit->currentIndex() > 0)
request.unitId().set(m_ui->cbUnit->currentIndex());
return request;
}
bool EditCombatNetRadioDialog::apply()
{
ModifyCnrConfigRequest request = createRequest();
if (m_ui->cbUnit->currentIndex() > 0)
{
request.unitId().set(m_ui->cbUnit->currentData().toInt());
INFO_LOG(m_logger)<< "currentIndex="<<m_ui->cbUnit->currentIndex()
<< " unitId ="<<request.unitId().get();
}
return m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->modifyCnrConfigRequest(request, this);
});
}
void EditCombatNetRadioDialog::init(LtsSession::LtsSession *session)
{
m_units = m_ppmDtc->units();
QMap<qint32, ParticipantManagement::Party> parties;
if (!m_ppmDtc->visibleParties(parties, LtsSessionData::LtsSessionData::getInstance()->getVisibleParties(m_exerciseId)))
ERROR_LOG(m_logger) << "Could not retrieve visible Parties!";
for (const ParticipantManagement::Party &party : qAsConst(parties))
m_ui->cbParty->addItem(ParticipantManagement::trPartyName(party.name()), party.id());
connect(m_ui->cbParty, QOverload<int>::of(&LtsWidgets::LtsComboBox::currentIndexChanged), this, &EditCombatNetRadioDialog::onPartyIndexChanged);
connect(m_ui->cbChannel, QOverload<int>::of(&LtsWidgets::LtsComboBox::currentIndexChanged), this, &EditCombatNetRadioDialog::onChannelChanged);
connect(m_avmDtc.get(), &Interface::Dtc::cnrConfigChanged, this, &EditCombatNetRadioDialog::updateFreeChannelsAndTakenNames);
connect(m_exmDtc, &ExerciseManagement::Interface::Dtc::exerciseChanged, this, &EditCombatNetRadioDialog::updateFreeChannelsAndTakenNames);
onPartyIndexChanged(m_ui->cbParty->currentIndex());
// Save/Load from Library Button
m_buttonBox->connectLibraryButton(this,
&EditCombatNetRadioDialog::onImportButtonClicked,
&EditCombatNetRadioDialog::onExportButtonClicked);
if (!session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui))
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Library, false);
m_uiRequirements->addRequirement({m_ui->lbRadioCircuit, [this] () { return isNameValid(m_ui->leRadioCircuit->text()); }, tr("name invalid")});
m_uiRequirements->addRequirement({m_ui->lbChannel, [this] () { return m_ui->cbChannel->currentData().toInt() > 0; }, tr("channel invalid")});
}
void EditCombatNetRadioDialog::initCbChannelsForCreate()
{
Q_ASSERT(!m_id.is_initialized());
Q_ASSERT(m_ui->cbChannel->count() == 0);
updateFreeChannelsAndTakenNames();
if (m_ui->cbChannel->count())
m_ui->cbChannel->setCurrentIndex(0); // select the first free channel
else
m_ui->cbChannel->addItem(NO_CHANNEL(), -1);
}
void EditCombatNetRadioDialog::initCbChannels(int channelId)
{
m_ui->cbChannel->clear();
updateFreeChannelsAndTakenNames();
m_ui->cbChannel->setCurrentIndex(m_ui->cbChannel->findData(channelId));
}
/**
* Called when one of the following changed:
* - Some Exercise
* - Some CnrConfig
*/
void EditCombatNetRadioDialog::updateFreeChannelsAndTakenNames()
{
const auto exercisesConsider = exercisesToConsider();
const auto exercisesIgnore = exercisesToIgnore();
m_takenNames.clear();
QVector<int> freeChannels = m_avmDtc->cnrChannels().keys().toVector();
QMap<int, QString> channelsToDisable; //<cnrChannelId, disable reason>
QMap<int, QString> exerciseNameCache;
const auto allCnrConfigs = m_avmDtc->cnrConfigs();
const ExerciseManagement::Exercise ownExercise = m_exmDtc->exercise(m_exerciseId);
ExerciseManagement::ExerciseState ownState = m_exmDtc->exercise(m_exerciseId).state();
for (const AudioVideoManagement::CnrConfig &cnrConfig : allCnrConfigs)
{
if (m_id.is_initialized() && *m_id == cnrConfig.id())
continue; // consider the channel of the CnrConfig we are editing right now as free
if (ownExercise.state() == ExerciseManagement::ExerciseState::Created && cnrConfig.exerciseId() != m_exerciseId)
{
if (ownExercise.linkedExerciseId() < 1 || cnrConfig.exerciseId() != ownExercise.linkedExerciseId())
continue;
if (!exerciseNameCache.contains(ownExercise.linkedExerciseId()))
exerciseNameCache.insert(ownExercise.linkedExerciseId(), QString::fromStdString(m_exmDtc->exercise(ownExercise.linkedExerciseId()).name()));
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in %1)").arg(exerciseNameCache.value(ownExercise.linkedExerciseId())));
continue;
}
if (ownExercise.state() == ExerciseManagement::ExerciseState::Started || ownExercise.state() == ExerciseManagement::ExerciseState::Paused)
{
if (cnrConfig.exerciseId().is_set())
{
int cnrExerciseId = cnrConfig.exerciseId().get();
if (exercisesConsider.contains(cnrExerciseId) && cnrExerciseId != m_exerciseId)
{
if (!exerciseNameCache.contains(cnrExerciseId))
exerciseNameCache.insert(cnrExerciseId, QString::fromStdString(m_exmDtc->exercise(cnrExerciseId).name()));
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in %1)").arg(exerciseNameCache.value(cnrExerciseId)));
continue;
}
}
}
if (!m_isGlobal)
{
if (cnrConfig.exerciseId().is_set() && !exercisesConsider.contains(cnrConfig.exerciseId().get()))
continue;
if (!cnrConfig.exerciseId().is_set() && ownState != ExerciseManagement::ExerciseState::Created)
{
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in global exercise)"));
continue;
}
}
if (m_isGlobal && cnrConfig.exerciseId().is_set() && exercisesConsider.contains(cnrConfig.exerciseId().get()))
{
int exerciseId = cnrConfig.exerciseId().get();
if (!exerciseNameCache.contains(exerciseId))
exerciseNameCache.insert(exerciseId, QString::fromStdString(m_exmDtc->exercise(exerciseId).name()));
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in %1)").arg(exerciseNameCache.value(exerciseId)));
continue;
}
if (cnrConfig.exerciseId().is_set() && exercisesIgnore.contains(cnrConfig.exerciseId().get()))
continue;
m_takenNames.push_back(QString::fromStdString(cnrConfig.radioCircuit()));
freeChannels.removeOne(cnrConfig.cnrChannelId());
}
if (m_id.is_initialized())
{
if (freeChannels.contains(*m_channelId))
updateCbChannels({*m_channelId}, {});
else
updateCbChannels({},{});
}
else
updateCbChannels(std::move(freeChannels), std::move(channelsToDisable));
}
/**
* Update the channel-Combobox with the given freeChannels and keep the currently
* selected channel if possible.
*/
void EditCombatNetRadioDialog::updateCbChannels(QVector<int> &&freeChannels, QMap<int, QString> &&channelsToDisable)
{
const auto allChannels = m_avmDtc->cnrChannels();
const auto byName = [&allChannels] (int leftChannelId, int rightChannelId) { return QLatin1String(allChannels[leftChannelId].name().c_str()) < QLatin1String(allChannels[rightChannelId].name().c_str()); };
std::sort(freeChannels.begin(), freeChannels.end(), byName);
bool bOk;
int currentlySelectedChannelId = m_ui->cbChannel->currentData().toInt(&bOk); // -1 when no channel was selected
if (!bOk)
{
currentlySelectedChannelId = -1;
if (freeChannels.count() && channelsToDisable.contains(freeChannels.at(0)))
{
currentlySelectedChannelId = freeChannels.at(0);
while (channelsToDisable.contains(currentlySelectedChannelId))
{
int channelIndex = freeChannels.indexOf(currentlySelectedChannelId);
if (channelIndex == freeChannels.size() - 1)
{
currentlySelectedChannelId = -1;
break;
}
currentlySelectedChannelId = freeChannels.at(channelIndex + 1);
}
}
}
m_ui->cbChannel->clear();
for (const auto &channelId : freeChannels)
{
QString channelName = QString::fromStdString(allChannels[channelId].name());
if (channelsToDisable.contains(channelId))
{
QString text = QLatin1String("%1 %2").arg(channelName).arg(channelsToDisable.value(channelId));
m_ui->cbChannel->addItem(text, channelId);
auto * model = qobject_cast<QStandardItemModel*>(m_ui->cbChannel->model());
if(!model)
continue;
auto * item = model->item(m_ui->cbChannel->count() - 1);
if(!item)
continue;
item->setEnabled(false);
continue;
}
m_ui->cbChannel->addItem(channelName, channelId);
}
if (!m_ui->cbChannel->count())
m_ui->cbChannel->addItem(NO_CHANNEL(), -1);
else if (freeChannels.size() == 1)
m_ui->cbChannel->setCurrentIndex(m_ui->cbChannel->findData(currentlySelectedChannelId));
else if (currentlySelectedChannelId != -1)
QTimer::singleShot(100, this, [this, currentlySelectedChannelId] () { m_ui->cbChannel->setCurrentIndex(m_ui->cbChannel->findData(currentlySelectedChannelId)); });
}
/**
* If the current exercise is active or ex_0, return all active exercises.
* If the current exercise is planned but linked to another exercise, return the linked exercise.
* Otherwise, return only the current exercise.
*/
QVector<int> EditCombatNetRadioDialog::exercisesToConsider() const
{
auto isActive = [](ExerciseManagement::ExerciseState state) -> bool
{
return (state == ExerciseManagement::ExerciseState::Started ||
state == ExerciseManagement::ExerciseState::Paused);
};
const auto allExercises = m_exmDtc->exercises();
const ExerciseManagement::Exercise &currentExercise = allExercises.value(m_exerciseId);
QVector<int> toBeConsidered;
toBeConsidered.reserve(allExercises.size());
if (!m_isGlobal && currentExercise.state() == ExerciseManagement::ExerciseState::Created)
{
toBeConsidered.append(currentExercise.id());
if (currentExercise.linkedExerciseId() > 0)
toBeConsidered.append(currentExercise.linkedExerciseId());
return toBeConsidered;
}
if (!m_isGlobal && !isActive(allExercises[m_exerciseId].state()))
return {m_exerciseId};
for (const auto &exercise : allExercises)
{
if (isActive(exercise.state()))
toBeConsidered.append(exercise.id());
}
return toBeConsidered;
}
QVector<int> EditCombatNetRadioDialog::exercisesToIgnore() const
{
const auto allExercises = m_exmDtc->exercises();
auto canBeIgnored = [this] (const ExerciseManagement::Exercise &ex) -> bool
{
if (m_isGlobal)
{
return (ex.state() == ExerciseManagement::ExerciseState::Created ||
ex.state() == ExerciseManagement::ExerciseState::Stopped ||
ex.state() == ExerciseManagement::ExerciseState::Terminated ||
ex.isDeleted());
}
return (ex.state() == ExerciseManagement::ExerciseState::Stopped ||
ex.state() == ExerciseManagement::ExerciseState::Terminated ||
ex.isDeleted());
};
QVector<int> exercisesToIgnore;
for (const auto &ex : allExercises)
{
if (canBeIgnored(ex))
exercisesToIgnore += ex.id();
}
return exercisesToIgnore;
}
bool EditCombatNetRadioDialog::isNameValid(const QString &name) const
{
if (!isStreamNameValid(name))
return false;
if (m_takenNames.contains(name))
return false;
return true;
}
void EditCombatNetRadioDialog::onPartyIndexChanged(int index)
{
m_ui->cbUnit->clear();
m_ui->cbUnit->addItem(QLatin1String("-"), QVariant::fromValue(0));
int32_t partyId = m_ui->cbParty->itemData(index).toInt();
for (const ParticipantManagement::Participant &unit : qAsConst(m_units))
{
QString unitStr;
if (unit.partyId() == partyId)
{
if (unit.designator().size() > 0)
{
unitStr = QString::fromStdString(unit.designator()) + QLatin1Char('/');
INFO_LOG(m_logger)<<"Designator != leer: unitStr = " << unitStr;
}
unitStr += QString::fromStdString(unit.tacticalIdentifier());
INFO_LOG(m_logger) << "unitStr =" << unitStr
<< " id =" << unit.id();
m_ui->cbUnit->addItem(unitStr, unit.id());
}
}
}
void EditCombatNetRadioDialog::onChannelChanged(int index)
{
int channelId = m_ui->cbChannel->itemData(index).toInt();
bool disableParty = (m_avmDtc->cnrChannel(channelId).frequencyBand() == FrequencyBand_def::REL) || m_isGlobal;
m_ui->cbUnit->setDisabled(disableParty);
m_ui->lbUnit->setDisabled(disableParty);
m_ui->cbParty->setDisabled(disableParty);
m_ui->lbParty->setDisabled(disableParty);
if(disableParty && !m_isGlobal)
{
m_ui->cbParty->addItem(QStringLiteral("-"));
m_ui->cbParty->setCurrentText(QStringLiteral("-"));
}
}
// REGG-4433: We need to revise in which way we store the CNR configs in the library.
QByteArray EditCombatNetRadioDialog::cnrConfigRequestToJson()
{
ModifyCnrConfigRequest req = createRequest();
QJsonObject obj;
obj[QStringLiteral("id")] = req.id();
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::AVMEditCombatNetRadio);
obj[QStringLiteral("callSign")] = QString::fromStdString(req.callSign());
obj[QStringLiteral("cnrChannelId")] = req.cnrChannelId();
obj[QStringLiteral("radioCircuit")] = QString::fromStdString(req.radioCircuit());
obj[QStringLiteral("recording")] = req.recording();
obj[QStringLiteral("partyId")] = req.partyId();
obj[QStringLiteral("isGlobal")] = !req.exerciseId().is_set();
if (req.unitId().is_set())
obj[QStringLiteral("unitId")] = req.unitId().get();
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
ModifyCnrConfigRequest EditCombatNetRadioDialog::jsonToModifyCnrConfig(const QByteArray &content)
{
ModifyCnrConfigRequest result;
const QJsonDocument doc(QJsonDocument::fromJson(content));
const QJsonObject obj(doc.object());
result.id(obj[QStringLiteral("id")].toInt()); //TODO Library: check id = 0?
result.callSign(obj[QStringLiteral("callSign")].toString().toStdString());
result.cnrChannelId(obj[QStringLiteral("cnrChannelId")].toInt());
result.radioCircuit(obj[QStringLiteral("radioCircuit")].toString().toStdString());
result.recording(obj[QStringLiteral("recording")].toInt());
result.partyId(obj[QStringLiteral("partyId")].toInt());
if (!obj.contains(QLatin1String("isGlobal")) || !obj[QLatin1String("isGlobal")].toBool())
result.exerciseId(m_exerciseId);
if (obj.contains(QStringLiteral("unitId")))
result.unitId(obj[QStringLiteral("unitId")].toInt());
return result;
}
void EditCombatNetRadioDialog::onExportButtonClicked()
{
QByteArray byteArray = cnrConfigRequestToJson();
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_exerciseId, byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void EditCombatNetRadioDialog::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << " onImportButtonClicked() ";
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_exerciseId, FileManagement::toString(FileManagement::XmlType::AVMEditCombatNetRadio));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMEditCombatNetRadio)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
ModifyCnrConfigRequest req = jsonToModifyCnrConfig(content);
setUiValues(toConfig(req));
INFO_LOG(m_logger) << "--- onImportButtonClicked() ";
}
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,131 @@
/**
* @RDE_HEADER@
* The EditCombatNetRadioDialog allows to set data for a CNR Config
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsSession/lts_session.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <QVector>
#include <QString>
#include <boost/optional.hpp>
#include <memory>
class QCheckBox;
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace Ui
{
class EditCombatNetRadioDialog;
}
namespace AudioVideoManagement
{
class EditCombatNetRadioDialog
: public LtsWidgets::LtsDialog
, protected Interface::Ftc::ModifyCnrConfigReplyHandler
{
Q_OBJECT
public:
/**
* Used to create a new CnrConfig.
*/
explicit EditCombatNetRadioDialog(int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent);
/**
* Used for modifying an existing CnrConfig.
*/
explicit EditCombatNetRadioDialog(const CnrConfig &cnrConfig,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent);
~EditCombatNetRadioDialog();
void retranslate() override;
void onModifyCnrConfigReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
protected:
bool apply() override;
private: // methods
EditCombatNetRadioDialog(const boost::optional<int32_t> &id,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent);
void init(LtsSession::LtsSession *session);
void initCbChannelsForCreate();
void initCbChannels(int channelId);
void updateFreeChannelsAndTakenNames();
void updateCbChannels(QVector<int> &&freeChannels, QMap<int, QString> &&channelsToDisable);
QVector<int> exercisesToConsider() const;
QVector<int> exercisesToIgnore() const;
bool isNameValid(const QString &name) const;
QByteArray cnrConfigRequestToJson();
ModifyCnrConfigRequest createRequest();
ModifyCnrConfigRequest jsonToModifyCnrConfig(const QByteArray &content);
void setUiValues(const CnrConfig &cnrConfig);
void setUiValuesForGlobal();
private Q_SLOTS:
void onPartyIndexChanged(int index);
void onExportButtonClicked();
void onImportButtonClicked();
void onChannelChanged(int index);
private: // attributes
std::unique_ptr<Ui::EditCombatNetRadioDialog> m_ui;
boost::optional<int32_t> m_id; //!< only set when editing
int32_t m_exerciseId;
boost::optional<int32_t> m_channelId; //!< only set when editing
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<Interface::Ftc> m_avmFtc;
ExerciseManagement::Interface::Dtc *m_exmDtc;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
QMap<int32_t, ParticipantManagement::Participant> m_units;
QVector<QString> m_takenNames;
std::unique_ptr<LtsUtils::RequestHelper> m_requestHelper;
bool m_isGlobal;
LOGGER_T m_logger;
}; // class EditCombatNetRadioDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditCombatNetRadioDialog</class>
<widget class="QWidget" name="EditCombatNetRadioDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>459</width>
<height>275</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Asset Stocks</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="lbChannel">
<property name="text">
<string>Channel Number:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LtsWidgets::LtsComboBox" name="cbChannel"/>
</item>
<item row="0" column="2" rowspan="4">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="lbRecording">
<property name="text">
<string>Recording:</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QCheckBox" name="chkRecording">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbRadioCircuit">
<property name="text">
<string>Radio Circuit:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="leRadioCircuit">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Radio Circuit</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="lbParty">
<property name="text">
<string>Party:</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="LtsWidgets::LtsComboBox" name="cbParty">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbCallSign">
<property name="text">
<string>Code Word:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="leCallSign">
<property name="toolTip">
<string>Code Word</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="lbUnit">
<property name="text">
<string>Unit:</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="LtsWidgets::LtsComboBox" name="cbUnit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lbGlobal">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Global:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="chkGlobal">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cbChannel</tabstop>
<tabstop>leRadioCircuit</tabstop>
<tabstop>leCallSign</tabstop>
<tabstop>cbParty</tabstop>
<tabstop>cbUnit</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,82 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialog allows to solve config for loaded CNR Configs
* $Id: avm_edit_combat_net_radio_dialog.h 60982 2023-12-01 10:18:25Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2024-01-30
*/
#include "avm_load_combat_net_radio_dialog.h"
#include "ui_avm_load_combat_net_radio_dialog.h"
#include "avm_cnr_json_helper.h"
#include "avm_load_combat_net_radio_dialog_model.h"
namespace AudioVideoManagement
{
LoadCombatNetRadioDialog::LoadCombatNetRadioDialog(const QByteArray &data,
LtsSession::LtsSession *session,
QWidget *parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_ui(new Ui::LoadCombatNetRadioDialog)
, m_model(new LoadCombatNetRadioDialogModel(data, session))
, m_logger(getLogger("LoadCombatNetRadioDialog"))
{
m_ui->setupUi(this);
m_ui->dataView->setModel(m_model);
m_ui->cbAll->setChecked(m_model->allChecked());
connect(m_ui->cbAll, &QCheckBox::clicked,
m_model, &LoadCombatNetRadioDialogModel::setCheckState);
connect(m_model, &LoadCombatNetRadioDialogModel::dataChanged,
this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
if (!roles.contains(Qt::CheckStateRole)
|| topLeft.column() != 0
|| bottomRight.column() != 0
|| m_ui->cbAll->isChecked() == m_model->allChecked())
return;
const QSignalBlocker blocker(m_ui->cbAll);
Q_UNUSED(blocker);
m_ui->cbAll->setChecked(!m_ui->cbAll->isChecked());
});
connect(m_ui->pbOk, &QPushButton::clicked,
this, &LoadCombatNetRadioDialog::onOk);
connect(m_ui->pbCancel, &QPushButton::clicked,
this, &LoadCombatNetRadioDialog::close);
}
QVector<Core::RemoveElementRequest> LoadCombatNetRadioDialog::removeRequests() const
{
if (m_isApplied)
return m_model->removeRequests();
return {};
}
QVector<ModifyCnrConfigRequest> LoadCombatNetRadioDialog::modifyRequests() const
{
if (m_isApplied)
return m_model->modifyRequests();
return {};
}
bool LoadCombatNetRadioDialog::isApplied() const
{
return m_isApplied;
}
void LoadCombatNetRadioDialog::onOk()
{
m_isApplied = true;
close();
}
LoadCombatNetRadioDialog::~LoadCombatNetRadioDialog() = default;
} // namespace AudioVideoManagement
@@ -0,0 +1,72 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialog allows to solve config for loaded CNR Configs
* $Id: avm_edit_combat_net_radio_dialog.h 60982 2023-12-01 10:18:25Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2024-01-30
*/
#pragma once
#include <LtsWidgets/lts_frameless_dialog.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
namespace Ui
{
class LoadCombatNetRadioDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class LoadCombatNetRadioDialogModel;
class LoadCombatNetRadioDialog
: public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
explicit LoadCombatNetRadioDialog(const QByteArray &data,
LtsSession::LtsSession *session,
QWidget *parent = nullptr);
~LoadCombatNetRadioDialog();
QVector<Core::RemoveElementRequest> removeRequests() const;
QVector<ModifyCnrConfigRequest> modifyRequests() const;
bool isApplied() const;
private Q_SLOTS:
void onOk();
private: // attributes
std::unique_ptr<Ui::LoadCombatNetRadioDialog> m_ui;
LoadCombatNetRadioDialogModel *m_model;
bool m_isApplied = false;
LOGGER_T m_logger;
}; // class LoadCombatNetRadioDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoadCombatNetRadioDialog</class>
<widget class="QWidget" name="LoadCombatNetRadioDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>922</width>
<height>376</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Load Cnr Configs</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="cbAll">
<property name="text">
<string>Check/Uncheck all</string>
</property>
</widget>
</item>
<item>
<widget class="LtsDataView::LtsDataView" name="dataView" native="true"/>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pbOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,434 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialogModel provices data for LoadCombatNetRadioDialog
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#include "avm_load_combat_net_radio_dialog_model.h"
#include <ParticipantManagement/ppm_translation.h>
#include <LtsSession/lts_session_data.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <Core/core.hpp>
#include <FileManagement/flm_translation.h>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include "avm_cnr_json_helper.h"
#include <LtsSession/lts_session.h>
#include <ExerciseManagement/exm_data_plugin_interface.h>
#include <AudioVideoManagement/avm_data_plugin_interface.h>
namespace AudioVideoManagement
{
LoadCombatNetRadioDialogModel::LoadCombatNetRadioDialogModel(const QByteArray &data,
LtsSession::LtsSession *session,
QObject *parent)
: LtsAbstractTableModel<CnrConfig>(
{
tr("Include"),
tr("Channel"),
tr("Radio Circuit"),
tr("Code Word"),
tr("Conflict(s)")
}, parent)
, m_exerciseId(session == nullptr ? 0 : session->exerciseId())
, m_logger(getLogger("LoadCombatNetRadioDialogModel"))
{
DEBUG_LOG(m_logger) << "Own exercise id: " << m_exerciseId;
if (session != nullptr)
{
std::shared_ptr<AudioVideoManagement::Interface::Dtc> avmDtc;
if (!session->getDtc<AudioVideoManagement::DataInterface>(avmDtc)
|| !avmDtc->enableCnrChannel()
|| !avmDtc->enableCnrConfig())
ERROR_LOG(m_logger) << "Could not get AVM data";
else
{
m_cnrChannels = avmDtc->cnrChannels();
m_cnrConfigs = avmDtc->cnrConfigs();
}
DEBUG_LOG(m_logger) << "cnrconfigs: " << m_cnrConfigs.size();
DEBUG_LOG(m_logger) << "ownExerciseId: " << m_exerciseId;
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
if (!session->getDtc<ExerciseManagement::DataInterface>(exmDtc)
|| !exmDtc->enableExercise())
ERROR_LOG(m_logger) << "Could not get EXM data";
else
m_exercises = exmDtc->exercises();
DEBUG_LOG(m_logger) << "exercises: " << m_exercises.size();
}
else
ERROR_LOG(m_logger) << "Got invalid session.";
m_vecData = jsonToCnrConfigVector(data);
m_includeStates.reserve(m_vecData.size());
m_conflicts.reserve(m_vecData.size());
for (int32_t idxCounter = 0; idxCounter < m_vecData.size(); ++idxCounter)
{
Conflicts conflicts = getConflicts(idxCounter, m_vecData.at(idxCounter));
m_conflicts.append(conflicts);
m_includeStates.append(conflicts == NO_CONFLICT ? Qt::Checked : Qt::Unchecked);
if (conflicts & CHANNEL_TAKEN_IN_GLOBAL || conflicts & RADIO_CIRCUIT_TAKEN_IN_GLOBAL) // these conflicts must not be solved, so we disable the rows
m_disabledRows.append(idxCounter);
}
}
Qt::ItemFlags LoadCombatNetRadioDialogModel::flags(const QModelIndex &index) const
{
if (indexIsValid(index) && m_disabledRows.contains(index.row()))
return Qt::NoItemFlags;
if (index.column() > 0)
return LtsAbstractTableModel::flags(index);
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | LtsAbstractTableModel::flags(index);
}
bool LoadCombatNetRadioDialogModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (indexIsValid(index)
&& role == Qt::CheckStateRole
&& index.column() == 0
&& value.canConvert<Qt::CheckState>())
{
m_includeStates[index.row()] = value.value<Qt::CheckState>();
emit dataChanged(index, index, {role});
return true;
}
return QAbstractTableModel::setData(index, value, role);
}
QVariant LoadCombatNetRadioDialogModel::data(const QModelIndex &index, int role) const
{
if (!indexIsValid(index))
return QVariant();
switch (role)
{
case Qt::DisplayRole:
{
const CnrConfig &config = m_vecData.at(index.row());
switch (index.column())
{
case CHANNEL:
return QString::fromStdString(m_cnrChannels.value(config.cnrChannelId()).name());
case RADIO_CIRCUIT:
return QString::fromStdString(config.radioCircuit());
case CALL_SIGN:
return QString::fromStdString(config.callSign());
case CONFLICTS:
return conflictsText(m_conflicts.at(index.row()));
default:
return QVariant();
}
}
case Qt::CheckStateRole:
if (index.column() != 0)
return QVariant();
return m_includeStates.at(index.row());
case Qt::BackgroundRole:
return getConflictsRowColor(m_conflicts.at(index.row()));
default:
return QVariant();
}
}
bool LoadCombatNetRadioDialogModel::allChecked() const
{
for (int32_t row = 0; row < m_includeStates.size(); ++row)
{
if (m_disabledRows.contains(row))
continue;
if (m_includeStates.at(row) == Qt::Unchecked)
return false;
}
return true;
}
void LoadCombatNetRadioDialogModel::setCheckState(bool isChecked)
{
const QVariant data = QVariant(isChecked ? Qt::Checked : Qt::Unchecked);
const int role = Qt::CheckStateRole;
for (int32_t row = 0; row < rowCount(); ++row)
{
if (m_disabledRows.contains(row))
continue;
setData(createIndex(row, 0), data, role);
}
}
LoadCombatNetRadioDialogModel::Conflicts LoadCombatNetRadioDialogModel::getConflicts(int32_t row, const CnrConfig &config)
{
Conflicts conflicts = NO_CONFLICT;
for (auto it = m_cnrConfigs.constBegin(); it != m_cnrConfigs.constEnd(); ++it)
{
if (!(conflicts & RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG) && isRadioCircuitTakenInOwnConfig(config, it.value()))
{
conflicts |= RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE) && isRadioCircuitTakenInAnotherExercise(config, it.value()))
{
conflicts |= RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_ACTIVE_EXERCISE) && isChannelTakenInActiveExercise(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_ACTIVE_EXERCISE;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_LINKED_EXERCISE) && isChannelTakenInLinkedExercise(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_LINKED_EXERCISE;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_OWN_CONFIG) && isChannelTakenInOwnConfig(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_OWN_CONFIG;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_GLOBAL) && isChannelTakenInGlobal(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_GLOBAL;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & RADIO_CIRCUIT_TAKEN_IN_GLOBAL) && isRadioCircuitTakenInGlobal(config, it.value()))
{
conflicts |= RADIO_CIRCUIT_TAKEN_IN_GLOBAL;
addConflictedCnr(row, it.value().id());
}
}
return conflicts;
}
QString LoadCombatNetRadioDialogModel::conflictsText(Conflicts conflicts) const
{
QStringList conflictTexts;
if (conflicts & RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG)
conflictTexts.append(tr("Radio Circuit is already taken in own config."));
if (conflicts & RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE)
conflictTexts.append(tr("Radio Circuit is already taken in another exercise."));
if (conflicts & CHANNEL_TAKEN_IN_ACTIVE_EXERCISE)
conflictTexts.append(tr("Channel already taken in active exercise."));
if (conflicts & CHANNEL_TAKEN_IN_LINKED_EXERCISE)
conflictTexts.append(tr("Channel already taken in linked exercise."));
if (conflicts & CHANNEL_TAKEN_IN_OWN_CONFIG)
conflictTexts.append(tr("Channel is already taken in own Cnr Config."));
if (conflicts & CHANNEL_TAKEN_IN_GLOBAL)
conflictTexts.append(tr("Channel already taken in global Cnr Config."));
if (conflicts & RADIO_CIRCUIT_TAKEN_IN_GLOBAL)
conflictTexts.append(tr("Radio Circuit already taken in global Cnr Config."));
return conflictTexts.isEmpty() ? QLatin1String("-") : conflictTexts.join(QLatin1Char('\n'));
}
void LoadCombatNetRadioDialogModel::addConflictedCnr(int32_t row, int32_t cnrConfigId)
{
m_conflictedCnrIds[row].append(cnrConfigId);
}
bool LoadCombatNetRadioDialogModel::isActiveExercise(int32_t exerciseId) const
{
if (exerciseId == 0) // the global exercise is always active
return true;
using namespace ExerciseManagement;
ExerciseState exerciseState = m_exercises.value(exerciseId).state();
return exerciseState == ExerciseState::Started || exerciseState == ExerciseState::Paused;
}
QColor LoadCombatNetRadioDialogModel::getConflictsRowColor(Conflicts conflicts) const
{
if (conflicts >= CHANNEL_TAKEN_IN_GLOBAL)
return QColor(Qt::red).lighter();
if (conflicts > NO_CONFLICT)
return QColor(Qt::yellow).lighter();
return QColor(Qt::green).lighter();
}
bool LoadCombatNetRadioDialogModel::isRadioCircuitTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (m_exerciseId == 0)
return !existingConfig.exerciseId().is_set() && existingConfig.radioCircuit() == loadConfig.radioCircuit();
if (!existingConfig.exerciseId().is_set()
|| existingConfig.exerciseId().get() != m_exerciseId
|| existingConfig.radioCircuit() != loadConfig.radioCircuit())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isRadioCircuitTakenInAnotherExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (!isActiveExercise(m_exerciseId)
|| !existingConfig.exerciseId().is_set()
|| !isActiveExercise(existingConfig.exerciseId().get())
|| existingConfig.exerciseId().get() == m_exerciseId
|| existingConfig.radioCircuit() != loadConfig.radioCircuit())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInActiveExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
int32_t exerciseId = existingConfig.exerciseId().is_set() ? existingConfig.exerciseId().get() : 0;
if (!isActiveExercise(m_exerciseId)
|| exerciseId == 0
|| exerciseId == m_exerciseId
|| !isActiveExercise(exerciseId)
|| existingConfig.cnrChannelId() != loadConfig.cnrChannelId()
|| !isActiveExercise(exerciseId))
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInLinkedExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (m_exerciseId == 0 || m_exercises.value(m_exerciseId).linkedExerciseId() == 0)
return false;
int32_t linkedExerciseId = m_exercises.value(m_exerciseId).linkedExerciseId();
int32_t exerciseId = existingConfig.exerciseId().is_set() ? existingConfig.exerciseId().get() : 0;
if (exerciseId != linkedExerciseId || existingConfig.cnrChannelId() != loadConfig.cnrChannelId())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
int32_t exerciseId = existingConfig.exerciseId().is_set() ? existingConfig.exerciseId().get() : 0;
return existingConfig.cnrChannelId() == loadConfig.cnrChannelId() && exerciseId == m_exerciseId;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (!isActiveExercise(m_exerciseId)
|| m_exerciseId == 0
|| existingConfig.exerciseId().is_set()
|| existingConfig.cnrChannelId() != loadConfig.cnrChannelId())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isRadioCircuitTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (!isActiveExercise(m_exerciseId)
|| m_exerciseId == 0
|| existingConfig.exerciseId().is_set()
|| existingConfig.radioCircuit() != loadConfig.radioCircuit())
return false;
return true;
}
QVector<Core::RemoveElementRequest> LoadCombatNetRadioDialogModel::removeRequests() const
{
QVector<Core::RemoveElementRequest> removeRequestsVec;
for (int32_t idxCounter = 0; idxCounter < m_includeStates.size(); ++idxCounter)
{
if (m_includeStates.at(idxCounter) == Qt::Checked && m_conflicts.at(idxCounter) != NO_CONFLICT)
{
if (!m_conflictedCnrIds.contains(idxCounter))
{
WARNING_LOG(m_logger) << "Tried to get remove request for row " << idxCounter << " but it is not present.";
continue;
}
const QVector<int32_t> cnrConfigIds = m_conflictedCnrIds.value(idxCounter);
for (int32_t cnrConfigId : cnrConfigIds)
{
Core::RemoveElementRequest removeRequest(cnrConfigId);
if (!removeRequestsVec.contains(removeRequest))
removeRequestsVec.append(removeRequest);
}
}
}
return removeRequestsVec;
}
QVector<ModifyCnrConfigRequest> LoadCombatNetRadioDialogModel::modifyRequests() const
{
const dds::core::optional<int32_t> exerciseId = m_exerciseId == 0 ? dds::core::optional<int32_t>() : dds::core::optional<int32_t>(m_exerciseId);
const int32_t partyId = m_exerciseId == 0 ? 1 : 0;
QVector<ModifyCnrConfigRequest> modifyRequestsVec;
modifyRequestsVec.reserve(m_includeStates.size());
for (int32_t row = 0; row < m_includeStates.size(); ++row)
{
if (m_includeStates.at(row) != Qt::Checked)
continue;
const CnrConfig &cnrConfig = m_vecData.at(row);
modifyRequestsVec.append({0,
cnrConfig.cnrChannelId(),
exerciseId,
cnrConfig.radioCircuit(),
cnrConfig.recording(),
{},
partyId,
cnrConfig.callSign()});
}
return modifyRequestsVec;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,119 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialogModel provices data for LoadCombatNetRadioDialog
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#pragma once
#include <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <ExerciseManagement/exm_dtc.h>
namespace Core
{
class RemoveElementRequest;
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
class LoadCombatNetRadioDialogModel final
: public LtsWidgets::LtsAbstractTableModel<CnrConfig>
{
Q_OBJECT
public:
enum Columns
{
INCLUDE,
CHANNEL,
RADIO_CIRCUIT,
CALL_SIGN,
CONFLICTS
};
enum Conflict
{
NO_CONFLICT = 0,
RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG = 0b00000001,
RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE = 0b00000010,
CHANNEL_TAKEN_IN_ACTIVE_EXERCISE = 0b00000100,
CHANNEL_TAKEN_IN_LINKED_EXERCISE = 0b00001000,
CHANNEL_TAKEN_IN_OWN_CONFIG = 0b00010000,
// not solvable
CHANNEL_TAKEN_IN_GLOBAL = 0b00100000,
RADIO_CIRCUIT_TAKEN_IN_GLOBAL = 0b01000000,
};
Q_DECLARE_FLAGS(Conflicts, Conflict)
explicit LoadCombatNetRadioDialogModel(const QByteArray &data,
LtsSession::LtsSession *session,
QObject *parent = nullptr);
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QVariant data(const QModelIndex &index, int role) const override;
bool allChecked() const;
QVector<Core::RemoveElementRequest> removeRequests() const;
QVector<AudioVideoManagement::ModifyCnrConfigRequest> modifyRequests() const;
public Q_SLOTS:
void setCheckState(bool isChecked);
private: // methods
LoadCombatNetRadioDialogModel::Conflicts getConflicts(int32_t row, const CnrConfig &config);
QString conflictsText(Conflicts conflicts) const;
bool isRadioCircuitTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isRadioCircuitTakenInAnotherExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInActiveExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInLinkedExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isRadioCircuitTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
void addConflictedCnr(int32_t row, int32_t cnrConfigId);
bool isActiveExercise(int32_t exerciseId) const;
QColor getConflictsRowColor(Conflicts conflicts) const;
private: // attributes
int32_t m_exerciseId;
QMap<int32_t, CnrChannel> m_cnrChannels;
QMap<int32_t, CnrConfig> m_cnrConfigs;
QMap<int32_t, ExerciseManagement::Exercise> m_exercises;
QMap<int32_t, QVector<int32_t> > m_conflictedCnrIds; // <row, QVector<CnrConfig::id>>
QVector<Qt::CheckState> m_includeStates;
QVector<int32_t> m_disabledRows;
QVector<Conflicts> m_conflicts;
LOGGER_T m_logger;
};
} // namespace AudioVideoManagement
@@ -0,0 +1,430 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningDialog allows to edit Frequency related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_edit_frequency_planning_dialog.h"
#include "ui_avm_edit_frequency_planning_dialog.h"
#include "avm_edit_frequency_planning_frequency_data_model.h"
#include <LtsUtilsGuiDds/request_helper.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_translation.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <ExerciseManagement/exm_dtc.h>
#include <LtsSession/lts_session_data.h>
#include <ngltools/ngltools_dtg.h>
#include <LtsDataView/lts_data_view.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
#include "avm_load_frequency_planning_dialog.h"
#include <LtsSession/lts_session.h>
#include <LtsPrinting/printing.h>
#include <QTreeView>
namespace
{
bool isActive(ExerciseManagement::ExerciseState state)
{
return (state == ExerciseManagement::ExerciseState::Started ||
state == ExerciseManagement::ExerciseState::Paused);
}
} // anonymous namespace
namespace AudioVideoManagement
{
EditFrequencyPlanningDialog::EditFrequencyPlanningDialog(LtsSession::LtsSession *session,
const QString &menuGroup,
const QString &freqPlanningDialogIdentifier,
const FrequencyPlanning &planning,
const bool copy,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_session(session)
, m_menuGroup(menuGroup)
, m_freqPlanningDialogIdentifier(freqPlanningDialogIdentifier)
, m_ui(new Ui::EditFrequencyPlanningDialog)
, m_planning(planning)
, m_avmDtc(avmDtc)
, m_avmFtc(avmFtc)
, m_ppmDtc(ppmDtc)
, m_ismDtc(ismDtc)
, m_exmDtc(exmDtc)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_frequencyDataModel(new EditFrequencyPlanningFrequencyDataModel(planning, m_session->exerciseId(), avmDtc, ismDtc, ppmDtc, this))
, m_frequencyPlanningId(copy ? 0 : planning.id())
, m_exerciseId(session->exerciseId())
, m_isEdit(EditFrequencyPlanningSetResourceDialog::DialogMode::Assign)
, m_activeFrequencyPlanningStatus(0)
, m_logger(getLogger("EditFrequencyPlanningDialog"))
{
m_ui->setupUi(this);
m_ui->leName->setText(QString::fromStdString(m_planning.name()));
m_ui->lbDate->setText(DTG::toStringMSec(m_planning.date()));
// FreqPlanName muss gesetzt sein (Pruefung erfolgt intern in apply()
m_uiRequirements->addRequirement( { m_ui->label, [this]() { return !m_ui->leName->text().isEmpty(); } });
FPFDelegate *treeDelegate = new FPFDelegate(m_frequencyDataModel);
m_ui->dvFrequencyData->treeView()->setItemDelegateForColumn(EditFrequencyPlanningFrequencyDataModel::Columns::PROPERTIES, treeDelegate);
m_ui->dvFrequencyData->setModel(m_frequencyDataModel);
m_ui->dvFrequencyData->expandAll();
#ifndef LTS_DEBUG
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::RADIO_CIRCUIT_ID);
//m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::FREQ_SET_DATA_ID);
#endif
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::AKW_BUNDLE);
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::AKW_RADIO_CIRCUIT);
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::FSB_BUNDLE);
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::COMSEC);
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::DEVICE);
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningFrequencyDataModel::Columns::PILOTTON);
// connects
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &EditFrequencyPlanningDialog::onAddClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &EditFrequencyPlanningDialog::onRemoveClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &EditFrequencyPlanningDialog::onPrint);
connect(m_ui->dvFrequencyData, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &EditFrequencyPlanningDialog::enableSelectedUnassignButton);
connect(m_ui->dvFrequencyData, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &EditFrequencyPlanningDialog::onAddClicked);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Add, false); // Assign button
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false); // Unassign button
if (m_frequencyPlanningId == 0) // new?
slotChangesMade();
// Beschriftung der Button der Funktionalitaet anpassen......
m_buttonBox->setText(LtsWidgets::DialogButton::Add, tr("Assign"));
m_buttonBox->setText(LtsWidgets::DialogButton::Remove, tr("Unassign"));
// Save/Load from Library Button
m_buttonBox->connectLibraryButton(this,
&EditFrequencyPlanningDialog::onImportButtonClicked,
&EditFrequencyPlanningDialog::onExportButtonClicked);
if (!session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui))
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Library, false);
setOnlineHelpFilename(QStringLiteral("Situation-Frequency_Planning"));
}
EditFrequencyPlanningDialog::~EditFrequencyPlanningDialog()
{
emit aboutToCloseMe(m_frequencyPlanningId);
}
void EditFrequencyPlanningDialog::retranslate()
{
translate(m_ui.get(), m_avmDtc->frequencyPlanning(m_frequencyPlanningId), tr("Frequency Planning"));
}
void EditFrequencyPlanningDialog::onRemoveFrequencyPlanningReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << reply.str();
m_requestHelper->requestReply(reply);
}
void EditFrequencyPlanningDialog::onModifyFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject)
{
if (!replyObject.error())
m_frequencyDataModel->setFrequencePlanningId(replyObject.id());
m_requestHelper->requestReply(replyObject);
}
bool EditFrequencyPlanningDialog::apply()
{
if (!m_frequencyDataModel->checkResourceAssignments())
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Multiple assignments with the same resource are not possible. Please correct the selected entries."));
return false;
}
ModifyFrequencyPlanningRequest request = m_frequencyDataModel->modifyFrequencyPlanningRequest();
request.date(m_session->partitionTime());
m_ui->lbDate->setText(DTG::toStringMSec(request.date()));
m_planning.date() = request.date();
request.id(m_frequencyPlanningId);
request.name(m_ui->leName->text().toStdString());
return m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->modifyFrequencyPlanningRequest(request, this);
});
}
void EditFrequencyPlanningDialog::onAddClicked() // Assign
{
// Daten der selektierten Zeile zusammensuchen
QModelIndex idx = m_ui->dvFrequencyData->currentIndex();
int row = idx.row();
if (row < 0)
return;
CnrConfig cnrConfigRow = m_frequencyDataModel->itemData(idx).cnrConfig;
int cnrConfigId = cnrConfigRow.id();
if (cnrConfigId <= 0)
return;
DEBUG_LOG(m_logger) << "---RessourceDialog aufrufen...."
<< " cnrConfigId: " << cnrConfigId
<< " radioCircuit: " << cnrConfigRow.radioCircuit()
<< " FrequencySetId: " << m_frequencyDataModel->getFrequencySetId();
FrequencyPlanningData fpd = m_frequencyDataModel->getfrequencyPlanningData(cnrConfigId);
if (!fpd.cnrConfigId())
{
fpd.cnrConfigId(cnrConfigId);
}
// true: Bereits zugewiesener Sprechkreis wird editiert. Dabei ist jedoch keine Aenderung des FreqSets mehr moeglich!
// false: Einem noch nicht zugeordneten Sprechkreis koennen die noch zur Verfuegung stehenden Ressourcen zugewiesen werden.
EditFrequencyPlanningResourceDialog *frequencyPlanningResourceDialog
= new EditFrequencyPlanningResourceDialog(m_frequencyDataModel->getFrequencySetId(),
m_isEdit,
this );
frequencyPlanningResourceDialog->init(m_session, fpd, m_frequencyDataModel->getAllocatedFrequencyPlanningSetIds());
// Schliesst den Resource Dialog wenn der 'vorgeschaltete' Edit Dialog geschlossen wird
connect(this, &QObject::destroyed,
frequencyPlanningResourceDialog, &QObject::deleteLater);
addDialog(frequencyPlanningResourceDialog, m_menuGroup, QLatin1String("avmFrequencyPlanning") + QString::number(cnrConfigId), LtsWidgets::LayoutArea::eDialogTab);
connect(frequencyPlanningResourceDialog, &EditFrequencyPlanningResourceDialog::signalAssignRadioCircuit,
this, &EditFrequencyPlanningDialog::onAssignRadioCircuit);
m_ui->dvFrequencyData->expandAll();
}
void EditFrequencyPlanningDialog::onRemoveClicked() // Unassign
{
QModelIndex idx = m_ui->dvFrequencyData->currentIndex();
int row = idx.row();
if (row < 0)
return;
CnrConfig cnrConfigRow = m_frequencyDataModel->itemData(idx).cnrConfig;
int cnrConfigId = cnrConfigRow.id();
if (cnrConfigId <= 0)
return;
unassignRadioCircuit(cnrConfigId);
}
void EditFrequencyPlanningDialog::enableSelectedUnassignButton()
{
QModelIndex idx = m_ui->dvFrequencyData->currentIndex();
int row = idx.row();
if (row < 0)
return;
auto itemData = m_frequencyDataModel->itemData(idx);
CnrConfig cnrConfigRow = itemData.cnrConfig;
int cnrConfigId = cnrConfigRow.id();
if (cnrConfigId <= 0)
return;
// Pruefen ob der selektierte Sprechkreis bereits zugewiesen ist...
FrequencyPlanningData fpd = m_frequencyDataModel->getfrequencyPlanningData(cnrConfigId);
QVector<RadioCircuit*> circuits = AudioVideoManagement::fromJson(fpd.properties());
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Add, true);
if( (!cnrConfigRow.exerciseId().is_set()
&& m_planning.exerciseId().is_set())
||
itemData.readOnly
)
{
m_buttonBox->setText(LtsWidgets::DialogButton::Add, tr("Show"));
m_isEdit = EditFrequencyPlanningSetResourceDialog::DialogMode::Show;
if (circuits.size() == 0)
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Add, false);
}
else if (circuits.size() > 0)
{
m_isEdit = EditFrequencyPlanningSetResourceDialog::DialogMode::Edit;
m_buttonBox->setText(LtsWidgets::DialogButton::Add, tr("Edit"));
}
else
{
m_isEdit = EditFrequencyPlanningSetResourceDialog::DialogMode::Assign;
m_buttonBox->setText(LtsWidgets::DialogButton::Add, tr("Assign"));
}
DEBUG_LOG(m_logger) << "---enableAssignUnassignButtons()...."
<< " cnrConfigId: " << cnrConfigId
<< " radioCircuitId: " << cnrConfigRow.radioCircuit();
// En-/dis-able Unassign Button
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, !itemData.readOnly && m_isEdit == EditFrequencyPlanningSetResourceDialog::DialogMode::Edit);
qDeleteAll(circuits);
}
void EditFrequencyPlanningDialog::onAssignRadioCircuit(const FrequencyPlanningData &fpd)
{
DEBUG_LOG(m_logger) << "---onAssignRadioCircuit()...."
<< "CnrConfigId: " << fpd.cnrConfigId();
m_frequencyDataModel->setFrequencyPlanningData(fpd);
// pruefen ob doppelte Ressourcen verwendet werden
if (!m_frequencyDataModel->checkResourceAssignments())
{
// auf den Tab der Frequenzplanung wechseln
focusDialog(m_menuGroup, m_freqPlanningDialogIdentifier, LtsWidgets::LayoutArea::eDialogTab);
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Multiple assignments with the same resource are not possible. Please correct the selected entries."));
return;
}
enableSelectedUnassignButton();
slotChangesMade();
m_ui->dvFrequencyData->repaint();
m_ui->dvFrequencyData->expandAll();
}
void EditFrequencyPlanningDialog::onPrint()
{
LtsPrinting::printView(m_session, this, m_ui->dvFrequencyData->treeView() );
}
int32_t EditFrequencyPlanningDialog::currentFrequencySetId() const
{
QModelIndex currentFrequencySetDataIndex = m_ui->dvFrequencyData->currentIndex();
if (!currentFrequencySetDataIndex.isValid())
return -1;
return m_frequencyDataModel->data(currentFrequencySetDataIndex, Qt::UserRole).toInt();
}
/**
* Return all active exercises except the current one.
*/
QVector<int> EditFrequencyPlanningDialog::otherActiveExercises() const
{
const auto allExercises_ = m_exmDtc->exercises();
Q_ASSERT(::isActive(allExercises_[m_exerciseId].state()));
auto allExercises = allExercises_.values();
auto newEnd = std::remove_if(allExercises.begin(), allExercises.end(), [] (const ExerciseManagement::Exercise &ex) { return ex.isDeleted(); }); // remove deleted exercises
allExercises.erase(newEnd, allExercises.end());
QVector<int> toBeConsidered;
for (const auto &exercise : qAsConst(allExercises))
{
if (exercise.id() == m_exerciseId)
continue;
if (::isActive(exercise.state()))
toBeConsidered.append(exercise.id());
}
return toBeConsidered;
}
/**
* Return the active FrequencyPlannings of other active Exercises.
*/
QVector<int> EditFrequencyPlanningDialog::otherActivePlans() const
{
QVector<int> result;
QVector<int> exercises = otherActiveExercises();
const auto activePlans = m_avmDtc->activeFrequencyPlannings();
for (const auto &activePlan : activePlans)
{
if (exercises.contains(activePlan.exerciseId()))
result.append(activePlan.frequencyPlanningId());
}
return result;
}
void EditFrequencyPlanningDialog::unassignRadioCircuit(int32_t cnrConfigId)
{
m_frequencyDataModel->unassignRadioCircuit(cnrConfigId);
enableSelectedUnassignButton();
slotChangesMade();
m_ui->dvFrequencyData->expandAll();
}
void EditFrequencyPlanningDialog::onExportButtonClicked()
{
QByteArray byteArray = m_frequencyDataModel->uiToJson();
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_session->exerciseId(), byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void EditFrequencyPlanningDialog::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << "--- onImportButtonClicked() ";
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_session->exerciseId(), FileManagement::toString(FileManagement::XmlType::AVMEditFrequencyPlanning));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMEditFrequencyPlanning)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
LoadFrequencyPlanningDialog loadingDialog(content, m_frequencyDataModel->toFrequencyPlanning(), m_avmDtc, m_ismDtc) ;
loadingDialog.exec();
if (loadingDialog.isApplied())
{
const QVector<int> unassignments = loadingDialog.unassignments();
for (int idToUnassign : unassignments)
m_frequencyDataModel->unassignRadioCircuit(idToUnassign);
const QVector<FrequencyPlanningData> planningData = loadingDialog.newPlanningData();
for (const FrequencyPlanningData &currentData : planningData)
m_frequencyDataModel->setFrequencyPlanningData(currentData);
slotChangesMade();
m_ui->dvFrequencyData->expandAll();
}
}
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,133 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningDialog allows to edit Frequency related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include "avm_edit_frequency_planning_resource_dialog.h"
namespace Ui
{
class EditFrequencyPlanningDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class EditFrequencyPlanningFrequencyDataModel;
class EditFrequencyPlanningResourceDialog;
class EditFrequencyPlanningDialog : public LtsWidgets::LtsDialog
, protected Interface::Ftc::RemoveFrequencyPlanningReplyHandler
, protected Interface::Ftc::ModifyFrequencyPlanningReplyHandler
{
Q_OBJECT
public:
explicit EditFrequencyPlanningDialog(LtsSession::LtsSession *session,
const QString &menuGroup,
const QString &freqPlanningDialogIdentifier,
const FrequencyPlanning &planning,
const bool copy,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent);
~EditFrequencyPlanningDialog();
void retranslate() override;
void onExportButtonClicked();
void onImportButtonClicked();
protected:
void onRemoveFrequencyPlanningReply(const Core::FunctionReplyObject &reply) override final;
void onModifyFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject) override final;
bool apply() override;
private slots:
void onAddClicked();
void onRemoveClicked();
void enableSelectedUnassignButton();
void onPrint();
void onAssignRadioCircuit(const FrequencyPlanningData &fpd);
signals:
void aboutToCloseMe(int);
private:
int32_t currentFrequencySetId() const; // -1 = invalid
QVector<int> otherActiveExercises() const;
QVector<int> otherActivePlans() const;
void unassignRadioCircuit(int32_t cnrConfigId);
bool isCriteriumEditDialog(const QByteArray &content);
LtsSession::LtsSession *m_session;
QString m_menuGroup;
QString m_freqPlanningDialogIdentifier;
std::unique_ptr<Ui::EditFrequencyPlanningDialog> m_ui;
FrequencyPlanning m_planning;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<Interface::Ftc> m_avmFtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
ExerciseManagement::Interface::Dtc *m_exmDtc;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
std::unique_ptr<LtsUtils::RequestHelper> m_requestHelper;
EditFrequencyPlanningFrequencyDataModel *m_frequencyDataModel;
int m_frequencyPlanningId;
int m_exerciseId;
EditFrequencyPlanningSetResourceDialog::DialogMode m_isEdit;
int m_activeFrequencyPlanningStatus;
LOGGER_T m_logger;
}; // class EditFrequencyPlanningDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditFrequencyPlanningDialog</class>
<widget class="QWidget" name="EditFrequencyPlanningDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>655</width>
<height>528</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Asset Stocks</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1" colspan="3">
<widget class="QLabel" name="lbDate">
<property name="text">
<string notr="true">&lt;DATE&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leName">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>264</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="4">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="LtsDataView::LtsDataView" name="dvFrequencyData">
<property name="rootIsDecorated">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,964 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningDataModel class offers FrequencyPlanning related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_edit_frequency_planning_frequency_data_model.h"
#include <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include <FileManagement/flm_translation.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <AudioVideoManagement/avm_translation.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <ParticipantManagement/ppm_translation.h>
#include <LtsUtilsCore/json_object.h>
#include <nglwidgets/ngltreeitem.h>
#include <QPainter>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
namespace AudioVideoManagement
{
void EditFrequencyPlanningFrequencyDataModel::onFrequencyPlanningChanged(const FrequencyPlanning &planning, DataModification dm)
{
// Nicht meine Planning ID, aber auch nicht die globale Frequenzplanung:
if (planning.id() != m_initPlan.id() && planning.exerciseId().is_set())
return;
// Die Frequenzplanung wurde einfach mal so gelöscht...
if (dm == DataModification::eRemoved)
{
beginResetModel();
// Also hauenb wir die RadioCircuits wech...
for (NGLTreeItem* item : m_rootItem->constChildren())
{
if (!item->data().canConvert<ItemData>())
continue;
ItemData data = item->data().value<ItemData>();
delete data.radioCircuit;
data.radioCircuit = nullptr;
item->setData(QVariant::fromValue(data));
}
endResetModel();
return;
}
QHash<int, FrequencyPlanningData> fpdMap;
for(const auto &elem : planning.data())
fpdMap.insert(elem.cnrConfigId(), elem);
for (NGLTreeItem* item : m_rootItem->constChildren())
{
if (!item->data().canConvert<ItemData>())
continue;
ItemData itemData = item->data().value<ItemData>();
if (itemData.cnrConfig.exerciseId().is_set() && itemData.cnrConfig.exerciseId().get() != m_exerciseId)
continue;
if (!itemData.cnrConfig.exerciseId().is_set() && 0 != m_exerciseId)
continue;
if (fpdMap.contains(itemData.cnrConfig.id()))
{
// We have a row with (possibly) altered data.
const FrequencyPlanningData &fpd = fpdMap[itemData.cnrConfig.id()];
if (item->childCount())
{
beginRemoveRows(indexFromItem(item),0,item->childCount()-1);
item->removeAllChildren();
endRemoveRows();
}
delete itemData.radioCircuit;
itemData.radioCircuit = nullptr;
QVector<RadioCircuit*> circuits = AudioVideoManagement::fromJson(fpd.properties());
if (circuits.size() > 1)
{
for (RadioCircuit *circuit : qAsConst(circuits))
{
itemData.radioCircuit = circuit;
ConfigTreeItem *childItem = new ConfigTreeItem(QVariant::fromValue(itemData));
insertRow(item->childCount(), indexFromItem(item), childItem);
}
}
else if (circuits.size() == 1)
{
itemData.radioCircuit = circuits[0];
item->setData(QVariant::fromValue(itemData));
}
m_frequencyPlanningData[fpd.cnrConfigId()] = fpd;
fpdMap.remove(itemData.cnrConfig.id());
}
else
{
// No frequency Planning Data for this item any more...
if (item->childCount())
{
beginRemoveRows(indexFromItem(item),0,item->childCount()-1);
item->removeAllChildren();
endRemoveRows();
}
delete itemData.radioCircuit;
itemData.radioCircuit = nullptr;
item->setData(QVariant::fromValue(itemData));
m_frequencyPlanningData.remove(itemData.cnrConfig.id());
}
}
// Some elements in fpdMap, that are new?
for (const FrequencyPlanningData &item : qAsConst(fpdMap))
{
setFrequencyPlanningData(item);
}
}
EditFrequencyPlanningFrequencyDataModel::EditFrequencyPlanningFrequencyDataModel(const FrequencyPlanning &initPlanning,
int exerciseId,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
QObject *parent)
: NGLTreeModel(parent, new ConfigTreeItem(QVariant()),
{
tr("Channel Number"),
tr("RC id"),
tr("Radio Circuit"),
tr("Party"),
tr("Unit"),
tr("Radio Station"),
tr("Frequency"),
tr("Function"),
tr("Device"),
tr("AKW Bundle"),
tr("FSB Bundle"),
tr("AKW Radio Circuit"),
tr("COMSEC"),
tr("Pilotton"),
tr("Operating Mode"),
tr("Properties"),
tr("Channel Spacing"),
tr("Squelch"),
tr("Global"),
})
, m_exerciseId(exerciseId)
, m_avmDtc(avmDtc)
, m_ismDtc(ismDtc)
, m_ppmDtc(ppmDtc)
, m_initPlan(initPlanning)
, m_logger(getLogger("EditFrequencyPlanningDataModel"))
{
Q_ASSERT(avmDtc);
Q_ASSERT(ppmDtc);
DEBUG_LOG(m_logger) << "---EditFrequencyPlanningDataMod m_frequencyPlanningData[c.id()];el() ";
int frequencySetId = initPlanning.frequencySetId();
std::vector<FrequencySetDataWithId> fsdVec = m_avmDtc->frequencySet(frequencySetId).data();
for(FrequencySetDataWithId fsd : fsdVec)
m_freqSetData.insert(fsd.id(), fsd);
m_units = m_ppmDtc->units();
QVector<CnrConfig> dataVec;
const auto &cnrConfigs = m_avmDtc->cnrConfigs();
for (const CnrConfig &cnrConfig : cnrConfigs)
{
// Lokale Sprechkreise anderer Exercises ausblenden
if (cnrConfig.exerciseId().is_set() && cnrConfig.exerciseId().get() != m_exerciseId)
continue;
dataVec.append(cnrConfig);
}
setCnrConfigs(dataVec);
m_avmDtc->activeFrequencyPlannings([this](const ActiveFrequencyPlanning &afp)
{
if (afp.exerciseId() <= 0)
{
onFrequencyPlanningChanged(m_avmDtc->frequencyPlanning(afp.frequencyPlanningId()), DataModification::eAdded);
}
});
onFrequencyPlanningChanged(initPlanning, DataModification::eAdded);
connect(m_avmDtc.get(), &Interface::Dtc::cnrConfigChanged, this, &EditFrequencyPlanningFrequencyDataModel::onCnrConfigChanged);
connect(m_avmDtc.get(), &Interface::Dtc::frequencyPlanningChanged, this, &EditFrequencyPlanningFrequencyDataModel::onFrequencyPlanningChanged);
}
void EditFrequencyPlanningFrequencyDataModel::setCnrConfigs(const QVector<CnrConfig> &dataVec)
{
m_cnrConfigs.clear();
for(const CnrConfig &c : dataVec)
{
m_cnrConfigs.insert(c.id(), c);
ItemData id(c);
if (m_frequencyPlanningData.contains(c.id()))
{
FrequencyPlanningData fpd = m_frequencyPlanningData[c.id()];
id.readOnly = isReadOnly(fpd.properties());
QVector<RadioCircuit*> circuits = AudioVideoManagement::fromJson(fpd.properties());
if (circuits.size() > 1)
{
ConfigTreeItem *configItem = new ConfigTreeItem(QVariant::fromValue(id));
insertRow(m_rootItem->childCount(), indexFromItem(m_rootItem), configItem);
for (RadioCircuit *circuit : qAsConst(circuits))
{
id.radioCircuit = circuit;
ConfigTreeItem *childItem = new ConfigTreeItem(QVariant::fromValue(id));
insertRow(configItem->childCount(), indexFromItem(configItem), childItem);
}
}
else if (circuits.size() == 1)
{
id.radioCircuit = circuits[0];
ConfigTreeItem *configItem = new ConfigTreeItem(QVariant::fromValue(id));
insertRow(m_rootItem->childCount(), indexFromItem(m_rootItem), configItem);
}
}
else
{
ConfigTreeItem *configItem = new ConfigTreeItem(QVariant::fromValue(id));
insertRow(m_rootItem->childCount(), indexFromItem(m_rootItem), configItem);
}
}
}
EditFrequencyPlanningFrequencyDataModel::ItemData EditFrequencyPlanningFrequencyDataModel::itemData(const QModelIndex &index)
{
NGLTreeItem *item = privateData(index);
if (!item->data().canConvert<ItemData>())
return {};
return item->data().value<ItemData>();
}
QVariant EditFrequencyPlanningFrequencyDataModel::data(const QModelIndex &index, int role) const
{
if ((role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::UserRole && role != Qt::TextColorRole && role != Qt::BackgroundRole) || !index.isValid())
return NGLTreeModel::data(index, role);
ConfigTreeItem *item = static_cast<ConfigTreeItem *>(privateData(index));
if (!item->data().canConvert<ItemData>())
return NGLTreeModel::data(index, role);
ItemData id = item->data().value<ItemData>();
const CnrConfig &setData = id.cnrConfig;
RadioCircuit *rc = id.radioCircuit;
if (role == Qt::ToolTipRole)
{
if (index.column() != PROPERTIES)
return {};
RadioCircuitVHF *rcVhf = dynamic_cast<RadioCircuitVHF*>(rc);
if (rcVhf && !isAkwMode(rcVhf->operatingMode))
return tr("Pilot Tone: %1")
.arg(rcVhf->pilotTone ? tr("On") : tr("Off"));
RadioCircuitUHF *rcUhf = dynamic_cast<RadioCircuitUHF*>(rc);
if (rcUhf)
return tr("Clipper: %1, AGC: %2, Modulation Type: %3")
.arg(rcUhf->clipper ? tr("On") : tr("Off"),
rcUhf->agc ? tr("On") : tr("Off"),
toString(rcUhf->modulationType));
RadioCircuitHF *rcHf = dynamic_cast<RadioCircuitHF*>(rc);
if (rcHf)
return tr("Channel Spacing: %1, Squelch: %2")
.arg(toString(rcHf->channelSpacing),
rcHf->squelch > 0 ? QString::number(rcHf->squelch) : tr("Off"));
}
if (role == Qt::DisplayRole)
{
switch (index.column())
{
// FrequenySetDaten fuer den aktuellen Sprechkreis (cnrConfigId) ermitteln/anzeigen
case RADIO_CIRCUIT_ID:
return setData.id();
case FUNCTION:
{
if (!rc)
return QStringLiteral("-");
return QString::fromStdString(m_freqSetData[rc->frequencySetDataId()].function());
}
case DEVICE:
{
if (!rc)
return QStringLiteral("-");
RadioCircuitVHF *rcVhf = dynamic_cast<RadioCircuitVHF*>(rc);
if (!rcVhf)
return QStringLiteral("-");
return toString(m_freqSetData[rc->frequencySetDataId()].frequencyBand().underlying());
}
case FREQUENCY:
{
QLocale locale(QLocale::German);
if (!rc)
return QStringLiteral("-");
if (rc->frequencySetDataId() > 0)
{
RadioCircuitVHF *rcVhf = dynamic_cast<RadioCircuitVHF*>(rc);
if (rcVhf && isAkwMode(rcVhf->operatingMode))
return QStringLiteral("-");
return tr("%1 MHz").arg(locale.toString(m_freqSetData[rc->frequencySetDataId()].frequency() / 1000., 'f', 3));
}
else
{
RadioCircuitUHF *rcUhf = dynamic_cast<RadioCircuitUHF*>(rc);
RadioCircuitHF *rcHf = dynamic_cast<RadioCircuitHF*>(rc);
if (rcUhf)
return tr("%1 MHz").arg(locale.toString(rcUhf->frequency / 1000000., 'f', 3));
if (rcHf)
return tr("%1 MHz").arg(locale.toString(rcHf->frequency / 1000000., 'f', 3));
return QStringLiteral("-");
}
}
case AKW_BUNDLE:
{
if (!rc)
return QStringLiteral("-");
if (rc->frequencySetDataId() <= 0)
return QStringLiteral("-");
if (!m_freqSetData[rc->frequencySetDataId()].akwBundle().is_set())
return QStringLiteral("-");
return m_freqSetData[rc->frequencySetDataId()].akwBundle().get();
}
case FSB_BUNDLE:
{
if (!rc)
return QStringLiteral("-");
if (rc->frequencySetDataId() <= 0)
return QStringLiteral("-");
if (!m_freqSetData[rc->frequencySetDataId()].fsbBundle().is_set())
return QStringLiteral("-");
return m_freqSetData[rc->frequencySetDataId()].fsbBundle().get();
}
case AKW_RADIO_CIRCUIT:
{
if (!rc)
return QStringLiteral("-");
if (rc->frequencySetDataId() <= 0)
return QStringLiteral("-");
if (!m_freqSetData[rc->frequencySetDataId()].akwRadioCircuit().is_set())
return QStringLiteral("-");
return m_freqSetData[rc->frequencySetDataId()].akwRadioCircuit().get();
}
case COMSEC:
{
if (!rc)
return QStringLiteral("-");
if (rc->frequencySetDataId() <= 0)
return QStringLiteral("-");
if (!m_freqSetData[rc->frequencySetDataId()].comsec().is_set())
return QStringLiteral("-");
return m_freqSetData[rc->frequencySetDataId()].comsec().get();
}
// cnrConfig Daten...
case RADIO_CIRCUIT:
{
if (!rc)
return QString::fromStdString(setData.radioCircuit());
return id.name();
}
case PARTY:
{
ParticipantManagement::Party party = m_ppmDtc->party(setData.partyId());
return ParticipantManagement::trPartyName(party.name());
}
case UNIT:
{
if (!setData.unitId().is_set())
return {};
const int unitId = setData.unitId().get();
if (!m_units.contains(unitId))
return QStringLiteral("-");
const auto unit = m_units[unitId];
INFO_LOG(m_logger) << " config.unitId()=" << unitId
<< " Str=" << unit.name();
QString unitStr;
if (unit.designator().size() > 0)
unitStr = QString::fromStdString(unit.designator()) + QLatin1Char('/');
unitStr.append(QString::fromStdString(unit.tacticalIdentifier()));
INFO_LOG(m_logger) << "unitStr = " << unitStr;
return unitStr;
}
case RADIO_STATION:
{
if (!rc)
return QStringLiteral("-");
if (rc->radioStationId == 0)
return tr("Automatic");
else
return QString::fromStdString(m_ismDtc->mainStructureElement(rc->radioStationId).name());
}
case PILOTTON:
{
if (!rc)
return QStringLiteral("-");
RadioCircuitVHF *rcVhf = dynamic_cast<RadioCircuitVHF*>(rc);
if (!rcVhf)
return QStringLiteral("-");
return rcVhf->pilotTone ? tr("On") : tr("Off");
}
case OPERATING_MODE:
{
if (!rc)
return QStringLiteral("-");
RadioCircuitVHF *rcVhf = dynamic_cast<RadioCircuitVHF*>(rc);
if (!rcVhf)
return QStringLiteral("-");
return toString(rcVhf->operatingMode);
}
case CHANNEL:
{
CnrChannel channel = m_avmDtc->cnrChannel(setData.cnrChannelId());
return QString::fromStdString(channel.name());
}
case GLOBAL:
{
return setData.exerciseId().is_set() ? tr("No") : tr("Yes");
}
case PROPERTIES:
{
if (!rc)
return QStringLiteral("-");
else
{
RadioCircuitUHF *rcUhf = dynamic_cast<RadioCircuitUHF*>(rc);
RadioCircuitHF *rcHf = dynamic_cast<RadioCircuitHF*>(rc);
RadioCircuitVHF *rcVhf = dynamic_cast<RadioCircuitVHF*>(rc);
QStringList sl;
if (rcUhf)
{
sl << createIcon(ICON_CLIPPER, rcUhf->clipper );
sl << createIcon(ICON_AGC, rcUhf->agc );
sl << createIcon(ICON_MODULATION_TYPE, 0, rcUhf->modulationType);
return sl.join(QStringLiteral("&nbsp;"));
}
if (rcVhf && !isAkwMode(rcVhf->operatingMode))
{
sl << createIcon(ICON_PILOTTON, rcVhf->pilotTone > 0);
return sl.join(QStringLiteral("&nbsp;"));
}
if (rcHf)
{
return sl.join(QStringLiteral("&nbsp;"));
}
return QStringLiteral("-");
}
}
case CHANNEL_SPACING:
{
RadioCircuitUHF *rcUhf = dynamic_cast<RadioCircuitUHF*>(rc);
RadioCircuitHF *rcHf = dynamic_cast<RadioCircuitHF*>(rc);
if (rcUhf)
return toString(rcUhf->channelSpacing);
if (rcHf)
return toString(rcHf->channelSpacing);
return {};
}
case SQUELCH:
{
RadioCircuitUHF *rcUhf = dynamic_cast<RadioCircuitUHF*>(rc);
RadioCircuitHF *rcHf = dynamic_cast<RadioCircuitHF*>(rc);
if (rcUhf)
return rcUhf->squelchDB > 0 ? tr("%1 dB").arg(rcUhf->squelchDB) : QVariant();
if (rcHf)
return rcHf->squelch > 0 ? QString::number(rcHf->squelch) : QVariant();
return {};
}
default:
break;
} // switch
}
if (role == Qt::UserRole)
return setData.id();
if (role == Qt::TextColorRole)
{
// Zeilen der doppelt vergebenen Resourcen markieren
for (int id : m_multipleAssignedCnrIds)
{
if (id == setData.id())
return QColor(Qt::red);
}
}
if (role == Qt::BackgroundRole)
{
if (setData.exerciseId().is_set() && setData.exerciseId().get() == m_exerciseId)
return NGLTreeModel::data(index, role);
if (!setData.exerciseId().is_set() && m_exerciseId <= 0)
return NGLTreeModel::data(index, role);
return QColor(Qt::gray);
}
return NGLTreeModel::data(index, role);
}
QString EditFrequencyPlanningFrequencyDataModel::createIcon(PropertyData propertyDataType, bool on, ModulationType type) const
{
QString imgHeader = QStringLiteral("<img src=\"%1\" height=\"32\" title=\"%2\" image-rendering=\"optimizeQuality\">");
switch(propertyDataType)
{
case ICON_PILOTTON:
return imgHeader
.arg(on ?
QStringLiteral(":/style/pilotton-on.svg")
: QStringLiteral(":/style/pilotton-off.svg"),
on ?
tr("Pilot Tone: On")
: tr("Pilot Tone: Off"));
case ICON_MODULATION_TYPE:
return imgHeader
.arg(type == ModulationType::AM ?
QStringLiteral(":/style/AM.svg")
: QStringLiteral(":/style/FM.svg"),
on ?
tr("Modulation Type: AM")
: tr("Modulation Type: FM"));
case ICON_SQUELCH:
return imgHeader
.arg(on ?
QStringLiteral(":/style/squelch-on.svg")
: QStringLiteral(":/style/squelch-off.svg"),
on ?
tr("Squelch: On")
: tr("Squelch: Off"));
case ICON_CLIPPER:
return imgHeader
.arg(on ?
QStringLiteral(":/style/clipper-on.svg")
: QStringLiteral(":/style/clipper-off.svg"),
on ?
tr("Clipper: On")
: tr("Clipper: Off"));
case ICON_AGC:
return imgHeader
.arg(on ?
QStringLiteral(":/style/agc-on.svg")
: QStringLiteral(":/style/agc-off.svg"),
on ?
tr("Automatic Gain Control (AGC): On")
: tr("Automatic Gain Control (AGC): Off"));
case ICON_CHANNEL_SPACING:
return imgHeader
.arg(QStringLiteral(":/style/channel-spacing-on.svg"),
tr("Channel Spacing"));
case ICON_SPACER:
return imgHeader
.arg(QStringLiteral(":/style/line_style_solid_v.svg"),
tr("Channel Spacing"));
default:
return QStringLiteral("-");
}
}
bool EditFrequencyPlanningFrequencyDataModel::unassignRadioCircuit(int32_t cnrConfigId)
{
if (cnrConfigId < 0)
return false;
// ggf. markierte Zeile zuruecksetzen
m_multipleAssignedCnrIds.erase(
std::remove(m_multipleAssignedCnrIds.begin(), m_multipleAssignedCnrIds.end(), cnrConfigId),
m_multipleAssignedCnrIds.end());
beginResetModel();
m_frequencyPlanningData.remove(cnrConfigId);
ConfigTreeItem* item = searchCnrConfig(m_rootItem, cnrConfigId);
if(!item)
return false;
ItemData id = item->data().value<ItemData>();
id.clearRadioCircuit();
item->setData(QVariant::fromValue(id));
item->removeAllChildren();
endResetModel();
return true;
}
// Setzt die id nach erfolgreichem ModifyRequest (Create)
void EditFrequencyPlanningFrequencyDataModel::setFrequencePlanningId(int id)
{
if (m_initPlan.id() == id)
return;
if (m_initPlan.id() <= 0)
{
m_initPlan.id(id);
return;
}
WARNING_LOG(m_logger) << "Trying to set a new frequency planning id but there is alreay an id set ("
<< m_initPlan.id() << " - " << id << ")";
}
ModifyFrequencyPlanningRequest EditFrequencyPlanningFrequencyDataModel::modifyFrequencyPlanningRequest() const
{
std::vector<FrequencyPlanningData> planningDataVec;
planningDataVec.reserve(m_frequencyPlanningData.size());
for (const auto &keyValue : overKeyValue(m_frequencyPlanningData))
{
Q_ASSERT(keyValue.first > 0);
CnrConfig config = m_avmDtc->cnrConfig(keyValue.second.cnrConfigId());
if(( config.exerciseId().is_set() && config.exerciseId().get() == m_exerciseId) ||
(!config.exerciseId().is_set() && m_exerciseId == 0))
{
FrequencyPlanningData fpd = keyValue.second;
fpd.frequencyPlanningId() = m_initPlan.id();
planningDataVec.push_back(fpd);
}
}
return { m_initPlan.id(),
m_initPlan.exerciseId().is_set() ? m_initPlan.exerciseId().get() : 0,
m_initPlan.name(),
m_initPlan.date(),
m_initPlan.frequencySetId(),
planningDataVec};
}
FrequencyPlanning EditFrequencyPlanningFrequencyDataModel::toFrequencyPlanning() const
{
std::vector<FrequencyPlanningData> planningDataVec;
planningDataVec.reserve(m_frequencyPlanningData.size());
for (const auto &keyValue : overKeyValue(m_frequencyPlanningData))
{
Q_ASSERT(keyValue.first > 0);
CnrConfig config = m_avmDtc->cnrConfig(keyValue.second.cnrConfigId());
if(( config.exerciseId().is_set() && config.exerciseId().get() == m_exerciseId) ||
(!config.exerciseId().is_set() && m_exerciseId == 0))
{
FrequencyPlanningData fpd = keyValue.second;
fpd.frequencyPlanningId() = m_initPlan.id();
planningDataVec.push_back(fpd);
}
}
FrequencyPlanning frequencyPlanning = m_initPlan;
frequencyPlanning.data(planningDataVec);
return frequencyPlanning;
}
int EditFrequencyPlanningFrequencyDataModel::findCnrConfigByName(const QString &name) const
{
int id = -1;
std::function<bool(const CnrConfig &)> findByNameInExercise = [this, &name, &id] (const CnrConfig &cnrConfig)
{
if (cnrConfig.exerciseId().is_set() && cnrConfig.exerciseId() != m_initPlan.exerciseId())
return false;
if (QString::fromStdString(cnrConfig.radioCircuit()) != name)
return false;
id = cnrConfig.id();
return true;
};
m_avmDtc->cnrConfigs(findByNameInExercise);
return id;
}
/**
* In order to save the FrequencyPlanning in the library we need to store the associations
* of CnrConfig + Params (FrequencyPlanningData) with an entry of the FrequencySet.
* FrequencySets and their entries are constant and thus not specific to an Exercise
* so that we can simply store their Ids.
* But the CnrConfigs are Exercise-specific. Thus we cannot store their Id in the library.
* Instead we store the name of the CnrConfig. When loading the stored FrequencyPlanning in
* another Exercise we try to find a CnrConfig with that name in that Exercise.
*/
QByteArray EditFrequencyPlanningFrequencyDataModel::uiToJson() const
{
QJsonObject obj;
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::AVMEditFrequencyPlanning);
obj[QStringLiteral("frequencySetId")] = m_initPlan.frequencySetId();
QJsonArray arr;
for (const auto &keyValue : overKeyValue(m_frequencyPlanningData))
{
QJsonObject inner;
const FrequencyPlanningData &info = keyValue.second;
inner[QStringLiteral("cnrChannelId")] = m_avmDtc->cnrConfig(info.cnrConfigId()).cnrChannelId();
inner[QStringLiteral("properties")] = QString::fromStdString(info.properties());
arr.append(inner);
}
obj[QStringLiteral("planningInfos")] = arr;
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
// Assign FrequencyPlanningData to RadioCircuit
void EditFrequencyPlanningFrequencyDataModel::setFrequencyPlanningData(const FrequencyPlanningData &fpd)
{
ConfigTreeItem* item = searchCnrConfig(m_rootItem, fpd.cnrConfigId());
if(!item)
{
if (!m_cnrConfigs.contains(fpd.cnrConfigId()))
return;
beginResetModel();
ItemData data(m_cnrConfigs[fpd.cnrConfigId()]);
data.readOnly = isReadOnly(fpd.properties());
QVector<RadioCircuit*> circuits = AudioVideoManagement::fromJson(fpd.properties());
if (circuits.size() > 1)
{
ConfigTreeItem *configItem = new ConfigTreeItem(QVariant::fromValue(data));
insertRow(m_rootItem->childCount(), indexFromItem(m_rootItem), configItem);
for (RadioCircuit *circuit : qAsConst(circuits))
{
data.radioCircuit = circuit;
ConfigTreeItem *childItem = new ConfigTreeItem(QVariant::fromValue(data));
insertRow(configItem->childCount(), indexFromItem(configItem), childItem);
}
}
else if (circuits.size() == 1)
{
data.radioCircuit = circuits[0];
ConfigTreeItem *configItem = new ConfigTreeItem(QVariant::fromValue(data));
insertRow(m_rootItem->childCount(), indexFromItem(m_rootItem), configItem);
}
m_frequencyPlanningData[fpd.cnrConfigId()] = fpd;
endResetModel();
return;
}
QVector<RadioCircuit*> circuits = AudioVideoManagement::fromJson(fpd.properties());
ItemData data = item->data().value<ItemData>();
item->removeAllChildren();
if (circuits.size() == 1)
{
data.radioCircuit = circuits[0];
item->setData(QVariant::fromValue(data));
m_frequencyPlanningData.insert(fpd.cnrConfigId(), fpd);
}
if (circuits.size() <= 1)
return;
for (RadioCircuit *circuit : qAsConst(circuits))
{
data.radioCircuit = circuit;
ConfigTreeItem *childItem = new ConfigTreeItem(QVariant::fromValue(data));
insertRow(item->childCount(), indexFromItem(item), childItem);
}
m_frequencyPlanningData.insert(fpd.cnrConfigId(), fpd);
endResetModel();
}
bool EditFrequencyPlanningFrequencyDataModel::checkResourceAssignments()
{
bool bOk = true;
m_multipleAssignedCnrIds.clear();
for (auto it = m_frequencyPlanningData.constBegin(); it != m_frequencyPlanningData.constEnd();)
{
const FrequencyPlanningData &p = it.value();
// Rest der Liste durchsuchen
QSet<int> fsdIdsOuter = frequencySetDataIds(p.properties());
for (auto startSearchIt = ++it; startSearchIt != m_frequencyPlanningData.constEnd();++startSearchIt)
{
const FrequencyPlanningData &pI= startSearchIt.value();
QSet<int> fsdIdsInner = frequencySetDataIds(pI.properties());
for (int inner : qAsConst(fsdIdsInner))
{
for (int outer : qAsConst(fsdIdsOuter))
{
if (inner != outer)
{
DEBUG_LOG(m_logger) << "---ist ungleich cnrConfigId(A): " << p.cnrConfigId()
<< " (B): " << pI.cnrConfigId();
}
else
{
DEBUG_LOG(m_logger) << "---ist gleich cnrConfigId(A): " << p.cnrConfigId()
<< " (B): " << pI.cnrConfigId();
if (!m_multipleAssignedCnrIds.contains(p.cnrConfigId()) )
m_multipleAssignedCnrIds.append(p.cnrConfigId());
if (!m_multipleAssignedCnrIds.contains(pI.cnrConfigId()) )
m_multipleAssignedCnrIds.append(pI.cnrConfigId());
bOk = false;
}
}
}
}
}
#ifdef LTS_DEBUG
QString qs;
for (int id : qAsConst(m_multipleAssignedCnrIds))
{
qs.append(QString::number(id));
qs.append(QStringLiteral(", "));
}
DEBUG_LOG(m_logger) << "---Number of multiple assigned frequency set data: " << m_multipleAssignedCnrIds.size()
<< " cnrConfigIds: " << qs;
#endif
return bOk;
}
bool EditFrequencyPlanningFrequencyDataModel::onCnrConfigChanged(const AudioVideoManagement::CnrConfig &data, DataModification modType)
{
ConfigTreeItem* item = searchCnrConfig(m_rootItem, data.id());
if (modType == DataModification::eRemoved)
{
// Doesn't affect this model
if (!item)
return true;
m_cnrConfigs.remove(data.id());
m_frequencyPlanningData.remove(data.id());
return removeRow(item->row());
}
// New CNR Config or CNR Config moved from another exercise to this exercise:
if (modType == DataModification::eAdded ||
(!item && data.exerciseId().is_set() && m_exerciseId == data.exerciseId().get())
)
{
Q_ASSERT(!item);
m_cnrConfigs.insert(data.id(), data);
ConfigTreeItem *newItem = new ConfigTreeItem(QVariant::fromValue(ItemData(data)));
m_rootItem->setModified(true);
insertRow(0, indexFromItem(m_rootItem), newItem);
return true;
}
// CNR Config moved to another exercise and thus is no longer part of this exercise
if (item && data.exerciseId().is_set() && m_exerciseId != data.exerciseId().get())
return removeRow(item->row());
// CNR config smply changed
if(item)
{
ItemData id = item->data().value<ItemData>();
id.cnrConfig = data;
item->setData(QVariant::fromValue(id));
//maybe radioCircuit name in cnrConfig changed
if(id.radioCircuit)
id.radioCircuit->name = QString::fromStdString(data.radioCircuit());
item->setModified(true);
return true;
}
return false;
}
EditFrequencyPlanningFrequencyDataModel::ConfigTreeItem* EditFrequencyPlanningFrequencyDataModel::searchCnrConfig(const NGLTreeItem *parentItem, int cnrConfigId) const
{
const int rowCount = parentItem->childCount();
for (int r = 0; r < rowCount; ++r)
{
ConfigTreeItem *child = static_cast<ConfigTreeItem *>(parentItem->child(r));
if (child->data().value<ItemData>().cnrConfig.id() == cnrConfigId)
return child;
child = searchCnrConfig(child, cnrConfigId);
if (child)
return child;
}
return nullptr;
}
QSet<int> EditFrequencyPlanningFrequencyDataModel::getAllocatedFrequencyPlanningSetIds()
{
QSet<int> result;
for (const auto &fpd : qAsConst(m_frequencyPlanningData))
{
result += AudioVideoManagement::frequencySetDataIds(fpd.properties());
}
if(m_exerciseId == 0)
{
QMap<qint32,AudioVideoManagement::FrequencyPlanning> frequencyPlannings = m_avmDtc->frequencyPlannings();
for(const auto &frequencyPlanning : qAsConst(frequencyPlannings))
{
if(!frequencyPlanning.exerciseId().is_set())
continue;
for(const auto &frequencyPlanningData : frequencyPlanning.data())
{
result += AudioVideoManagement::frequencySetDataIds(frequencyPlanningData.properties());
}
}
}
return result;
}
//=============================================================================================================================
// FPFDelegate
//=============================================================================================================================
FPFDelegate::FPFDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
void FPFDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.column() == EditFrequencyPlanningFrequencyDataModel::Columns::PROPERTIES)
{
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
painter->save();
QTextDocument doc;
doc.setHtml(options.text);
options.text = QString();
options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);
painter->translate(options.rect.left(), options.rect.top());
QRect clip(0, 0, options.rect.width(), options.rect.height());
doc.drawContents(painter, clip);
painter->restore();
}
else
QStyledItemDelegate::paint(painter, option, index);
}
QSize FPFDelegate::sizeHint( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
QTextDocument doc;
doc.setHtml(options.text);
doc.setTextWidth(options.rect.width());
return QSize(doc.idealWidth(), doc.size().height());
}
} // namespace AudioVideoManagement
@@ -0,0 +1,180 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningFrequencyDataModel class offers FrequencyPlanning related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <nglwidgets/ngltreeitem.h>
#include <AudioVideoManagement/logger.h>
#include "avm_frequency_planning_helper.h"
#include <nglwidgets/ngltreemodel.h>
#include <QStyledItemDelegate>
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
class CnrConfig;
class FPFDelegate
: public QStyledItemDelegate
{
Q_OBJECT
public:
FPFDelegate(QObject *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint( const QStyleOptionViewItem & option, const QModelIndex & index ) const override;
}; // FPFDelegate
class EditFrequencyPlanningFrequencyDataModel
: public NGLTreeModel
{
Q_OBJECT
public:
enum Columns
{
CHANNEL,
RADIO_CIRCUIT_ID,
RADIO_CIRCUIT,
PARTY,
UNIT,
RADIO_STATION,
FREQUENCY,
FUNCTION,
DEVICE,
AKW_BUNDLE,
FSB_BUNDLE,
AKW_RADIO_CIRCUIT,
COMSEC,
PILOTTON,
OPERATING_MODE,
PROPERTIES,
CHANNEL_SPACING,
SQUELCH,
GLOBAL,
COUNT,
};
enum PropertyData
{
ICON_PILOTTON,
ICON_MODULATION_TYPE,
ICON_SQUELCH,
ICON_CLIPPER,
ICON_AGC,
ICON_CHANNEL_SPACING,
ICON_SPACER,
ICON_COUNT,
};
struct ItemData
{
CnrConfig cnrConfig;
RadioCircuit* radioCircuit = nullptr;
QString _name; // eigtl. Private...
bool readOnly = false;
ItemData() = default;
ItemData(const CnrConfig &_cnrConfig, const QString &itemName = {}, RadioCircuit* _rc = nullptr)
: cnrConfig(_cnrConfig)
, radioCircuit(_rc)
, _name(itemName)
{}
QString name() const { return radioCircuit ? radioCircuit->name : _name; }
void setName(const QString &itemName) { _name = itemName; }
void setName(const std::string &itemName) { setName(QString::fromStdString(itemName)); }
void clearRadioCircuit() { delete radioCircuit; radioCircuit = nullptr; }
};
class ConfigTreeItem final : public NGLTreeItem
{
public:
using NGLTreeItem::NGLTreeItem;
};
explicit EditFrequencyPlanningFrequencyDataModel(const FrequencyPlanning &initPlanning,
int exerciseId,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override final;
ItemData itemData(const QModelIndex &index);
void setFrequencePlanningId(int id);
bool unassignRadioCircuit(int32_t cnrConfigId);
int32_t getFrequencySetId() const { return m_initPlan.frequencySetId(); }
QSet<int> getAllocatedFrequencyPlanningSetIds();
ModifyFrequencyPlanningRequest modifyFrequencyPlanningRequest() const;
FrequencyPlanning toFrequencyPlanning() const;
QByteArray uiToJson() const;
FrequencyPlanningData getfrequencyPlanningData(int cnrConfigId) { return m_frequencyPlanningData.value(cnrConfigId); }
bool checkResourceAssignments(); // return false and marked lines with multiple assignments. Otherwise returns true;
void setFrequencyPlanningData(const FrequencyPlanningData &fpd);
private slots:
void onFrequencyPlanningChanged(const FrequencyPlanning &planning, DataModification dm);
private:
void removeCnrId(int32_t cnrId);
int findCnrConfigByName(const QString &name) const;
void setCnrConfigs(const QVector<CnrConfig> &dataVec);
bool onCnrConfigChanged(const AudioVideoManagement::CnrConfig &data, DataModification modType);
EditFrequencyPlanningFrequencyDataModel::ConfigTreeItem* searchCnrConfig(const NGLTreeItem *parentItem, int cnrConfigId) const;
QString createIcon(PropertyData PropertyDataType, bool on = true, ModulationType type = ModulationType::AM) const;
int m_exerciseId;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
FrequencyPlanning m_initPlan;
QMap<int, FrequencySetDataWithId> m_freqSetData; // key = freqSetDataId
QMap<int, ParticipantManagement::Participant> m_units;
QMap<int, FrequencyPlanningData> m_frequencyPlanningData; // key = cnrConfigId (radioCircuit)
QMap<int, CnrConfig> m_cnrConfigs; // key = cnrConfigId (radioCircuit)
QVector<int> m_multipleAssignedCnrIds;
int32_t m_frequencySetId;
LOGGER_T m_logger;
}; // class EditFrequencyPlanningFrequencyDataModelB
} // namespace AudioVideoManagement
Q_DECLARE_METATYPE(AudioVideoManagement::EditFrequencyPlanningFrequencyDataModel::ItemData)
@@ -0,0 +1,106 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningResourceDataModel class offers FrequencyPlanning related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_edit_frequency_planning_resource_data_model.h"
#include <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include <AudioVideoManagement/avm_translation.h>
namespace AudioVideoManagement
{
EditFrequencyPlanningResourceDataModel::EditFrequencyPlanningResourceDataModel(const QSet<int> &assignedFrequencySetDataIds,
const FrequencyBand frequencyBand,
QObject *parent)
: LtsAbstractTableModel<FrequencySetDataWithId>(
{
tr("FSD id"),
tr("Function"),
tr("Device"),
tr("Frequency"),
tr("AKW Bundle"),
tr("FSB Bundle"),
tr("AKW Radio Circuit"),
tr("COMSEC"),
}, parent)
, m_assignedFrequencySetDataIds(assignedFrequencySetDataIds)
, m_frequencyBand(frequencyBand)
, m_logger(getLogger("EditFrequencyPlanningResourceDataModel"))
{
}
QVariant EditFrequencyPlanningResourceDataModel::data(const QModelIndex &index, int role) const
{
if ((role != Qt::DisplayRole && role != Qt::UserRole) || !indexIsValid(index))
return QVariant();
const FrequencySetDataWithId &setData = m_vecData.at(index.row());
if (role == Qt::DisplayRole)
{
QLocale locale(QLocale::German);
switch (index.column())
{
case FREQ_SET_DATA_ID:
return setData.id();
case FUNCTION:
return QString::fromStdString(setData.function());
case DEVICE:
return toString(setData.frequencyBand());
case FREQUENCY:
return tr("%1 MHz").arg(locale.toString(setData.frequency() / 1000., 'f', 3));
case AKW_BUNDLE:
if (setData.akwBundle().is_set())
return setData.akwBundle().get();
return QStringLiteral("-");
case FSB_BUNDLE:
if (setData.fsbBundle().is_set())
return setData.fsbBundle().get();
return QStringLiteral("-");
case AKW_RADIO_CIRCUIT:
if (setData.akwRadioCircuit())
return setData.akwRadioCircuit().get();
return QStringLiteral("-");
case COMSEC:
if (setData.comsec().is_set())
return setData.comsec().get();
return QStringLiteral("-");
}
}
else if (role == Qt::UserRole)
return setData.id();
return QVariant();
}
bool EditFrequencyPlanningResourceDataModel::isFiltered(const AudioVideoManagement::FrequencySetDataWithId &data, DataModification modType) const
{
// Um einen Eintrag nicht darzustellen muss isFiltered() true zurueckgeben.
// Frequenztyp muss stimmen (ausser bei REL...)
if (m_frequencyBand != FrequencyBand_def::REL &&
data.frequencyBand() != m_frequencyBand)
return true;
// ...denn da muss es VHF sein.
if(m_frequencyBand == FrequencyBand_def::REL &&
data.frequencyBand() != FrequencyBand_def::VHF)
return true;
Q_UNUSED(modType);
// Bereits in diesem FreqPlan vergebene FreqSetDataIds hier ausblenden
if (m_assignedFrequencySetDataIds.contains(data.id()))
{
DEBUG_LOG(m_logger) << "---m_assignments.size(): " << m_assignedFrequencySetDataIds.size()
<< " frequencySetDataId: " << data.id() << " ist bereits enthalten";
return true; // um den Eintrag nicht darzustellen muss true zurueckgegeben werden
}
return false;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,59 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningResourceDataModel class offers FrequencyPlanning related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <LtsWidgets/lts_abstract_table_model.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
namespace AudioVideoManagement
{
class EditFrequencyPlanningResourceDataModel
: public LtsWidgets::LtsAbstractTableModel<FrequencySetDataWithId>
{
Q_OBJECT
public:
enum Columns
{
FREQ_SET_DATA_ID,
FUNCTION,
DEVICE,
FREQUENCY,
AKW_BUNDLE,
FSB_BUNDLE,
AKW_RADIO_CIRCUIT,
COMSEC,
RADIO_CIRCUIT,
PARTY,
UNIT,
RADIO_STATION,
PILOTTON,
OPERATING_MODE,
COUNT
};
explicit EditFrequencyPlanningResourceDataModel(const QSet<int> &assignedFrequencySetDataIds,
const FrequencyBand frequencyBand,
QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override final;
protected:
bool isFiltered(const AudioVideoManagement::FrequencySetDataWithId &data, DataModification modType) const override;
private:
QSet<int> m_assignedFrequencySetDataIds;
FrequencyBand m_frequencyBand;
LOGGER_T m_logger;
}; // class EditFrequencyPlanningResourceDataModel
} // namespace AudioVideoManagement
@@ -0,0 +1,231 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningResourceDialog allows to edit Frequency related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_edit_frequency_planning_frequency_data_model.h"
#include "avm_edit_frequency_planning_resource_dialog.h"
#include "ui_avm_edit_frequency_planning_resource_dialog.h"
#include "avm_frequency_planning_widget.h"
#include <LtsWidgets/lts_combo_box.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_translation.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <AudioVideoManagement/avm_translation.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <LtsSession/lts_session_data.h>
#include <ngltools/ngltools_dtg.h>
#include <LtsDataView/lts_data_view.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <LtsSession/lts_session.h>
#include <QTreeView>
#include <QJsonArray>
namespace
{
} // anonymous namespace
namespace AudioVideoManagement
{
EditFrequencyPlanningResourceDialog::EditFrequencyPlanningResourceDialog(int frequencySetId,
const EditFrequencyPlanningSetResourceDialog::DialogMode dialogMode,
QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkCancel)
, m_ui(new Ui::EditFrequencyPlanningResourceDialog)
, m_frequencySetId(frequencySetId)
, m_dialogMode(dialogMode)
, m_logger(getLogger("EditFrequencyPlanningResourceDialog"))
{
m_ui->setupUi(this);
setOnlineHelpFilename(QStringLiteral("Situation-Frequency_Planning"));
}
bool EditFrequencyPlanningResourceDialog::init(LtsSession::LtsSession *session, const FrequencyPlanningData &planningData, const QSet<int> &assignedFrequencySetDataIds)
{
bool succ = true;
m_session = session;
m_planningData = planningData;
m_assignedFrequencySetDataIds = assignedFrequencySetDataIds;
succ &= m_session->getDtc<AudioVideoManagement::DataInterface>(m_avmDtc);
succ &= m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc);
succ &= m_session->getDtc<InfrastructureManagement::DataInterface>(m_ismDtc);
if(!succ)
return succ;
m_units = m_ppmDtc->units();
m_config = m_avmDtc->cnrConfig(m_planningData.cnrConfigId());
// m_config.radioCircuit() == REL
// --> 2 Elemente mindestens!
// Device Type & RadioCircuit Namen ermitteln
m_radioCircuitName = QString::fromStdString(m_config.radioCircuit());
CnrChannel channel = m_avmDtc->cnrChannel(m_config.cnrChannelId());
m_frequencyBand = channel.frequencyBand();
QVector<RadioCircuit*> circuits = AudioVideoManagement::fromJson(planningData.properties());
bool readOnly = isReadOnly(planningData.properties());
for (RadioCircuit *circuit : qAsConst(circuits))
{
addMemberWidget(circuit, readOnly);
}
if (circuits.count() < 2 && m_frequencyBand == FrequencyBand::REL)
addMemberWidget();
if (circuits.count() < 1)
addMemberWidget();
setLbRadioCiruit(m_config);
connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::cnrConfigChanged,
this, &EditFrequencyPlanningResourceDialog::onCnrConfigChanged);
connect(m_ui->tswFrequencyMember, &LtsWidgets::LtsTabSequenceWidget::addMember,
this, &EditFrequencyPlanningResourceDialog::addMemberWidgetEmpty);
qDeleteAll(circuits);
return succ;
}
EditFrequencyPlanningResourceDialog::~EditFrequencyPlanningResourceDialog() = default;
void EditFrequencyPlanningResourceDialog::retranslate()
{
// 'Edit / Assign Radio Circuit: CircuitName' im Tab anzeigen
if (m_dialogMode == EditFrequencyPlanningSetResourceDialog::DialogMode::Edit)
{
translate(m_ui.get(), m_avmDtc->cnrConfig(m_planningData.cnrConfigId()), m_radioCircuitName, tr("Radio Circuit"));
}
else // Assign Mode
{
QString qs = tr("Assign Radio Circuit: %1").arg(m_radioCircuitName);
translate(m_ui.get(), qs);
}
}
bool EditFrequencyPlanningResourceDialog::apply()
{
QJsonArray array;
for(int memberCount = 0; memberCount < m_ui->tswFrequencyMember->getMemberlistSize(); memberCount++)
{
FrequencyPlanningWidget *planningWidget = qobject_cast<FrequencyPlanningWidget*>(m_ui->tswFrequencyMember->getWidget(memberCount));
if(!planningWidget)
continue;
array.append(planningWidget->toJson());
}
QJsonDocument doc;
QJsonObject obj;
obj.insert(QStringLiteral("subCircuits"), array);
doc.setObject(obj);
FrequencyPlanningData fpd;
fpd.properties(doc.toJson().toStdString());
fpd.cnrConfigId() = m_config.id();
emit signalAssignRadioCircuit(fpd);
return true;
}
void EditFrequencyPlanningResourceDialog::onEnableOkButton()
{
slotChangesMade();
}
// Todo: Take correct name. If not relais: m_radioCircuitName! ALWAYS / Dunno by first subcircuit!
void EditFrequencyPlanningResourceDialog::addMemberWidget(RadioCircuit *radioCircuit, bool readOnly)
{
FrequencyPlanningWidget *widget = new FrequencyPlanningWidget(m_avmDtc, m_assignedFrequencySetDataIds, this);
widget->init(m_session,
radioCircuit,
m_config,
m_frequencySetId,
m_frequencyBand);
connect(widget, &FrequencyPlanningWidget::onNameChanged,
this, &EditFrequencyPlanningResourceDialog::onNameChanged);
widget->setDisabled(readOnly || m_dialogMode == EditFrequencyPlanningSetResourceDialog::DialogMode::Show);
// To set Tab name initially.
widget->setObjectName((radioCircuit && m_frequencyBand == FrequencyBand::REL) ? radioCircuit->name : m_radioCircuitName);
widget->setName( (radioCircuit && m_frequencyBand == FrequencyBand::REL) ? radioCircuit->name : m_radioCircuitName);
const auto &reqs = widget->requirements();
for (const auto &req : reqs)
m_uiRequirements->addRequirement({req.first, req.second});
m_ui->tswFrequencyMember->addWidget(widget, true, false);
for (int i=0 ; i< m_ui->tswFrequencyMember->getMemberlistSize(); ++i)
{
FrequencyPlanningWidget *w = qobject_cast<FrequencyPlanningWidget*>(m_ui->tswFrequencyMember->getWidget(i));
if (!w)
continue;
w->setNameDisabled(m_ui->tswFrequencyMember->getMemberlistSize() == 1);
w->setRelaisRestrictions(m_ui->tswFrequencyMember->getMemberlistSize() > 1);
}
}
void EditFrequencyPlanningResourceDialog::onCnrConfigChanged(const AudioVideoManagement::CnrConfig &config, DataModification modType)
{
Q_UNUSED(modType);
if (m_config.id() == config.id())
{
setLbRadioCiruit(config);
m_radioCircuitName = QString::fromStdString(config.radioCircuit());
retranslate();
}
}
void EditFrequencyPlanningResourceDialog::setLbRadioCiruit(const AudioVideoManagement::CnrConfig &config)
{
// RadioCircuit mit zugehoerigen Infos anzeigen
ParticipantManagement::Party party = m_ppmDtc->party(config.partyId());
QString partyName = ParticipantManagement::trPartyName(party.name());
QString unitString;
if (config.unitId().is_set())
{
int unitId = config.unitId().get();
auto unit = m_units.value(unitId);
if (unit.id() == unitId)
{
if (unit.designator().size() > 0)
unitString = QString::fromStdString(unit.designator()) + QLatin1Char('/');
unitString.append(QString::fromStdString(unit.tacticalIdentifier()));
}
}
m_ui->lblUnit->setText(unitString);
CnrChannel channel = m_avmDtc->cnrChannel(m_config.cnrChannelId());
m_ui->lblChannel->setText(QString::fromStdString(channel.name()));
m_ui->lblFrequencyBand->setText(toString(m_frequencyBand));
m_ui->lblRadioCircuit->setText(m_radioCircuitName);
m_ui->lblParty->setText(partyName);
}
void EditFrequencyPlanningResourceDialog::onNameChanged(const QString &name)
{
m_ui->tswFrequencyMember->changeTabName(name);
if (m_ui->tswFrequencyMember->getMemberlistSize() > 1)
return;
m_radioCircuitName = name;
m_ui->lblRadioCircuit->setText(m_radioCircuitName);
}
} // namespace AudioVideoManagement
@@ -0,0 +1,89 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningResourceDialog allows to edit Frequency related data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include "avm_frequency_planning_helper.h"
#include "avm_edit_frequency_planning_set_resource_dialog.h"
namespace Ui
{
class EditFrequencyPlanningResourceDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace LtsWidgets
{
class LtsComboBox;
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class EditFrequencyPlanningResourceDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit EditFrequencyPlanningResourceDialog(int frequencySetId,
const EditFrequencyPlanningSetResourceDialog::DialogMode dialogMode,
QWidget *parent);
~EditFrequencyPlanningResourceDialog();
void retranslate() override;
bool init(LtsSession::LtsSession *session, const FrequencyPlanningData &planningData, const QSet<int> &assignedFrequencySetDataIds);
protected:
bool apply() override;
signals:
void signalAssignRadioCircuit(const FrequencyPlanningData &doc);
private slots:
void onEnableOkButton();
void onCnrConfigChanged(const AudioVideoManagement::CnrConfig &config, DataModification modType);
void setLbRadioCiruit(const CnrConfig &config);
void addMemberWidget(RadioCircuit *radioCircuit = nullptr, bool readOnly = false);
void addMemberWidgetEmpty() { addMemberWidget(); }
void onNameChanged(const QString &name);
private:
QMap<int32_t, ParticipantManagement::Participant> m_units;
std::unique_ptr<Ui::EditFrequencyPlanningResourceDialog> m_ui;
int m_frequencySetId;
FrequencyPlanningData m_planningData;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
int m_exerciseId;
CnrConfig m_config;
EditFrequencyPlanningSetResourceDialog::DialogMode m_dialogMode;
FrequencyBand m_frequencyBand;
QString m_radioCircuitName;
QSet<int> m_assignedFrequencySetDataIds;
LtsSession::LtsSession *m_session;
LOGGER_T m_logger;
}; // class EditFrequencyPlanningResourceDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditFrequencyPlanningResourceDialog</class>
<widget class="QWidget" name="EditFrequencyPlanningResourceDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>542</width>
<height>284</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Select Function</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLabel" name="lblFrequencyBand">
<property name="text">
<string notr="true">&lt;RadioCircuit&gt;</string>
</property>
</widget>
</item>
<item row="0" column="3" rowspan="6">
<widget class="LtsWidgets::LtsTabSequenceWidget" name="tswFrequencyMember"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lbxUnit">
<property name="text">
<string>Unit:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbxRadioCircuit">
<property name="text">
<string>Radio Circuit:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblRadioCircuit">
<property name="text">
<string notr="true">&lt;SET&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lbxParty">
<property name="text">
<string>Party:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lblParty">
<property name="text">
<string notr="true">&lt;PARTY&gt;</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="lblUnit">
<property name="text">
<string notr="true">&lt;UNIT&gt;</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lbxFrequencyBand">
<property name="text">
<string>Frequency Band:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>13</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>171</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbxChannel">
<property name="text">
<string>Channel:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblChannel">
<property name="text">
<string notr="true">&lt;CHANNEL&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsTabSequenceWidget</class>
<extends>QGroupBox</extends>
<header>LtsWidgets/lts_tab_sequence_widget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,94 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningSetResourceDialog allows to select a Frequency set
* $Id$
* $HeadURL$
* $Author$
* Created by Verena Eberlein, SPMO, 2023-09-21
*/
#include "avm_edit_frequency_planning_frequency_data_model.h"
#include "avm_edit_frequency_planning_set_resource_dialog.h"
#include "ui_avm_edit_frequency_planning_set_resource_dialog.h"
#include "avm_edit_frequency_planning_resource_data_model.h"
#include <ngltools/ngltools_dtg.h>
#include <LtsDataView/lts_data_view.h>
namespace
{
} // anonymous namespace
namespace AudioVideoManagement
{
EditFrequencyPlanningSetResourceDialog::EditFrequencyPlanningSetResourceDialog(
const QSet<int> &assignedFrequencySetDataIds,
const RcOperatingMode operatingMode,
int32_t frequencySetId,
const int &cnrConfigId,
const std::shared_ptr<Interface::Dtc> &avmDtc,
QWidget *parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_ui(new Ui::EditFrequencyPlanningSetResourceDialog)
, m_frequencySetId(frequencySetId)
, m_avmDtc(avmDtc)
, m_frequencyResourceDataModel(nullptr)
, m_config(m_avmDtc->cnrConfig(cnrConfigId))
, m_logger(getLogger("EditFrequencyPlanningSetResourceDialog"))
{
m_ui->setupUi(this);
// Device Type & RadioCircuit Namen ermitteln
CnrChannel channel = m_avmDtc->cnrChannel(m_config.cnrChannelId());
// Normalerweise werden die in m_assignments uebergebenen Eintraege im Model ausgeblendet..
// Im EditMode wird diese Funktionalitaet genau invertiert behandelt; Alles bis auf den Eintrag in m_assignments wird ausgeblendet
m_frequencyResourceDataModel = new EditFrequencyPlanningResourceDataModel(assignedFrequencySetDataIds, channel.frequencyBand(), this);
std::vector<FrequencySetDataWithId> dataVec = m_avmDtc->frequencySet(m_frequencySetId).data();
for (const auto &it : dataVec)
{
if((operatingMode == RcOperatingMode_def::AkwEncEccm ||
operatingMode == RcOperatingMode_def::AkwEncNone)
&& it.akwRadioCircuit() == 0) //show only akw bundel
continue;
if((operatingMode != RcOperatingMode_def::AkwEncEccm &&
operatingMode != RcOperatingMode_def::AkwEncNone)
&& it.akwRadioCircuit() != 0)
continue;
m_frequencyResourceDataModel->insertRow(it);
}
m_ui->dvFrequencyData->setModel(m_frequencyResourceDataModel);
#ifndef LTS_DEBUG
m_ui->dvFrequencyData->hideColumn(EditFrequencyPlanningResourceDataModel::Columns::FREQ_SET_DATA_ID);
#endif
// connects
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Ok), &QPushButton::clicked,
this, &EditFrequencyPlanningSetResourceDialog::accept);
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked,
this, &EditFrequencyPlanningSetResourceDialog::reject);
connect(m_ui->dvFrequencyData, &LtsDataView::LtsDataView::doubleClicked,
this, &EditFrequencyPlanningSetResourceDialog::accept);
}
EditFrequencyPlanningSetResourceDialog::~EditFrequencyPlanningSetResourceDialog() = default;
void EditFrequencyPlanningSetResourceDialog::accept()
{
LtsWidgets::LtsFramelessDialog::accept();
const QModelIndex idx = m_ui->dvFrequencyData->currentIndex();
if (!idx.isValid())
return;
emit onSelectedFrequencySet(m_frequencyResourceDataModel->getRowData(idx.row()).id());
}
} // namespace AudioVideoManagement
@@ -0,0 +1,80 @@
/**
* @RDE_HEADER@
* The EditFrequencyPlanningSetResourceDialog allows to select a Frequency set
* $Id$
* $HeadURL$
* $Author$
* Created by Verena Eberlein, SPMO, 2023-09-21
*/
#pragma once
#include <LtsWidgets/lts_frameless_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_enums.hpp>
#include <AudioVideoManagement/avm.hpp>
namespace Ui
{
class EditFrequencyPlanningSetResourceDialog;
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class EditFrequencyPlanningResourceDataModel;
namespace Interface
{
class Dtc;
}
class EditFrequencyPlanningSetResourceDialog : public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
enum DialogMode
{
Edit,
Assign,
Show
};
explicit EditFrequencyPlanningSetResourceDialog(const QSet<int> &assignedFrequencySetDataIds,
const RcOperatingMode operatingMode,
int32_t frequencySetId,
const int &cnrConfigId,
const std::shared_ptr<Interface::Dtc> &avmDtc,
QWidget *parent);
~EditFrequencyPlanningSetResourceDialog();
private slots:
void accept();
signals:
void onSelectedFrequencySet(int frequencySetDataId);
private:
std::unique_ptr<Ui::EditFrequencyPlanningSetResourceDialog> m_ui;
int32_t m_frequencySetId;
std::shared_ptr<Interface::Dtc> m_avmDtc;
EditFrequencyPlanningResourceDataModel *m_frequencyResourceDataModel;
CnrConfig m_config;
DialogMode m_dialogMode;
LOGGER_T m_logger;
}; // class EditFrequencyPlanningSetResourceDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditFrequencyPlanningSetResourceDialog</class>
<widget class="QWidget" name="EditFrequencyPlanningSetResourceDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>933</width>
<height>587</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Asset Stocks</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lblDataView">
<property name="text">
<string>To assign select an entry:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsDataView::LtsDataView" name="dvFrequencyData"/>
</item>
<item>
<widget class="LtsWidgets::LtsDialogButtonBox" name="ltsDialogButtonBox"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsDialogButtonBox</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_dialog_button_box.h</header>
</customwidget>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,498 @@
/**
* @RDE_HEADER@
* The FrequencyPlanningDialog lists all FrequencyPlanning data and offers to set the active Planning for the current exercise
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_frequency_planning_dialog.h"
#include "ui_avm_frequency_planning_dialog.h"
#include "avm_frequency_planning_dialog_model.h"
#include "avm_edit_frequency_planning_dialog.h"
#include "avm_init_frequency_planning_dialog.h"
#include <InfrastructureManagement/ism_dtc.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsSession/lts_session.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <AudioVideoManagement/avm_translation.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsPrinting/printing.h>
#include <QTreeView>
#include <QMessageBox>
namespace AudioVideoManagement
{
FrequencyPlanningDialog::FrequencyPlanningDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_ui(new Ui::FrequencyPlanningDialog)
, m_menuGroup(menuGroup)
, m_copy(false)
, m_session(session)
, m_model(nullptr)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_activePlanning( { -1, -1 })
, m_avmDtc(nullptr)
, m_avmFtc(nullptr)
, m_ppmDtc(nullptr)
, m_ismDtc(nullptr)
, m_flmGui(nullptr)
, m_activeFrequencyPlanningStatus(0)
, m_logger(getLogger("FrequencyPlanningDialog"))
{
m_ui->setupUi(this);
DEBUG_LOG(m_logger) << "---FrequencyPlanningDialog() ";
m_ui->dataView->setSelectionMode(QAbstractItemView::SingleSelection);
m_ui->dataView->treeView()->setSelectionBehavior(QAbstractItemView::SelectRows);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &FrequencyPlanningDialog::onPrint);
setOnlineHelpFilename(QStringLiteral("Situation-Frequency_Planning"));
}
FrequencyPlanningDialog::~FrequencyPlanningDialog()
{
delete m_ui;
}
void FrequencyPlanningDialog::retranslate()
{
translate(m_ui, tr("Frequency Planning"));
}
void FrequencyPlanningDialog::unsetActiveFrequencyPlanning()
{
m_ui->lbActiveFrequencyPlanning->setText(tr("No Frequency Planning set"));
m_activePlanning = { -1, -1 };
m_model->unsetActivePlanning();
onSelectionChanged();
}
void FrequencyPlanningDialog::setActiveFrequencyPlanning(const ActiveFrequencyPlanning &activePlanning)
{
m_ui->lbActiveFrequencyPlanning->setText(QString::fromStdString(m_avmDtc->frequencyPlanning(activePlanning.frequencyPlanningId()).name()));
m_activePlanning = activePlanning;
m_model->setActivePlanning(m_activePlanning.frequencyPlanningId());
onSelectionChanged();
}
bool FrequencyPlanningDialog::init(const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
ExerciseManagement::Interface::Dtc *exmDtc)
{
if (!(ppmDtc->enableParty() &&
ppmDtc->enableParticipant() &&
ppmDtc->enableType() &&
avmDtc->enableCnrConfig() &&
avmDtc->enableFrequencyPlanning() &&
avmDtc->enableFrequencySet() &&
avmDtc->enableActiveFrequencyPlanning() &&
ismDtc->enableMainStructureElement()))
{
ERROR_LOG(m_logger) << "Could not enable topics";
return false;
}
delete m_model;
m_model = new FrequencyPlanningDialogModel(m_session->exerciseId(), this);
m_avmDtc = avmDtc;
m_avmFtc = avmFtc;
m_ppmDtc = ppmDtc;
m_ismDtc = ismDtc;
m_exmDtc = exmDtc;
if (!m_model->init(avmDtc))
{
delete m_model;
m_model = nullptr;
ERROR_LOG(m_logger) << "Could not init model";
return false;
}
m_proxyModel = new FrequencyPlanningDialogProxyModel(this);
m_proxyModel->setSourceModel(m_model);
m_ui->dataView->setModel(m_proxyModel);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &FrequencyPlanningDialog::onAddClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &FrequencyPlanningDialog::onEditClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &FrequencyPlanningDialog::onRemoveClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save, this, &FrequencyPlanningDialog::onActivateDeactivatePlanningClicked);
connect(m_ui->dataView, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &FrequencyPlanningDialog::onSelectionChanged);
unsetActiveFrequencyPlanning();
connect(m_ui->dataView, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &FrequencyPlanningDialog::onEditClicked);
connect(m_avmDtc.get(), &Interface::Dtc::activeFrequencyPlanningChanged,
this, &FrequencyPlanningDialog::onActiveFrequencyPlanningChanged);
m_ui->dataView->hideColumn(FrequencyPlanningDialogModel::Columns::SET);
m_avmDtc->activeFrequencyPlannings([this](const ActiveFrequencyPlanning & data)
{
onActiveFrequencyPlanningChanged(data, DataModification::eAdded);
});
onSelectionChanged();
setStatus(tr("No active Radio Controller connected."), {}, time(0) * 1000, TransmissionReason::TR_Unknown, Qt::red);
for(auto &elem : m_avmDtc->frequencyPlanningStatuss())
{
if (!elem.isActive())
continue;
m_activeFrequencyPlanningStatus = elem.id();
onFrequencyPlanningStatusChanged(elem, DataModification::eAdded);
}
connect(m_avmDtc.get(), &Interface::Dtc::frequencyPlanningStatusChanged, this, &FrequencyPlanningDialog::onFrequencyPlanningStatusChanged);
return true;
}
void FrequencyPlanningDialog::onFrequencyPlanningStatusChanged(const AudioVideoManagement::FrequencyPlanningStatus &data, DataModification modType)
{
if (modType == DataModification::eModified)
{
if (data.id() != m_activeFrequencyPlanningStatus)
return;
}
else if (modType == DataModification::eRemoved)
{
if (data.id() != m_activeFrequencyPlanningStatus)
return;
m_activeFrequencyPlanningStatus = 0;
setStatus(tr("No active Radio Controller connected."), {}, time(0) * 1000, TransmissionReason::TR_Unknown, Qt::red);
return;
}
else // added....
{
if (!data.isActive())
return;
m_activeFrequencyPlanningStatus = data.id();
}
// Active and data.id == m_active...Status:
if (data.activated().is_set())
{
setStatus(tr("Activated"), QString::fromStdString(data.requester()), data.activated().get(), data.reason(), Qt::green);
return;
}
if (data.received().is_set())
{
setStatus(tr("In Progress"), QString::fromStdString(data.requester()), data.received().get(), data.reason(), Qt::yellow);
return;
}
if (data.sent().is_set())
{
setStatus(tr("Sent"), QString::fromStdString(data.requester()), data.sent().get(), TransmissionReason::TR_Unknown, Qt::yellow);
return;
}
setStatus(tr("Connection established, status unknown."), {}, time(0) * 1000, TransmissionReason::TR_Unknown, Qt::yellow);
}
void FrequencyPlanningDialog::setStatus(const QString &statusText, const QString &requester, qint64 timeStampMsecs, TransmissionReason reason, Qt::GlobalColor color)
{
m_ui->lblStatus->setText(statusText);
m_ui->lblStatusTime->setText(DTG::toStringMSec(timeStampMsecs));
m_ui->lblStatus->setToolTip(toString(reason));
m_ui->lblStatusTime->setToolTip(requester);
setBackgroundColor(m_ui->lblStatus, color, color != Qt::black);
}
void FrequencyPlanningDialog::tableColumnOrganiserSetting()
{
//LtsColumnOrganiser
m_ui->dataView->setColumnsOrganiserEnabled(identifier(), true);
}
void FrequencyPlanningDialog::setBackgroundColor(QWidget *w, Qt::GlobalColor color, bool bSetColor)
{
if (!w)
return;
if (bSetColor)
{
QPalette pal = QApplication::palette(w);
pal.setColor(QPalette::Window, color);
w->setPalette(pal);
w->setAutoFillBackground(true);
}
else
{
// reset to default palette
w->setPalette(QApplication::palette(w));
w->setAutoFillBackground(false);
}
}
void FrequencyPlanningDialog::onModifyFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void FrequencyPlanningDialog::onRemoveFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void FrequencyPlanningDialog::onRemoveActiveFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void FrequencyPlanningDialog::onModifyActiveFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void FrequencyPlanningDialog::onAddClicked() // TODO: add-button in nav bar ?
{
const QModelIndexList selectedRows = m_ui->dataView->selectedRows();
Q_ASSERT(selectedRows.empty() || selectedRows.size() == 1);
QMap<int32_t, FrequencySet> frequencySets = m_avmDtc->frequencySets();
if (selectedRows.empty())
{
if(frequencySets.size() == 1)
{
FrequencyPlanning planning;
planning.id(0);
if(m_session->exerciseId() != 0)
planning.exerciseId(m_session->exerciseId());
planning.frequencySetId(frequencySets.first().id());
planning.date(m_session->partitionTime());
showEditDialog(planning);
}
else
{
InitFrequencyPlanningDialog *initDialog = new InitFrequencyPlanningDialog(m_session, m_avmDtc, this);
connect(initDialog, &InitFrequencyPlanningDialog::newInitFrequencyPlanning,
this, &FrequencyPlanningDialog::showEditDialog);
m_copy = false;
addDialog(initDialog, m_menuGroup, QStringLiteral("NewFrequencyPlanning"));
}
}
else
{
m_copy = true; // Neuer Plan auf Basis einer Kopie mit alten Daten ... ==> akt. CreationTime eintragen
qint64 dateOfCreation = m_session->partitionTime();
FrequencyPlanning planning = m_model->data(selectedRows.constFirst(), Qt::UserRole).value<FrequencyPlanning>();
planning.date(dateOfCreation);
showEditDialog(planning);
}
}
void FrequencyPlanningDialog::onEditClicked()
{
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
const FrequencyPlanning &fp = m_model->data(currentIndex, Qt::UserRole).value<FrequencyPlanning>();
DEBUG_LOG(m_logger) << "---onEditClicked() FrequencyPlanningId: " << fp.id() << " FrequencySetDataId: " << fp.frequencySetId();
m_copy = false;
showEditDialog(m_model->data(currentIndex, Qt::UserRole).value<FrequencyPlanning>());
}
void FrequencyPlanningDialog::onRemoveClicked()
{
const QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
int planningId = m_model->data(currentIndex, Qt::UserRole).value<FrequencyPlanning>().id();
// Schliesst ggf. noch offene zugehoerige Edit und Resource Dialoge wenn der Frequenzplan geloescht wird
if (m_editDlgMap.contains(planningId))
{
m_editDlgMap.value(planningId)->deleteLater();
m_editDlgMap.remove(planningId);
}
Core::RemoveElementRequest req;
req.id(planningId);
m_requestHelper->startSingleRequest([this, req]()
{
return m_avmFtc->removeFrequencyPlanningRequest(req, this);
});
}
void FrequencyPlanningDialog::onSelectionChanged()
{
const QModelIndexList selectedRows = m_ui->dataView->selectedRows();
bool hasSelection = !selectedRows.empty();
bool selectedIsActive = false;
if (hasSelection)
{
const QModelIndex currentIndex = selectedRows.constFirst();
selectedIsActive = m_model->data(currentIndex, Qt::UserRole).value<FrequencyPlanning>().id() == m_activePlanning.frequencyPlanningId();
}
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, hasSelection);
// Do not allow remove if selected Planing is active
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, hasSelection && !selectedIsActive);
// "Save" actually means "active/deactivate planning"
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Save, hasSelection && m_session->exerciseId() && m_session->exerciseState() == LtsWidgets::ExcState::eStarted);
if (selectedIsActive)
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setText(tr("Deactivate"));
else
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setText(tr("Activate"));
bool disableGlobalPlanning = m_session->exerciseId() == 0 && m_proxyModel->rowCount() !=0;
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Add, !disableGlobalPlanning);
}
void FrequencyPlanningDialog::onActivateDeactivatePlanningClicked()
{
if (QMessageBox::question(this, tr("Frequency Planning"), tr("Are you sure you want to switch the active frequency planning?"),
QMessageBox::StandardButtons(QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No))
== QMessageBox::StandardButton::No)
return;
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
int32_t planningId = m_model->data(currentIndex, Qt::UserRole).value<FrequencyPlanning>().id();
bool isActive = planningId == m_activePlanning.frequencyPlanningId();
if (!isActive)
{
ModifyActiveFrequencyPlanningRequest request(m_session->exerciseId(), planningId);
m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->modifyActiveFrequencyPlanningRequest(request, this);
});
}
else
{
RemoveActiveFrequencyPlanningRequest request(m_session->exerciseId());
m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->removeActiveFrequencyPlanningRequest(request, this);
});
}
}
void FrequencyPlanningDialog::showEditDialog(const FrequencyPlanning &planning)
{
QString widgetIdentifier(QStringLiteral("FrequencyPlanning%1").arg(QString::number(planning.id())));
if (m_copy)
widgetIdentifier.append(QStringLiteral("Copy"));
if (baseLayoutWidget()->focusTab(menuGroup(), widgetIdentifier, LtsWidgets::LayoutArea::eDialogTab))
return;
EditFrequencyPlanningDialog *dlg = new EditFrequencyPlanningDialog(m_session, m_menuGroup, widgetIdentifier, planning, m_copy, m_avmDtc, m_avmFtc, m_ppmDtc, m_ismDtc, m_exmDtc, this);
connect(dlg, &EditFrequencyPlanningDialog::aboutToCloseMe, this , [this](int planningId)
{
if (planningId <= 0 || !m_editDlgMap.contains(planningId))
return;
m_editDlgMap.remove(planningId);
});
addDialog(dlg, m_menuGroup, widgetIdentifier);
if (!m_copy)
{
m_editDlgMap.insert(planning.id(), dlg);
}
}
void FrequencyPlanningDialog::onActiveFrequencyPlanningChanged(const ActiveFrequencyPlanning &data, DataModification modType)
{
if (data.exerciseId() != m_session->exerciseId())
return;
if (modType == DataModification::eRemoved)
unsetActiveFrequencyPlanning();
else
setActiveFrequencyPlanning(data);
}
void FrequencyPlanningDialog::onPrint()
{
LtsPrinting::printView(m_session, this, m_ui->dataView->treeView() );
}
// Library
void FrequencyPlanningDialog::onExportButtonClicked()
{
QByteArray byteArray = m_model->modelToJson();
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_session->exerciseId(), byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void FrequencyPlanningDialog::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << " onImportButtonClicked() ";
if (m_model->rowCount() > 0)
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Only available if the list is empty."),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
return;
}
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_session->exerciseId(), FileManagement::toString(FileManagement::XmlType::AVMFrequencyPlanning));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMFrequencyPlanning)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
const QVector<ModifyFrequencyPlanningRequest> requests = m_model->jsonToFrequencyPlanningVector(content);
for (const auto &request : requests)
{
m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->modifyFrequencyPlanningRequest(request, this);
});
}
INFO_LOG(m_logger) << "--- onImportButtonClicked() ";
}
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,136 @@
/**
* @RDE_HEADER@
* The FrequencyPlanningDialog lists all FrequencyPlanning data and offers to set the active Planning for the current exercise
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <LtsUtilsCore/data_modification_enum.h>
#include <memory>
namespace Ui
{
class FrequencyPlanningDialog;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
class FrequencyPlanningDialogModel;
class FrequencyPlanningDialogProxyModel;
class EditFrequencyPlanningDialog;
class FrequencyPlanningDialog
: public LtsWidgets::LtsDialog
, protected Interface::Ftc::ModifyFrequencyPlanningReplyHandler
, protected Interface::Ftc::RemoveFrequencyPlanningReplyHandler
, protected Interface::Ftc::ModifyActiveFrequencyPlanningReplyHandler
, protected Interface::Ftc::RemoveActiveFrequencyPlanningReplyHandler
{
Q_OBJECT
public:
explicit FrequencyPlanningDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent = nullptr);
~FrequencyPlanningDialog();
bool init(const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
ExerciseManagement::Interface::Dtc *exmDtc);
void onModifyFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject) override final;
void onRemoveFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject) override final;
void onRemoveActiveFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject) override final;
void onModifyActiveFrequencyPlanningReply(const Core::FunctionReplyObject &replyObject) override final;
private Q_SLOTS:
void onAddClicked();
void onEditClicked();
void onRemoveClicked();
void onSelectionChanged();
void onActivateDeactivatePlanningClicked();
void showEditDialog(const FrequencyPlanning &planning);
void onActiveFrequencyPlanningChanged(const ActiveFrequencyPlanning &data, DataModification modType);
void onFrequencyPlanningStatusChanged(const FrequencyPlanningStatus &data, DataModification modType);
void onPrint();
void onExportButtonClicked();
void onImportButtonClicked();
signals:
void aboutToCloseMe(int);
private: // methods
void retranslate() override;
void unsetActiveFrequencyPlanning();
void setActiveFrequencyPlanning(const ActiveFrequencyPlanning &activePlanning);
void setButtonSetActivePlanningEnabled();
static void setBackgroundColor(QWidget *w, Qt::GlobalColor color, bool bSetColor);
void setStatus(const QString &statusText, const QString &requester, qint64 timeStampMsecs, TransmissionReason reason = TransmissionReason_def::TR_Unknown, Qt::GlobalColor color = Qt::black);
void tableColumnOrganiserSetting() override;
private: // attributes
Ui::FrequencyPlanningDialog *m_ui;
QString m_menuGroup;
bool m_copy;
LtsSession::LtsSession *m_session;
FrequencyPlanningDialogModel *m_model;
FrequencyPlanningDialogProxyModel *m_proxyModel;
LtsUtils::RequestHelper *m_requestHelper;
ActiveFrequencyPlanning m_activePlanning;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<Interface::Ftc> m_avmFtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
ExerciseManagement::Interface::Dtc *m_exmDtc;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
QMap<int, EditFrequencyPlanningDialog*> m_editDlgMap;
int m_activeFrequencyPlanningStatus;
LOGGER_T m_logger;
}; // class FrequencyPlanningDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FrequencyPlanningDialog</class>
<widget class="QWidget" name="FrequencyPlanningDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>524</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="4">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>321</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbxStatus">
<property name="text">
<string>Status:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lbxActiveFrequencyPlanning">
<property name="text">
<string>Active Frequency Planning:</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="5">
<widget class="LtsDataView::LtsDataView" name="dataView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0" colspan="5">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="lblStatus">
<property name="text">
<string notr="true">[STATUS]</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblStatusTime">
<property name="text">
<string notr="true">[TIME]</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLabel" name="lbActiveFrequencyPlanning">
<property name="text">
<string notr="true">&lt;ACTIVE PLANNING&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,204 @@
/**
* @RDE_HEADER@
* The FrequencyPlanningDialogModel offers data for the FrequencyPlanningDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_frequency_planning_dialog_model.h"
#include <FileManagement/flm_translation.h>
#include <ngltools/ngltools_dtg.h>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
namespace AudioVideoManagement
{
FrequencyPlanningDialogModel::FrequencyPlanningDialogModel(int32_t exerciseId, QObject *parent)
: LtsAbstractTableModel<FrequencyPlanning>(
{
tr("Name"),
tr("Date"),
tr("Set"),
tr("Active")
}, parent)
, m_exerciseId(exerciseId)
, m_logger(getLogger("FrequencyPlanningDialogModel"))
{
DEBUG_LOG(m_logger) << "---FrequencyPlanningDialogModel() ";
}
QVariant FrequencyPlanningDialogModel::data(const QModelIndex &index, int role) const
{
if ((role != Qt::DisplayRole && role != Qt::UserRole) || !indexIsValid(index))
return QVariant();
const FrequencyPlanning &planning = m_vecData.at(index.row());
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case NAME:
return QString::fromStdString(planning.name());
case DATE:
return QDateTime::fromMSecsSinceEpoch(planning.date());
case SET:
{
const FrequencySet frequencySet = m_avmDtc->frequencySet(planning.frequencySetId());
return QString::fromStdString(frequencySet.name());
}
case ACTIVE:
return planning.id() == m_activePlanningId ? tr("Active") : QStringLiteral("-");
}
}
else if (role == Qt::UserRole)
return QVariant::fromValue(planning);
return QVariant();
}
bool FrequencyPlanningDialogModel::init(const std::shared_ptr<Interface::Dtc> &avmDtc)
{
m_avmDtc = avmDtc;
m_frequencySets = m_avmDtc->frequencySets();
connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::frequencyPlanningChanged,
this, &FrequencyPlanningDialogModel::onFrequencyPlanningChanged);
m_avmDtc->frequencyPlannings([this](const FrequencyPlanning & p)
{
if (p.exerciseId().is_set() && p.exerciseId().get() == m_exerciseId)
insertRow(p);
});
return true;
}
FrequencySet FrequencyPlanningDialogModel::frequencySetByIndex(const QModelIndex &index) const
{
if (!indexIsValid(index))
return FrequencySet();
return m_frequencySets.value(m_vecData.at(index.row()).frequencySetId());
}
void FrequencyPlanningDialogModel::onFrequencyPlanningChanged(const FrequencyPlanning &data, DataModification modType)
{
INFO_LOG(m_logger) << "onFrequencyPlanningChanged "
<< " data.exerciseId()" << data.exerciseId()
<< " m_exerciseId=" << m_exerciseId;
if ((data.exerciseId().is_set() && data.exerciseId().get() != m_exerciseId)
|| (!data.exerciseId().is_set() && m_exerciseId != 0))
return;
onDataChanged(data, modType);
}
QByteArray FrequencyPlanningDialogModel::modelToJson()
{
QJsonObject obj;
QJsonArray arr;
QJsonArray arrData;
for (int row = 0; row < rowCount(); row++)
{
DEBUG_LOG(m_logger) << "row =" << ": " << row;
QJsonObject inner;
const FrequencyPlanning &planning = m_vecData.at(row);
inner[QStringLiteral("id")] = planning.id();
inner[QStringLiteral("name")] = QString::fromStdString(planning.name());
inner[QStringLiteral("dateOfCreation")] = planning.date();
if(planning.exerciseId().is_set())
{
inner[QStringLiteral("exerciseId")] = planning.exerciseId().get();
}
inner[QStringLiteral("frequencySetId")] = planning.frequencySetId();
arrData = {};
for (const FrequencyPlanningData &planningData : planning.data())
{
QJsonObject innerData;
innerData[QStringLiteral("cnrConfigId")] = planningData.cnrConfigId();
innerData[QStringLiteral("properties")] = QString::fromStdString(planningData.properties());
arrData += innerData;
}
inner[QStringLiteral("data")] = arrData;
arr += inner;
}
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::AVMFrequencyPlanning);
obj[QStringLiteral("FrequencyPlanning")] = arr;
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
QVector<ModifyFrequencyPlanningRequest> FrequencyPlanningDialogModel::jsonToFrequencyPlanningVector(const QByteArray &content)
{
QVector<ModifyFrequencyPlanningRequest> result;
const QJsonDocument doc(QJsonDocument::fromJson(content));
const QJsonObject toplevelObj(doc.object());
const QJsonArray arr(toplevelObj.value(QStringLiteral("FrequencyPlanning")).toArray());
for (const auto &ele : arr)
{
const QJsonObject obj(ele.toObject());
ModifyFrequencyPlanningRequest frequencyPlanning;
frequencyPlanning.date(obj[QStringLiteral("dateOfCreation")].toDouble());
frequencyPlanning.exerciseId(m_exerciseId); // Actual exercise
frequencyPlanning.frequencySetId(obj[QStringLiteral("frequencySetId")].toInt());
frequencyPlanning.id(0); // New entry
frequencyPlanning.name(obj[QStringLiteral("name")].toString().toStdString());
//Start frequencyPlanning.data();
const QJsonArray arrData = (obj.value(QStringLiteral("data")).toArray());
frequencyPlanning.data().reserve(arrData.size());
for (const auto &eleData : arrData)
{
const QJsonObject objData(eleData.toObject());
FrequencyPlanningData frequencyPlanningData;
frequencyPlanningData.cnrConfigId(objData[QStringLiteral("cnrConfigId")].toInt());
frequencyPlanningData.properties(objData[QStringLiteral("properties")].toString().toStdString());
frequencyPlanning.data().push_back(frequencyPlanningData);
}
//End frequencyPlanning.data();
result.push_back(std::move(frequencyPlanning));
}
return result;
}
void FrequencyPlanningDialogProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<FrequencyPlanningDialogModel*>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
bool FrequencyPlanningDialogProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel ||
source_left.column() != source_right.column() ||
!source_left.isValid() ||
!source_right.isValid())
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
const auto dataRight = m_sourceModel->m_vecData.at(source_right.row());
switch (source_left.column())
{
case FrequencyPlanningDialogModel::DATE:
return dataLeft.date() < dataRight.date();
case FrequencyPlanningDialogModel::NAME:
case FrequencyPlanningDialogModel::SET:
case FrequencyPlanningDialogModel::ACTIVE:
case FrequencyPlanningDialogModel::COUNT:
break;
}
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
}
} // namespace FrequencyPlanningDialogModel
@@ -0,0 +1,78 @@
/**
* @RDE_HEADER@
* The FrequencyPlanningDialogModel offers data for the FrequencyPlanningDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <boost/optional.hpp>
#include <memory>
namespace AudioVideoManagement
{
class FrequencyPlanningDialogModel final
: public LtsWidgets::LtsAbstractTableModel<FrequencyPlanning>
{
Q_OBJECT
public:
enum Columns
{
NAME,
DATE,
SET,
ACTIVE,
COUNT
};
explicit FrequencyPlanningDialogModel(int32_t exerciseId, QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override final;
bool init(const std::shared_ptr<Interface::Dtc> &avmDtc);
void setActivePlanning(int activePlanningId) { m_activePlanningId = activePlanningId; }
void unsetActivePlanning() { m_activePlanningId.reset(); }
FrequencySet frequencySetByIndex(const QModelIndex &index) const;
QByteArray modelToJson();
QVector<ModifyFrequencyPlanningRequest> jsonToFrequencyPlanningVector(const QByteArray &content);
private Q_SLOTS:
void onFrequencyPlanningChanged(const FrequencyPlanning &data, DataModification modType);
private:
int32_t m_exerciseId;
boost::optional<int> m_activePlanningId;
std::shared_ptr<Interface::Dtc> m_avmDtc;
QMap<int32_t, FrequencySet> m_frequencySets;
friend class FrequencyPlanningDialogProxyModel;
LOGGER_T m_logger;
}; // class FrequencyPlanningDialogModel
class FrequencyPlanningDialogProxyModel final : public LtsWidgets::LtsColumnFilterProxyModel
{
Q_OBJECT
public:
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
void setSourceModel(QAbstractItemModel *sourceModel) override;
protected:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
private:
FrequencyPlanningDialogModel *m_sourceModel = nullptr;
};
} // namespace AudioVideoManagement
Q_DECLARE_METATYPE(AudioVideoManagement::FrequencyPlanning)
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* Some data structures for frequency planning
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_frequency_planning_helper.h"
#include <QObject>
namespace AudioVideoManagement
{
QString getOperatingModeText(int operatingMode)
{
switch (operatingMode)
{
case 0:
return QObject::tr("Fixed frequency, no encryption");
case 1:
return QObject::tr("Fixed frequency, encryption: HW_KR");
case 2:
return QObject::tr("Fixed frequency, encryption: HW_KR_KL");
case 3:
return QObject::tr("AKW, no encryption");
case 4:
return QObject::tr("AKW, encryption: ECCM");
default:
return QObject::tr("Unknown Operating Mode");
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,97 @@
/**
* @RDE_HEADER@
* Some data structures for frequency planning
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include "../CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h" // only for Q_DECLARE_METATYPE(AudioVideoManagement::CnrConfig)
namespace AudioVideoManagement
{
QString getOperatingModeText(int operatingMode);
} // namespace AudioVideoManagement
namespace
{
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
template<typename Key, typename T, class Iterator>
class QKeyValueIterator
{
public:
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::difference_type difference_type;
typedef std::pair<Key, T> value_type;
typedef const value_type *pointer;
typedef const value_type &reference;
QKeyValueIterator() = default;
Q_DECL_CONSTEXPR explicit QKeyValueIterator(Iterator o) Q_DECL_NOEXCEPT_EXPR(std::is_nothrow_move_constructible<Iterator>::value)
: i(std::move(o)) {}
std::pair<Key, T> operator*() const {
return std::pair<Key, T>(i.key(), i.value());
}
friend bool operator==(QKeyValueIterator lhs, QKeyValueIterator rhs) Q_DECL_NOEXCEPT { return lhs.i == rhs.i; }
friend bool operator!=(QKeyValueIterator lhs, QKeyValueIterator rhs) Q_DECL_NOEXCEPT { return lhs.i != rhs.i; }
inline QKeyValueIterator &operator++() { ++i; return *this; }
inline QKeyValueIterator operator++(int) { return QKeyValueIterator(i++); }
inline QKeyValueIterator &operator--() { --i; return *this; }
inline QKeyValueIterator operator--(int) { return QKeyValueIterator(i--); }
Iterator base() const { return i; }
private:
Iterator i;
};
#endif
/**
* Helper class in order to iterate over key-value-pairs of a QMap using
* a range-for loop. The returned iterator allows only read-only access.
* The class keeps a const reference; the user has to make sure
* that the referenced instance lives long enough.
*/
template<class Key, class T>
class OverKeyValue
{
public:
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
using const_iterator_tmp = typename QMap<Key, T>::const_iterator;
using const_key_value_iterator = QKeyValueIterator<const Key&, const T&, const_iterator_tmp>;
using const_iterator = typename OverKeyValue<Key, T>::const_key_value_iterator;
#else
using const_iterator = typename QMap<Key, T>::const_key_value_iterator;
#endif
explicit OverKeyValue(const QMap<Key, T> &map)
: m_map(map)
{}
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
const_iterator begin() const { return const_key_value_iterator(m_map.begin()); }
const_iterator end() const { return const_key_value_iterator(m_map.end()); }
#else
const_iterator begin() const { return m_map.keyValueBegin(); }
const_iterator end() const { return m_map.keyValueEnd(); }
#endif
private:
const QMap<Key, T> &m_map;
};
template<class Key, class T>
OverKeyValue<Key, T> overKeyValue(const QMap<Key, T> &map)
{
return OverKeyValue<Key, T>(map);
}
} // anonymous namespace
@@ -0,0 +1,246 @@
/**
* @RDE_HEADER@
* The FrequencyPlanningDialog contains the controls for the editFrequencyPlanningResourceDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Verena Eberlein, SPMO, 2023-09-21
*/
#include "avm_frequency_planning_widget.h"
#include <LtsWidgets/lts_spinbox.h>
#include "ui_avm_frequency_planning_widget.h"
#include "avm_edit_frequency_planning_set_resource_dialog.h"
#include "avm_radio_specifica_hf_widget.h"
#include "avm_radio_specifica_vhf_widget.h"
#include "avm_radio_specifica_uhf_widget.h"
#include <AudioVideoManagement/avm_translation.h>
#include <InfrastructureManagement/Gui/IsmComboBox/ism_combobox_gui_plugin.h>
#include <QJsonArray>
namespace AudioVideoManagement
{
FrequencyPlanningWidget::FrequencyPlanningWidget(const std::shared_ptr<Interface::Dtc> &avmDtc, QSet<int> &allocatedDataSets, QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::FrequencyPlanningWidget)
, m_assignedFrequencySetDataIds(allocatedDataSets)
, m_avmDtc(avmDtc)
, m_logger(getLogger("EditFrequencyPlanningResourceDialog"))
{
m_ui->setupUi(this);
m_ui->cbFrequencyBand->addItem(toString(FrequencyBand_def::VHF), FrequencyBand_def::VHF);
m_ui->cbFrequencyBand->addItem(toString(FrequencyBand_def::HF), FrequencyBand_def::HF);
m_ui->cbFrequencyBand->addItem(toString(FrequencyBand_def::UHF), FrequencyBand_def::UHF);
connect(m_ui->leName, &QLineEdit::textChanged,
this, &FrequencyPlanningWidget::onNameChanged);
connect(m_ui->cbFrequencyBand, qOverload<int>(&QComboBox::currentIndexChanged),
this, &FrequencyPlanningWidget::onFrequencyBandChanged);
connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::cnrConfigChanged,
this, &FrequencyPlanningWidget::onCnrConfigChanged);
}
bool FrequencyPlanningWidget::init(LtsSession::LtsSession *session, RadioCircuit *data, const CnrConfig &cnrConfigId, int frequencySetId, FrequencyBand ft)
{
m_frequencySetId = frequencySetId;
m_cnrConfigId = cnrConfigId.id();
m_hasParty = cnrConfigId.partyId() != 0;
if (!session->getForeignGuiInterface<InfrastructureManagement::ForeignGuiComboboxInterface>(m_ismCbxIfc))
return false;
m_ui->cbFrequencyBand->setCurrentIndex(m_ui->cbFrequencyBand->findData(ft.underlying()));
m_ui->cbFrequencyBand->setVisible(false);
m_ui->lbxFrequencyBand->setVisible(false);
switch (ft.underlying())
{
// Mehrere OperationModes nur fuer Devices 'VHF' oder 'REL'/'R01'/'R02' moeglich
case FrequencyBand_def::HF:
{
createHFWidget(data);
break;
}
case FrequencyBand_def::UHF:
{
createUHFWidget(data);
break;
}
case FrequencyBand_def::VHF:
{
createVHFWidget(data);
break;
}
default: //FrequencyBand_def::REL
{
ft = data ? data->freqBand : FrequencyBand::VHF;
m_isRelay = true;
m_ui->cbFrequencyBand->setCurrentIndex(m_ui->cbFrequencyBand->findData(ft.underlying()));
m_ui->cbFrequencyBand->setVisible(true);
m_ui->lbxFrequencyBand->setVisible(true);
switch (ft.underlying())
{
case FrequencyBand_def::HF:
createHFWidget(data);
break;
case FrequencyBand_def::UHF:
createUHFWidget(data);
break;
case FrequencyBand_def::VHF:
createVHFWidget(data);
break;
default:
break;
break;
}
}
}
m_ismCbxIfc->fillRadioStationsComboBox(m_ui->cbRadioStation, ft.underlying());
if (m_hasParty && !m_isRelay)
m_ui->cbRadioStation->addItem(tr("Automatic"), 0);
if (!data)
m_ui->leName->clear();
else
{
m_ui->leName->setText(data->name);
m_ui->cbRadioStation->setCurrentIndex(m_ui->cbRadioStation->findData(data->radioStationId));
}
adjustSize();
return true;
}
void FrequencyPlanningWidget::setNameDisabled(bool disable)
{
m_ui->leName->setDisabled(disable);
}
void FrequencyPlanningWidget::setName(const QString &name)
{
m_ui->leName->setText(name);
}
// Aktuell sollen bei Relais nur VHF-Kanäle möglich sein!
void FrequencyPlanningWidget::setRelaisRestrictions(bool restrict)
{
if (restrict)
{
m_ui->cbFrequencyBand->setCurrentIndex(m_ui->cbFrequencyBand->findData(FrequencyBand_def::VHF));
m_ui->cbFrequencyBand->setEnabled(false);
}
else
{
m_ui->cbFrequencyBand->setEnabled(true);
}
}
QJsonObject FrequencyPlanningWidget::toJson() const
{
return frequencyPlanningJson(m_radioSpecificaWidget->toJson(),
m_ui->leName->text(),
static_cast<FrequencyBand::inner_enum>(m_ui->cbFrequencyBand->currentData().toInt()),
m_ui->cbRadioStation->currentData().toInt());
}
QVector<QPair<QLabel *, const std::function<bool ()> > > FrequencyPlanningWidget::requirements() const
{
return m_radioSpecificaWidget->requirements();
}
FrequencyPlanningWidget::~FrequencyPlanningWidget()
{
if(m_radioSpecificaWidget)
{
delete m_radioSpecificaWidget;
m_radioSpecificaWidget = nullptr;
}
delete m_ui;
}
void FrequencyPlanningWidget::onFrequencyBandChanged()
{
switch (m_ui->cbFrequencyBand->currentData().toInt())
{
case FrequencyBand_def::HF:
createHFWidget();
break;
case FrequencyBand_def::UHF:
createUHFWidget();
break;
case FrequencyBand_def::VHF:
createVHFWidget();
break;
}
m_ui->cbRadioStation->clear();
m_ismCbxIfc->fillRadioStationsComboBox(m_ui->cbRadioStation, m_ui->cbFrequencyBand->currentData().toInt());
if(m_hasParty && !m_isRelay)
m_ui->cbRadioStation->addItem(tr("Automatic"), 0);
if(m_radioSpecificaWidget)
m_ui->gridLayout->addWidget(m_radioSpecificaWidget,5,0,1,2);
adjustSize();
}
void FrequencyPlanningWidget::createHFWidget(RadioCircuit* circuit)
{
if(m_radioSpecificaWidget)
{
delete m_radioSpecificaWidget;
m_radioSpecificaWidget = nullptr;
}
m_radioSpecificaWidget = new RadioSpecificaHfWidget(m_ui->radioSpecificaWidget);
if(!circuit)
return;
RadioCircuitHF *dataHf = dynamic_cast<RadioCircuitHF*>(circuit);
m_radioSpecificaWidget->setValues(dataHf);
}
void FrequencyPlanningWidget::createUHFWidget(RadioCircuit* circuit)
{
if(m_radioSpecificaWidget)
{
delete m_radioSpecificaWidget;
m_radioSpecificaWidget = nullptr;
}
m_radioSpecificaWidget = new RadioSpecificaUhfWidget(m_ui->radioSpecificaWidget);
if(!circuit)
return;
RadioCircuitUHF* dataUhf = dynamic_cast<RadioCircuitUHF*>(circuit);
m_radioSpecificaWidget->setValues(dataUhf);
}
void FrequencyPlanningWidget::createVHFWidget(RadioCircuit* circuit)
{
if(m_radioSpecificaWidget)
{
delete m_radioSpecificaWidget;
m_radioSpecificaWidget = nullptr;
}
auto *widget = new RadioSpecificaVhfWidget(m_assignedFrequencySetDataIds, m_ui->radioSpecificaWidget);
widget->init(m_frequencySetId, m_avmDtc, m_cnrConfigId);
m_radioSpecificaWidget = widget;
if(!circuit)
return;
RadioCircuitVHF* dataVhf = dynamic_cast<RadioCircuitVHF*>(circuit);
m_radioSpecificaWidget->setValues(dataVhf);
}
void FrequencyPlanningWidget::onCnrConfigChanged(const AudioVideoManagement::CnrConfig &config, DataModification modType)
{
Q_UNUSED(modType);
if (m_cnrConfigId == config.id())
{
m_ui->leName->setText(QString::fromStdString(config.radioCircuit()));
}
}
} // namespace LtsWidgets
@@ -0,0 +1,90 @@
/**
* @RDE_HEADER@
* The FrequencyPlanningWidget contains the controls for the editFrequencyPlanningResourceDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Verena Eberlein, SPMO, 2023-09-21
*/
#pragma once
#include <QWidget>
#include <AudioVideoManagement/avm.hpp>
#include <AudioVideoManagement/avm_dtc.h>
#include <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include <AudioVideoManagement/logger.h>
class QLabel;
namespace Ui
{
class FrequencyPlanningWidget;
}
namespace InfrastructureManagement
{
class ForeignGuiComboboxInterface;
}
namespace LtsSession
{
class LtsSession;
}
namespace LtsWidgets
{
class LtsSpinBox;
class LtsComboBox;
}
namespace AudioVideoManagement
{
class RadioSpecificaWidget;
class FrequencyPlanningWidget : public QWidget
{
Q_OBJECT
public:
FrequencyPlanningWidget(const std::shared_ptr<Interface::Dtc> &avmDtc, QSet<int> &allocatedDataSets, QWidget *parent = nullptr);
~FrequencyPlanningWidget();
bool init(LtsSession::LtsSession *session, RadioCircuit *data, const CnrConfig &cnrConfigId, int frequencySetId = -1, FrequencyBand ft = FrequencyBand_def::VHF);
void setNameDisabled(bool disable);
void setName(const QString& name);
void setRelaisRestrictions(bool restrict);
QJsonObject toJson() const;
QVector< QPair<QLabel*, const std::function<bool()> > > requirements() const;
signals:
void onNameChanged(const QString&);
private slots:
void onFrequencyBandChanged();
void onCnrConfigChanged(const AudioVideoManagement::CnrConfig &config, DataModification modType);
private:
Ui::FrequencyPlanningWidget *m_ui {nullptr};
RadioSpecificaWidget *m_radioSpecificaWidget {nullptr};
int m_frequencySetId {0};
int m_cnrConfigId {0};
QSet<int> &m_assignedFrequencySetDataIds;
std::shared_ptr<InfrastructureManagement::ForeignGuiComboboxInterface> m_ismCbxIfc;
bool m_hasParty;
bool m_isRelay {false};
std::shared_ptr<Interface::Dtc> m_avmDtc;
LOGGER_T m_logger;
void createHFWidget(RadioCircuit* circuit = nullptr);
void createUHFWidget(RadioCircuit* circuit = nullptr);
void createVHFWidget(RadioCircuit* circuit = nullptr);
};
} // namespace AudioVideoManagement
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FrequencyPlanningWidget</class>
<widget class="QWidget" name="FrequencyPlanningWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>561</width>
<height>526</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="lbxName">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leName"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbxFrequencyBand">
<property name="text">
<string>Frequenzband:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cbFrequencyBand"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbxRadioStation">
<property name="text">
<string>Funkstation:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="LtsWidgets::LtsComboBox" name="cbRadioStation"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QWidget" name="radioSpecificaWidget" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,77 @@
/**
* @RDE_HEADER@
* InitFrequencyPlanningDialog - Dialog to get initial data for a FrequencyPlanning
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#include "avm_init_frequency_planning_dialog.h"
#include "ui_avm_init_frequency_planning_dialog.h"
#include <LtsSession/lts_session.h>
#include <ngltools/ngltools_dtg.h>
namespace AudioVideoManagement
{
InitFrequencyPlanningDialog::InitFrequencyPlanningDialog(LtsSession::LtsSession *session, const std::shared_ptr<Interface::Dtc> &avmDtc, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkCancel)
, m_ui(new Ui::InitFrequencyPlanningDialog)
, m_dateOfCreation(session->partitionTime())
, m_session(session)
, m_logger(getLogger("InitFrequencyPlanningDialog"))
{
DEBUG_LOG(m_logger) << "---InitFrequencyPlanningDialog() ";
m_ui->setupUi(this);
QMap<int32_t, FrequencySet> frequencySets = avmDtc->frequencySets();
for (auto it = frequencySets.constBegin(); it != frequencySets.constEnd(); ++it)
m_ui->cbFrequencySets->addItem(QString::fromStdString(it.value().name()), QVariant::fromValue(it.key()));
m_ui->tbTime->setText(DTG::toStringMSec(m_dateOfCreation));
connect(m_ui->tbTime, &QToolButton::clicked, this, [this]()
{
m_dateOfCreation = m_session->partitionTime();
m_ui->tbTime->setText(DTG::toStringMSec(m_dateOfCreation));
});
m_uiRequirements->addRequirement( { m_ui->lblName, [this]() { return !m_ui->leName->text().isEmpty(); } });
m_uiRequirements->addRequirement( { m_ui->lblFrequencySets, [this]() { return !m_ui->cbFrequencySets->currentText().isEmpty(); } });
setOnlineHelpFilename(QStringLiteral("Situation-Frequency_Planning"));
}
InitFrequencyPlanningDialog::~InitFrequencyPlanningDialog()
{
delete m_ui;
}
void InitFrequencyPlanningDialog::retranslate()
{
translate(m_ui, tr("New Frequency Planning"));
}
bool InitFrequencyPlanningDialog::apply()
{
FrequencyPlanning planning;
planning.id(0);
if(m_session->exerciseId() != 0)
planning.exerciseId(m_session->exerciseId());
planning.frequencySetId(m_ui->cbFrequencySets->currentData().toInt());
planning.name(m_ui->leName->text().toStdString());
planning.date(m_dateOfCreation);
emit newInitFrequencyPlanning(planning);
return true;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,52 @@
/**
* @RDE_HEADER@
* InitFrequencyPlanningDialog - Dialog to get initial data for a FrequencyPlanning
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-09-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
class QCheckBox;
namespace Ui
{
class InitFrequencyPlanningDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
class InitFrequencyPlanningDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit InitFrequencyPlanningDialog(LtsSession::LtsSession *session, const std::shared_ptr<Interface::Dtc> &avmDtc, QWidget *parent);
~InitFrequencyPlanningDialog();
signals:
void newInitFrequencyPlanning(const FrequencyPlanning &);
private:
void retranslate() override;
bool apply() override;
Ui::InitFrequencyPlanningDialog *m_ui;
qint64 m_dateOfCreation;
LtsSession::LtsSession *m_session;
LOGGER_T m_logger;
}; // class InitFrequencyPlanningDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InitFrequencyPlanningDialog</class>
<widget class="QWidget" name="InitFrequencyPlanningDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>427</width>
<height>228</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Asset Stocks</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="lblName">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leName">
<property name="toolTip">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Date of Creation:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="tbTime">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;TIME&gt;</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblFrequencySets">
<property name="text">
<string>Frequency Set:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="LtsWidgets::LtsComboBox" name="cbFrequencySets"/>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,66 @@
/**
* @RDE_HEADER@
* The LoadFrequencyPlanningDialog allows to solve clonflicts for loaded FrequencyPlannings
* $Id: avm_edit_combat_net_radio_dialog.h 60982 2023-12-01 10:18:25Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2024-02-06
*/
#include "avm_load_frequency_planning_dialog.h"
#include "ui_avm_load_frequency_planning_dialog.h"
#include "avm_load_frequency_planning_dialog_model.h"
#include <QTreeView>
namespace AudioVideoManagement
{
LoadFrequencyPlanningDialog::LoadFrequencyPlanningDialog(const QByteArray &data, const FrequencyPlanning &initPlan, const std::shared_ptr<Interface::Dtc> &avmDtc, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, QWidget *parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_ui(new Ui::LoadFrequencyPlanningDialog)
, m_model(new LoadFrequencyPlanningDialogModel(initPlan, avmDtc, ismDtc, this))
, m_logger(getLogger("LoadFrequencyPlanningDialog"))
{
m_ui->setupUi(this);
DrawDelegate *treeDelegate = new DrawDelegate(m_model);
m_ui->dataView->treeView()->setItemDelegateForColumn(LoadFrequencyPlanningDialogModel::Columns::PROPERTIES, treeDelegate);
m_model->setData(data);
m_ui->dataView->setModel(m_model);
connect(m_ui->pbOk, &QPushButton::clicked,
this, &LoadFrequencyPlanningDialog::onOk);
connect(m_ui->pbCancel, &QPushButton::clicked,
this, &LoadFrequencyPlanningDialog::close);
connect(m_ui->cbAll, &QCheckBox::clicked,
m_model, &LoadFrequencyPlanningDialogModel::checkAll);
m_ui->dataView->expandAll();
}
QVector<int32_t> LoadFrequencyPlanningDialog::unassignments() const
{
return m_model->unassignments();
}
QVector<FrequencyPlanningData> LoadFrequencyPlanningDialog::newPlanningData() const
{
return m_model->planningData();
}
bool LoadFrequencyPlanningDialog::isApplied() const
{
return m_isApplied;
}
void LoadFrequencyPlanningDialog::onOk()
{
m_isApplied = true;
close();
}
LoadFrequencyPlanningDialog::~LoadFrequencyPlanningDialog() = default;
} // namespace AudioVideoManagement
Loaded 100 of 16245 files, more files were not shown because too many files have changed in this diff. Show more