Skip to content

Commit

Permalink
Merge pull request #3 from charlestytler/v1.0.2
Browse files Browse the repository at this point in the history
v1.0.2 updates
  • Loading branch information
charlestytler authored Apr 17, 2020
2 parents c5c3601 + 15b5cd4 commit 702a564
Show file tree
Hide file tree
Showing 19 changed files with 123 additions and 53 deletions.
Binary file modified Release/com.ctytler.dcs.streamDeckPlugin
Binary file not shown.
12 changes: 9 additions & 3 deletions Sources/DcsInterface/DcsIdLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,22 @@
#include <vector>

json get_installed_modules(const std::string &dcs_install_path, const std::string &module_subdir) {
json installed_modules;
json installed_modules_and_result;
installed_modules_and_result["installed_modules"] = json::array();
installed_modules_and_result["result"] = "";
if (std::filesystem::exists(dcs_install_path + module_subdir)) {
for (const auto &dir : std::filesystem::directory_iterator(dcs_install_path + module_subdir)) {
const std::string module_abs_path = dir.path().string();
const auto mods_subdir_str_loc = module_abs_path.find(module_subdir);
installed_modules.push_back(
installed_modules_and_result["installed_modules"].push_back(
module_abs_path.substr(mods_subdir_str_loc + module_subdir.size(), module_abs_path.size()));
}
installed_modules_and_result["result"] = "success";
} else {
installed_modules_and_result["result"] =
"DCS Install path [" + dcs_install_path + module_subdir + "] not found.";
}
return installed_modules;
return installed_modules_and_result;
}

json get_clickabledata(const std::string &dcs_install_path,
Expand Down
2 changes: 1 addition & 1 deletion Sources/DcsInterface/DcsInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include "StringUtilities.h"

DcsInterface::DcsInterface(const DcsConnectionSettings &settings)
: dcs_socket_(settings.rx_port, settings.tx_port, settings.ip_address), connection_settings_(settings) {
: dcs_socket_(settings.ip_address, settings.rx_port, settings.tx_port), connection_settings_(settings) {
// Send a reset to request a resend of data in case DCS mission is already running.
send_dcs_reset_command();
}
Expand Down
29 changes: 17 additions & 12 deletions Sources/DcsInterface/DcsSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// Set default timeout for socket.
DWORD socket_timeout_ms = 100;

DcsSocket::DcsSocket(const std::string &rx_port, const std::string &tx_port, const std::string &ip_address) {
DcsSocket::DcsSocket(const std::string &ip_address, const std::string &rx_port, const std::string &tx_port) {
// Detect any missing input settings.
if (rx_port.empty() || tx_port.empty() || ip_address.empty()) {
const std::string error_msg =
Expand Down Expand Up @@ -60,37 +60,42 @@ DcsSocket::DcsSocket(const std::string &rx_port, const std::string &tx_port, con
throw std::runtime_error(error_msg);
}

// Define send destination port.
getaddrinfo(ip_address.c_str(), tx_port.c_str(), &hints, &dest_port_);
if (tx_port != "dynamic") {
// Define send destination port.
addrinfo *send_to_port;
getaddrinfo(ip_address.c_str(), tx_port.c_str(), &hints, &send_to_port);
dest_addr_ = *send_to_port->ai_addr;
dest_addr_len_ = static_cast<int>(send_to_port->ai_addrlen);
freeaddrinfo(send_to_port);
}
}

DcsSocket::~DcsSocket() {
// Delete opened socket.
freeaddrinfo(dest_port_);
closesocket(socket_id_);
WSACleanup();
}

std::stringstream DcsSocket::DcsReceive() {
// Sender address - dummy variable as it is unused outside recvfrom.
sockaddr_in sender_addr;
sockaddr sender_addr;
int sender_addr_size = sizeof(sender_addr);

// Receive next UDP message.
constexpr int MAX_UDP_MSG_SIZE = 1024; // Maximum UDP buffer size to read.
char msg[MAX_UDP_MSG_SIZE] = {};
(void)recvfrom(socket_id_, msg, MAX_UDP_MSG_SIZE, 0, (SOCKADDR *)&sender_addr, &sender_addr_size);
(void)recvfrom(socket_id_, msg, MAX_UDP_MSG_SIZE, 0, &sender_addr, &sender_addr_size);

if (dest_addr_len_ == 0) {
dest_addr_ = sender_addr;
dest_addr_len_ = sender_addr_size;
}

std::stringstream ss;
ss << msg;
return ss;
}

void DcsSocket::DcsSend(const std::string &message) {
(void)sendto(socket_id_,
message.c_str(),
static_cast<int>(message.length()),
0,
dest_port_->ai_addr,
static_cast<int>(dest_port_->ai_addrlen));
(void)sendto(socket_id_, message.c_str(), static_cast<int>(message.length()), 0, &dest_addr_, dest_addr_len_);
}
15 changes: 8 additions & 7 deletions Sources/DcsInterface/DcsSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@

class DcsSocket {
public:
// Binds a UDP socket to the rx port and also intializes the destination address using the tx port.
// Binds a UDP socket to the rx port and also initializes the destination address using the tx port.
/**
* @brief Construct a new Dcs Socket object bound to the rx port and initializes the destination address using the
* tx port.
* tx port if provided, or determines tx_port address dynamically if not.
*
* @param rx_port UDP receive port.
* @param tx_port UDP transmit port.
* @param tx_ip_address UDP transmit IP address.
* @param rx_port UDP receive port.
* @param tx_port UDP transmit port, defaults to dynamic (use recvfrom address) if not provided.
*/
DcsSocket(const std::string &rx_port, const std::string &tx_port, const std::string &ip_address);
DcsSocket(const std::string &ip_address, const std::string &rx_port, const std::string &tx_port = "dynamic");

/**
* @brief Destroy the Dcs Socket object
Expand Down Expand Up @@ -45,6 +45,7 @@ class DcsSocket {
void DcsSend(const std::string &message);

private:
SOCKET socket_id_; // Socket which is binded to the rx port.
addrinfo *dest_port_; // UDP address info for port which will be transmitted to.
SOCKET socket_id_; // Socket which is binded to the rx port.
sockaddr dest_addr_; // UDP address info for port which will be transmitted to.
int dest_addr_len_ = 0; // Size of dest address.
};
30 changes: 16 additions & 14 deletions Sources/DcsInterface/StringUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,31 @@
#include <stdlib.h>

bool is_integer(const std::string &str) {
if (str.empty()) {
return false;
} else {
if (!str.empty()) {
// Add special handling for stripping of trailing spaces
const std::string str_trailing_spaces_stripped = str.substr(0, str.find_last_not_of(" ") + 1);
// Check if all characters are base 10 digits (0-9).
char *ptr_to_first_non_numeric_char;
strtol(str_trailing_spaces_stripped.c_str(), &ptr_to_first_non_numeric_char, 10);
return (*ptr_to_first_non_numeric_char == '\0');
if (!str_trailing_spaces_stripped.empty()) {
// Check if all characters are base 10 digits (0-9).
char *ptr_to_first_non_numeric_char;
strtol(str_trailing_spaces_stripped.c_str(), &ptr_to_first_non_numeric_char, 10);
return (*ptr_to_first_non_numeric_char == '\0');
}
}
return false;
}

bool is_number(const std::string &str) {
if (str.empty()) {
return false;
} else {
if (!str.empty()) {
// Add special handling for stripping of trailing spaces
const std::string str_trailing_spaces_stripped = str.substr(0, str.find_last_not_of(" ") + 1);
// Check if all characters can be converted to a floating point number.
char *ptr_to_first_non_numeric_char;
strtof(str_trailing_spaces_stripped.c_str(), &ptr_to_first_non_numeric_char);
return (*ptr_to_first_non_numeric_char == '\0');
if (!str_trailing_spaces_stripped.empty()) {
// Check if all characters can be converted to a floating point number.
char *ptr_to_first_non_numeric_char;
strtof(str_trailing_spaces_stripped.c_str(), &ptr_to_first_non_numeric_char);
return (*ptr_to_first_non_numeric_char == '\0');
}
}
return false;
}

bool pop_key_and_value(std::stringstream &ss,
Expand Down
11 changes: 9 additions & 2 deletions Sources/MyStreamDeckPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,16 @@ void MyStreamDeckPlugin::SendToPlugin(const std::string &inAction,
if (event == "RequestInstalledModules") {
const std::string dcs_install_path = EPLJSONUtils::GetStringByName(inPayload, "dcs_install_path");
const std::string modules_subdir = "/mods/aircraft/";
const json installed_modules_list = get_installed_modules(dcs_install_path, modules_subdir);
const json installed_modules_and_result = get_installed_modules(dcs_install_path, modules_subdir);
const std::string result = EPLJSONUtils::GetStringByName(installed_modules_and_result, "result");
if (result != "success") {
mConnectionManager->LogMessage("Get Installed Modules Failure: " + result);
}
mConnectionManager->SendToPropertyInspector(
inAction, inContext, json({{"event", "InstalledModules"}, {"installed_modules", installed_modules_list}}));
inAction,
inContext,
json({{"event", "InstalledModules"},
{"installed_modules", installed_modules_and_result["installed_modules"]}}));
}

if (event == "RequestIdLookup") {
Expand Down
5 changes: 3 additions & 2 deletions Sources/Test/DcsIdLookupTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ namespace test {
TEST(DcsIdLookupTest, get_installed_modules_bad_path) {
const std::string dcs_install_path = "non-existant-path";
const std::string module_subdir = "/Test/";
json found_files = get_installed_modules(dcs_install_path, module_subdir);
EXPECT_TRUE(found_files.empty());
json found_files_and_result = get_installed_modules(dcs_install_path, module_subdir);
EXPECT_EQ("DCS Install path [non-existant-path/Test/] not found.", found_files_and_result["result"]);
EXPECT_EQ(0, found_files_and_result["installed_modules"].size());
}

TEST(DcsIdLookupTest, nonexistant_lua_file) {
Expand Down
4 changes: 2 additions & 2 deletions Sources/Test/DcsInterfaceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ TEST(DcsInterfaceTest, invalid_connection_port_settings) {

TEST(DcsInterfaceTest, dcs_reset_command_on_construction) {
DcsConnectionSettings connection_settings = {"1908", "1909", "127.0.0.1"};
DcsSocket mock_dcs(connection_settings.tx_port, connection_settings.rx_port, connection_settings.ip_address);
DcsSocket mock_dcs(connection_settings.ip_address, connection_settings.tx_port, connection_settings.rx_port);
DcsInterface dcs_interface(connection_settings);

// Test that the reset message "R" is received by DCS on creation of DcsInterface.
Expand All @@ -25,7 +25,7 @@ class DcsInterfaceTestFixture : public ::testing::Test {
public:
DcsInterfaceTestFixture()
: // Mock DCS socket uses the reverse rx and tx ports of dcs_interface so it can communicate with it.
mock_dcs(connection_settings.tx_port, connection_settings.rx_port, connection_settings.ip_address),
mock_dcs(connection_settings.ip_address, connection_settings.tx_port, connection_settings.rx_port),
dcs_interface(connection_settings) {

// Consume intial reset command sent to to mock_dcs.
Expand Down
23 changes: 19 additions & 4 deletions Sources/Test/DcsSocketTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ namespace test {
TEST(DcsSocketTest, empty_connection_settings) { EXPECT_THROW(DcsSocket dcs_socket("", "", ""), std::runtime_error); }

TEST(DcsSocketTest, invalid_connection_port_settings) {
EXPECT_THROW(DcsSocket dcs_socket("19ab", "abc", "127.0.0.1"), std::runtime_error);
EXPECT_THROW(DcsSocket dcs_socket("127.0.0.1", "19ab", "abc"), std::runtime_error);
}

TEST(DcsSocketTest, invalid_connection_ip_addr_settings) {
EXPECT_THROW(DcsSocket dcs_socket("1908", "1909", "127001"), std::runtime_error);
EXPECT_THROW(DcsSocket dcs_socket("127001", "1908", "1909"), std::runtime_error);
}

class DcsSocketTestFixture : public ::testing::Test {
public:
DcsSocketTestFixture()
: sender_socket("1788", common_port, ip_address), receiver_socket(common_port, "1790", ip_address) {}
: sender_socket(ip_address, "1788", common_port), receiver_socket(ip_address, common_port, "1790") {}

DcsSocket sender_socket;
DcsSocket receiver_socket;
Expand All @@ -36,12 +36,27 @@ TEST_F(DcsSocketTestFixture, send_and_receive) {

TEST_F(DcsSocketTestFixture, unavailable_port_bind) {
// Expect exception thrown if try to bind a new socket to same rx_port.
EXPECT_THROW(DcsSocket duplicate_socket(common_port, "1801", ip_address), std::runtime_error);
EXPECT_THROW(DcsSocket duplicate_socket(ip_address, common_port, "1801"), std::runtime_error);
}

TEST_F(DcsSocketTestFixture, receive_timeout) {
std::stringstream ss_received = receiver_socket.DcsReceive();
// Expect timeout after 100 msec and return of empty string.
EXPECT_EQ(ss_received.str(), "");
}

TEST_F(DcsSocketTestFixture, dynamic_tx_port_discovery) {
const std::string new_common_port = "1791";
DcsSocket server_socket(ip_address, "1792", new_common_port);
DcsSocket client_socket(ip_address, new_common_port);
const std::string test_msg_a = "test_a";
server_socket.DcsSend(test_msg_a);
std::stringstream client_received_ss = client_socket.DcsReceive();
EXPECT_EQ(test_msg_a, client_received_ss.str());
// Expect client socket to have dynamically set tx_port to sender_socket's bound port.
const std::string test_msg_b = "test_b";
client_socket.DcsSend(test_msg_b);
std::stringstream server_received_ss = server_socket.DcsReceive();
EXPECT_EQ(test_msg_b, server_received_ss.str());
}
} // namespace test
11 changes: 9 additions & 2 deletions Sources/Test/StreamdeckContextTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class StreamdeckContextTestFixture : public ::testing::Test {
public:
StreamdeckContextTestFixture()
: // Mock DCS socket uses the reverse rx and tx ports of dcs_interface so it can communicate with it.
mock_dcs(connection_settings.tx_port, connection_settings.rx_port, connection_settings.ip_address),
mock_dcs(connection_settings.ip_address, connection_settings.tx_port, connection_settings.rx_port),
dcs_interface(connection_settings), fixture_context(fixture_context_id) {

// Consume intial reset command sent to to mock_dcs.
Expand Down Expand Up @@ -292,14 +292,21 @@ TEST_F(StreamdeckContextComparisonTestFixture, dcs_id_float_compare_to_alphanume
EXPECT_EQ(esd_connection_manager.state_, 0);
}
TEST_F(StreamdeckContextComparisonTestFixture, dcs_id_float_compare_to_empty) {

// Send game state as empty -- should treat as string and not try comparison.
const std::string mock_dcs_message = "header*123=";
mock_dcs.DcsSend(mock_dcs_message);
dcs_interface.update_dcs_state();
context_with_greater_than.updateContextState(&dcs_interface, &esd_connection_manager);
EXPECT_EQ(esd_connection_manager.state_, 0);
}
TEST_F(StreamdeckContextComparisonTestFixture, dcs_id_float_compare_to_string_of_spaces) {
// Send game state value as string of spaces -- should treat as (non-numeric) string and not try comparison.
const std::string mock_dcs_message = "header*123= ";
mock_dcs.DcsSend(mock_dcs_message);
dcs_interface.update_dcs_state();
context_with_greater_than.updateContextState(&dcs_interface, &esd_connection_manager);
EXPECT_EQ(esd_connection_manager.state_, 0);
}
TEST_F(StreamdeckContextComparisonTestFixture, dcs_id_float_compare_to_dcs_id_float) {
// Send DCS ID as a float -- should not read update.
const std::string mock_dcs_message = "header*123.0=2.0";
Expand Down
10 changes: 10 additions & 0 deletions Sources/Test/StringUtilitiesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ TEST(StringUtilitiesTest, is_integer_with_trailing_spaces) {
EXPECT_EQ(std::stoi("0 "), 0);
}

TEST(StringUtilitiesTest, is_integer_with_only_spaces) {
EXPECT_FALSE(is_integer(" "));
EXPECT_FALSE(is_integer(" "));
}

TEST(StringUtilitiesTest, is_integer_with_alpha_chars) {
EXPECT_FALSE(is_integer("25a"));
EXPECT_FALSE(is_integer("b-25"));
Expand Down Expand Up @@ -67,6 +72,11 @@ TEST(StringUtilitiesTest, is_number_with_trailing_spaces) {
EXPECT_EQ(std::stod("0 "), 0);
}

TEST(StringUtilitiesTest, is_number_with_only_spaces) {
EXPECT_FALSE(is_number(" "));
EXPECT_FALSE(is_number(" "));
}

TEST(StringUtilitiesTest, is_number_with_alpha_chars) {
EXPECT_FALSE(is_number("25a"));
EXPECT_FALSE(is_number("b-25"));
Expand Down
Binary file modified Sources/com.ctytler.dcs.sdPlugin/dcs_interface.exe
Binary file not shown.
3 changes: 2 additions & 1 deletion Sources/com.ctytler.dcs.sdPlugin/extract_clickabledata.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ end
-- All should use the configured "LockOn_Options.script_path" variable.
call_dofile = dofile
function inspect_and_dofile(path)
if (string.match(path, "DCS World")) then
local is_absolute_path = (string.match(path, "DCS") and string.match(path, "World"))
if is_absolute_path then
call_dofile(path)
else

Expand Down
Binary file added Sources/com.ctytler.dcs.sdPlugin/msvcp140.dll
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,18 @@
</div>
</div>
</div>

</div>

<div id="cached_data_in_use_notification" hidden>
<div class="wrap" style="max-width: 425px">
<div class="sdpi-item" id="modules">
<div>
<p><i>NOTE: Unable to query from DCS installation, using Cached clickabledata set</i>
</p>
</div>
</div>
</div>
</div>

<script src="json/general_commands.js"></script>
<script src="json/cached_commands.js"></script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function callbackRequestIdLookup() {
else {
var dcs_install_path = document.getElementById("dcs_install_path").value;
var payload = { "dcs_install_path": dcs_install_path, "module": module };
gotClickabledata(cached_commands[module]); // Load cached data until new data arrives.
gotClickabledata(cached_commands[module], true); // Load cached data until new data arrives.
sendmessage("RequestIdLookup", payload);
console.log("Request ID Lookup for: ", payload);
}
Expand Down Expand Up @@ -156,7 +156,7 @@ function clearTableContents() {
*
* @param {Json} clickabledata_elements
*/
function gotClickabledata(clickabledata_elements) {
function gotClickabledata(clickabledata_elements, is_cached_data = false) {
if (clickabledata_elements.length > 0) {
// Create rows in a new table body so it is easy to replace any old content.
var new_table_body = document.createElement('tbody');
Expand All @@ -179,6 +179,12 @@ function gotClickabledata(clickabledata_elements) {
document_table_body.parentNode.replaceChild(new_table_body, document_table_body);

document.getElementById("type_hints_text").hidden = false;

if (is_cached_data) {
document.getElementById("cached_data_in_use_notification").hidden = false;
} else {
document.getElementById("cached_data_in_use_notification").hidden = true;
}
}
}

Expand Down
Binary file added Sources/com.ctytler.dcs.sdPlugin/vcruntime140.dll
Binary file not shown.
Binary file not shown.

0 comments on commit 702a564

Please sign in to comment.