Skip to content

Commit

Permalink
[rtns] Add functionality to enable and disable webserver (#6270)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse authored Jan 21, 2024
1 parent 7957f4a commit e408f3a
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 32 deletions.
113 changes: 87 additions & 26 deletions roborioteamnumbersetter/src/main/native/cpp/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ struct TeamNumberRefHolder {
static std::unique_ptr<TeamNumberRefHolder> teamNumberRef;
static std::unordered_map<std::string, std::pair<unsigned int, std::string>>
foundDevices;
static std::unordered_map<std::string, std::optional<sysid::DeviceStatus>>
deviceStatuses;
static wpi::Logger logger;
static sysid::DeploySession deploySession{logger};
static std::unique_ptr<wpi::MulticastServiceResolver> multicastResolver;
Expand All @@ -76,7 +78,12 @@ static void FindDevices() {
[](const auto& a) { return a.first == "MAC"; });
if (macKey != data.txt.end()) {
auto& mac = macKey->second;
foundDevices[mac] = std::make_pair(data.ipv4Address, data.hostName);
auto& foundDevice = foundDevices[mac];
foundDevice = std::make_pair(data.ipv4Address, data.hostName);
auto& deviceStatus = deviceStatuses[mac];
if (!deviceStatus) {
deploySession.GetStatus(mac, foundDevice.first);
}
}
}
}
Expand Down Expand Up @@ -146,15 +153,12 @@ static void DisplayGui() {
int macWidth = ImGui::CalcTextSize("88:88:88:88:88:88").x;
int ipAddressWidth = ImGui::CalcTextSize("255.255.255.255").x;
int setWidth = ImGui::CalcTextSize(" Set Team To 99999 ").x;
int blinkWidth = ImGui::CalcTextSize(" Blink ").x;
int rebootWidth = ImGui::CalcTextSize(" Reboot ").x;

minWidth = nameWidth + macWidth + ipAddressWidth + setWidth + blinkWidth +
rebootWidth + 100;
minWidth = nameWidth + macWidth + ipAddressWidth + setWidth + 100;

std::string setString = fmt::format("Set team to {}", teamNumber);

if (ImGui::BeginTable("Table", 6)) {
if (ImGui::BeginTable("Table", 4)) {
ImGui::TableSetupColumn(
"Name",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
Expand All @@ -171,17 +175,33 @@ static void DisplayGui() {
"Set",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
setWidth);
ImGui::TableSetupColumn(
"Blink",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
blinkWidth);
ImGui::TableSetupColumn(
"Reboot",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
rebootWidth);
ImGui::TableHeadersRow();

for (auto&& i : foundDevices) {
ImGui::EndTable();
}

for (auto&& i : foundDevices) {
std::future<int>* future = deploySession.GetFuture(i.first);
std::future<sysid::DeviceStatus>* futureStatus =
deploySession.GetStatusFuture(i.first);
if (ImGui::BeginTable("Table", 4)) {
ImGui::TableSetupColumn(
"Name",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
nameWidth);
ImGui::TableSetupColumn(
"MAC Address",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
macWidth);
ImGui::TableSetupColumn(
"IP Address",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
ipAddressWidth);
ImGui::TableSetupColumn(
"Set",
ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed,
setWidth);

ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", i.second.second.c_str());
Expand All @@ -192,11 +212,8 @@ static void DisplayGui() {
in.s_addr = i.second.first;
ImGui::Text("%s", inet_ntoa(in));
ImGui::TableNextColumn();
std::future<int>* future = deploySession.GetFuture(i.first);
ImGui::PushID(i.first.c_str());
if (future) {
ImGui::Button("Deploying");
ImGui::TableNextColumn();
ImGui::TableNextColumn();
const auto fs = future->wait_for(std::chrono::seconds(0));
if (fs == std::future_status::ready) {
Expand All @@ -207,18 +224,62 @@ static void DisplayGui() {
deploySession.ChangeTeamNumber(i.first, teamNumber, i.second.first);
}
ImGui::TableNextColumn();
if (ImGui::Button("Blink")) {
deploySession.Blink(i.first, i.second.first);
}
ImGui::TableNextColumn();
if (ImGui::Button("Reboot")) {
deploySession.Reboot(i.first, i.second.first);
}
}

ImGui::PopID();
ImGui::EndTable();
}

ImGui::EndTable();
ImGui::PushID(i.first.c_str());
if (futureStatus) {
ImGui::Text("Refreshing Status");
const auto fs = futureStatus->wait_for(std::chrono::seconds(0));
if (fs == std::future_status::ready) {
deviceStatuses[i.first] = futureStatus->get();
deploySession.DestroyStatusFuture(i.first);
}
} else {
auto& deviceStatus = deviceStatuses[i.first];
if (deviceStatus) {
if (ImGui::Button("Refresh Status")) {
deploySession.GetStatus(i.first, i.second.first);
}
std::string formatted =
fmt::format("Image: {}", deviceStatus.value().image);
ImGui::Text("%s", formatted.c_str());
formatted = fmt::format("Serial Number: {}",
deviceStatus.value().serialNumber);
ImGui::Text("%s", formatted.c_str());
formatted = fmt::format(
"Web Server Status: {}",
deviceStatus.value().webServerEnabled ? "Enabled" : "Disabled");
ImGui::Text("%s", formatted.c_str());
} else {
ImGui::Text("Waiting for refresh");
}
}

if (future) {
ImGui::Text("Deploying");
} else {
if (ImGui::Button("Blink")) {
deploySession.Blink(i.first, i.second.first);
}
ImGui::SameLine();
if (ImGui::Button("Reboot")) {
deploySession.Reboot(i.first, i.second.first);
}
ImGui::SameLine();
if (ImGui::Button("Disable Web Server")) {
deploySession.DisableWebServer(i.first, i.second.first);
}
ImGui::SameLine();
if (ImGui::Button("Enable Web Server")) {
deploySession.EnableWebServer(i.first, i.second.first);
}
}
ImGui::Separator();
ImGui::PopID();
}

ImGui::Columns(6, "Devices");
Expand Down
158 changes: 158 additions & 0 deletions roborioteamnumbersetter/src/main/native/cpp/DeploySession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ static constexpr std::string_view kUsername = "admin";
static constexpr std::string_view kPassword = "";

std::unordered_map<std::string, std::future<int>> s_outstanding;
std::unordered_map<std::string, std::future<DeviceStatus>> s_outstandingStatus;

DeploySession::DeploySession(wpi::Logger& logger) : m_logger{logger} {}

Expand All @@ -59,6 +60,19 @@ void DeploySession::DestroyFuture(const std::string& macAddress) {
s_outstanding.erase(macAddress);
}

std::future<DeviceStatus>* DeploySession::GetStatusFuture(
const std::string& macAddress) {
auto itr = s_outstandingStatus.find(macAddress);
if (itr == s_outstandingStatus.end()) {
return nullptr;
}
return &itr->second;
}

void DeploySession::DestroyStatusFuture(const std::string& macAddress) {
s_outstandingStatus.erase(macAddress);
}

bool DeploySession::ChangeTeamNumber(const std::string& macAddress,
int teamNumber, unsigned int ipAddress) {
auto itr = s_outstanding.find(macAddress);
Expand Down Expand Up @@ -184,3 +198,147 @@ bool DeploySession::Blink(const std::string& macAddress,
s_outstanding[macAddress] = std::move(future);
return true;
}

bool DeploySession::DisableWebServer(const std::string& macAddress,
unsigned int ipAddress) {
auto itr = s_outstanding.find(macAddress);
if (itr != s_outstanding.end()) {
return false;
}

std::future<int> future =
std::async(std::launch::async, [this, ipAddress, mac = macAddress]() {
// Convert to IP address.
wpi::SmallString<16> ip;
in_addr addr;
addr.s_addr = ipAddress;
wpi::uv::AddrToName(addr, &ip);
DEBUG("Trying to establish SSH connection to {}.", ip.str());
try {
SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
session.Open();
DEBUG("SSH connection to {} was successful.", ip.str());

SUCCESS("roboRIO Connected!");

try {
session.Execute(
"/bin/bash -c \"/etc/init.d/systemWebServer stop\"");
session.Execute(
"/bin/bash -c \"update-rc.d -f systemWebServer remove\"");
session.Execute("/bin/bash -c \"sync\"");
} catch (const SshSession::SshException& e) {
ERROR("An exception occurred: {}", e.what());
throw e;
}
} catch (const SshSession::SshException& e) {
DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
throw e;
}
return 0;
});

s_outstanding[macAddress] = std::move(future);
return true;
}

bool DeploySession::EnableWebServer(const std::string& macAddress,
unsigned int ipAddress) {
auto itr = s_outstanding.find(macAddress);
if (itr != s_outstanding.end()) {
return false;
}

std::future<int> future =
std::async(std::launch::async, [this, ipAddress, mac = macAddress]() {
// Convert to IP address.
wpi::SmallString<16> ip;
in_addr addr;
addr.s_addr = ipAddress;
wpi::uv::AddrToName(addr, &ip);
DEBUG("Trying to establish SSH connection to {}.", ip.str());
try {
SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
session.Open();
DEBUG("SSH connection to {} was successful.", ip.str());

SUCCESS("roboRIO Connected!");

try {
session.Execute(
"/bin/bash -c \"update-rc.d -f systemWebServer defaults\"");
session.Execute(
"/bin/bash -c \"/etc/init.d/systemWebServer start\"");
session.Execute("/bin/bash -c \"sync\"");
} catch (const SshSession::SshException& e) {
ERROR("An exception occurred: {}", e.what());
throw e;
}
} catch (const SshSession::SshException& e) {
DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
throw e;
}
return 0;
});

s_outstanding[macAddress] = std::move(future);
return true;
}

bool DeploySession::GetStatus(const std::string& macAddress,
unsigned int ipAddress) {
auto itr = s_outstandingStatus.find(macAddress);
if (itr != s_outstandingStatus.end()) {
return false;
}

std::future<DeviceStatus> future =
std::async(std::launch::async, [this, ipAddress, mac = macAddress]() {
// Convert to IP address.
wpi::SmallString<16> ip;
in_addr addr;
addr.s_addr = ipAddress;
wpi::uv::AddrToName(addr, &ip);
DEBUG("Trying to establish SSH connection to {}.", ip.str());
DeviceStatus status;
try {
SshSession session{ip.str(), kPort, kUsername, kPassword, m_logger};
session.Open();
DEBUG("SSH connection to {} was successful.", ip.str());

SUCCESS("roboRIO Connected!");

try {
int exitStatus = 0;
session.ExecuteResult(
"start-stop-daemon --status -x "
"/usr/local/natinst/share/NIWebServer/SystemWebServer",
&exitStatus);
status.webServerEnabled = exitStatus == 0;
auto serialNumber = session.ExecuteResult(
"/sbin/fw_printenv -n serial#", &exitStatus);
if (exitStatus == 0) {
status.serialNumber = wpi::trim(serialNumber);
}
auto image = session.ExecuteResult(
"/usr/local/natinst/bin/nirtcfg --file "
"/etc/natinst/share/scs_imagemetadata.ini --get "
"section=ImageMetadata,token=IMAGEVERSION,value=UNKNOWN",
&exitStatus);
if (exitStatus == 0) {
status.image = wpi::trim(image);
}
} catch (const SshSession::SshException& e) {
ERROR("An exception occurred: {}", e.what());
throw e;
}
} catch (const SshSession::SshException& e) {
DEBUG("SSH connection to {} failed with {}.", ip.str(), e.what());
throw e;
}
return status;
});

s_outstandingStatus[macAddress] = std::move(future);
return true;
}
18 changes: 14 additions & 4 deletions roborioteamnumbersetter/src/main/native/cpp/DeploySession.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ namespace sysid {
// GUI).
static constexpr unsigned int kLogSuccess = 31;

struct DeviceStatus {
bool webServerEnabled = false;
std::string serialNumber;
std::string image;
};

/**
* Represents a single deploy session.
*
Expand Down Expand Up @@ -49,15 +55,19 @@ class DeploySession {

bool Blink(const std::string& macAddress, unsigned int ipAddress);

bool DisableWebServer(const std::string& macAddress, unsigned int ipAddress);

bool EnableWebServer(const std::string& macAddress, unsigned int ipAddress);

bool Reboot(const std::string& macAddress, unsigned int ipAddress);

bool GetStatus(const std::string& macAddress, unsigned int ipAddress);

std::future<int>* GetFuture(const std::string& macAddress);
void DestroyFuture(const std::string& macAddress);

/**
* Returns the state of the deploy session.
*/
Status GetStatus() const;
std::future<DeviceStatus>* GetStatusFuture(const std::string& macAddress);
void DestroyStatusFuture(const std::string& macAddress);

private:
// Logger reference where log messages will be sent.
Expand Down
Loading

0 comments on commit e408f3a

Please sign in to comment.