Skip to content

Commit

Permalink
feat: send chat area message for release requested
Browse files Browse the repository at this point in the history
  • Loading branch information
AndyTWF committed May 24, 2023
1 parent d591be3 commit 5d790d7
Show file tree
Hide file tree
Showing 16 changed files with 346 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/plugin/CMakeLists.txt
Expand Up @@ -814,7 +814,7 @@ set(src__releases
releases/EnrouteRelease.cpp releases/EnrouteReleaseType.cpp
releases/RejectDepartureReleaseDialog.cpp releases/RejectDepartureReleaseDialog.h
releases/ReleaseApprovalRemarksUserMessage.cpp releases/ReleaseApprovalRemarksUserMessage.h
releases/ReleaseRejectionRemarksUserMessage.cpp releases/ReleaseRejectionRemarksUserMessage.h)
releases/ReleaseRejectionRemarksUserMessage.cpp releases/ReleaseRejectionRemarksUserMessage.h releases/DepartureReleaseRequestedEvent.h releases/SendReleaseRequestedChatAreaMessage.cpp releases/SendReleaseRequestedChatAreaMessage.h releases/DepartureReleaseRelevanceChecker.h releases/ReleaseIsTargetedAtUser.cpp releases/ReleaseIsTargetedAtUser.h)
source_group("src\\releases" FILES ${src__releases})

set(src__runway
Expand Down
3 changes: 3 additions & 0 deletions src/plugin/euroscope/GeneralSettingsEntries.cpp
Expand Up @@ -10,6 +10,9 @@ namespace UKControllerPlugin {
const std::string GeneralSettingsEntries::prenoteChatAreaMessagesSettingsKey = "sendPrenotesToChat";
const std::string GeneralSettingsEntries::prenoteChatAreaMessagesSettingsDescription =
"Show prenote activity in chat area";
const std::string GeneralSettingsEntries::releaseChatAreaMessagesSettingsKey = "sendReleasesToChat";
const std::string GeneralSettingsEntries::releaseChatAreaMessagesSettingsDescription =
"Show release activity in chat area";

// SQUAWKS
const std::string GeneralSettingsEntries::squawkToggleSettingsKey = "autoAssignSquawks";
Expand Down
2 changes: 2 additions & 0 deletions src/plugin/euroscope/GeneralSettingsEntries.h
Expand Up @@ -13,6 +13,8 @@ namespace UKControllerPlugin {
static const std::string usePrenoteSettingsDescription;
static const std::string prenoteChatAreaMessagesSettingsKey;
static const std::string prenoteChatAreaMessagesSettingsDescription;
static const std::string releaseChatAreaMessagesSettingsKey;
static const std::string releaseChatAreaMessagesSettingsDescription;

// SQUAWKS
static const std::string squawkToggleSettingsKey;
Expand Down
11 changes: 9 additions & 2 deletions src/plugin/releases/DepartureReleaseEventHandler.cpp
Expand Up @@ -2,6 +2,7 @@
#include "DepartureReleaseCountdownColours.h"
#include "DepartureReleaseEventHandler.h"
#include "DepartureReleaseRequest.h"
#include "DepartureReleaseRequestedEvent.h"
#include "DepartureReleaseRequestView.h"
#include "ReleaseApprovalRemarksUserMessage.h"
#include "ReleaseRejectionRemarksUserMessage.h"
Expand All @@ -15,6 +16,7 @@
#include "dialog/DialogManager.h"
#include "euroscope/EuroScopeCFlightPlanInterface.h"
#include "euroscope/EuroscopePluginLoopbackInterface.h"
#include "eventhandler/EventBus.h"
#include "message/UserMessager.h"
#include "tag/TagData.h"
#include "task/TaskRunnerInterface.h"
Expand Down Expand Up @@ -524,19 +526,24 @@ namespace UKControllerPlugin::Releases {
int releaseRequestId = data.at("id").get<int>();
int targetController = data.at("target_controller").get<int>();
auto callsign = data.at("callsign").get<std::string>();
this->releaseRequests->Add(std::make_shared<DepartureReleaseRequest>(
auto request = std::make_shared<DepartureReleaseRequest>(
releaseRequestId,
callsign,
data.at("requesting_controller").get<int>(),
targetController,
Time::ParseTimeString(data.at("expires_at").get<std::string>())));
Time::ParseTimeString(data.at("expires_at").get<std::string>()));
this->releaseRequests->Add(request);

// Remove any others for the same callsign and controller
this->releaseRequests->RemoveWhere([callsign, targetController, releaseRequestId](const auto& releaseRequest) {
return releaseRequest->Callsign() == callsign && releaseRequest->TargetController() == targetController &&
releaseRequest->Id() != releaseRequestId;
});

// Trigger an event
LogDebug("Triggering departure release requested event");
UKControllerPluginUtils::EventHandler::EventBus::Bus().OnEvent(DepartureReleaseRequestedEvent{request});

// Play a sound to alert the controller if we are the target
if (this->activeCallsigns.UserHasCallsign() &&
this->activeCallsigns.GetUserCallsign().GetNormalisedPosition().GetId() == targetController) {
Expand Down
16 changes: 16 additions & 0 deletions src/plugin/releases/DepartureReleaseRelevanceChecker.h
@@ -0,0 +1,16 @@
#pragma once

namespace UKControllerPlugin::Releases {

class DepartureReleaseRequest;

/**
* Checks if a release request is relevant to the current user.
*/
class DepartureReleaseRelevanceChecker
{
public:
virtual ~DepartureReleaseRelevanceChecker() = default;
[[nodiscard]] virtual bool IsRelevant(const DepartureReleaseRequest& releaseRequest) const = 0;
};
} // namespace UKControllerPlugin::Releases
11 changes: 11 additions & 0 deletions src/plugin/releases/DepartureReleaseRequestedEvent.h
@@ -0,0 +1,11 @@
#pragma once

namespace UKControllerPlugin::Releases {

class DepartureReleaseRequest;

using DepartureReleaseRequestedEvent = struct DepartureReleaseRequestedEvent
{
std::shared_ptr<DepartureReleaseRequest> releaseRequest;
};
} // namespace UKControllerPlugin::Releases
25 changes: 25 additions & 0 deletions src/plugin/releases/ReleaseIsTargetedAtUser.cpp
@@ -0,0 +1,25 @@
#include "ReleaseIsTargetedAtUser.h"
#include "controller/ActiveCallsignCollection.h"
#include "controller/ActiveCallsign.h"
#include "controller/ControllerPosition.h"
#include "releases/DepartureReleaseRequest.h"

namespace UKControllerPlugin::Releases {

ReleaseIsTargetedAtUser::ReleaseIsTargetedAtUser(
std::shared_ptr<const Controller::ActiveCallsignCollection> activeCallsigns)
: activeCallsigns(std::move(activeCallsigns))
{
assert(this->activeCallsigns && "activeCallsigns must not be null");
}

/**
* Check that the user has a callsign, and that the callsign has the same normalised controller position
* as the target of the release request.
*/
bool ReleaseIsTargetedAtUser::IsRelevant(const DepartureReleaseRequest& releaseRequest) const
{
return activeCallsigns->UserHasCallsign() &&
activeCallsigns->GetUserCallsign().GetNormalisedPosition().GetId() == releaseRequest.TargetController();
}
} // namespace UKControllerPlugin::Releases
21 changes: 21 additions & 0 deletions src/plugin/releases/ReleaseIsTargetedAtUser.h
@@ -0,0 +1,21 @@
#pragma once
#include "releases/DepartureReleaseRelevanceChecker.h"

namespace UKControllerPlugin::Controller {
class ActiveCallsignCollection;
} // namespace UKControllerPlugin::Controller

namespace UKControllerPlugin::Releases {

class ReleaseIsTargetedAtUser : public DepartureReleaseRelevanceChecker
{
public:
explicit ReleaseIsTargetedAtUser(std::shared_ptr<const Controller::ActiveCallsignCollection> activeCallsigns);
[[nodiscard]] bool IsRelevant(const DepartureReleaseRequest& releaseRequest) const override;

private:
// The active callsigns
const std::shared_ptr<const Controller::ActiveCallsignCollection> activeCallsigns;
};

} // namespace UKControllerPlugin::Releases
12 changes: 12 additions & 0 deletions src/plugin/releases/ReleaseModule.cpp
Expand Up @@ -2,18 +2,23 @@
#include "CompareEnrouteReleaseTypes.h"
#include "DepartureReleaseEventHandler.h"
#include "DepartureReleaseRequestView.h"
#include "DepartureReleaseRequestedEvent.h"
#include "EnrouteReleaseEventHandler.h"
#include "EnrouteReleaseTypesSerializer.h"
#include "RejectDepartureReleaseDialog.h"
#include "ReleaseIsTargetedAtUser.h"
#include "ReleaseModule.h"
#include "RequestDepartureReleaseDialog.h"
#include "SendReleaseRequestedChatAreaMessage.h"
#include "bootstrap/PersistenceContainer.h"
#include "collection/Collection.h"
#include "controller/HandoffEventHandlerCollection.h"
#include "dependency/DependencyLoaderInterface.h"
#include "dialog/DialogManager.h"
#include "euroscope/CallbackFunction.h"
#include "euroscope/EuroscopePluginLoopbackInterface.h"
#include "eventhandler/EventBus.h"
#include "eventhandler/EventHandlerFlags.h"
#include "plugin/FunctionCallEventHandler.h"
#include "plugin/UKPlugin.h"
#include "push/PushEventProcessorCollection.h"
Expand Down Expand Up @@ -229,6 +234,13 @@ namespace UKControllerPlugin::Releases {

// Add to handlers
container.timedHandler->RegisterEvent(departureHandler, departureReleaseEventFrequency);

// Event handlers for displaying chat area messages
auto releaseIsTargetedAtUser = std::make_shared<ReleaseIsTargetedAtUser>(container.activeCallsigns);
UKControllerPluginUtils::EventHandler::EventBus::Bus().AddHandler<DepartureReleaseRequestedEvent>(
std::make_shared<SendReleaseRequestedChatAreaMessage>(
releaseIsTargetedAtUser, *container.plugin, *container.pluginUserSettingHandler),
UKControllerPluginUtils::EventHandler::EventHandlerFlags::Sync);
}

void
Expand Down
42 changes: 42 additions & 0 deletions src/plugin/releases/SendReleaseRequestedChatAreaMessage.cpp
@@ -0,0 +1,42 @@
#include "DepartureReleaseRelevanceChecker.h"
#include "DepartureReleaseRequest.h"
#include "DepartureReleaseRequestedEvent.h"
#include "SendReleaseRequestedChatAreaMessage.h"
#include "euroscope/EuroscopePluginLoopbackInterface.h"
#include "euroscope/GeneralSettingsEntries.h"
#include "euroscope/UserSetting.h"

namespace UKControllerPlugin::Releases {

SendReleaseRequestedChatAreaMessage::SendReleaseRequestedChatAreaMessage(
std::shared_ptr<const DepartureReleaseRelevanceChecker> relevanceChecker,
Euroscope::EuroscopePluginLoopbackInterface& plugin,
Euroscope::UserSetting& userSettings)
: relevanceChecker(std::move(relevanceChecker)), plugin(plugin), userSettings(userSettings)
{
assert(this->relevanceChecker && "relevanceChecker must not be null");
}

void SendReleaseRequestedChatAreaMessage::OnEvent(const DepartureReleaseRequestedEvent& event)
{
if (!this->relevanceChecker->IsRelevant(*event.releaseRequest)) {
LogDebug("Not sending release requested chat area message because it is not relevant");
return;
}

if (!userSettings.GetBooleanEntry(Euroscope::GeneralSettingsEntries::releaseChatAreaMessagesSettingsKey)) {
LogDebug("Not sending release requested chat area message because user does not want chat area messages");
return;
}

plugin.ChatAreaMessage(
"UKCP_COORDINATION",
"UKCP",
"Departure release requested for " + event.releaseRequest->Callsign() + ".",
true,
true,
true,
true,
true);
}
} // namespace UKControllerPlugin::Releases
35 changes: 35 additions & 0 deletions src/plugin/releases/SendReleaseRequestedChatAreaMessage.h
@@ -0,0 +1,35 @@
#pragma once
#include "eventhandler/EventHandler.h"

namespace UKControllerPlugin::Euroscope {
class EuroscopePluginLoopbackInterface;
class UserSetting;
} // namespace UKControllerPlugin::Euroscope

namespace UKControllerPlugin::Releases {

class DepartureReleaseRelevanceChecker;
struct DepartureReleaseRequestedEvent;

class SendReleaseRequestedChatAreaMessage
: public UKControllerPluginUtils::EventHandler::EventHandler<DepartureReleaseRequestedEvent>
{
public:
SendReleaseRequestedChatAreaMessage(
std::shared_ptr<const DepartureReleaseRelevanceChecker> relevanceChecker,
Euroscope::EuroscopePluginLoopbackInterface& plugin,
Euroscope::UserSetting& userSettings);
void OnEvent(const DepartureReleaseRequestedEvent& event) override;

private:
// Checks prenote relevance
const std::shared_ptr<const DepartureReleaseRelevanceChecker> relevanceChecker;

// For sending messages
Euroscope::EuroscopePluginLoopbackInterface& plugin;

// For checking user settings
Euroscope::UserSetting& userSettings;
};

} // namespace UKControllerPlugin::Releases
2 changes: 1 addition & 1 deletion test/plugin/CMakeLists.txt
Expand Up @@ -508,7 +508,7 @@ set(test__releases
"releases/EnrouteReleaseTypesSerializerTest.cpp"
"releases/ReleaseModuleTest.cpp"
releases/ReleaseApprovalRemarksUserMessageTest.cpp
releases/ReleaseRejectionRemarksUserMessageTest.cpp)
releases/ReleaseRejectionRemarksUserMessageTest.cpp releases/ReleaseIsTargetedAtUserTest.cpp releases/SendReleaseRequestedChatAreaMessageTest.cpp)
source_group("test\\releases" FILES ${test__releases})

set(test__runway
Expand Down
8 changes: 7 additions & 1 deletion test/plugin/releases/DepartureReleaseEventHandlerTest.cpp
Expand Up @@ -8,10 +8,12 @@
#include "releases/DepartureReleaseColours.h"
#include "releases/DepartureReleaseEventHandler.h"
#include "releases/DepartureReleaseRequest.h"
#include "releases/DepartureReleaseRequestedEvent.h"
#include "releases/DepartureReleaseRequestView.h"
#include "time/ParseTimeStrings.h"
#include "time/SystemClock.h"
#include "tag/TagData.h"
#include "test/EventBusTestCase.h"

using testing::_;
using testing::NiceMock;
Expand All @@ -30,7 +32,7 @@ using UKControllerPlugin::Time::TimeNow;

namespace UKControllerPluginTest::Releases {

class DepartureReleaseEventHandlerTest : public Test
class DepartureReleaseEventHandlerTest : public UKControllerPluginUtilsTest::EventBusTestCase
{
public:
DepartureReleaseEventHandlerTest()
Expand Down Expand Up @@ -778,6 +780,10 @@ namespace UKControllerPluginTest::Releases {
EXPECT_EQ(2, release->RequestingController());
EXPECT_EQ(3, release->TargetController());
EXPECT_EQ(ParseTimeString("2021-05-12 19:55:00"), release->RequestExpiryTime());

AssertSingleEventDispatched();
AssertFirstEventDispatched<UKControllerPlugin::Releases::DepartureReleaseRequestedEvent>(
[release](const auto& event) { EXPECT_EQ(release, event.releaseRequest); });
}

TEST_F(DepartureReleaseEventHandlerTest, ItDoesntPlaySoundOnRequestIfUserNotActive)
Expand Down
54 changes: 54 additions & 0 deletions test/plugin/releases/ReleaseIsTargetedAtUserTest.cpp
@@ -0,0 +1,54 @@
#include "controller/ActiveCallsign.h"
#include "controller/ActiveCallsignCollection.h"
#include "controller/ControllerPosition.h"
#include "releases/DepartureReleaseRequest.h"
#include "releases/ReleaseIsTargetedAtUser.h"

namespace UKControllerPluginTest::Releases {
class ReleaseIsTargetedAtUserTest : public ::testing::Test
{
protected:
ReleaseIsTargetedAtUserTest()
: position1(std::make_shared<UKControllerPlugin::Controller::ControllerPosition>(
1, "EGGD_APP", 125.650, std::vector<std::string>{}, true, true)),
position2(std::make_shared<UKControllerPlugin::Controller::ControllerPosition>(
2, "EGGD_TWR", 133.850, std::vector<std::string>{}, true, true)),
userCallsign("EGGD_APP", "EGGD_APP", *position1, true),
nonUserCallsign("EGGD_TWR", "EGGD_TWR", *position2, false),
callsignCollection(std::make_shared<UKControllerPlugin::Controller::ActiveCallsignCollection>()),
releaseIsTargetedAtUser(callsignCollection)
{
callsignCollection->AddCallsign(nonUserCallsign);
}

std::shared_ptr<UKControllerPlugin::Controller::ControllerPosition> position1;
std::shared_ptr<UKControllerPlugin::Controller::ControllerPosition> position2;
UKControllerPlugin::Controller::ActiveCallsign userCallsign;
UKControllerPlugin::Controller::ActiveCallsign nonUserCallsign;
std::shared_ptr<UKControllerPlugin::Controller::ActiveCallsignCollection> callsignCollection;
UKControllerPlugin::Releases::ReleaseIsTargetedAtUser releaseIsTargetedAtUser;
};

TEST_F(ReleaseIsTargetedAtUserTest, IsRelevantReturnsTrueWhenCallsignIsUser)
{
callsignCollection->AddUserCallsign(userCallsign);
UKControllerPlugin::Releases::DepartureReleaseRequest releaseRequest(
1, "BAW555", 99, 1, std::chrono::system_clock::now());
EXPECT_TRUE(releaseIsTargetedAtUser.IsRelevant(releaseRequest));
}

TEST_F(ReleaseIsTargetedAtUserTest, IsRelevantReturnsFalseWhenCallsignIsNotUser)
{
callsignCollection->AddUserCallsign(userCallsign);
UKControllerPlugin::Releases::DepartureReleaseRequest releaseRequest(
1, "BAW555", 99, 2, std::chrono::system_clock::now());
EXPECT_FALSE(releaseIsTargetedAtUser.IsRelevant(releaseRequest));
}

TEST_F(ReleaseIsTargetedAtUserTest, IsRelevantReturnsFalseWhenUserCallsignIsNotInCollection)
{
UKControllerPlugin::Releases::DepartureReleaseRequest releaseRequest(
1, "BAW555", 99, 1, std::chrono::system_clock::now());
EXPECT_FALSE(releaseIsTargetedAtUser.IsRelevant(releaseRequest));
}
} // namespace UKControllerPluginTest::Releases

0 comments on commit 5d790d7

Please sign in to comment.