Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Look for settings.json in multiple places #715

Merged
merged 1 commit into from
Dec 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AirLib/include/common/common_utils/FileSystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class FileSystem

static std::string getUserDocumentsFolder();

static std::string getExecutableFolder();

static std::string getAppDataFolder() {
return ensureFolder(combine(getUserDocumentsFolder(), ProductFolderName));
}
Expand Down
20 changes: 13 additions & 7 deletions AirLib/include/controllers/Settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,17 @@ class Settings {

std::string getFileName() { return file_; }

static std::string getFullPath(std::string fileName)
{
std::string path = common_utils::FileSystem::getAppDataFolder();
return common_utils::FileSystem::combine(path, fileName);
}
static std::string getUserDirectoryFullPath(std::string fileName)
{
std::string path = common_utils::FileSystem::getAppDataFolder();
return common_utils::FileSystem::combine(path, fileName);
}

static std::string getExecutableFullPath(std::string fileName)
{
std::string path = common_utils::FileSystem::getExecutableFolder();
return common_utils::FileSystem::combine(path, fileName);
}

static Settings& loadJSonString(const std::string& json_str)
{
Expand Down Expand Up @@ -72,7 +78,7 @@ class Settings {
static Settings& loadJSonFile(std::string fileName)
{
std::lock_guard<std::mutex> guard(getFileAccessMutex());
std::string path = getFullPath(fileName);
std::string path = getUserDirectoryFullPath(fileName);
singleton().file_ = fileName;

singleton().load_success_ = false;
Expand Down Expand Up @@ -100,7 +106,7 @@ class Settings {
void saveJSonFile(std::string fileName)
{
std::lock_guard<std::mutex> guard(getFileAccessMutex());
std::string path = getFullPath(fileName);
std::string path = getUserDirectoryFullPath(fileName);
std::ofstream s;
common_utils::FileSystem::createTextFile(path, s);
s << std::setw(2) << doc_ << std::endl;
Expand Down
37 changes: 35 additions & 2 deletions AirLib/src/common/common_utils/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ using namespace common_utils;
// File names are unicode (std::wstring), because users can create folders containing unicode characters on both
// Windows, OSX and Linux.
std::string FileSystem::createDirectory(const std::string& fullPath) {

#ifdef _WIN32
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
std::wstring wide_path = converter.from_bytes(fullPath);
Expand Down Expand Up @@ -82,4 +82,37 @@ std::string FileSystem::getUserDocumentsFolder() {
return ensureFolder(path);
}

#endif
#endif

std::string FileSystem::getExecutableFolder() {
std::string path;
#ifdef _WIN32
wchar_t szPath[MAX_PATH];

HMODULE hModule = GetModuleHandle(NULL);

if (NULL != hModule) {
if (0 < GetModuleFileName(hModule, szPath, sizeof(szPath))) {
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
path = converter.to_bytes(szPath);
}
else {
HRESULT hr = GetLastError();
throw std::invalid_argument(Utils::stringf("Error getting executable folder, hr = %d", hr));
}
}
else {
HRESULT hr = GetLastError();
throw std::invalid_argument(Utils::stringf("Error getting executable folder - hModule is null. Last error = %d", hr));
}
#else
char szPath[8192];
readlink("/proc/self/exe", szPath, sizeof(szPath));
path = std::string(szPath);
#endif

size_t pathSeparatorIndex = path.find_last_of(kPathSeparator);
path = path.substr(0, pathSeparatorIndex);

return ensureFolder(path);
}
114 changes: 84 additions & 30 deletions Unreal/Plugins/AirSim/Source/SimHUD/SimHUD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "MessageDialog.h"
#include "Kismet/KismetSystemLibrary.h"

#include <stdexcept>

ASimHUD* ASimHUD::instance_ = nullptr;

ASimHUD::ASimHUD()
Expand All @@ -20,7 +22,7 @@ void ASimHUD::BeginPlay()
Super::BeginPlay();

initializeSettings();

//TODO: should we only do below on SceneCapture2D components and cameras?
//avoid motion blur so capture images don't get
GetWorld()->GetGameViewport()->GetEngineShowFlags()->SetMotionBlur(false);
Expand Down Expand Up @@ -56,7 +58,7 @@ void ASimHUD::BeginPlay()
updateWidgetSubwindowVisibility();
}

void ASimHUD::Tick( float DeltaSeconds )
void ASimHUD::Tick(float DeltaSeconds)
{
if (simmode_->EnableReport)
widget_->updateReport(simmode_->getReport());
Expand Down Expand Up @@ -144,7 +146,7 @@ void ASimHUD::updateWidgetSubwindowVisibility()
if (camera != nullptr)
camera->setCameraTypeEnabled(camera_type, is_visible);

widget_->setSubwindowVisibility(window_index,
widget_->setSubwindowVisibility(window_index,
is_visible,
is_visible ? camera->getRenderTarget(camera_type, false) : nullptr
);
Expand Down Expand Up @@ -203,9 +205,9 @@ void ASimHUD::createSimMode()
std::string simmode_name = settings.getString("SimMode", "");
if (simmode_name == "") {
FText title = FText::FromString("Choose Vehicle");
if (EAppReturnType::No == FMessageDialog::Open(EAppMsgType::YesNo,
FText::FromString("Would you like to use car simulation? Choose no to use quadrotor simulation."),
& title))
if (EAppReturnType::No == FMessageDialog::Open(EAppMsgType::YesNo,
FText::FromString("Would you like to use car simulation? Choose no to use quadrotor simulation."),
&title))
{
simmode_name = "Multirotor";
}
Expand Down Expand Up @@ -250,12 +252,12 @@ void ASimHUD::initializeSubWindows()
Settings json_settings_parent;
if (json_settings_root.getChild("SubWindows", json_settings_parent)) {
for (size_t child_index = 0; child_index < json_settings_parent.size(); ++child_index) {
Settings json_settings_child;
Settings json_settings_child;
if (json_settings_parent.getChild(child_index, json_settings_child)) {
int index = json_settings_child.getInt("WindowID", -1);

if (index == -1) {
UAirBlueprintLib::LogMessageString("WindowID not set in <SubWindows> element(s) in settings.json",
UAirBlueprintLib::LogMessageString("WindowID not set in <SubWindows> element(s) in settings.json",
std::to_string(child_index), LogDebugLevel::Failure);
continue;
}
Expand All @@ -266,9 +268,9 @@ void ASimHUD::initializeSubWindows()
int camera_id = json_settings_child.getInt("CameraID", 0);
if (camera_id >= 0 && camera_id < camera_count)
subwindow_cameras_[index] = wrapper->getCamera(camera_id);
else
else

UAirBlueprintLib::LogMessageString("CameraID in <SubWindows> element in settings.json is invalid",
UAirBlueprintLib::LogMessageString("CameraID in <SubWindows> element in settings.json is invalid",
std::to_string(child_index), LogDebugLevel::Failure);
}
}
Expand All @@ -281,34 +283,30 @@ void ASimHUD::initializeSettings()
//load settings file if found
typedef msr::airlib::Settings Settings;
try {
FString settings_filename = FString(Settings::getFullPath("settings.json").c_str());
FString json_fstring;
std::string settings_str;
std::string json_settings_text;
bool load_success = false;
bool file_found = FPaths::FileExists(settings_filename);
if (file_found) {
bool read_sucess = FFileHelper::LoadFileToString(json_fstring, *settings_filename);
if (read_sucess) {
Settings& settings = Settings::loadJSonString(TCHAR_TO_UTF8(*json_fstring));
if (settings.isLoadSuccess()) {
UAirBlueprintLib::setLogMessagesHidden(!settings.getBool("LogMessagesVisible", true));
UAirBlueprintLib::LogMessageString("Loaded settings from ", TCHAR_TO_UTF8(*settings_filename), LogDebugLevel::Informational);
load_success = true;
}
else
UAirBlueprintLib::LogMessageString("Possibly invalid json string in ", TCHAR_TO_UTF8(*settings_filename), LogDebugLevel::Failure);
bool settings_found = getSettingsText(json_settings_text);
if (settings_found && json_settings_text.size() > 0) {
Settings& settings = Settings::loadJSonString(json_settings_text);
if (settings.isLoadSuccess()) {
UAirBlueprintLib::setLogMessagesHidden(!settings.getBool("LogMessagesVisible", true));
load_success = true;
}
else {
UAirBlueprintLib::LogMessageString("Cannot parse JSON settings string.", "", LogDebugLevel::Failure);
}
else
UAirBlueprintLib::LogMessageString("Cannot read settings from ", TCHAR_TO_UTF8(*settings_filename), LogDebugLevel::Failure);
}

if (!load_success) {
FString settings_filename = FString(Settings::getUserDirectoryFullPath("settings.json").c_str());
//create default settings
Settings& settings = Settings::loadJSonString("{}");
//write some settings in new file otherwise the string "null" is written if all settigs are empty
settings.setString("SeeDocsAt", "https://github.com/Microsoft/AirSim/blob/master/docs/settings.md");
settings.setDouble("SettingsVersion", 1.0);

if (!file_found) {
if (!settings_found) {
std::string json_content;
//TODO: there is a crash in Linux due to settings.saveJSonString(). Remove this workaround after we only support Unreal 4.17
//https://answers.unrealengine.com/questions/664905/unreal-crashes-on-two-lines-of-extremely-simple-st.html
Expand All @@ -317,13 +315,69 @@ void ASimHUD::initializeSettings()
#else
json_content = "{ \"SettingsVersion\": 1, \"SeeDocsAt\": \"https://github.com/Microsoft/AirSim/blob/master/docs/settings.md\"}";
#endif
json_fstring = FString(json_content.c_str());
FString json_fstring = FString(json_content.c_str());
FFileHelper::SaveStringToFile(json_fstring, *settings_filename);
UAirBlueprintLib::LogMessageString("Created settings file at ", TCHAR_TO_UTF8(*settings_filename), LogDebugLevel::Informational);
UAirBlueprintLib::LogMessageString("Settings not provided. Created defalut settings file at ", TCHAR_TO_UTF8(*settings_filename), LogDebugLevel::Informational);
}
}
}
catch (std::exception& ex) {
UAirBlueprintLib::LogMessage(FString("Error loading settings from ~/Documents/AirSim/settings.json"), FString(ex.what()), LogDebugLevel::Failure, 30);
UAirBlueprintLib::LogMessage(FString("Error loading settings"), FString(ex.what()), LogDebugLevel::Failure, 30);
}
}

// Attempts to parse the settings text from one of multiple locations.
// First, check the command line for settings provided via "-s" or "--settings" arguments
// Next, check the executable's working directory for the settings file.
// Finally, check the user's documents folder.
// If the settings file cannot be read, throw an exception

bool ASimHUD::getSettingsText(std::string& settingsText) {
return (getSettingsTextFromCommandLine(settingsText)
||
readSettingsTextFromFile(FString(Settings::getExecutableFullPath("settings.json").c_str()), settingsText)
||
readSettingsTextFromFile(FString(Settings::getUserDirectoryFullPath("settings.json").c_str()), settingsText));
}

// Attempts to parse the settings text from the command line
// Looks for the flag "--settings". If it exists, settingsText will be set to the value.
// Example: AirSim.exe -s '{"foo" : "bar"}' -> settingsText will be set to {"foo": "bar"}
// Returns true if the argument is present, false otherwise.
bool ASimHUD::getSettingsTextFromCommandLine(std::string& settingsText) {

bool found = false;
FString settingsTextFString;
const TCHAR* commandLineArgs = FCommandLine::Get();

if (FParse::Param(commandLineArgs, TEXT("-settings"))) {
FString commandLineArgsFString = FString(commandLineArgs);
int idx = commandLineArgsFString.Find(TEXT("-settings"));
FString settingsJsonFString = commandLineArgsFString.RightChop(idx + 10);
if (FParse::QuotedString(*settingsJsonFString, settingsTextFString)) {
settingsText = std::string(TCHAR_TO_UTF8(*settingsTextFString));
found = true;
}
}

return found;
}

bool ASimHUD::readSettingsTextFromFile(FString settingsFilepath, std::string& settingsText) {

bool found = FPaths::FileExists(settingsFilepath);
if (found) {
FString settingsTextFStr;
bool readSuccessful = FFileHelper::LoadFileToString(settingsTextFStr, *settingsFilepath);
if (readSuccessful) {
UAirBlueprintLib::LogMessageString("Loaded settings from ", TCHAR_TO_UTF8(*settingsFilepath), LogDebugLevel::Informational);
settingsText = TCHAR_TO_UTF8(*settingsTextFStr);
}
else {
UAirBlueprintLib::LogMessageString("Cannot read file ", TCHAR_TO_UTF8(*settingsFilepath), LogDebugLevel::Failure);
throw std::runtime_error("Cannot read settings file.");
}
}

return found;
}
10 changes: 7 additions & 3 deletions Unreal/Plugins/AirSim/Source/SimHUD/SimHUD.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
UENUM(BlueprintType)
enum class ESimulatorMode : uint8
{
SIM_MODE_HIL UMETA(DisplayName="Hardware-in-loop")
SIM_MODE_HIL UMETA(DisplayName = "Hardware-in-loop")
};

UCLASS()
Expand Down Expand Up @@ -39,11 +39,11 @@ class AIRSIM_API ASimHUD : public AHUD
void setSubwindowCamera(int window_index, APIPCamera* camera);
bool getSubwindowVisible(int window_index);
void setSubwindowVisible(int window_index, bool is_visible);

ASimHUD();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Tick( float DeltaSeconds ) override;
virtual void Tick(float DeltaSeconds) override;

static ASimHUD* GetInstance() {
return instance_;
Expand All @@ -61,6 +61,10 @@ class AIRSIM_API ASimHUD : public AHUD
void initializeSubWindows();
void createSimMode();

bool getSettingsText(std::string& settingsText);
bool getSettingsTextFromCommandLine(std::string& settingsText);
bool readSettingsTextFromFile(FString fileName, std::string& settingsText);

private:
typedef common_utils::Utils Utils;
UClass* widget_class_;
Expand Down