2018-12-21 08:08:34 +00:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
|
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
|
|
/* the project. */
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#include "Application.h"
|
|
|
|
|
2018-12-30 08:13:52 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2018-12-21 08:08:34 +00:00
|
|
|
#include <wpi/FileSystem.h>
|
|
|
|
#include <wpi/json.h>
|
|
|
|
#include <wpi/raw_istream.h>
|
|
|
|
#include <wpi/raw_ostream.h>
|
|
|
|
|
|
|
|
#include "VisionStatus.h"
|
|
|
|
|
|
|
|
#define TYPE_TAG "### TYPE:"
|
|
|
|
|
|
|
|
std::shared_ptr<Application> Application::GetInstance() {
|
|
|
|
static auto inst = std::make_shared<Application>(private_init{});
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::Set(wpi::StringRef appType,
|
|
|
|
std::function<void(wpi::StringRef)> onFail) {
|
|
|
|
wpi::StringRef appDir;
|
2019-01-11 11:20:41 +00:00
|
|
|
wpi::StringRef appEnv;
|
2018-12-21 08:08:34 +00:00
|
|
|
wpi::StringRef appCommand;
|
|
|
|
|
|
|
|
if (appType == "builtin") {
|
2018-12-30 10:07:22 +00:00
|
|
|
appCommand = "/usr/local/frc/bin/multiCameraServer";
|
2018-12-21 08:08:34 +00:00
|
|
|
} else if (appType == "example-java") {
|
2019-01-11 09:11:03 +00:00
|
|
|
appDir = "examples/java-multiCameraServer";
|
2018-12-21 08:08:34 +00:00
|
|
|
appCommand =
|
|
|
|
"env LD_LIBRARY_PATH=/usr/local/frc/lib java -jar "
|
|
|
|
"build/libs/java-multiCameraServer-all.jar";
|
|
|
|
} else if (appType == "example-cpp") {
|
2019-01-11 09:11:03 +00:00
|
|
|
appDir = "examples/cpp-multiCameraServer";
|
2018-12-21 08:08:34 +00:00
|
|
|
appCommand = "./multiCameraServerExample";
|
|
|
|
} else if (appType == "example-python") {
|
2019-01-11 09:11:03 +00:00
|
|
|
appDir = "examples/python-multiCameraServer";
|
2019-01-11 11:20:41 +00:00
|
|
|
appEnv = "export PYTHONUNBUFFERED=1";
|
2018-12-21 08:08:34 +00:00
|
|
|
appCommand = "./multiCameraServer.py";
|
|
|
|
} else if (appType == "upload-java") {
|
|
|
|
appCommand =
|
|
|
|
"env LD_LIBRARY_PATH=/usr/local/frc/lib java -jar uploaded.jar";
|
|
|
|
} else if (appType == "upload-cpp") {
|
|
|
|
appCommand = "./uploaded";
|
|
|
|
} else if (appType == "upload-python") {
|
2019-01-11 11:20:41 +00:00
|
|
|
appEnv = "export PYTHONUNBUFFERED=1";
|
2018-12-21 08:08:34 +00:00
|
|
|
appCommand = "./uploaded.py";
|
|
|
|
} else if (appType == "custom") {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
wpi::SmallString<64> msg;
|
|
|
|
msg = "unrecognized application type '";
|
|
|
|
msg += appType;
|
|
|
|
msg += "'";
|
|
|
|
onFail(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// write file
|
|
|
|
std::error_code ec;
|
|
|
|
wpi::raw_fd_ostream os(EXEC_HOME "/runCamera", ec, wpi::sys::fs::F_Text);
|
|
|
|
if (ec) {
|
|
|
|
onFail("could not write " EXEC_HOME "/runCamera");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
os << "#!/bin/sh\n";
|
|
|
|
os << TYPE_TAG << ' ' << appType << '\n';
|
|
|
|
os << "echo \"Waiting 5 seconds...\"\n";
|
|
|
|
os << "sleep 5\n";
|
|
|
|
if (!appDir.empty()) os << "cd " << appDir << '\n';
|
2019-01-11 11:20:41 +00:00
|
|
|
if (!appEnv.empty()) os << appEnv << '\n';
|
2018-12-21 08:08:34 +00:00
|
|
|
os << "exec " << appCommand << '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
m_appType = appType;
|
|
|
|
|
|
|
|
// terminate vision process so it reloads
|
|
|
|
VisionStatus::GetInstance()->Terminate(onFail);
|
|
|
|
|
|
|
|
UpdateStatus();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::Upload(wpi::ArrayRef<uint8_t> contents,
|
|
|
|
std::function<void(wpi::StringRef)> onFail) {
|
|
|
|
wpi::StringRef filename;
|
2019-01-11 09:10:37 +00:00
|
|
|
bool text = false;
|
2018-12-21 08:08:34 +00:00
|
|
|
if (m_appType == "upload-java") {
|
|
|
|
filename = "/uploaded.jar";
|
|
|
|
} else if (m_appType == "upload-cpp") {
|
|
|
|
filename = "/uploaded";
|
|
|
|
} else if (m_appType == "upload-python") {
|
|
|
|
filename = "/uploaded.py";
|
2019-01-11 09:10:37 +00:00
|
|
|
text = true;
|
2018-12-21 08:08:34 +00:00
|
|
|
} else {
|
|
|
|
wpi::SmallString<64> msg;
|
|
|
|
msg = "cannot upload application type '";
|
|
|
|
msg += m_appType;
|
|
|
|
msg += "'";
|
|
|
|
onFail(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpi::SmallString<64> pathname;
|
|
|
|
pathname = EXEC_HOME;
|
|
|
|
pathname += filename;
|
|
|
|
|
2018-12-30 08:13:52 +00:00
|
|
|
// remove old file (need to do this as we can't overwrite a running exe)
|
|
|
|
if (unlink(pathname.c_str()) == -1) {
|
|
|
|
wpi::errs() << "could not remove app executable: " << std::strerror(errno)
|
|
|
|
<< '\n';
|
|
|
|
}
|
|
|
|
|
2018-12-21 08:08:34 +00:00
|
|
|
{
|
2018-12-30 08:13:52 +00:00
|
|
|
// open file for writing
|
2018-12-21 08:08:34 +00:00
|
|
|
std::error_code ec;
|
2018-12-30 08:13:52 +00:00
|
|
|
int fd;
|
|
|
|
if (wpi::sys::fs::openFileForWrite(pathname, fd, wpi::sys::fs::F_None)) {
|
2018-12-21 08:08:34 +00:00
|
|
|
wpi::SmallString<64> msg;
|
|
|
|
msg = "could not write ";
|
|
|
|
msg += pathname;
|
|
|
|
onFail(msg);
|
|
|
|
return;
|
|
|
|
}
|
2018-12-30 08:13:52 +00:00
|
|
|
|
|
|
|
// change ownership
|
|
|
|
if (fchown(fd, APP_UID, APP_GID) == -1) {
|
|
|
|
wpi::errs() << "could not change app ownership: " << std::strerror(errno)
|
|
|
|
<< '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
// set file to be executable
|
|
|
|
if (fchmod(fd, 0775) == -1) {
|
|
|
|
wpi::errs() << "could not change app permissions: "
|
|
|
|
<< std::strerror(errno) << '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
// write contents and close file
|
2019-01-11 09:10:37 +00:00
|
|
|
wpi::raw_fd_ostream out(fd, true);
|
|
|
|
if (text) {
|
|
|
|
wpi::StringRef str(reinterpret_cast<const char*>(contents.data()),
|
|
|
|
contents.size());
|
|
|
|
// convert any Windows EOL to Unix
|
|
|
|
for (;;) {
|
|
|
|
size_t idx = str.find("\r\n");
|
|
|
|
if (idx == wpi::StringRef::npos) break;
|
|
|
|
out << str.slice(0, idx) << '\n';
|
|
|
|
str = str.slice(idx + 2, wpi::StringRef::npos);
|
|
|
|
}
|
|
|
|
out << str;
|
|
|
|
// ensure file ends with EOL
|
|
|
|
if (!str.empty() && str.back() != '\n') out << '\n';
|
|
|
|
} else {
|
|
|
|
out << contents;
|
|
|
|
}
|
2018-12-21 08:08:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// terminate vision process so it reloads
|
|
|
|
VisionStatus::GetInstance()->Terminate(onFail);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::UpdateStatus() { status(GetStatusJson()); }
|
|
|
|
|
|
|
|
wpi::json Application::GetStatusJson() {
|
|
|
|
wpi::json j = {{"type", "applicationSettings"},
|
|
|
|
{"applicationType", "custom"}};
|
|
|
|
|
|
|
|
std::error_code ec;
|
|
|
|
wpi::raw_fd_istream is(EXEC_HOME "/runCamera", ec);
|
|
|
|
if (ec) {
|
|
|
|
wpi::errs() << "could not read " EXEC_HOME "/runCamera\n";
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
|
|
|
// scan file
|
|
|
|
wpi::SmallString<256> lineBuf;
|
|
|
|
while (!is.has_error()) {
|
|
|
|
wpi::StringRef line = is.getline(lineBuf, 256).trim();
|
|
|
|
if (line.startswith(TYPE_TAG)) {
|
|
|
|
j["applicationType"] = line.substr(strlen(TYPE_TAG)).trim();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return j;
|
|
|
|
}
|