[rtns] Remove roboRIO team number setter (#7667)
It won't be needed in 2027.
3
.github/labeler.yml
vendored
@@ -33,9 +33,6 @@
|
||||
'component: sysid':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: sysid/**
|
||||
'component: teamnumbersetter':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: roborioteamnumbersetter/**
|
||||
'component: wpilibc':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: wpilibc/**
|
||||
|
||||
@@ -324,7 +324,6 @@ if(WITH_GUI)
|
||||
add_subdirectory(wpical)
|
||||
endif()
|
||||
if(LIBSSH_FOUND)
|
||||
add_subdirectory(roborioteamnumbersetter)
|
||||
add_subdirectory(datalogtool)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -24,7 +24,6 @@ WPILib is normally built with Gradle, however for some systems, such as Linux ba
|
||||
* datalogtool
|
||||
* glass
|
||||
* outlineviewer
|
||||
* roborioteamnumbersetter
|
||||
* sysid
|
||||
* halsim_gui (if simulation extensions are enabled)
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
cppHeaderFileInclude {
|
||||
\.h$
|
||||
\.inc$
|
||||
\.inl$
|
||||
}
|
||||
|
||||
cppSrcFileInclude {
|
||||
\.cpp$
|
||||
}
|
||||
|
||||
generatedFileExclude {
|
||||
src/main/native/resources/
|
||||
src/main/native/win/roborioteamnumbersetter.ico
|
||||
src/main/native/mac/rtns.icns
|
||||
}
|
||||
|
||||
repoRootNameOverride {
|
||||
roborioteamnumbersetter
|
||||
}
|
||||
|
||||
includeOtherLibs {
|
||||
^GLFW
|
||||
^fmt/
|
||||
^imgui
|
||||
^libssh/
|
||||
^ntcore
|
||||
^wpi/
|
||||
^wpigui
|
||||
^wpinet/
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
project(roborioteamnumbersetter)
|
||||
|
||||
include(CompileWarnings)
|
||||
include(GenResources)
|
||||
include(LinkMacOSGUI)
|
||||
|
||||
configure_file(src/main/generate/WPILibVersion.cpp.in WPILibVersion.cpp)
|
||||
generate_resources(src/main/native/resources generated/main/cpp RTNS rtns rtns_resources_src)
|
||||
|
||||
file(GLOB rtns_src src/main/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp)
|
||||
|
||||
if(WIN32)
|
||||
set(rtns_rc src/main/native/win/roborioteamnumbersetter.rc)
|
||||
elseif(APPLE)
|
||||
set(MACOSX_BUNDLE_ICON_FILE rtns.icns)
|
||||
set(APP_ICON_MACOSX src/main/native/mac/rtns.icns)
|
||||
set_source_files_properties(${APP_ICON_MACOSX} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
endif()
|
||||
|
||||
add_executable(
|
||||
roborioteamnumbersetter
|
||||
${rtns_src}
|
||||
${rtns_resources_src}
|
||||
${rtns_rc}
|
||||
${APP_ICON_MACOSX}
|
||||
)
|
||||
wpilib_link_macos_gui(roborioteamnumbersetter)
|
||||
target_link_libraries(roborioteamnumbersetter libglass wpinet ssh)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(roborioteamnumbersetter PROPERTIES WIN32_EXECUTABLE YES)
|
||||
elseif(APPLE)
|
||||
set_target_properties(
|
||||
roborioteamnumbersetter
|
||||
PROPERTIES MACOSX_BUNDLE YES OUTPUT_NAME "roborioTeamNumberSetter"
|
||||
)
|
||||
endif()
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>roboRIOTeamNumberSetter</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>roborioteamnumbersetter</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>roboRIOTeamNumberSetter</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>edu.wpi.first.tools.roboRIOTeamNumberSetter</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>rtns.icns</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2021</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2021</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.11</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,124 +0,0 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
if (project.hasProperty('onlylinuxathena') || project.hasProperty('onlylinuxsystemcore')) {
|
||||
return;
|
||||
}
|
||||
|
||||
description = "roboRIO Team Number Setter"
|
||||
|
||||
apply plugin: 'cpp'
|
||||
apply plugin: 'visual-studio'
|
||||
apply plugin: 'edu.wpi.first.NativeUtils'
|
||||
|
||||
if (OperatingSystem.current().isWindows()) {
|
||||
apply plugin: 'windows-resources'
|
||||
}
|
||||
|
||||
ext {
|
||||
nativeName = 'roborioteamnumbersetter'
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/resources.gradle"
|
||||
apply from: "${rootDir}/shared/config.gradle"
|
||||
|
||||
def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in")
|
||||
def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp")
|
||||
|
||||
apply from: "${rootDir}/shared/libssh.gradle"
|
||||
|
||||
task generateCppVersion() {
|
||||
description = 'Generates the wpilib version class'
|
||||
group = 'WPILib'
|
||||
|
||||
outputs.file wpilibVersionFileOutput
|
||||
inputs.file wpilibVersionFileInput
|
||||
|
||||
if (wpilibVersioning.releaseMode) {
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
// We follow a simple set of checks to determine whether we should generate a new version file:
|
||||
// 1. If the release type is not development, we generate a new version file
|
||||
// 2. If there is no generated version number, we generate a new version file
|
||||
// 3. If there is a generated build number, and the release type is development, then we will
|
||||
// only generate if the publish task is run.
|
||||
doLast {
|
||||
def version = wpilibVersioning.version.get()
|
||||
println "Writing version ${version} to $wpilibVersionFileOutput"
|
||||
|
||||
if (wpilibVersionFileOutput.exists()) {
|
||||
wpilibVersionFileOutput.delete()
|
||||
}
|
||||
def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version)
|
||||
wpilibVersionFileOutput.write(read)
|
||||
}
|
||||
}
|
||||
|
||||
gradle.taskGraph.addTaskExecutionGraphListener { graph ->
|
||||
def willPublish = graph.hasTask(publish)
|
||||
if (willPublish) {
|
||||
generateCppVersion.outputs.upToDateWhen { false }
|
||||
}
|
||||
}
|
||||
|
||||
def generateTask = createGenerateResourcesTask('main', 'RTNS', 'rtns', project)
|
||||
|
||||
project(':').libraryBuild.dependsOn build
|
||||
tasks.withType(CppCompile) {
|
||||
dependsOn generateTask
|
||||
dependsOn generateCppVersion
|
||||
}
|
||||
|
||||
model {
|
||||
components {
|
||||
// By default, a development executable will be generated. This is to help the case of
|
||||
// testing specific functionality of the library.
|
||||
"${nativeName}"(NativeExecutableSpec) {
|
||||
baseName = 'roborioteamnumbersetter'
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp"
|
||||
include '**/*.cpp'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include'
|
||||
}
|
||||
}
|
||||
if (OperatingSystem.current().isWindows()) {
|
||||
rc.source {
|
||||
srcDirs 'src/main/native/win'
|
||||
include '*.rc'
|
||||
}
|
||||
}
|
||||
}
|
||||
binaries.all {
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.systemcore) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
it.cppCompiler.define("LIBSSH_STATIC")
|
||||
lib project: ':glass', library: 'glass', linkage: 'static'
|
||||
lib project: ':wpinet', library: 'wpinet', linkage: 'static'
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
|
||||
lib project: ':wpigui', library: 'wpigui', linkage: 'static'
|
||||
nativeUtils.useRequiredLibrary(it, 'libssh')
|
||||
lib project: ':thirdparty:imgui_suite', library: 'imguiSuite', linkage: 'static'
|
||||
if (it.targetPlatform.operatingSystem.isWindows()) {
|
||||
it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
|
||||
it.linker.args << 'ws2_32.lib' << 'advapi32.lib' << 'crypt32.lib' << 'user32.lib'
|
||||
} else if (it.targetPlatform.operatingSystem.isMacOsX()) {
|
||||
it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
|
||||
it.linker.args << '-framework' << 'Kerberos'
|
||||
} else {
|
||||
it.linker.args << '-lX11'
|
||||
if (it.targetPlatform.name.startsWith('linuxarm')) {
|
||||
it.linker.args << '-lGL'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: 'publish.gradle'
|
||||
@@ -1,124 +0,0 @@
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
def baseArtifactId = 'roboRIOTeamNumberSetter'
|
||||
def artifactGroupId = 'edu.wpi.first.tools'
|
||||
def zipBaseName = '_GROUP_edu_wpi_first_tools_ID_roboRIOTeamNumberSetter_CLS'
|
||||
|
||||
def outputsFolder = file("$project.buildDir/outputs")
|
||||
|
||||
model {
|
||||
tasks {
|
||||
// Create the run task.
|
||||
$.components.roborioteamnumbersetter.binaries.each { bin ->
|
||||
if (bin.buildable && bin.name.toLowerCase().contains("debug") && nativeUtils.isNativeDesktopPlatform(bin.targetPlatform)) {
|
||||
Task run = project.tasks.create("run", Exec) {
|
||||
commandLine bin.tasks.install.runScriptFile.get().asFile.toString()
|
||||
}
|
||||
run.dependsOn bin.tasks.install
|
||||
}
|
||||
}
|
||||
}
|
||||
publishing {
|
||||
def roboRIOTeamNumberSetterTaskList = []
|
||||
$.components.each { component ->
|
||||
component.binaries.each { binary ->
|
||||
if (binary in NativeExecutableBinarySpec && binary.component.name.contains("roborioteamnumbersetter")) {
|
||||
if (binary.buildable && (binary.name.contains('Release') || binary.name.contains('release'))) {
|
||||
// We are now in the binary that we want.
|
||||
// This is the default application path for the ZIP task.
|
||||
def applicationPath = binary.executable.file
|
||||
def icon = file("$project.projectDir/src/main/native/mac/rtns.icns")
|
||||
|
||||
// Create the ZIP.
|
||||
def task = project.tasks.create("copyroboRIOTeamNumberSetterExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
|
||||
description("Copies the roboRIOTeamNumberSetter executable to the outputs directory.")
|
||||
destinationDirectory = outputsFolder
|
||||
|
||||
archiveBaseName = zipBaseName
|
||||
duplicatesStrategy = 'exclude'
|
||||
archiveClassifier = nativeUtils.getPublishClassifier(binary)
|
||||
|
||||
from(licenseFile) {
|
||||
into '/'
|
||||
}
|
||||
|
||||
if (binary.targetPlatform.operatingSystem.isWindows()) {
|
||||
def exePath = binary.executable.file.absolutePath
|
||||
exePath = exePath.substring(0, exePath.length() - 4)
|
||||
def pdbPath = new File(exePath + '.pdb')
|
||||
from(pdbPath)
|
||||
}
|
||||
|
||||
into(nativeUtils.getPlatformPath(binary))
|
||||
}
|
||||
|
||||
if (binary.targetPlatform.operatingSystem.isMacOsX()) {
|
||||
// Create the macOS bundle.
|
||||
def bundleTask = project.tasks.create("bundleroboRIOTeamNumberSetterOsxApp" + binary.targetPlatform.architecture.name, Copy) {
|
||||
description("Creates a macOS application bundle for roboRIO Team Number Setter")
|
||||
from(file("$project.projectDir/Info.plist"))
|
||||
into(file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/roboRIOTeamNumberSetter.app/Contents"))
|
||||
into("MacOS") {
|
||||
with copySpec {
|
||||
from binary.executable.file
|
||||
}
|
||||
}
|
||||
into("Resources") {
|
||||
with copySpec {
|
||||
from icon
|
||||
}
|
||||
}
|
||||
|
||||
inputs.property "HasDeveloperId", project.hasProperty("developerID")
|
||||
|
||||
doLast {
|
||||
if (project.hasProperty("developerID")) {
|
||||
// Get path to binary.
|
||||
exec {
|
||||
workingDir rootDir
|
||||
def args = [
|
||||
"sh",
|
||||
"-c",
|
||||
"codesign --force --strict --deep " +
|
||||
"--timestamp --options=runtime " +
|
||||
"--verbose -s ${project.findProperty("developerID")} " +
|
||||
"$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name/roboRIOTeamNumberSetter.app/"
|
||||
]
|
||||
commandLine args
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the application path if we are creating a bundle.
|
||||
applicationPath = file("$project.buildDir/outputs/bundles/$binary.targetPlatform.architecture.name")
|
||||
task.from(applicationPath)
|
||||
project.build.dependsOn bundleTask
|
||||
|
||||
bundleTask.dependsOn binary.tasks.link
|
||||
task.dependsOn(bundleTask)
|
||||
} else {
|
||||
task.from(applicationPath)
|
||||
}
|
||||
|
||||
task.dependsOn binary.tasks.link
|
||||
roboRIOTeamNumberSetterTaskList.add(task)
|
||||
project.build.dependsOn task
|
||||
project.artifacts { task }
|
||||
addTaskToCopyAllOutputs(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
roborioteamnumbersetter(MavenPublication) {
|
||||
roboRIOTeamNumberSetterTaskList.each { artifact it }
|
||||
|
||||
artifactId = baseArtifactId
|
||||
groupId = artifactGroupId
|
||||
version wpilibVersioning.version.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Autogenerated file! Do not manually edit this file. This version is regenerated
|
||||
* any time the publish task is run, or when this file is deleted.
|
||||
*/
|
||||
const char* GetWPILibVersion() {
|
||||
return "${wpilib_version}";
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <glass/MainMenuBar.h>
|
||||
#include <glass/Context.h>
|
||||
#include <glass/Storage.h>
|
||||
#include <glass/Window.h>
|
||||
#include <glass/WindowManager.h>
|
||||
#include <glass/other/Log.h>
|
||||
#include <imgui.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpinet/MulticastServiceResolver.h>
|
||||
#include <wpigui.h>
|
||||
#include "DeploySession.h"
|
||||
|
||||
namespace gui = wpi::gui;
|
||||
|
||||
const char* GetWPILibVersion();
|
||||
|
||||
namespace rtns {
|
||||
std::string_view GetResource_rtns_16_png();
|
||||
std::string_view GetResource_rtns_32_png();
|
||||
std::string_view GetResource_rtns_48_png();
|
||||
std::string_view GetResource_rtns_64_png();
|
||||
std::string_view GetResource_rtns_128_png();
|
||||
std::string_view GetResource_rtns_256_png();
|
||||
std::string_view GetResource_rtns_512_png();
|
||||
} // namespace rtns
|
||||
|
||||
#define GLFWAPI extern "C"
|
||||
GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height);
|
||||
#define GLFW_DONT_CARE -1
|
||||
GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth,
|
||||
int minheight, int maxwidth,
|
||||
int maxheight);
|
||||
GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height);
|
||||
|
||||
struct TeamNumberRefHolder {
|
||||
explicit TeamNumberRefHolder(glass::Storage& storage)
|
||||
: teamNumber{storage.GetInt("TeamNumber", 0)} {}
|
||||
int& teamNumber;
|
||||
};
|
||||
|
||||
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<rtns::DeviceStatus>>
|
||||
deviceStatuses;
|
||||
static wpi::Logger logger;
|
||||
static rtns::DeploySession deploySession{logger};
|
||||
static std::unique_ptr<wpi::MulticastServiceResolver> multicastResolver;
|
||||
static glass::MainMenuBar gMainMenu;
|
||||
|
||||
static void FindDevices() {
|
||||
WPI_EventHandle resolveEvent = multicastResolver->GetEventHandle();
|
||||
|
||||
bool timedOut = 0;
|
||||
if (wpi::WaitForObject(resolveEvent, 0, &timedOut)) {
|
||||
auto allData = multicastResolver->GetData();
|
||||
|
||||
for (auto&& data : allData) {
|
||||
// search for MAC
|
||||
auto macKey =
|
||||
std::find_if(data.txt.begin(), data.txt.end(),
|
||||
[](const auto& a) { return a.first == "MAC"; });
|
||||
if (macKey != data.txt.end()) {
|
||||
auto& mac = macKey->second;
|
||||
auto& foundDevice = foundDevices[mac];
|
||||
foundDevice = std::pair{data.ipv4Address, data.hostName};
|
||||
auto& deviceStatus = deviceStatuses[mac];
|
||||
if (!deviceStatus) {
|
||||
deploySession.GetStatus(mac, foundDevice.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int minWidth = 400;
|
||||
|
||||
static void DisplayGui() {
|
||||
int& teamNumber = teamNumberRef->teamNumber;
|
||||
FindDevices();
|
||||
|
||||
ImGui::GetStyle().WindowRounding = 0;
|
||||
|
||||
// fill entire OS window with this window
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
int width, height;
|
||||
glfwGetWindowSize(gui::GetSystemWindow(), &width, &height);
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(width, height));
|
||||
|
||||
ImGui::Begin("Entries", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_MenuBar |
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
ImGui::BeginMenuBar();
|
||||
gMainMenu.WorkspaceMenu();
|
||||
gui::EmitViewMenu();
|
||||
|
||||
bool about = false;
|
||||
if (ImGui::BeginMenu("Info")) {
|
||||
if (ImGui::MenuItem("About")) {
|
||||
about = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
|
||||
if (about) {
|
||||
ImGui::OpenPopup("About");
|
||||
}
|
||||
if (ImGui::BeginPopupModal("About")) {
|
||||
ImGui::Text("roboRIO Team Number Setter");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("v%s", GetWPILibVersion());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Has mDNS Implementation: %d",
|
||||
static_cast<int>(multicastResolver->HasImplementation()));
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
|
||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate,
|
||||
ImGui::GetIO().Framerate);
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (multicastResolver->HasImplementation()) {
|
||||
ImGui::InputInt("Team Number", &teamNumber);
|
||||
|
||||
if (teamNumber < 0) {
|
||||
teamNumber = 0;
|
||||
}
|
||||
|
||||
int nameWidth = ImGui::CalcTextSize("roboRIO2-0000-FRC.local. ").x;
|
||||
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;
|
||||
|
||||
minWidth = nameWidth + macWidth + ipAddressWidth + setWidth + 100;
|
||||
|
||||
std::string setString = fmt::format("Set team to {}", teamNumber);
|
||||
|
||||
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::TableHeadersRow();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
for (auto&& i : foundDevices) {
|
||||
std::future<int>* future = deploySession.GetFuture(i.first);
|
||||
std::future<rtns::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());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", i.first.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
struct in_addr in;
|
||||
in.s_addr = i.second.first;
|
||||
ImGui::Text("%s", inet_ntoa(in));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(i.first.c_str());
|
||||
if (future) {
|
||||
ImGui::TableNextColumn();
|
||||
const auto fs = future->wait_for(std::chrono::seconds(0));
|
||||
if (fs == std::future_status::ready) {
|
||||
deploySession.DestroyFuture(i.first);
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Button(setString.c_str())) {
|
||||
deploySession.ChangeTeamNumber(i.first, teamNumber, i.second.first);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
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) {
|
||||
// DeploySession may throw exceptions. They've already been logged, so
|
||||
// we can ignore them.
|
||||
try {
|
||||
deviceStatuses[i.first] = futureStatus->get();
|
||||
} catch (const std::exception&) {
|
||||
// pass, already been logged
|
||||
}
|
||||
// Always destroy the future so the UI updates
|
||||
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");
|
||||
|
||||
// TODO make columns better
|
||||
} else {
|
||||
// Missing MDNS Implementation
|
||||
ImGui::Text("mDNS Implementation is missing.");
|
||||
#ifdef _WIN32
|
||||
ImGui::Text("Windows 10 1809 or newer is required for this tool");
|
||||
#else
|
||||
ImGui::Text("avahi-client 3 and avahi-core 3 are required for this tool");
|
||||
ImGui::Text(
|
||||
"Install libavahi-client3 and libavahi-core3 from your package "
|
||||
"manager");
|
||||
#endif
|
||||
}
|
||||
ImGui::Columns();
|
||||
ImGui::End();
|
||||
|
||||
glfwSetWindowSizeLimits(gui::GetSystemWindow(), minWidth, 200, GLFW_DONT_CARE,
|
||||
GLFW_DONT_CARE);
|
||||
if (width < minWidth) {
|
||||
width = minWidth;
|
||||
glfwSetWindowSize(gui::GetSystemWindow(), width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void Application(std::string_view saveDir) {
|
||||
gui::CreateContext();
|
||||
glass::CreateContext();
|
||||
|
||||
// Add icons
|
||||
gui::AddIcon(rtns::GetResource_rtns_16_png());
|
||||
gui::AddIcon(rtns::GetResource_rtns_32_png());
|
||||
gui::AddIcon(rtns::GetResource_rtns_48_png());
|
||||
gui::AddIcon(rtns::GetResource_rtns_64_png());
|
||||
gui::AddIcon(rtns::GetResource_rtns_128_png());
|
||||
gui::AddIcon(rtns::GetResource_rtns_256_png());
|
||||
gui::AddIcon(rtns::GetResource_rtns_512_png());
|
||||
|
||||
glass::SetStorageName("roborioteamnumbersetter");
|
||||
glass::SetStorageDir(saveDir.empty() ? gui::GetPlatformSaveFileDir()
|
||||
: saveDir);
|
||||
|
||||
ssh_init();
|
||||
|
||||
teamNumberRef =
|
||||
std::make_unique<TeamNumberRefHolder>(glass::GetStorageRoot());
|
||||
|
||||
multicastResolver =
|
||||
std::make_unique<wpi::MulticastServiceResolver>("_ni._tcp");
|
||||
multicastResolver->Start();
|
||||
|
||||
gui::AddLateExecute(DisplayGui);
|
||||
gui::Initialize("roboRIO Team Number Setter", 600, 400);
|
||||
|
||||
gui::Main();
|
||||
multicastResolver->Stop();
|
||||
multicastResolver = nullptr;
|
||||
|
||||
glass::DestroyContext();
|
||||
gui::DestroyContext();
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "DeploySession.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpinet/uv/Error.h>
|
||||
#include <wpinet/uv/GetAddrInfo.h>
|
||||
#include <wpinet/uv/Work.h>
|
||||
#include <wpinet/uv/util.h>
|
||||
|
||||
#include "SshSession.h"
|
||||
|
||||
using namespace rtns;
|
||||
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
|
||||
// Macros to make logging easier.
|
||||
#define INFO(fmt, ...) WPI_INFO(m_logger, fmt, __VA_ARGS__)
|
||||
#define DEBUG(fmt, ...) WPI_DEBUG(m_logger, fmt, __VA_ARGS__)
|
||||
#define ERROR(fmt, ...) WPI_DEBUG(m_logger, fmt, __VA_ARGS__)
|
||||
#define SUCCESS(fmt, ...) WPI_LOG(m_logger, kLogSuccess, fmt, __VA_ARGS__)
|
||||
|
||||
// roboRIO SSH constants.
|
||||
static constexpr int kPort = 22;
|
||||
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} {}
|
||||
|
||||
template <typename T>
|
||||
struct SafeDeleter {
|
||||
explicit SafeDeleter(T d) : deleter(d) {}
|
||||
~SafeDeleter() noexcept { deleter(); }
|
||||
T deleter;
|
||||
};
|
||||
|
||||
std::future<int>* DeploySession::GetFuture(const std::string& macAddress) {
|
||||
auto itr = s_outstanding.find(macAddress);
|
||||
if (itr == s_outstanding.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &itr->second;
|
||||
}
|
||||
|
||||
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);
|
||||
if (itr != s_outstanding.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::future<int> future = std::async(
|
||||
std::launch::async, [this, ipAddress, teamNumber, 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(fmt::format(
|
||||
"/usr/local/natinst/bin/nirtcfg "
|
||||
"--file=/etc/natinst/share/ni-rt.ini --set "
|
||||
"section=systemsettings,token=host_name,value=roboRIO-{"
|
||||
"}-FRC ; sync",
|
||||
teamNumber));
|
||||
} 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::Reboot(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(fmt::format("sync ; shutdown -r now"));
|
||||
} 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::Blink(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(fmt::format(
|
||||
"for i in 1 2 3 4 5 ; do ` echo 255 > "
|
||||
"/sys/class/leds/nilrt:wifi:primary/brightness; sleep 0.5; "
|
||||
"echo 0 > /sys/class/leds/nilrt:wifi:primary/brightness; sleep "
|
||||
"0.5 ` ; done"));
|
||||
} 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::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;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/Logger.h>
|
||||
|
||||
namespace rtns {
|
||||
// Define an integer for a successful message in the log (shown in green on the
|
||||
// GUI).
|
||||
static constexpr unsigned int kLogSuccess = 31;
|
||||
|
||||
struct DeviceStatus {
|
||||
bool webServerEnabled = false;
|
||||
std::string serialNumber;
|
||||
std::string image;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a single deploy session.
|
||||
*
|
||||
* An instance of this class must be kept alive in memory until GetStatus()
|
||||
* returns kDiscoveryFailure or kDone. Otherwise, the deploy will fail!
|
||||
*/
|
||||
class DeploySession {
|
||||
public:
|
||||
/** Represents the status of the deploy session. */
|
||||
enum class Status { kInProgress, kDiscoveryFailure, kDone };
|
||||
|
||||
/**
|
||||
* Constructs an instance of the deploy session.
|
||||
*
|
||||
* @param team The team number (or an IP address/hostname).
|
||||
* @param drive Whether the drive program should be deployed to the roboRIO.
|
||||
* If this is set to false, the mechanism project will be
|
||||
* deployed.
|
||||
* @param config The generation configuration file to be sent to the roboRIO.
|
||||
* @param logger A reference to a logger where log messages should be sent.
|
||||
*/
|
||||
explicit DeploySession(wpi::Logger& logger);
|
||||
|
||||
/**
|
||||
* Executes the deploy. This can be called from any thread.
|
||||
*/
|
||||
bool ChangeTeamNumber(const std::string& macAddress, int team,
|
||||
unsigned int ipAddress);
|
||||
|
||||
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);
|
||||
|
||||
std::future<DeviceStatus>* GetStatusFuture(const std::string& macAddress);
|
||||
void DestroyStatusFuture(const std::string& macAddress);
|
||||
|
||||
private:
|
||||
// Logger reference where log messages will be sent.
|
||||
wpi::Logger& m_logger;
|
||||
|
||||
// The number of hostnames that have completed their resolution/connection
|
||||
// attempts.
|
||||
std::atomic_int m_visited = 0;
|
||||
};
|
||||
} // namespace rtns
|
||||
@@ -1,227 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "SshSession.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/sftp.h>
|
||||
#include <wpi/Logger.h>
|
||||
|
||||
using namespace rtns;
|
||||
|
||||
#define INFO(fmt, ...) WPI_INFO(m_logger, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
|
||||
SshSession::SshSession(std::string_view host, int port, std::string_view user,
|
||||
std::string_view pass, wpi::Logger& logger)
|
||||
: m_host{host},
|
||||
m_port{port},
|
||||
m_username{user},
|
||||
m_password{pass},
|
||||
m_logger{logger} {
|
||||
// Create a new SSH session.
|
||||
m_session = ssh_new();
|
||||
if (!m_session) {
|
||||
throw SshException("The SSH session could not be allocated.");
|
||||
}
|
||||
|
||||
// Set the host, user, and port.
|
||||
ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.c_str());
|
||||
ssh_options_set(m_session, SSH_OPTIONS_USER, m_username.c_str());
|
||||
ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port);
|
||||
|
||||
// Set timeout to 3 seconds.
|
||||
int64_t timeout = 3L;
|
||||
ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT, &timeout);
|
||||
|
||||
// Set other miscellaneous options.
|
||||
ssh_options_set(m_session, SSH_OPTIONS_STRICTHOSTKEYCHECK, "no");
|
||||
}
|
||||
|
||||
SshSession::~SshSession() {
|
||||
ssh_disconnect(m_session);
|
||||
ssh_free(m_session);
|
||||
}
|
||||
|
||||
void SshSession::Open() {
|
||||
// Connect to the server.
|
||||
int rc = ssh_connect(m_session);
|
||||
if (rc != SSH_OK) {
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Authenticate with password.
|
||||
rc = ssh_userauth_password(m_session, nullptr, m_password.c_str());
|
||||
if (rc != SSH_AUTH_SUCCESS) {
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
}
|
||||
|
||||
void SshSession::Execute(std::string_view cmd) {
|
||||
// Allocate a new channel.
|
||||
ssh_channel channel = ssh_channel_new(m_session);
|
||||
if (!channel) {
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Open the channel.
|
||||
int rc = ssh_channel_open_session(channel);
|
||||
if (rc != SSH_OK) {
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Execute the command.
|
||||
std::string command{cmd};
|
||||
rc = ssh_channel_request_exec(channel, command.c_str());
|
||||
if (rc != SSH_OK) {
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
uint32_t exitCode = 0;
|
||||
#if LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR >= 11
|
||||
ssh_channel_get_exit_state(channel, &exitCode, nullptr, nullptr);
|
||||
#else
|
||||
exitCode = ssh_channel_get_exit_status(channel);
|
||||
#endif
|
||||
INFO("{} {}", exitCode, cmd);
|
||||
|
||||
// Log output.
|
||||
char buf[512];
|
||||
int read = ssh_channel_read(channel, buf, sizeof(buf), 0);
|
||||
if (read != 0) {
|
||||
if (read < static_cast<int>(sizeof(buf) / sizeof(buf[0]))) {
|
||||
buf[read] = 0;
|
||||
} else {
|
||||
buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0;
|
||||
}
|
||||
INFO("stdout: {} {}", read, buf);
|
||||
}
|
||||
|
||||
read = ssh_channel_read(channel, buf, sizeof(buf), 1);
|
||||
if (read != 0) {
|
||||
if (read < static_cast<int>(sizeof(buf) / sizeof(buf[0]))) {
|
||||
buf[read] = 0;
|
||||
} else {
|
||||
buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0;
|
||||
}
|
||||
INFO("stderr: {} {}", read, buf);
|
||||
}
|
||||
|
||||
// Close and free channel.
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
}
|
||||
|
||||
std::string SshSession::ExecuteResult(std::string_view cmd, int* exitStatus) {
|
||||
// Allocate a new channel.
|
||||
ssh_channel channel = ssh_channel_new(m_session);
|
||||
if (!channel) {
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Open the channel.
|
||||
int rc = ssh_channel_open_session(channel);
|
||||
if (rc != SSH_OK) {
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Execute the command.
|
||||
std::string command{cmd};
|
||||
rc = ssh_channel_request_exec(channel, command.c_str());
|
||||
if (rc != SSH_OK) {
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
uint32_t exitCode = 0;
|
||||
#if LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR >= 11
|
||||
ssh_channel_get_exit_state(channel, &exitCode, nullptr, nullptr);
|
||||
#else
|
||||
exitCode = ssh_channel_get_exit_status(channel);
|
||||
#endif
|
||||
INFO("{} {}", exitCode, cmd);
|
||||
|
||||
std::string result;
|
||||
if (exitStatus) {
|
||||
*exitStatus = exitCode;
|
||||
}
|
||||
|
||||
// Log output.
|
||||
char buf[512];
|
||||
int read = ssh_channel_read(channel, buf, sizeof(buf), 0);
|
||||
if (read != 0) {
|
||||
if (read < static_cast<int>(sizeof(buf) / sizeof(buf[0]))) {
|
||||
buf[read] = 0;
|
||||
} else {
|
||||
buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0;
|
||||
}
|
||||
result = buf;
|
||||
INFO("stdout: {} {}", read, buf);
|
||||
}
|
||||
|
||||
read = ssh_channel_read(channel, buf, sizeof(buf), 1);
|
||||
if (read != 0) {
|
||||
if (read < static_cast<int>(sizeof(buf) / sizeof(buf[0]))) {
|
||||
buf[read] = 0;
|
||||
} else {
|
||||
buf[(sizeof(buf) / sizeof(buf[0])) - 1] = 0;
|
||||
}
|
||||
INFO("stderr: {} {}", read, buf);
|
||||
}
|
||||
|
||||
// Close and free channel.
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SshSession::Put(std::string_view path, std::string_view contents) {
|
||||
// Allocate the SFTP session.
|
||||
sftp_session sftp = sftp_new(m_session);
|
||||
if (!sftp) {
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Initialize.
|
||||
int rc = sftp_init(sftp);
|
||||
if (rc != SSH_OK) {
|
||||
sftp_free(sftp);
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Copy.
|
||||
sftp_file file =
|
||||
sftp_open(sftp, path.data(), O_WRONLY | O_CREAT | O_TRUNC, S_IFMT);
|
||||
if (!file) {
|
||||
sftp_free(sftp);
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
|
||||
// Send 150K at a time.
|
||||
static constexpr size_t kChunkSize = 150000;
|
||||
for (size_t i = 0; i < contents.size(); i += kChunkSize) {
|
||||
size_t len = (std::min)(kChunkSize, contents.size() - i);
|
||||
size_t written = sftp_write(file, contents.data() + i, len);
|
||||
if (written != len) {
|
||||
sftp_close(file);
|
||||
sftp_free(sftp);
|
||||
throw SshException(ssh_get_error(m_session));
|
||||
}
|
||||
}
|
||||
|
||||
INFO("[SFTP] Deployed {}!", path);
|
||||
|
||||
// Close file, free memory.
|
||||
sftp_close(file);
|
||||
sftp_free(sftp);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <libssh/libssh.h>
|
||||
#include <wpi/Logger.h>
|
||||
|
||||
namespace rtns {
|
||||
/**
|
||||
* This class is a C++ implementation of the SshSessionController in
|
||||
* wpilibsuite/deploy-utils. It handles connecting to an SSH server, running
|
||||
* commands, and transferring files.
|
||||
*/
|
||||
class SshSession {
|
||||
public:
|
||||
/**
|
||||
* This is the exception that will be thrown by any of the methods in this
|
||||
* class if something goes wrong.
|
||||
*/
|
||||
class SshException : public std::runtime_error {
|
||||
public:
|
||||
explicit SshException(const char* msg) : runtime_error(msg) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a new session controller.
|
||||
*
|
||||
* @param host The hostname of the server to connect to.
|
||||
* @param port The port that the sshd server is operating on.
|
||||
* @param user The username to login as.
|
||||
* @param pass The password for the given username.
|
||||
* @param logger A reference to a logger to log messages to.
|
||||
*/
|
||||
SshSession(std::string_view host, int port, std::string_view user,
|
||||
std::string_view pass, wpi::Logger& logger);
|
||||
|
||||
/**
|
||||
* Destroys the controller object. This also disconnects the session from the
|
||||
* server.
|
||||
*/
|
||||
~SshSession();
|
||||
|
||||
/**
|
||||
* Opens the SSH connection to the given host.
|
||||
*/
|
||||
void Open();
|
||||
|
||||
/**
|
||||
* Executes a command and logs the output (if there is any).
|
||||
*
|
||||
* @param cmd The command to execute on the server.
|
||||
*/
|
||||
void Execute(std::string_view cmd);
|
||||
|
||||
std::string ExecuteResult(std::string_view cmd, int* exitStatus);
|
||||
|
||||
/**
|
||||
* Puts a file on the server using SFTP.
|
||||
*
|
||||
* @param path The path to the file to put (on the server).
|
||||
* @param contents The contents of the file.
|
||||
*/
|
||||
void Put(std::string_view path, std::string_view contents);
|
||||
|
||||
private:
|
||||
ssh_session m_session;
|
||||
std::string m_host;
|
||||
|
||||
int m_port;
|
||||
|
||||
std::string m_username;
|
||||
std::string m_password;
|
||||
|
||||
wpi::Logger& m_logger;
|
||||
};
|
||||
} // namespace rtns
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
void Application(std::string_view saveDir);
|
||||
|
||||
#ifdef _WIN32
|
||||
int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine,
|
||||
int nCmdShow) {
|
||||
int argc = __argc;
|
||||
char** argv = __argv;
|
||||
#else
|
||||
int main(int argc, char** argv) {
|
||||
#endif
|
||||
std::string_view saveDir;
|
||||
if (argc == 2) {
|
||||
saveDir = argv[1];
|
||||
}
|
||||
|
||||
Application(saveDir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 153 B |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 951 B |
|
Before Width: | Height: | Size: 974 B |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 23 KiB |
@@ -1 +0,0 @@
|
||||
IDI_ICON1 ICON "roborioteamnumbersetter.ico"
|
||||
@@ -38,7 +38,6 @@ include 'crossConnIntegrationTests'
|
||||
include 'fieldImages'
|
||||
include 'glass'
|
||||
include 'outlineviewer'
|
||||
include 'roborioteamnumbersetter'
|
||||
include 'datalogtool'
|
||||
include 'sysid'
|
||||
include 'simulation:halsim_ds_socket'
|
||||
|
||||