mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-27 02:01:42 +00:00
Compare commits
112 Commits
v2022.1.1-
...
v2022.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
269cf03472 | ||
|
|
5ccfc4adbd | ||
|
|
b6f44f98be | ||
|
|
0dca57e9ec | ||
|
|
22c4da152e | ||
|
|
05d66f862d | ||
|
|
b09f5b2cf2 | ||
|
|
a2510aaa0e | ||
|
|
947f589916 | ||
|
|
bbd8980a20 | ||
|
|
831052f118 | ||
|
|
c137569f91 | ||
|
|
dae61226fa | ||
|
|
3ad4594a88 | ||
|
|
112acb9a62 | ||
|
|
ecee224e81 | ||
|
|
a3645dea34 | ||
|
|
7c09f44898 | ||
|
|
f401ea9aae | ||
|
|
bf8517f1e6 | ||
|
|
528087e308 | ||
|
|
1f59ff72f9 | ||
|
|
315be873c4 | ||
|
|
b8d019cdb4 | ||
|
|
102f23bbdb | ||
|
|
b85c24a79c | ||
|
|
eee29daaf9 | ||
|
|
aa9dfabde2 | ||
|
|
5999a26fba | ||
|
|
1e82595ffb | ||
|
|
e373fa476b | ||
|
|
dceb5364f4 | ||
|
|
baacbc8e24 | ||
|
|
84b15f0883 | ||
|
|
c0da9d2d35 | ||
|
|
0fe0be2733 | ||
|
|
eafa947338 | ||
|
|
9d13ae8d01 | ||
|
|
2a64e4bae5 | ||
|
|
c3fd20db59 | ||
|
|
6f91f37cd0 | ||
|
|
5158730b81 | ||
|
|
2ad2d2ca96 | ||
|
|
b5fd29774f | ||
|
|
9f8f330e96 | ||
|
|
1ad3b1b333 | ||
|
|
dfc24425c3 | ||
|
|
c02577bb51 | ||
|
|
c9e6a96a61 | ||
|
|
9778626f34 | ||
|
|
34b2d0dae1 | ||
|
|
59a7528fd6 | ||
|
|
11d9859ef1 | ||
|
|
e44ed752ad | ||
|
|
52b2dd5b89 | ||
|
|
c46636f218 | ||
|
|
dc531462e1 | ||
|
|
92ba98621c | ||
|
|
d41d051f1b | ||
|
|
c5ae0effac | ||
|
|
b3974c6ed3 | ||
|
|
589a00e379 | ||
|
|
8d9836ca02 | ||
|
|
8b5bf8632e | ||
|
|
1846114491 | ||
|
|
2c461c794e | ||
|
|
109363daa4 | ||
|
|
41d26bee8d | ||
|
|
7269a170fb | ||
|
|
441f2ed9b0 | ||
|
|
15275433d4 | ||
|
|
1ac02d2f58 | ||
|
|
8ee6257e92 | ||
|
|
d81ef2bc5c | ||
|
|
acb64dff97 | ||
|
|
3f6cf76a8c | ||
|
|
3ef2dab465 | ||
|
|
a5a56dd067 | ||
|
|
04957a6d30 | ||
|
|
5da54888f8 | ||
|
|
6c93365b0f | ||
|
|
1c4a8bfb66 | ||
|
|
d51a1d3b3d | ||
|
|
aced2e7da6 | ||
|
|
fa1ceca83a | ||
|
|
0ea05d34e6 | ||
|
|
09db4f672b | ||
|
|
4ba80a3a8c | ||
|
|
ae208d2b17 | ||
|
|
6f51cb3b98 | ||
|
|
f6159ee1a2 | ||
|
|
7f401ae895 | ||
|
|
0587b7043a | ||
|
|
0bbf51d566 | ||
|
|
92c6eae6b0 | ||
|
|
141354cd79 | ||
|
|
f6e9fc7d71 | ||
|
|
d8418be7d1 | ||
|
|
82066946e5 | ||
|
|
4b1defc8d8 | ||
|
|
da90c1cd2c | ||
|
|
3aa54fa027 | ||
|
|
b156db400d | ||
|
|
9aba2b7583 | ||
|
|
a9931223f0 | ||
|
|
aacf9442e4 | ||
|
|
7db10ecf00 | ||
|
|
a0a5b2aea5 | ||
|
|
eb835598a4 | ||
|
|
f0ab6df5b6 | ||
|
|
075144faa3 | ||
|
|
32468a40cb |
4
.github/workflows/cmake.yml
vendored
4
.github/workflows/cmake.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
name: Linux
|
||||
container: wpilib/roborio-cross-ubuntu:2022-20.04
|
||||
flags: ""
|
||||
- os: macos-latest
|
||||
- os: macOS-11
|
||||
name: macOS
|
||||
container: ""
|
||||
flags: "-DWITH_JAVA=OFF"
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
build-vcpkg:
|
||||
name: "Build - Windows"
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Prepare vcpkg
|
||||
|
||||
6
.github/workflows/gradle.yml
vendored
6
.github/workflows/gradle.yml
vendored
@@ -44,13 +44,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
- os: windows-2019
|
||||
artifact-name: Win64
|
||||
architecture: x64
|
||||
- os: windows-latest
|
||||
- os: windows-2019
|
||||
artifact-name: Win32
|
||||
architecture: x86
|
||||
- os: macos-latest
|
||||
- os: macOS-11
|
||||
artifact-name: macOS
|
||||
architecture: x64
|
||||
name: "Build - ${{ matrix.artifact-name }}"
|
||||
|
||||
@@ -11,6 +11,7 @@ cppSrcFileInclude {
|
||||
|
||||
modifiableFileExclude {
|
||||
\.patch$
|
||||
gradlew
|
||||
}
|
||||
|
||||
generatedFileExclude {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"enableCppIntellisense": true,
|
||||
"currentLanguage": "cpp",
|
||||
"projectYear": "2021",
|
||||
"projectYear": "intellisense",
|
||||
"teamNumber": 0
|
||||
}
|
||||
|
||||
@@ -6,11 +6,17 @@ FATAL: In-source builds are not allowed.
|
||||
")
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
|
||||
set(CMAKE_SYSTEM_VERSION 10.0.18362.0 CACHE STRING INTERNAL FORCE)
|
||||
set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION 10.0.18362.0 CACHE STRING INTERNAL FORCE)
|
||||
endif()
|
||||
|
||||
project(allwpilib)
|
||||
cmake_minimum_required(VERSION 3.3.0)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
message(STATUS "Platform version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
|
||||
|
||||
set(WPILIB_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
INCLUDE(CPack)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2021 FIRST and other WPILib contributors
|
||||
Copyright (c) 2009-2022 FIRST and other WPILib contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -69,15 +69,36 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
||||
|
||||
* wpiutil
|
||||
|
||||
* wpigui
|
||||
* imgui
|
||||
|
||||
* ntcore
|
||||
* wpiutil
|
||||
|
||||
* wpimath
|
||||
* wpiutil
|
||||
|
||||
* glass/libglass
|
||||
* wpiutil
|
||||
* wpimath
|
||||
* wpigui
|
||||
|
||||
* glass/libglassnt
|
||||
* wpiutil
|
||||
* ntcore
|
||||
* wpimath
|
||||
* wpigui
|
||||
|
||||
* hal
|
||||
* wpiutil
|
||||
|
||||
* halsim
|
||||
* imgui
|
||||
* wpiutil
|
||||
|
||||
* ntcore
|
||||
* wpiutil
|
||||
* ntcore
|
||||
* wpimath
|
||||
* wpigui
|
||||
* libglass
|
||||
* libglassnt
|
||||
|
||||
* cscore
|
||||
* opencv
|
||||
@@ -101,6 +122,7 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
||||
* cameraserver
|
||||
* ntcore
|
||||
* cscore
|
||||
* wpimath
|
||||
* wpiutil
|
||||
|
||||
* wpilibNewCommands
|
||||
@@ -109,9 +131,10 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
||||
* cameraserver
|
||||
* ntcore
|
||||
* cscore
|
||||
* wpimath
|
||||
* wpiutil
|
||||
|
||||
* wpilibNewCommands
|
||||
* wpilibOldCommands
|
||||
* wpilibc
|
||||
* hal
|
||||
* cameraserver
|
||||
@@ -119,6 +142,7 @@ All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
||||
* cscore
|
||||
* wpiutil
|
||||
|
||||
|
||||
### Third Party Artifacts
|
||||
|
||||
This repository provides the builds of the following third party software.
|
||||
@@ -128,3 +152,4 @@ All artifacts are based at `edu.wpi.first.thirdparty.frcYEAR` in the repository.
|
||||
* googletest
|
||||
* imgui
|
||||
* opencv
|
||||
* libssh
|
||||
|
||||
@@ -11,6 +11,7 @@ Development builds are the per-commit build hosted everytime a commit is pushed
|
||||
In order to build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version.
|
||||
|
||||
```groovy
|
||||
wpi.maven.useLocal = false
|
||||
wpi.maven.useDevelopment = true
|
||||
wpi.versions.wpilibVersion = 'YEAR.+'
|
||||
wpi.versions.wpimathVersion = 'YEAR.+
|
||||
|
||||
@@ -39,7 +39,7 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
||||
- On macOS, install the JDK 11 .pkg from the link above
|
||||
- C++ compiler
|
||||
- On Linux, install GCC 8 or greater
|
||||
- On Windows, install [Visual Studio Community 2019](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio 2019)
|
||||
- On Windows, install [Visual Studio Community 2022 or 2019](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
|
||||
- On macOS, install the Xcode command-line build tools via `xcode-select --install`
|
||||
- ARM compiler toolchain
|
||||
- Run `./gradlew installRoboRioToolchain` after cloning this repository
|
||||
@@ -71,6 +71,8 @@ The gradlew wrapper only exists in the root of the main project, so be sure to r
|
||||
|
||||
There are a few tasks other than `build` available. To see them, run the meta-task `tasks`. This will print a list of all available tasks, with a description of each task.
|
||||
|
||||
If opening from a fresh clone, generated java dependencies will not exist. Most IDEs will not run the generation tasks, which will cause lots of IDE errors. Manually run `./gradlew compileJava` from a terminal to run all the compile tasks, and then refresh your IDE's configuration (In VS Code open settings.gradle and save).
|
||||
|
||||
### Faster builds
|
||||
|
||||
`./gradlew build` builds _everything_, which includes debug and release builds for desktop and all installed cross compilers. Many developers don't need or want to build all of this. Therefore, common tasks have shortcuts to only build necessary components for common development and testing tasks.
|
||||
|
||||
14
build.gradle
14
build.gradle
@@ -5,7 +5,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.hubspot.jinjava:jinjava:2.5.8'
|
||||
classpath 'com.hubspot.jinjava:jinjava:2.6.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@ plugins {
|
||||
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
|
||||
id 'edu.wpi.first.NativeUtils' apply false
|
||||
id 'edu.wpi.first.GradleJni' version '1.0.0'
|
||||
id 'edu.wpi.first.GradleVsCode' version '1.0.0'
|
||||
id 'edu.wpi.first.GradleVsCode'
|
||||
id 'idea'
|
||||
id 'visual-studio'
|
||||
id 'net.ltgt.errorprone' version '1.1.1' apply false
|
||||
id 'com.github.johnrengelman.shadow' version '5.2.0' apply false
|
||||
id 'com.diffplug.spotless' version '5.5.0' apply false
|
||||
id 'com.github.spotbugs' version '5.0.0-beta.1' apply false
|
||||
id 'net.ltgt.errorprone' version '2.0.2' apply false
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.1' apply false
|
||||
id 'com.diffplug.spotless' version '6.0.5' apply false
|
||||
id 'com.github.spotbugs' version '5.0.3' apply false
|
||||
}
|
||||
|
||||
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
|
||||
@@ -147,5 +147,5 @@ ext.getCurrentArch = {
|
||||
}
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '7.1.1'
|
||||
gradleVersion = '7.3.2'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ repositories {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "edu.wpi.first:native-utils:2022.3.1"
|
||||
implementation "edu.wpi.first:native-utils:2022.7.1"
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
|
||||
implementation project(':wpiutil')
|
||||
implementation project(':ntcore')
|
||||
|
||||
@@ -56,9 +56,9 @@ public final class Main {
|
||||
public JsonObject config;
|
||||
}
|
||||
|
||||
static int team;
|
||||
static boolean server;
|
||||
static List<CameraConfig> cameras = new ArrayList<>();
|
||||
private static int team;
|
||||
private static boolean server;
|
||||
private static List<CameraConfig> cameras = new ArrayList<>();
|
||||
|
||||
private Main() {}
|
||||
|
||||
|
||||
@@ -450,9 +450,9 @@ Instance::Instance() {
|
||||
entry.SetString(VideoModeToString(sourceIt->second.GetVideoMode()));
|
||||
return;
|
||||
} else if (wpi::starts_with(relativeKey, "Property/")) {
|
||||
propName = relativeKey.substr(9);
|
||||
propName = wpi::substr(relativeKey, 9);
|
||||
} else if (wpi::starts_with(relativeKey, "RawProperty/")) {
|
||||
propName = relativeKey.substr(12);
|
||||
propName = wpi::substr(relativeKey, 12);
|
||||
} else {
|
||||
return; // ignore
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
|
||||
if (wpi::trim(key) == "boundary") {
|
||||
value = wpi::trim(wpi::trim(value), '"'); // value may be quoted
|
||||
if (wpi::starts_with(value, "--")) {
|
||||
value = value.substr(2);
|
||||
value = wpi::substr(value, 2);
|
||||
}
|
||||
boundary.append(value.begin(), value.end());
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "JpegUtil.h"
|
||||
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/raw_istream.h>
|
||||
|
||||
namespace cs {
|
||||
@@ -64,7 +65,7 @@ bool GetJpegSize(std::string_view data, int* width, int* height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data = data.substr(2); // Get to the first block
|
||||
data = wpi::substr(data, 2); // Get to the first block
|
||||
for (;;) {
|
||||
if (data.size() < 4) {
|
||||
return false; // EOF
|
||||
@@ -89,7 +90,7 @@ bool GetJpegSize(std::string_view data, int* width, int* height) {
|
||||
return true;
|
||||
}
|
||||
// Go to the next block
|
||||
data = data.substr(bytes[2] * 256 + bytes[3] + 2);
|
||||
data = wpi::substr(data, bytes[2] * 256 + bytes[3] + 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +103,7 @@ bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF) {
|
||||
*locSOF = *size;
|
||||
|
||||
// Search until SOS for DHT tag
|
||||
sdata = sdata.substr(2); // Get to the first block
|
||||
sdata = wpi::substr(sdata, 2); // Get to the first block
|
||||
for (;;) {
|
||||
if (sdata.size() < 4) {
|
||||
return false; // EOF
|
||||
@@ -121,7 +122,7 @@ bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF) {
|
||||
*locSOF = sdata.data() - data; // SOF
|
||||
}
|
||||
// Go to the next block
|
||||
sdata = sdata.substr(bytes[2] * 256 + bytes[3] + 2);
|
||||
sdata = wpi::substr(sdata, bytes[2] * 256 + bytes[3] + 2);
|
||||
}
|
||||
|
||||
// Only add DHT if we also found SOF (insertion point)
|
||||
|
||||
@@ -797,14 +797,14 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
|
||||
// compatibility, others are for Axis camera compatibility.
|
||||
if ((pos = req.find("POST /stream")) != std::string_view::npos) {
|
||||
kind = kStream;
|
||||
parameters = req.substr(req.find('?', pos + 12)).substr(1);
|
||||
parameters = wpi::substr(wpi::substr(req, req.find('?', pos + 12)), 1);
|
||||
} else if ((pos = req.find("GET /?action=stream")) !=
|
||||
std::string_view::npos) {
|
||||
kind = kStream;
|
||||
parameters = req.substr(req.find('&', pos + 19)).substr(1);
|
||||
parameters = wpi::substr(wpi::substr(req, req.find('&', pos + 19)), 1);
|
||||
} else if ((pos = req.find("GET /stream.mjpg")) != std::string_view::npos) {
|
||||
kind = kStream;
|
||||
parameters = req.substr(req.find('?', pos + 16)).substr(1);
|
||||
parameters = wpi::substr(wpi::substr(req, req.find('?', pos + 16)), 1);
|
||||
} else if (req.find("GET /settings") != std::string_view::npos &&
|
||||
req.find(".json") != std::string_view::npos) {
|
||||
kind = kGetSettings;
|
||||
@@ -820,7 +820,7 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
|
||||
} else if ((pos = req.find("GET /?action=command")) !=
|
||||
std::string_view::npos) {
|
||||
kind = kCommand;
|
||||
parameters = req.substr(req.find('&', pos + 20)).substr(1);
|
||||
parameters = wpi::substr(wpi::substr(req, req.find('&', pos + 20)), 1);
|
||||
} else if (req.find("GET / ") != std::string_view::npos || req == "GET /\n") {
|
||||
kind = kRootPage;
|
||||
} else {
|
||||
@@ -833,7 +833,7 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
|
||||
pos = parameters.find_first_not_of(
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
|
||||
"-=&1234567890%./");
|
||||
parameters = parameters.substr(0, pos);
|
||||
parameters = wpi::substr(parameters, 0, pos);
|
||||
SDEBUG("command parameters: \"{}\"", parameters);
|
||||
|
||||
// Read the rest of the HTTP request.
|
||||
|
||||
@@ -107,7 +107,7 @@ static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) {
|
||||
|
||||
static bool IsPercentageProperty(std::string_view name) {
|
||||
if (wpi::starts_with(name, "raw_")) {
|
||||
name = name.substr(4);
|
||||
name = wpi::substr(name, 4);
|
||||
}
|
||||
return name == "brightness" || name == "contrast" || name == "saturation" ||
|
||||
name == "hue" || name == "sharpness" || name == "gain" ||
|
||||
@@ -181,13 +181,13 @@ static bool GetVendorProduct(int dev, int* vendor, int* product) {
|
||||
}
|
||||
std::string_view readStr{readBuf};
|
||||
if (auto v = wpi::parse_integer<int>(
|
||||
readStr.substr(readStr.find('v')).substr(1, 4), 16)) {
|
||||
wpi::substr(wpi::substr(readStr, readStr.find('v')), 1, 4), 16)) {
|
||||
*vendor = v.value();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (auto v = wpi::parse_integer<int>(
|
||||
readStr.substr(readStr.find('p')).substr(1, 4), 16)) {
|
||||
wpi::substr(wpi::substr(readStr, readStr.find('p')), 1, 4), 16)) {
|
||||
*product = v.value();
|
||||
} else {
|
||||
return false;
|
||||
@@ -236,8 +236,8 @@ static bool GetDescriptionIoctl(const char* cpath, std::string* desc) {
|
||||
std::optional<int> vendor;
|
||||
std::optional<int> product;
|
||||
if (wpi::starts_with(card, "UVC Camera (") &&
|
||||
(vendor = wpi::parse_integer<int>(card.substr(12, 4), 16)) &&
|
||||
(product = wpi::parse_integer<int>(card.substr(17, 4), 16))) {
|
||||
(vendor = wpi::parse_integer<int>(wpi::substr(card, 12, 4), 16)) &&
|
||||
(product = wpi::parse_integer<int>(wpi::substr(card, 17, 4), 16))) {
|
||||
std::string card2 = GetUsbNameFromId(vendor.value(), product.value());
|
||||
if (!card2.empty()) {
|
||||
*desc = std::move(card2);
|
||||
@@ -283,7 +283,7 @@ static int GetDeviceNum(const char* cpath) {
|
||||
if (!wpi::starts_with(fn, "video")) {
|
||||
return -1;
|
||||
}
|
||||
if (auto dev = wpi::parse_integer<int>(fn.substr(5), 10)) {
|
||||
if (auto dev = wpi::parse_integer<int>(wpi::substr(fn, 5), 10)) {
|
||||
return dev.value();
|
||||
}
|
||||
return -1;
|
||||
@@ -1635,7 +1635,8 @@ std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
|
||||
}
|
||||
|
||||
unsigned int dev = 0;
|
||||
if (auto v = wpi::parse_integer<unsigned int>(fname.substr(5), 10)) {
|
||||
if (auto v =
|
||||
wpi::parse_integer<unsigned int>(wpi::substr(fname, 5), 10)) {
|
||||
dev = v.value();
|
||||
} else {
|
||||
continue;
|
||||
@@ -1686,7 +1687,8 @@ std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
|
||||
std::string fname = fs::path{target}.filename();
|
||||
std::optional<unsigned int> dev;
|
||||
if (wpi::starts_with(fname, "video") &&
|
||||
(dev = wpi::parse_integer<unsigned int>(fname.substr(5), 10)) &&
|
||||
(dev = wpi::parse_integer<unsigned int>(wpi::substr(fname, 5),
|
||||
10)) &&
|
||||
dev.value() < retval.size()) {
|
||||
retval[dev.value()].otherPaths.emplace_back(path.str());
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "UsbUtil.h"
|
||||
|
||||
@@ -93,7 +94,7 @@ static int GetStringCtrlIoctl(int fd, int id, int maximum, std::string* value) {
|
||||
|
||||
static int SetStringCtrlIoctl(int fd, int id, int maximum,
|
||||
std::string_view value) {
|
||||
wpi::SmallString<64> str{value.substr(0, maximum)};
|
||||
wpi::SmallString<64> str{wpi::substr(value, 0, maximum)};
|
||||
|
||||
struct v4l2_ext_control ctrl;
|
||||
struct v4l2_ext_controls ctrls;
|
||||
|
||||
@@ -49,7 +49,7 @@ static std::string GetUsbNameFromFile(int vendor, int product) {
|
||||
// look for vendor at start of line
|
||||
if (wpi::starts_with(line, vendorStr)) {
|
||||
foundVendor = true;
|
||||
buf += wpi::trim(line.substr(5));
|
||||
buf += wpi::trim(wpi::substr(line, 5));
|
||||
buf += ' ';
|
||||
continue;
|
||||
}
|
||||
@@ -62,8 +62,8 @@ static std::string GetUsbNameFromFile(int vendor, int product) {
|
||||
}
|
||||
|
||||
// look for product
|
||||
if (wpi::starts_with(line.substr(1), productStr)) {
|
||||
buf += wpi::trim(line.substr(6));
|
||||
if (wpi::starts_with(wpi::substr(line, 1), productStr)) {
|
||||
buf += wpi::trim(wpi::substr(line, 6));
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <codecvt>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -25,7 +24,9 @@
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <wpi/ConvertUTF.h>
|
||||
#include <wpi/MemAlloc.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
@@ -72,8 +73,9 @@ UsbCameraImpl::UsbCameraImpl(std::string_view name, wpi::Logger& logger,
|
||||
Notifier& notifier, Telemetry& telemetry,
|
||||
std::string_view path)
|
||||
: SourceImpl{name, logger, notifier, telemetry}, m_path{path} {
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||
m_widePath = utf8_conv.from_bytes(m_path.c_str());
|
||||
wpi::SmallVector<wchar_t, 128> wideStorage;
|
||||
wpi::sys::windows::UTF8ToUTF16(m_path, wideStorage);
|
||||
m_widePath = std::wstring{wideStorage.data(), wideStorage.size()};
|
||||
m_deviceId = -1;
|
||||
StartMessagePump();
|
||||
}
|
||||
@@ -227,7 +229,7 @@ void UsbCameraImpl::PostRequestNewFrame() {
|
||||
|
||||
bool UsbCameraImpl::CheckDeviceChange(WPARAM wParam, DEV_BROADCAST_HDR* pHdr,
|
||||
bool* connected) {
|
||||
DEV_BROADCAST_DEVICEINTERFACE* pDi = NULL;
|
||||
DEV_BROADCAST_DEVICEINTERFACE_A* pDi = NULL;
|
||||
|
||||
*connected = false;
|
||||
|
||||
@@ -240,9 +242,9 @@ bool UsbCameraImpl::CheckDeviceChange(WPARAM wParam, DEV_BROADCAST_HDR* pHdr,
|
||||
|
||||
// Compare the device name with the symbolic link.
|
||||
|
||||
pDi = reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(pHdr);
|
||||
pDi = reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE_A*>(pHdr);
|
||||
|
||||
if (_stricmp(m_path.c_str(), pDi->dbcc_name) == 0) {
|
||||
if (wpi::equals_lower(m_path, pDi->dbcc_name)) {
|
||||
if (wParam == DBT_DEVICEARRIVAL) {
|
||||
*connected = true;
|
||||
return true;
|
||||
@@ -269,7 +271,7 @@ void UsbCameraImpl::DeviceDisconnect() {
|
||||
|
||||
static bool IsPercentageProperty(std::string_view name) {
|
||||
if (wpi::starts_with(name, "raw_"))
|
||||
name = name.substr(4);
|
||||
name = wpi::substr(name, 4);
|
||||
return name == "Brightness" || name == "Contrast" || name == "Saturation" ||
|
||||
name == "Hue" || name == "Sharpness" || name == "Gain" ||
|
||||
name == "Exposure";
|
||||
@@ -411,11 +413,12 @@ LRESULT UsbCameraImpl::PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam,
|
||||
// If has device ID, use the device ID from the event
|
||||
// because of windows bug
|
||||
auto&& device = devices[m_deviceId];
|
||||
DEV_BROADCAST_DEVICEINTERFACE* pDi =
|
||||
reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(parameter);
|
||||
DEV_BROADCAST_DEVICEINTERFACE_A* pDi =
|
||||
reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE_A*>(parameter);
|
||||
m_path = pDi->dbcc_name;
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||
m_widePath = utf8_conv.from_bytes(m_path.c_str());
|
||||
wpi::SmallVector<wchar_t, 128> wideStorage;
|
||||
wpi::sys::windows::UTF8ToUTF16(m_path, wideStorage);
|
||||
m_widePath = std::wstring{wideStorage.data(), wideStorage.size()};
|
||||
} else {
|
||||
// This device not found
|
||||
break;
|
||||
@@ -482,9 +485,16 @@ bool UsbCameraImpl::DeviceConnect() {
|
||||
const wchar_t* path = m_widePath.c_str();
|
||||
m_mediaSource = CreateVideoCaptureDevice(path);
|
||||
|
||||
if (!m_mediaSource)
|
||||
if (!m_mediaSource) {
|
||||
return false;
|
||||
m_imageCallback = CreateSourceReaderCB(shared_from_this(), m_mode);
|
||||
}
|
||||
auto weakThis = weak_from_this();
|
||||
auto sharedThis = weakThis.lock();
|
||||
if (sharedThis) {
|
||||
m_imageCallback = CreateSourceReaderCB(sharedThis, m_mode);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sourceReader =
|
||||
CreateSourceReader(m_mediaSource.Get(), m_imageCallback.Get());
|
||||
@@ -747,8 +757,9 @@ CS_StatusValue UsbCameraImpl::DeviceProcessCommand(
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_path = msg->dataStr;
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||
m_widePath = utf8_conv.from_bytes(m_path.c_str());
|
||||
wpi::SmallVector<wchar_t, 128> wideStorage;
|
||||
wpi::sys::windows::UTF8ToUTF16(m_path, wideStorage);
|
||||
m_widePath = std::wstring{wideStorage.data(), wideStorage.size()};
|
||||
}
|
||||
DeviceDisconnect();
|
||||
DeviceConnect();
|
||||
@@ -1048,7 +1059,8 @@ std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
|
||||
// Ensure we are initialized by grabbing the message pump
|
||||
// GetMessagePump();
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||
wpi::SmallString<128> storage;
|
||||
WCHAR buf[512];
|
||||
ComPtr<IMFAttributes> pAttributes;
|
||||
IMFActivate** ppDevices = nullptr;
|
||||
UINT32 count = 0;
|
||||
@@ -1080,14 +1092,19 @@ std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
|
||||
for (UINT32 i = 0; i < count; i++) {
|
||||
UsbCameraInfo info;
|
||||
info.dev = i;
|
||||
WCHAR buf[512];
|
||||
|
||||
UINT32 characters = 0;
|
||||
ppDevices[i]->GetString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, buf,
|
||||
sizeof(buf) / sizeof(WCHAR), NULL);
|
||||
info.name = utf8_conv.to_bytes(buf);
|
||||
sizeof(buf) / sizeof(WCHAR), &characters);
|
||||
storage.clear();
|
||||
wpi::sys::windows::UTF16ToUTF8(buf, characters, storage);
|
||||
info.name = storage.string();
|
||||
ppDevices[i]->GetString(
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, buf,
|
||||
sizeof(buf) / sizeof(WCHAR), NULL);
|
||||
info.path = utf8_conv.to_bytes(buf);
|
||||
sizeof(buf) / sizeof(WCHAR), &characters);
|
||||
storage.clear();
|
||||
wpi::sys::windows::UTF16ToUTF8(buf, characters, storage);
|
||||
info.path = storage.string();
|
||||
|
||||
// Try to parse path from symbolic link
|
||||
ParseVidAndPid(info.path, &info.productId, &info.vendorId);
|
||||
|
||||
@@ -89,7 +89,7 @@ static std::shared_ptr<ClassHolder> GetClassHolder() {
|
||||
WindowsMessagePump::WindowsMessagePump(
|
||||
std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)> callback) {
|
||||
m_callback = callback;
|
||||
auto handle = CreateEvent(NULL, true, false, NULL);
|
||||
auto handle = CreateEventA(NULL, true, false, NULL);
|
||||
m_mainThread = std::thread([=] { ThreadMain(handle); });
|
||||
auto waitResult = WaitForSingleObject(handle, 1000);
|
||||
if (waitResult == WAIT_OBJECT_0) {
|
||||
@@ -98,7 +98,7 @@ WindowsMessagePump::WindowsMessagePump(
|
||||
}
|
||||
|
||||
WindowsMessagePump::~WindowsMessagePump() {
|
||||
auto res = SendMessage(hwnd, WM_CLOSE, NULL, NULL);
|
||||
auto res = SendMessageA(hwnd, WM_CLOSE, NULL, NULL);
|
||||
if (m_mainThread.joinable())
|
||||
m_mainThread.join();
|
||||
}
|
||||
@@ -110,28 +110,28 @@ void WindowsMessagePump::ThreadMain(HANDLE eventHandle) {
|
||||
MFStartup(MF_VERSION);
|
||||
|
||||
auto classHolder = GetClassHolder();
|
||||
hwnd = CreateWindowEx(0, classHolder->class_name, "dummy_name", 0, 0, 0, 0, 0,
|
||||
HWND_MESSAGE, NULL, NULL, this);
|
||||
hwnd = CreateWindowExA(0, classHolder->class_name, "dummy_name", 0, 0, 0, 0,
|
||||
0, HWND_MESSAGE, NULL, NULL, this);
|
||||
|
||||
// Register for device notifications
|
||||
HDEVNOTIFY g_hdevnotify = NULL;
|
||||
HDEVNOTIFY g_hdevnotify2 = NULL;
|
||||
|
||||
DEV_BROADCAST_DEVICEINTERFACE di = {0};
|
||||
DEV_BROADCAST_DEVICEINTERFACE_A di = {0};
|
||||
di.dbcc_size = sizeof(di);
|
||||
di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
di.dbcc_classguid = KSCATEGORY_CAPTURE;
|
||||
|
||||
g_hdevnotify =
|
||||
RegisterDeviceNotification(hwnd, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
RegisterDeviceNotificationA(hwnd, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
DEV_BROADCAST_DEVICEINTERFACE di2 = {0};
|
||||
DEV_BROADCAST_DEVICEINTERFACE_A di2 = {0};
|
||||
di2.dbcc_size = sizeof(di2);
|
||||
di2.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
di2.dbcc_classguid = KSCATEGORY_VIDEO_CAMERA;
|
||||
|
||||
g_hdevnotify2 =
|
||||
RegisterDeviceNotification(hwnd, &di2, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
RegisterDeviceNotificationA(hwnd, &di2, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
SetEvent(eventHandle);
|
||||
|
||||
|
||||
@@ -125,9 +125,10 @@ doxygen {
|
||||
}
|
||||
|
||||
case_sense_names false
|
||||
extension_mapping 'inc=C++'
|
||||
extension_mapping 'inc=C++', 'no_extension=C++'
|
||||
extract_all true
|
||||
extract_static true
|
||||
file_patterns '*'
|
||||
full_path_names true
|
||||
generate_html true
|
||||
generate_latex false
|
||||
|
||||
@@ -185,15 +185,18 @@ if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxra
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
lib project: ':cscore', library: 'cscore', linkage: 'static'
|
||||
lib library: 'glassnt', linkage: 'static'
|
||||
lib library: nativeName, linkage: 'static'
|
||||
lib project: ':ntcore', library: 'ntcore', linkage: 'static'
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
|
||||
lib project: ':wpimath', library: 'wpimath', linkage: 'static'
|
||||
lib project: ':wpigui', library: 'wpigui', linkage: 'static'
|
||||
nativeUtils.useRequiredLibrary(it, 'opencv_static')
|
||||
nativeUtils.useRequiredLibrary(it, 'imgui_static')
|
||||
if (it.targetPlatform.operatingSystem.isWindows()) {
|
||||
it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
|
||||
it.linker.args << '/DELAYLOAD:MF.dll' << '/DELAYLOAD:MFReadWrite.dll' << '/DELAYLOAD:MFPlat.dll' << '/delay:nobind'
|
||||
} else if (it.targetPlatform.operatingSystem.isMacOsX()) {
|
||||
it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
|
||||
} else {
|
||||
|
||||
52
glass/src/app/native/cpp/camerasupport.cpp
Normal file
52
glass/src/app/native/cpp/camerasupport.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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 "camerasupport.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Windows.h"
|
||||
#include "delayimp.h"
|
||||
#pragma comment(lib, "delayimp.lib")
|
||||
static int CheckDelayException(int exception_value) {
|
||||
if (exception_value ==
|
||||
VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) ||
|
||||
exception_value ==
|
||||
VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND)) {
|
||||
// This example just executes the handler.
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
// Don't attempt to handle other errors
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
static bool TryDelayLoadAllImports(LPCSTR szDll) {
|
||||
__try {
|
||||
HRESULT hr = __HrLoadAllImportsForDll(szDll);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
} __except (CheckDelayException(GetExceptionCode())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
namespace glass {
|
||||
bool HasCameraSupport() {
|
||||
bool hasCameraSupport = false;
|
||||
hasCameraSupport = TryDelayLoadAllImports("MF.dll");
|
||||
if (hasCameraSupport) {
|
||||
hasCameraSupport = TryDelayLoadAllImports("MFPlat.dll");
|
||||
}
|
||||
if (hasCameraSupport) {
|
||||
hasCameraSupport = TryDelayLoadAllImports("MFReadWrite.dll");
|
||||
}
|
||||
return hasCameraSupport;
|
||||
}
|
||||
} // namespace glass
|
||||
#else
|
||||
namespace glass {
|
||||
bool HasCameraSupport() {
|
||||
return true;
|
||||
}
|
||||
} // namespace glass
|
||||
#endif
|
||||
9
glass/src/app/native/cpp/camerasupport.h
Normal file
9
glass/src/app/native/cpp/camerasupport.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// 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
|
||||
|
||||
namespace glass {
|
||||
bool HasCameraSupport();
|
||||
} // namespace glass
|
||||
@@ -11,7 +11,9 @@
|
||||
#include <wpigui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/MainMenuBar.h"
|
||||
#include "glass/Model.h"
|
||||
#include "glass/Storage.h"
|
||||
#include "glass/View.h"
|
||||
#include "glass/networktables/NetworkTables.h"
|
||||
#include "glass/networktables/NetworkTablesProvider.h"
|
||||
@@ -39,9 +41,12 @@ static std::unique_ptr<glass::NetworkTablesProvider> gNtProvider;
|
||||
static std::unique_ptr<glass::NetworkTablesModel> gNetworkTablesModel;
|
||||
static std::unique_ptr<glass::NetworkTablesSettings> gNetworkTablesSettings;
|
||||
static glass::LogData gNetworkTablesLog;
|
||||
static glass::Window* gNetworkTablesWindow;
|
||||
static glass::Window* gNetworkTablesSettingsWindow;
|
||||
static glass::Window* gNetworkTablesLogWindow;
|
||||
static std::unique_ptr<glass::Window> gNetworkTablesWindow;
|
||||
static std::unique_ptr<glass::Window> gNetworkTablesSettingsWindow;
|
||||
static std::unique_ptr<glass::Window> gNetworkTablesLogWindow;
|
||||
|
||||
static glass::MainMenuBar gMainMenu;
|
||||
static bool gAbout = false;
|
||||
|
||||
static void NtInitialize() {
|
||||
// update window title when connection status changes
|
||||
@@ -84,48 +89,65 @@ static void NtInitialize() {
|
||||
}
|
||||
});
|
||||
|
||||
gNetworkTablesLogWindow = gNtProvider->AddWindow(
|
||||
"NetworkTables Log",
|
||||
gNetworkTablesLogWindow = std::make_unique<glass::Window>(
|
||||
glass::GetStorageRoot().GetChild("NetworkTables Log"),
|
||||
"NetworkTables Log", glass::Window::kHide);
|
||||
gNetworkTablesLogWindow->SetView(
|
||||
std::make_unique<glass::LogView>(&gNetworkTablesLog));
|
||||
if (gNetworkTablesLogWindow) {
|
||||
gNetworkTablesLogWindow->SetDefaultPos(250, 615);
|
||||
gNetworkTablesLogWindow->SetDefaultSize(600, 130);
|
||||
gNetworkTablesLogWindow->SetVisible(false);
|
||||
gNetworkTablesLogWindow->DisableRenamePopup();
|
||||
}
|
||||
gNetworkTablesLogWindow->SetDefaultPos(250, 615);
|
||||
gNetworkTablesLogWindow->SetDefaultSize(600, 130);
|
||||
gNetworkTablesLogWindow->DisableRenamePopup();
|
||||
gui::AddLateExecute([] { gNetworkTablesLogWindow->Display(); });
|
||||
|
||||
// NetworkTables table window
|
||||
gNetworkTablesModel = std::make_unique<glass::NetworkTablesModel>();
|
||||
gui::AddEarlyExecute([] { gNetworkTablesModel->Update(); });
|
||||
|
||||
gNetworkTablesWindow = gNtProvider->AddWindow(
|
||||
"NetworkTables",
|
||||
gNetworkTablesWindow = std::make_unique<glass::Window>(
|
||||
glass::GetStorageRoot().GetChild("NetworkTables View"), "NetworkTables");
|
||||
gNetworkTablesWindow->SetView(
|
||||
std::make_unique<glass::NetworkTablesView>(gNetworkTablesModel.get()));
|
||||
if (gNetworkTablesWindow) {
|
||||
gNetworkTablesWindow->SetDefaultPos(250, 277);
|
||||
gNetworkTablesWindow->SetDefaultSize(750, 185);
|
||||
gNetworkTablesWindow->DisableRenamePopup();
|
||||
}
|
||||
gNetworkTablesWindow->SetDefaultPos(250, 277);
|
||||
gNetworkTablesWindow->SetDefaultSize(750, 185);
|
||||
gNetworkTablesWindow->DisableRenamePopup();
|
||||
gui::AddLateExecute([] { gNetworkTablesWindow->Display(); });
|
||||
|
||||
// NetworkTables settings window
|
||||
gNetworkTablesSettings = std::make_unique<glass::NetworkTablesSettings>();
|
||||
gNetworkTablesSettings = std::make_unique<glass::NetworkTablesSettings>(
|
||||
glass::GetStorageRoot().GetChild("NetworkTables Settings"));
|
||||
gui::AddEarlyExecute([] { gNetworkTablesSettings->Update(); });
|
||||
|
||||
gNetworkTablesSettingsWindow = gNtProvider->AddWindow(
|
||||
"NetworkTables Settings", [] { gNetworkTablesSettings->Display(); });
|
||||
if (gNetworkTablesSettingsWindow) {
|
||||
gNetworkTablesSettingsWindow->SetDefaultPos(30, 30);
|
||||
gNetworkTablesSettingsWindow->SetFlags(ImGuiWindowFlags_AlwaysAutoResize);
|
||||
gNetworkTablesSettingsWindow->DisableRenamePopup();
|
||||
}
|
||||
gNetworkTablesSettingsWindow = std::make_unique<glass::Window>(
|
||||
glass::GetStorageRoot().GetChild("NetworkTables Settings"),
|
||||
"NetworkTables Settings");
|
||||
gNetworkTablesSettingsWindow->SetView(
|
||||
glass::MakeFunctionView([] { gNetworkTablesSettings->Display(); }));
|
||||
gNetworkTablesSettingsWindow->SetDefaultPos(30, 30);
|
||||
gNetworkTablesSettingsWindow->SetFlags(ImGuiWindowFlags_AlwaysAutoResize);
|
||||
gNetworkTablesSettingsWindow->DisableRenamePopup();
|
||||
gui::AddLateExecute([] { gNetworkTablesSettingsWindow->Display(); });
|
||||
|
||||
gui::AddWindowScaler([](float scale) {
|
||||
// scale default window positions
|
||||
gNetworkTablesLogWindow->ScaleDefault(scale);
|
||||
gNetworkTablesWindow->ScaleDefault(scale);
|
||||
gNetworkTablesSettingsWindow->ScaleDefault(scale);
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine,
|
||||
int nCmdShow) {
|
||||
int argc = __argc;
|
||||
char** argv = __argv;
|
||||
#else
|
||||
int main() {
|
||||
int main(int argc, char** argv) {
|
||||
#endif
|
||||
std::string_view saveDir;
|
||||
if (argc == 2) {
|
||||
saveDir = argv[1];
|
||||
}
|
||||
|
||||
gui::CreateContext();
|
||||
glass::CreateContext();
|
||||
|
||||
@@ -137,20 +159,24 @@ int main() {
|
||||
gui::AddIcon(glass::GetResource_glass_256_png());
|
||||
gui::AddIcon(glass::GetResource_glass_512_png());
|
||||
|
||||
gPlotProvider = std::make_unique<glass::PlotProvider>("Plot");
|
||||
gNtProvider = std::make_unique<glass::NetworkTablesProvider>("NTProvider");
|
||||
gPlotProvider = std::make_unique<glass::PlotProvider>(
|
||||
glass::GetStorageRoot().GetChild("Plots"));
|
||||
gNtProvider = std::make_unique<glass::NetworkTablesProvider>(
|
||||
glass::GetStorageRoot().GetChild("NetworkTables"));
|
||||
|
||||
gui::ConfigurePlatformSaveFile("glass.ini");
|
||||
glass::SetStorageName("glass");
|
||||
glass::SetStorageDir(saveDir.empty() ? gui::GetPlatformSaveFileDir()
|
||||
: saveDir);
|
||||
gPlotProvider->GlobalInit();
|
||||
gui::AddInit([] { glass::ResetTime(); });
|
||||
gNtProvider->GlobalInit();
|
||||
gui::AddInit(NtInitialize);
|
||||
NtInitialize();
|
||||
|
||||
glass::AddStandardNetworkTablesViews(*gNtProvider);
|
||||
|
||||
gui::AddLateExecute([] {
|
||||
ImGui::BeginMainMenuBar();
|
||||
gui::EmitViewMenu();
|
||||
gui::AddLateExecute([] { gMainMenu.Display(); });
|
||||
|
||||
gMainMenu.AddMainMenu([] {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
if (ImGui::MenuItem("Reset Time")) {
|
||||
glass::ResetTime();
|
||||
@@ -181,23 +207,25 @@ int main() {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
bool about = false;
|
||||
if (ImGui::BeginMenu("Info")) {
|
||||
if (ImGui::MenuItem("About")) {
|
||||
about = true;
|
||||
gAbout = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
});
|
||||
|
||||
if (about) {
|
||||
gui::AddLateExecute([] {
|
||||
if (gAbout) {
|
||||
ImGui::OpenPopup("About");
|
||||
about = false;
|
||||
gAbout = false;
|
||||
}
|
||||
if (ImGui::BeginPopupModal("About")) {
|
||||
ImGui::Text("Glass: A different kind of dashboard");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("v%s", GetWPILibVersion());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Save location: %s", glass::GetStorageDir().c_str());
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
@@ -208,11 +236,15 @@ int main() {
|
||||
gui::Initialize("Glass - DISCONNECTED", 1024, 768);
|
||||
gui::Main();
|
||||
|
||||
gNetworkTablesSettingsWindow.reset();
|
||||
gNetworkTablesLogWindow.reset();
|
||||
gNetworkTablesWindow.reset();
|
||||
gNetworkTablesModel.reset();
|
||||
gNetworkTablesSettings.reset();
|
||||
gNtProvider.reset();
|
||||
gPlotProvider.reset();
|
||||
|
||||
glass::DestroyContext();
|
||||
gui::DestroyContext();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,21 @@
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/json.h>
|
||||
#include <wpi/json_serializer.h>
|
||||
#include <wpi/raw_istream.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
#include <wpi/timestamp.h>
|
||||
#include <wpigui.h>
|
||||
#include <wpigui_internal.h>
|
||||
|
||||
#include "glass/ContextInternal.h"
|
||||
|
||||
@@ -21,172 +29,292 @@ using namespace glass;
|
||||
|
||||
Context* glass::gContext;
|
||||
|
||||
static bool ConvertInt(Storage::Value* value) {
|
||||
value->type = Storage::Value::kInt;
|
||||
if (auto val = wpi::parse_integer<int>(value->stringVal, 10)) {
|
||||
value->intVal = val.value();
|
||||
return true;
|
||||
static void WorkspaceResetImpl() {
|
||||
// call reset functions
|
||||
for (auto&& reset : gContext->workspaceReset) {
|
||||
if (reset) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
// clear storage
|
||||
for (auto&& root : gContext->storageRoots) {
|
||||
root.second->Clear();
|
||||
}
|
||||
|
||||
// ImGui reset
|
||||
ImGui::ClearIniSettings();
|
||||
}
|
||||
|
||||
static bool ConvertInt64(Storage::Value* value) {
|
||||
value->type = Storage::Value::kInt64;
|
||||
if (auto val = wpi::parse_integer<int64_t>(value->stringVal, 10)) {
|
||||
value->int64Val = val.value();
|
||||
return true;
|
||||
static void WorkspaceInit() {
|
||||
for (auto&& init : gContext->workspaceInit) {
|
||||
if (init) {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto&& root : gContext->storageRoots) {
|
||||
root.getValue()->Apply();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ConvertBool(Storage::Value* value) {
|
||||
value->type = Storage::Value::kBool;
|
||||
if (auto val = wpi::parse_integer<int>(value->stringVal, 10)) {
|
||||
value->intVal = (val.value() != 0);
|
||||
return true;
|
||||
static bool JsonToWindow(const wpi::json& jfile, const char* filename) {
|
||||
if (!jfile.is_object()) {
|
||||
ImGui::LogText("%s top level is not object", filename);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
// loop over JSON and generate ini format
|
||||
std::string iniStr;
|
||||
wpi::raw_string_ostream ini{iniStr};
|
||||
|
||||
for (auto&& jsection : jfile.items()) {
|
||||
if (!jsection.value().is_object()) {
|
||||
ImGui::LogText("%s section %s is not object", filename,
|
||||
jsection.key().c_str());
|
||||
return false;
|
||||
}
|
||||
for (auto&& jsubsection : jsection.value().items()) {
|
||||
if (!jsubsection.value().is_object()) {
|
||||
ImGui::LogText("%s section %s subsection %s is not object", filename,
|
||||
jsection.key().c_str(), jsubsection.key().c_str());
|
||||
return false;
|
||||
}
|
||||
ini << '[' << jsection.key() << "][" << jsubsection.key() << "]\n";
|
||||
for (auto&& jkv : jsubsection.value().items()) {
|
||||
try {
|
||||
auto& value = jkv.value().get_ref<const std::string&>();
|
||||
ini << jkv.key() << '=' << value << "\n";
|
||||
} catch (wpi::json::exception&) {
|
||||
ImGui::LogText("%s section %s subsection %s value %s is not string",
|
||||
filename, jsection.key().c_str(),
|
||||
jsubsection.key().c_str(), jkv.key().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ini << '\n';
|
||||
}
|
||||
}
|
||||
ini.flush();
|
||||
|
||||
ImGui::LoadIniSettingsFromMemory(iniStr.data(), iniStr.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ConvertFloat(Storage::Value* value) {
|
||||
value->type = Storage::Value::kFloat;
|
||||
if (auto val = wpi::parse_float<float>(value->stringVal)) {
|
||||
value->floatVal = val.value();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ConvertDouble(Storage::Value* value) {
|
||||
value->type = Storage::Value::kDouble;
|
||||
if (auto val = wpi::parse_float<double>(value->stringVal)) {
|
||||
value->doubleVal = val.value();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void* GlassStorageReadOpen(ImGuiContext*, ImGuiSettingsHandler* handler,
|
||||
const char* name) {
|
||||
auto ctx = static_cast<Context*>(handler->UserData);
|
||||
auto& storage = ctx->storage[name];
|
||||
if (!storage) {
|
||||
storage = std::make_unique<Storage>();
|
||||
}
|
||||
return storage.get();
|
||||
}
|
||||
|
||||
static void GlassStorageReadLine(ImGuiContext*, ImGuiSettingsHandler*,
|
||||
void* entry, const char* line) {
|
||||
auto storage = static_cast<Storage*>(entry);
|
||||
auto [key, val] = wpi::split(line, '=');
|
||||
auto& keys = storage->GetKeys();
|
||||
auto& values = storage->GetValues();
|
||||
auto it = std::find(keys.begin(), keys.end(), key);
|
||||
if (it == keys.end()) {
|
||||
keys.emplace_back(key);
|
||||
values.emplace_back(std::make_unique<Storage::Value>(val));
|
||||
static bool LoadWindowStorageImpl(const std::string& filename) {
|
||||
std::error_code ec;
|
||||
wpi::raw_fd_istream is{filename, ec};
|
||||
if (ec) {
|
||||
ImGui::LogText("error opening %s: %s", filename.c_str(),
|
||||
ec.message().c_str());
|
||||
return false;
|
||||
} else {
|
||||
auto& value = *values[it - keys.begin()];
|
||||
value.stringVal = val;
|
||||
switch (value.type) {
|
||||
case Storage::Value::kInt:
|
||||
ConvertInt(&value);
|
||||
break;
|
||||
case Storage::Value::kInt64:
|
||||
ConvertInt64(&value);
|
||||
break;
|
||||
case Storage::Value::kBool:
|
||||
ConvertBool(&value);
|
||||
break;
|
||||
case Storage::Value::kFloat:
|
||||
ConvertFloat(&value);
|
||||
break;
|
||||
case Storage::Value::kDouble:
|
||||
ConvertDouble(&value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
try {
|
||||
return JsonToWindow(wpi::json::parse(is), filename.c_str());
|
||||
} catch (wpi::json::parse_error& e) {
|
||||
ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GlassStorageWriteAll(ImGuiContext*, ImGuiSettingsHandler* handler,
|
||||
ImGuiTextBuffer* out_buf) {
|
||||
auto ctx = static_cast<Context*>(handler->UserData);
|
||||
|
||||
// sort for output
|
||||
std::vector<wpi::StringMapConstIterator<std::unique_ptr<Storage>>> sorted;
|
||||
for (auto it = ctx->storage.begin(); it != ctx->storage.end(); ++it) {
|
||||
sorted.emplace_back(it);
|
||||
static bool LoadStorageRootImpl(Context* ctx, const std::string& filename,
|
||||
std::string_view rootName) {
|
||||
std::error_code ec;
|
||||
wpi::raw_fd_istream is{filename, ec};
|
||||
if (ec) {
|
||||
ImGui::LogText("error opening %s: %s", filename.c_str(),
|
||||
ec.message().c_str());
|
||||
return false;
|
||||
} else {
|
||||
auto& storage = ctx->storageRoots[rootName];
|
||||
bool createdStorage = false;
|
||||
if (!storage) {
|
||||
storage = std::make_unique<Storage>();
|
||||
createdStorage = true;
|
||||
}
|
||||
try {
|
||||
storage->FromJson(wpi::json::parse(is), filename.c_str());
|
||||
} catch (wpi::json::parse_error& e) {
|
||||
ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
|
||||
if (createdStorage) {
|
||||
ctx->storageRoots.erase(rootName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) {
|
||||
return a->getKey() < b->getKey();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto&& entryIt : sorted) {
|
||||
auto& entry = *entryIt;
|
||||
out_buf->append("[GlassStorage][");
|
||||
out_buf->append(entry.first().data(),
|
||||
entry.first().data() + entry.first().size());
|
||||
out_buf->append("]\n");
|
||||
auto& keys = entry.second->GetKeys();
|
||||
auto& values = entry.second->GetValues();
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
out_buf->append(keys[i].data(), keys[i].data() + keys[i].size());
|
||||
out_buf->append("=");
|
||||
auto& value = *values[i];
|
||||
switch (value.type) {
|
||||
case Storage::Value::kInt:
|
||||
out_buf->appendf("%d\n", value.intVal);
|
||||
break;
|
||||
case Storage::Value::kInt64:
|
||||
out_buf->appendf("%" PRId64 "\n", value.int64Val);
|
||||
break;
|
||||
case Storage::Value::kBool:
|
||||
out_buf->appendf("%d\n", value.boolVal ? 1 : 0);
|
||||
break;
|
||||
case Storage::Value::kFloat:
|
||||
out_buf->appendf("%f\n", value.floatVal);
|
||||
break;
|
||||
case Storage::Value::kDouble:
|
||||
out_buf->appendf("%f\n", value.doubleVal);
|
||||
break;
|
||||
case Storage::Value::kNone:
|
||||
case Storage::Value::kString:
|
||||
out_buf->append(value.stringVal.data(),
|
||||
value.stringVal.data() + value.stringVal.size());
|
||||
out_buf->append("\n");
|
||||
break;
|
||||
static bool LoadStorageImpl(Context* ctx, std::string_view dir,
|
||||
std::string_view name) {
|
||||
WorkspaceResetImpl();
|
||||
|
||||
bool rv = true;
|
||||
for (auto&& root : ctx->storageRoots) {
|
||||
std::string filename;
|
||||
auto rootName = root.getKey();
|
||||
if (rootName.empty()) {
|
||||
filename = (fs::path{dir} / fmt::format("{}.json", name)).string();
|
||||
} else {
|
||||
filename =
|
||||
(fs::path{dir} / fmt::format("{}-{}.json", name, rootName)).string();
|
||||
}
|
||||
if (!LoadStorageRootImpl(ctx, filename, rootName)) {
|
||||
rv = false;
|
||||
}
|
||||
}
|
||||
|
||||
WorkspaceInit();
|
||||
return rv;
|
||||
}
|
||||
|
||||
static wpi::json WindowToJson() {
|
||||
size_t iniLen;
|
||||
const char* iniData = ImGui::SaveIniSettingsToMemory(&iniLen);
|
||||
std::string_view ini{iniData, iniLen};
|
||||
|
||||
// parse the ini data and build JSON
|
||||
// JSON format:
|
||||
// {
|
||||
// "Section": {
|
||||
// "Subsection": {
|
||||
// "Key": "Value" // all values are saved as strings
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
wpi::json out = wpi::json::object();
|
||||
wpi::json* curSection = nullptr;
|
||||
while (!ini.empty()) {
|
||||
std::string_view line;
|
||||
std::tie(line, ini) = wpi::split(ini, '\n');
|
||||
line = wpi::trim(line);
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (line[0] == '[') {
|
||||
// new section
|
||||
auto [section, subsection] = wpi::split(line, ']');
|
||||
section = wpi::drop_front(section); // drop '['; ']' was dropped by split
|
||||
subsection = wpi::drop_back(wpi::drop_front(subsection)); // drop []
|
||||
auto& jsection = out[section];
|
||||
if (jsection.is_null()) {
|
||||
jsection = wpi::json::object();
|
||||
}
|
||||
curSection = &jsection[subsection];
|
||||
if (curSection->is_null()) {
|
||||
*curSection = wpi::json::object();
|
||||
}
|
||||
} else {
|
||||
// value
|
||||
if (!curSection) {
|
||||
continue; // shouldn't happen, but just in case
|
||||
}
|
||||
auto [name, value] = wpi::split(line, '=');
|
||||
(*curSection)[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool SaveWindowStorageImpl(const std::string& filename) {
|
||||
std::error_code ec;
|
||||
wpi::raw_fd_ostream os{filename, ec};
|
||||
if (ec) {
|
||||
ImGui::LogText("error opening %s: %s", filename.c_str(),
|
||||
ec.message().c_str());
|
||||
return false;
|
||||
}
|
||||
WindowToJson().dump(os, 2);
|
||||
os << '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SaveStorageRootImpl(Context* ctx, const std::string& filename,
|
||||
const Storage& storage) {
|
||||
std::error_code ec;
|
||||
wpi::raw_fd_ostream os{filename, ec};
|
||||
if (ec) {
|
||||
ImGui::LogText("error opening %s: %s", filename.c_str(),
|
||||
ec.message().c_str());
|
||||
return false;
|
||||
}
|
||||
storage.ToJson().dump(os, 2);
|
||||
os << '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SaveStorageImpl(Context* ctx, std::string_view dir,
|
||||
std::string_view name, bool exiting) {
|
||||
fs::path dirPath{dir};
|
||||
|
||||
std::error_code ec;
|
||||
fs::create_directories(dirPath, ec);
|
||||
if (ec) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle erasing save files on exit if requested
|
||||
if (exiting && wpi::gui::gContext->resetOnExit) {
|
||||
fs::remove(dirPath / fmt::format("{}-window.json", name), ec);
|
||||
for (auto&& root : ctx->storageRoots) {
|
||||
auto rootName = root.getKey();
|
||||
if (rootName.empty()) {
|
||||
fs::remove(dirPath / fmt::format("{}.json", name), ec);
|
||||
} else {
|
||||
fs::remove(dirPath / fmt::format("{}-{}.json", name, rootName), ec);
|
||||
}
|
||||
}
|
||||
out_buf->append("\n");
|
||||
}
|
||||
|
||||
bool rv = SaveWindowStorageImpl(
|
||||
(dirPath / fmt::format("{}-window.json", name)).string());
|
||||
|
||||
for (auto&& root : ctx->storageRoots) {
|
||||
auto rootName = root.getKey();
|
||||
std::string filename;
|
||||
if (rootName.empty()) {
|
||||
filename = (dirPath / fmt::format("{}.json", name)).string();
|
||||
} else {
|
||||
filename = (dirPath / fmt::format("{}-{}.json", name, rootName)).string();
|
||||
}
|
||||
if (!SaveStorageRootImpl(ctx, filename, *root.getValue())) {
|
||||
rv = false;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void Initialize(Context* ctx) {
|
||||
wpi::gui::AddInit([=] {
|
||||
ImGuiSettingsHandler ini_handler;
|
||||
ini_handler.TypeName = "GlassStorage";
|
||||
ini_handler.TypeHash = ImHashStr("GlassStorage");
|
||||
ini_handler.ReadOpenFn = GlassStorageReadOpen;
|
||||
ini_handler.ReadLineFn = GlassStorageReadLine;
|
||||
ini_handler.WriteAllFn = GlassStorageWriteAll;
|
||||
ini_handler.UserData = ctx;
|
||||
ImGui::GetCurrentContext()->SettingsHandlers.push_back(ini_handler);
|
||||
Context::Context()
|
||||
: sourceNameStorage{storageRoots.insert({"", std::make_unique<Storage>()})
|
||||
.first->getValue()
|
||||
->GetChild("sourceNames")} {
|
||||
storageStack.emplace_back(storageRoots[""].get());
|
||||
|
||||
ctx->sources.Initialize();
|
||||
});
|
||||
// override ImGui ini saving
|
||||
wpi::gui::ConfigureCustomSaveSettings(
|
||||
[this] { LoadStorageImpl(this, storageLoadDir, storageName); },
|
||||
[this] {
|
||||
LoadWindowStorageImpl((fs::path{storageLoadDir} /
|
||||
fmt::format("{}-window.json", storageName))
|
||||
.string());
|
||||
},
|
||||
[this](bool exiting) {
|
||||
SaveStorageImpl(this, storageAutoSaveDir, storageName, exiting);
|
||||
});
|
||||
}
|
||||
|
||||
static void Shutdown(Context* ctx) {}
|
||||
Context::~Context() {
|
||||
wpi::gui::ConfigureCustomSaveSettings(nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
Context* glass::CreateContext() {
|
||||
Context* ctx = new Context;
|
||||
if (!gContext) {
|
||||
SetCurrentContext(ctx);
|
||||
}
|
||||
Initialize(ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -194,7 +322,6 @@ void glass::DestroyContext(Context* ctx) {
|
||||
if (!ctx) {
|
||||
ctx = gContext;
|
||||
}
|
||||
Shutdown(ctx);
|
||||
if (gContext == ctx) {
|
||||
SetCurrentContext(nullptr);
|
||||
}
|
||||
@@ -217,215 +344,167 @@ uint64_t glass::GetZeroTime() {
|
||||
return gContext->zeroTime;
|
||||
}
|
||||
|
||||
Storage::Value& Storage::GetValue(std::string_view key) {
|
||||
auto it = std::find(m_keys.begin(), m_keys.end(), key);
|
||||
if (it == m_keys.end()) {
|
||||
m_keys.emplace_back(key);
|
||||
m_values.emplace_back(std::make_unique<Value>());
|
||||
return *m_values.back();
|
||||
} else {
|
||||
return *m_values[it - m_keys.begin()];
|
||||
void glass::WorkspaceReset() {
|
||||
WorkspaceResetImpl();
|
||||
WorkspaceInit();
|
||||
}
|
||||
|
||||
void glass::AddWorkspaceInit(std::function<void()> init) {
|
||||
if (init) {
|
||||
gContext->workspaceInit.emplace_back(std::move(init));
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFUN(CapsName, LowerName, CType) \
|
||||
CType Storage::Get##CapsName(std::string_view key, CType defaultVal) const { \
|
||||
auto it = std::find(m_keys.begin(), m_keys.end(), key); \
|
||||
if (it == m_keys.end()) \
|
||||
return defaultVal; \
|
||||
Value& value = *m_values[it - m_keys.begin()]; \
|
||||
if (value.type != Value::k##CapsName) { \
|
||||
if (!Convert##CapsName(&value)) \
|
||||
value.LowerName##Val = defaultVal; \
|
||||
} \
|
||||
return value.LowerName##Val; \
|
||||
} \
|
||||
\
|
||||
void Storage::Set##CapsName(std::string_view key, CType val) { \
|
||||
auto it = std::find(m_keys.begin(), m_keys.end(), key); \
|
||||
if (it == m_keys.end()) { \
|
||||
m_keys.emplace_back(key); \
|
||||
m_values.emplace_back(std::make_unique<Value>()); \
|
||||
m_values.back()->type = Value::k##CapsName; \
|
||||
m_values.back()->LowerName##Val = val; \
|
||||
} else { \
|
||||
Value& value = *m_values[it - m_keys.begin()]; \
|
||||
value.type = Value::k##CapsName; \
|
||||
value.LowerName##Val = val; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
CType* Storage::Get##CapsName##Ref(std::string_view key, CType defaultVal) { \
|
||||
auto it = std::find(m_keys.begin(), m_keys.end(), key); \
|
||||
if (it == m_keys.end()) { \
|
||||
m_keys.emplace_back(key); \
|
||||
m_values.emplace_back(std::make_unique<Value>()); \
|
||||
m_values.back()->type = Value::k##CapsName; \
|
||||
m_values.back()->LowerName##Val = defaultVal; \
|
||||
return &m_values.back()->LowerName##Val; \
|
||||
} else { \
|
||||
Value& value = *m_values[it - m_keys.begin()]; \
|
||||
if (value.type != Value::k##CapsName) { \
|
||||
if (!Convert##CapsName(&value)) \
|
||||
value.LowerName##Val = defaultVal; \
|
||||
} \
|
||||
return &value.LowerName##Val; \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFUN(Int, int, int)
|
||||
DEFUN(Int64, int64, int64_t)
|
||||
DEFUN(Bool, bool, bool)
|
||||
DEFUN(Float, float, float)
|
||||
DEFUN(Double, double, double)
|
||||
|
||||
std::string Storage::GetString(std::string_view key,
|
||||
std::string_view defaultVal) const {
|
||||
auto it = std::find(m_keys.begin(), m_keys.end(), key);
|
||||
if (it == m_keys.end()) {
|
||||
return std::string{defaultVal};
|
||||
}
|
||||
Value& value = *m_values[it - m_keys.begin()];
|
||||
value.type = Value::kString;
|
||||
return value.stringVal;
|
||||
}
|
||||
|
||||
void Storage::SetString(std::string_view key, std::string_view val) {
|
||||
auto it = std::find(m_keys.begin(), m_keys.end(), key);
|
||||
if (it == m_keys.end()) {
|
||||
m_keys.emplace_back(key);
|
||||
m_values.emplace_back(std::make_unique<Value>(val));
|
||||
m_values.back()->type = Value::kString;
|
||||
} else {
|
||||
Value& value = *m_values[it - m_keys.begin()];
|
||||
value.type = Value::kString;
|
||||
value.stringVal = val;
|
||||
void glass::AddWorkspaceReset(std::function<void()> reset) {
|
||||
if (reset) {
|
||||
gContext->workspaceReset.emplace_back(std::move(reset));
|
||||
}
|
||||
}
|
||||
|
||||
std::string* Storage::GetStringRef(std::string_view key,
|
||||
std::string_view defaultVal) {
|
||||
auto it = std::find(m_keys.begin(), m_keys.end(), key);
|
||||
if (it == m_keys.end()) {
|
||||
m_keys.emplace_back(key);
|
||||
m_values.emplace_back(std::make_unique<Value>(defaultVal));
|
||||
m_values.back()->type = Value::kString;
|
||||
return &m_values.back()->stringVal;
|
||||
void glass::SetStorageName(std::string_view name) {
|
||||
gContext->storageName = name;
|
||||
}
|
||||
|
||||
void glass::SetStorageDir(std::string_view dir) {
|
||||
if (dir.empty()) {
|
||||
gContext->storageLoadDir = ".";
|
||||
gContext->storageAutoSaveDir = ".";
|
||||
} else {
|
||||
Value& value = *m_values[it - m_keys.begin()];
|
||||
value.type = Value::kString;
|
||||
return &value.stringVal;
|
||||
gContext->storageLoadDir = dir;
|
||||
gContext->storageAutoSaveDir = dir;
|
||||
gContext->isPlatformSaveDir = (dir == wpi::gui::GetPlatformSaveFileDir());
|
||||
}
|
||||
}
|
||||
|
||||
std::string glass::GetStorageDir() {
|
||||
return gContext->storageAutoSaveDir;
|
||||
}
|
||||
|
||||
bool glass::LoadStorage(std::string_view dir) {
|
||||
SaveStorage();
|
||||
SetStorageDir(dir);
|
||||
LoadWindowStorageImpl((fs::path{gContext->storageLoadDir} /
|
||||
fmt::format("{}-window.json", gContext->storageName))
|
||||
.string());
|
||||
return LoadStorageImpl(gContext, dir, gContext->storageName);
|
||||
}
|
||||
|
||||
bool glass::SaveStorage() {
|
||||
return SaveStorageImpl(gContext, gContext->storageAutoSaveDir,
|
||||
gContext->storageName, false);
|
||||
}
|
||||
|
||||
bool glass::SaveStorage(std::string_view dir) {
|
||||
return SaveStorageImpl(gContext, dir, gContext->storageName, false);
|
||||
}
|
||||
|
||||
Storage& glass::GetCurStorageRoot() {
|
||||
return *gContext->storageStack.front();
|
||||
}
|
||||
|
||||
Storage& glass::GetStorageRoot(std::string_view rootName) {
|
||||
auto& storage = gContext->storageRoots[rootName];
|
||||
if (!storage) {
|
||||
storage = std::make_unique<Storage>();
|
||||
}
|
||||
return *storage;
|
||||
}
|
||||
|
||||
void glass::ResetStorageStack(std::string_view rootName) {
|
||||
if (gContext->storageStack.size() != 1) {
|
||||
ImGui::LogText("resetting non-empty storage stack");
|
||||
}
|
||||
gContext->storageStack.clear();
|
||||
gContext->storageStack.emplace_back(&GetStorageRoot(rootName));
|
||||
}
|
||||
|
||||
Storage& glass::GetStorage() {
|
||||
auto& storage = gContext->storage[gContext->curId];
|
||||
if (!storage) {
|
||||
storage = std::make_unique<Storage>();
|
||||
}
|
||||
return *storage;
|
||||
return *gContext->storageStack.back();
|
||||
}
|
||||
|
||||
Storage& glass::GetStorage(std::string_view id) {
|
||||
auto& storage = gContext->storage[id];
|
||||
if (!storage) {
|
||||
storage = std::make_unique<Storage>();
|
||||
}
|
||||
return *storage;
|
||||
void glass::PushStorageStack(std::string_view label_id) {
|
||||
gContext->storageStack.emplace_back(
|
||||
&gContext->storageStack.back()->GetChild(label_id));
|
||||
}
|
||||
|
||||
static void PushIDStack(std::string_view label_id) {
|
||||
gContext->idStack.emplace_back(gContext->curId.size());
|
||||
|
||||
auto [label, id] = wpi::split(label_id, "###");
|
||||
// if no ###id, use label as id
|
||||
if (id.empty()) {
|
||||
id = label;
|
||||
}
|
||||
if (!gContext->curId.empty()) {
|
||||
gContext->curId += "###";
|
||||
}
|
||||
gContext->curId += id;
|
||||
void glass::PushStorageStack(Storage& storage) {
|
||||
gContext->storageStack.emplace_back(&storage);
|
||||
}
|
||||
|
||||
static void PopIDStack() {
|
||||
gContext->curId.resize(gContext->idStack.back());
|
||||
gContext->idStack.pop_back();
|
||||
void glass::PopStorageStack() {
|
||||
if (gContext->storageStack.size() <= 1) {
|
||||
ImGui::LogText("attempted to pop empty storage stack, mismatch push/pop?");
|
||||
return; // ignore
|
||||
}
|
||||
gContext->storageStack.pop_back();
|
||||
}
|
||||
|
||||
bool glass::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) {
|
||||
PushIDStack(name);
|
||||
PushStorageStack(name);
|
||||
return ImGui::Begin(name, p_open, flags);
|
||||
}
|
||||
|
||||
void glass::End() {
|
||||
ImGui::End();
|
||||
PopIDStack();
|
||||
PopStorageStack();
|
||||
}
|
||||
|
||||
bool glass::BeginChild(const char* str_id, const ImVec2& size, bool border,
|
||||
ImGuiWindowFlags flags) {
|
||||
PushIDStack(str_id);
|
||||
PushStorageStack(str_id);
|
||||
return ImGui::BeginChild(str_id, size, border, flags);
|
||||
}
|
||||
|
||||
void glass::EndChild() {
|
||||
ImGui::EndChild();
|
||||
PopIDStack();
|
||||
PopStorageStack();
|
||||
}
|
||||
|
||||
bool glass::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) {
|
||||
wpi::SmallString<64> openKey;
|
||||
auto [name, id] = wpi::split(label, "###");
|
||||
// if no ###id, use name as id
|
||||
if (id.empty()) {
|
||||
id = name;
|
||||
}
|
||||
openKey = id;
|
||||
openKey += "###open";
|
||||
|
||||
bool* open = GetStorage().GetBoolRef(openKey.str());
|
||||
*open = ImGui::CollapsingHeader(
|
||||
label, flags | (*open ? ImGuiTreeNodeFlags_DefaultOpen : 0));
|
||||
return *open;
|
||||
bool& open = GetStorage().GetChild(label).GetBool(
|
||||
"open", (flags & ImGuiTreeNodeFlags_DefaultOpen) != 0);
|
||||
ImGui::SetNextItemOpen(open);
|
||||
open = ImGui::CollapsingHeader(label, flags);
|
||||
return open;
|
||||
}
|
||||
|
||||
bool glass::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) {
|
||||
PushIDStack(label);
|
||||
bool* open = GetStorage().GetBoolRef("open");
|
||||
*open = ImGui::TreeNodeEx(
|
||||
label, flags | (*open ? ImGuiTreeNodeFlags_DefaultOpen : 0));
|
||||
if (!*open) {
|
||||
PopIDStack();
|
||||
PushStorageStack(label);
|
||||
bool& open = GetStorage().GetBool(
|
||||
"open", (flags & ImGuiTreeNodeFlags_DefaultOpen) != 0);
|
||||
ImGui::SetNextItemOpen(open);
|
||||
open = ImGui::TreeNodeEx(label, flags);
|
||||
if (!open) {
|
||||
PopStorageStack();
|
||||
}
|
||||
return *open;
|
||||
return open;
|
||||
}
|
||||
|
||||
void glass::TreePop() {
|
||||
ImGui::TreePop();
|
||||
PopIDStack();
|
||||
PopStorageStack();
|
||||
}
|
||||
|
||||
void glass::PushID(const char* str_id) {
|
||||
PushIDStack(str_id);
|
||||
PushStorageStack(str_id);
|
||||
ImGui::PushID(str_id);
|
||||
}
|
||||
|
||||
void glass::PushID(const char* str_id_begin, const char* str_id_end) {
|
||||
PushIDStack(std::string_view(str_id_begin, str_id_end - str_id_begin));
|
||||
PushStorageStack(std::string_view(str_id_begin, str_id_end - str_id_begin));
|
||||
ImGui::PushID(str_id_begin, str_id_end);
|
||||
}
|
||||
|
||||
void glass::PushID(int int_id) {
|
||||
char buf[16];
|
||||
std::snprintf(buf, sizeof(buf), "%d", int_id);
|
||||
PushIDStack(buf);
|
||||
PushStorageStack(buf);
|
||||
ImGui::PushID(int_id);
|
||||
}
|
||||
|
||||
void glass::PopID() {
|
||||
ImGui::PopID();
|
||||
PopIDStack();
|
||||
PopStorageStack();
|
||||
}
|
||||
|
||||
bool glass::PopupEditName(const char* label, std::string* name) {
|
||||
|
||||
@@ -12,13 +12,9 @@ using namespace glass;
|
||||
|
||||
wpi::sig::Signal<const char*, DataSource*> DataSource::sourceCreated;
|
||||
|
||||
DataSource::DataSource(std::string_view id) : m_id{id} {
|
||||
auto it = gContext->sources.try_emplace(m_id, this);
|
||||
auto& srcName = it.first->getValue();
|
||||
m_name = srcName.name.get();
|
||||
if (!srcName.source) {
|
||||
srcName.source = this;
|
||||
}
|
||||
DataSource::DataSource(std::string_view id)
|
||||
: m_id{id}, m_name{gContext->sourceNameStorage.GetString(m_id)} {
|
||||
gContext->sources.try_emplace(m_id, this);
|
||||
sourceCreated(m_id.c_str(), this);
|
||||
}
|
||||
|
||||
@@ -32,43 +28,7 @@ DataSource::~DataSource() {
|
||||
if (!gContext) {
|
||||
return;
|
||||
}
|
||||
auto it = gContext->sources.find(m_id);
|
||||
if (it == gContext->sources.end()) {
|
||||
return;
|
||||
}
|
||||
auto& srcName = it->getValue();
|
||||
if (srcName.source == this) {
|
||||
srcName.source = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DataSource::SetName(std::string_view name) {
|
||||
m_name->SetName(name);
|
||||
}
|
||||
|
||||
const char* DataSource::GetName() const {
|
||||
return m_name->GetName();
|
||||
}
|
||||
|
||||
void DataSource::PushEditNameId(int index) {
|
||||
m_name->PushEditNameId(index);
|
||||
}
|
||||
|
||||
void DataSource::PushEditNameId(const char* name) {
|
||||
m_name->PushEditNameId(name);
|
||||
}
|
||||
|
||||
bool DataSource::PopupEditName(int index) {
|
||||
return m_name->PopupEditName(index);
|
||||
}
|
||||
|
||||
bool DataSource::PopupEditName(const char* name) {
|
||||
return m_name->PopupEditName(name);
|
||||
}
|
||||
|
||||
bool DataSource::InputTextName(const char* label_id,
|
||||
ImGuiInputTextFlags flags) {
|
||||
return m_name->InputTextName(label_id, flags);
|
||||
gContext->sources.erase(m_id);
|
||||
}
|
||||
|
||||
void DataSource::LabelText(const char* label, const char* fmt, ...) const {
|
||||
@@ -82,7 +42,7 @@ void DataSource::LabelText(const char* label, const char* fmt, ...) const {
|
||||
void DataSource::LabelTextV(const char* label, const char* fmt,
|
||||
va_list args) const {
|
||||
ImGui::PushID(label);
|
||||
ImGui::LabelTextV("##input", fmt, args);
|
||||
ImGui::LabelTextV("##input", fmt, args); // NOLINT
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::Selectable(label);
|
||||
ImGui::PopID();
|
||||
@@ -141,7 +101,7 @@ void DataSource::EmitDrag(ImGuiDragDropFlags flags) const {
|
||||
if (ImGui::BeginDragDropSource(flags)) {
|
||||
auto self = this;
|
||||
ImGui::SetDragDropPayload("DataSource", &self, sizeof(self)); // NOLINT
|
||||
const char* name = GetName();
|
||||
const char* name = GetName().c_str();
|
||||
ImGui::TextUnformatted(name[0] == '\0' ? m_id.c_str() : name);
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
@@ -152,5 +112,5 @@ DataSource* DataSource::Find(std::string_view id) {
|
||||
if (it == gContext->sources.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->getValue().source;
|
||||
return it->getValue();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wpigui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/ContextInternal.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
void MainMenuBar::AddMainMenu(std::function<void()> menu) {
|
||||
@@ -25,6 +29,8 @@ void MainMenuBar::AddOptionMenu(std::function<void()> menu) {
|
||||
void MainMenuBar::Display() {
|
||||
ImGui::BeginMainMenuBar();
|
||||
|
||||
WorkspaceMenu();
|
||||
|
||||
if (!m_optionMenus.empty()) {
|
||||
if (ImGui::BeginMenu("Options")) {
|
||||
for (auto&& menu : m_optionMenus) {
|
||||
@@ -55,3 +61,46 @@ void MainMenuBar::Display() {
|
||||
#endif
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
void MainMenuBar::WorkspaceMenu() {
|
||||
if (ImGui::BeginMenu("Workspace")) {
|
||||
if (ImGui::MenuItem("Open...")) {
|
||||
m_openFolder =
|
||||
std::make_unique<pfd::select_folder>("Choose folder to open");
|
||||
}
|
||||
if (ImGui::MenuItem("Save As...")) {
|
||||
m_saveFolder = std::make_unique<pfd::select_folder>("Choose save folder");
|
||||
}
|
||||
if (ImGui::MenuItem("Save As Global", nullptr, false,
|
||||
!gContext->isPlatformSaveDir)) {
|
||||
SetStorageDir(wpi::gui::GetPlatformSaveFileDir());
|
||||
SaveStorage();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Reset")) {
|
||||
WorkspaceReset();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Exit")) {
|
||||
wpi::gui::Exit();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (m_openFolder && m_openFolder->ready(0)) {
|
||||
auto result = m_openFolder->result();
|
||||
if (!result.empty()) {
|
||||
LoadStorage(result);
|
||||
}
|
||||
m_openFolder.reset();
|
||||
}
|
||||
|
||||
if (m_saveFolder && m_saveFolder->ready(0)) {
|
||||
auto result = m_saveFolder->result();
|
||||
if (!result.empty()) {
|
||||
SetStorageDir(result);
|
||||
SaveStorage(result);
|
||||
}
|
||||
m_saveFolder.reset();
|
||||
}
|
||||
}
|
||||
|
||||
688
glass/src/lib/native/cpp/Storage.cpp
Normal file
688
glass/src/lib/native/cpp/Storage.cpp
Normal file
@@ -0,0 +1,688 @@
|
||||
// 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 "glass/Storage.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/json.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
template <typename To>
|
||||
bool ConvertFromString(To* out, std::string_view str) {
|
||||
if constexpr (std::is_same_v<To, bool>) {
|
||||
if (str == "true") {
|
||||
*out = true;
|
||||
} else if (str == "false") {
|
||||
*out = false;
|
||||
} else if (auto val = wpi::parse_integer<int>(str, 10)) {
|
||||
*out = val.value() != 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if constexpr (std::is_floating_point_v<To>) {
|
||||
if (auto val = wpi::parse_float<To>(str)) {
|
||||
*out = val.value();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (auto val = wpi::parse_integer<To>(str, 10)) {
|
||||
*out = val.value();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define CONVERT(CapsName, LowerName, CType) \
|
||||
static bool Convert##CapsName(Storage::Value* value) { \
|
||||
switch (value->type) { \
|
||||
case Storage::Value::kBool: \
|
||||
value->LowerName##Val = value->boolVal; \
|
||||
value->LowerName##Default = value->boolDefault; \
|
||||
break; \
|
||||
case Storage::Value::kDouble: \
|
||||
value->LowerName##Val = value->doubleVal; \
|
||||
value->LowerName##Default = value->doubleDefault; \
|
||||
break; \
|
||||
case Storage::Value::kFloat: \
|
||||
value->LowerName##Val = value->floatVal; \
|
||||
value->LowerName##Default = value->floatDefault; \
|
||||
break; \
|
||||
case Storage::Value::kInt: \
|
||||
value->LowerName##Val = value->intVal; \
|
||||
value->LowerName##Default = value->intDefault; \
|
||||
break; \
|
||||
case Storage::Value::kInt64: \
|
||||
value->LowerName##Val = value->int64Val; \
|
||||
value->LowerName##Default = value->int64Default; \
|
||||
break; \
|
||||
case Storage::Value::kString: \
|
||||
if (!ConvertFromString(&value->LowerName##Val, value->stringVal)) { \
|
||||
return false; \
|
||||
} \
|
||||
if (!ConvertFromString(&value->LowerName##Default, \
|
||||
value->stringDefault)) { \
|
||||
return false; \
|
||||
} \
|
||||
break; \
|
||||
default: \
|
||||
return false; \
|
||||
} \
|
||||
value->type = Storage::Value::k##CapsName; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
CONVERT(Int, int, int)
|
||||
CONVERT(Int64, int64, int64_t)
|
||||
CONVERT(Float, float, float)
|
||||
CONVERT(Double, double, double)
|
||||
CONVERT(Bool, bool, bool)
|
||||
|
||||
static inline bool ConvertString(Storage::Value* value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Arrays can only come from JSON, so we only have to worry about conversions
|
||||
// between the various number types, not bool or string
|
||||
|
||||
template <typename From, typename To>
|
||||
static void ConvertArray(std::vector<To>** outPtr, std::vector<From>** inPtr) {
|
||||
if (*inPtr) {
|
||||
std::vector<To>* tmp;
|
||||
tmp = new std::vector<To>{(*inPtr)->begin(), (*inPtr)->end()};
|
||||
delete *inPtr;
|
||||
*outPtr = tmp;
|
||||
} else {
|
||||
*outPtr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#define CONVERT_ARRAY(CapsName, LowerName) \
|
||||
static bool Convert##CapsName##Array(Storage::Value* value) { \
|
||||
switch (value->type) { \
|
||||
case Storage::Value::kDoubleArray: \
|
||||
ConvertArray(&value->LowerName##Array, &value->doubleArray); \
|
||||
ConvertArray(&value->LowerName##ArrayDefault, \
|
||||
&value->doubleArrayDefault); \
|
||||
break; \
|
||||
case Storage::Value::kFloatArray: \
|
||||
ConvertArray(&value->LowerName##Array, &value->floatArray); \
|
||||
ConvertArray(&value->LowerName##ArrayDefault, \
|
||||
&value->floatArrayDefault); \
|
||||
break; \
|
||||
case Storage::Value::kIntArray: \
|
||||
ConvertArray(&value->LowerName##Array, &value->intArray); \
|
||||
ConvertArray(&value->LowerName##ArrayDefault, \
|
||||
&value->intArrayDefault); \
|
||||
break; \
|
||||
case Storage::Value::kInt64Array: \
|
||||
ConvertArray(&value->LowerName##Array, &value->int64Array); \
|
||||
ConvertArray(&value->LowerName##ArrayDefault, \
|
||||
&value->int64ArrayDefault); \
|
||||
break; \
|
||||
default: \
|
||||
return false; \
|
||||
} \
|
||||
value->type = Storage::Value::k##CapsName##Array; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
CONVERT_ARRAY(Int, int)
|
||||
CONVERT_ARRAY(Int64, int64)
|
||||
CONVERT_ARRAY(Float, float)
|
||||
CONVERT_ARRAY(Double, double)
|
||||
|
||||
static inline bool ConvertBoolArray(Storage::Value* value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool ConvertStringArray(Storage::Value* value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Storage::Value::Reset(Type newType) {
|
||||
switch (type) {
|
||||
case kChild:
|
||||
delete child;
|
||||
break;
|
||||
case kIntArray:
|
||||
delete intArray;
|
||||
delete intArrayDefault;
|
||||
break;
|
||||
case kInt64Array:
|
||||
delete int64Array;
|
||||
delete int64ArrayDefault;
|
||||
break;
|
||||
case kBoolArray:
|
||||
delete boolArray;
|
||||
delete boolArrayDefault;
|
||||
break;
|
||||
case kFloatArray:
|
||||
delete floatArray;
|
||||
delete floatArrayDefault;
|
||||
break;
|
||||
case kDoubleArray:
|
||||
delete doubleArray;
|
||||
delete doubleArrayDefault;
|
||||
break;
|
||||
case kStringArray:
|
||||
delete stringArray;
|
||||
delete stringArrayDefault;
|
||||
break;
|
||||
case kChildArray:
|
||||
delete childArray;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
type = newType;
|
||||
}
|
||||
|
||||
Storage::Value* Storage::FindValue(std::string_view key) {
|
||||
auto it = m_values.find(key);
|
||||
if (it == m_values.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
Storage::Value& Storage::GetValue(std::string_view key) {
|
||||
auto& val = m_values[key];
|
||||
if (!val) {
|
||||
val = std::make_unique<Value>();
|
||||
}
|
||||
return *val;
|
||||
}
|
||||
|
||||
#define DEFUN(CapsName, LowerName, CType, CParamType, ArrCType) \
|
||||
CType Storage::Read##CapsName(std::string_view key, CParamType defaultVal) \
|
||||
const { \
|
||||
auto it = m_values.find(key); \
|
||||
if (it == m_values.end()) { \
|
||||
return CType{defaultVal}; \
|
||||
} \
|
||||
Value& value = *it->second; \
|
||||
if (value.type != Value::k##CapsName) { \
|
||||
if (!Convert##CapsName(&value)) { \
|
||||
value.Reset(Value::k##CapsName); \
|
||||
value.LowerName##Val = defaultVal; \
|
||||
value.LowerName##Default = defaultVal; \
|
||||
value.hasDefault = true; \
|
||||
} \
|
||||
} \
|
||||
return value.LowerName##Val; \
|
||||
} \
|
||||
\
|
||||
void Storage::Set##CapsName(std::string_view key, CParamType val) { \
|
||||
auto& valuePtr = m_values[key]; \
|
||||
if (!valuePtr) { \
|
||||
valuePtr = std::make_unique<Value>(Value::k##CapsName); \
|
||||
} else { \
|
||||
valuePtr->Reset(Value::k##CapsName); \
|
||||
} \
|
||||
valuePtr->LowerName##Val = val; \
|
||||
valuePtr->LowerName##Default = {}; \
|
||||
} \
|
||||
\
|
||||
CType& Storage::Get##CapsName(std::string_view key, CParamType defaultVal) { \
|
||||
auto& valuePtr = m_values[key]; \
|
||||
bool setValue = false; \
|
||||
if (!valuePtr) { \
|
||||
valuePtr = std::make_unique<Value>(Value::k##CapsName); \
|
||||
setValue = true; \
|
||||
} else if (valuePtr->type != Value::k##CapsName) { \
|
||||
if (!Convert##CapsName(valuePtr.get())) { \
|
||||
valuePtr->Reset(Value::k##CapsName); \
|
||||
setValue = true; \
|
||||
} \
|
||||
} \
|
||||
if (setValue) { \
|
||||
valuePtr->LowerName##Val = defaultVal; \
|
||||
} \
|
||||
if (!valuePtr->hasDefault) { \
|
||||
valuePtr->LowerName##Default = defaultVal; \
|
||||
valuePtr->hasDefault = true; \
|
||||
} \
|
||||
return valuePtr->LowerName##Val; \
|
||||
} \
|
||||
\
|
||||
std::vector<ArrCType>& Storage::Get##CapsName##Array( \
|
||||
std::string_view key, wpi::span<const ArrCType> defaultVal) { \
|
||||
auto& valuePtr = m_values[key]; \
|
||||
bool setValue = false; \
|
||||
if (!valuePtr) { \
|
||||
valuePtr = std::make_unique<Value>(Value::k##CapsName##Array); \
|
||||
setValue = true; \
|
||||
} else if (valuePtr->type != Value::k##CapsName##Array) { \
|
||||
if (!Convert##CapsName##Array(valuePtr.get())) { \
|
||||
valuePtr->Reset(Value::k##CapsName##Array); \
|
||||
setValue = true; \
|
||||
} \
|
||||
} \
|
||||
if (setValue) { \
|
||||
valuePtr->LowerName##Array = \
|
||||
new std::vector<ArrCType>{defaultVal.begin(), defaultVal.end()}; \
|
||||
} \
|
||||
if (!valuePtr->hasDefault) { \
|
||||
if (defaultVal.empty()) { \
|
||||
valuePtr->LowerName##ArrayDefault = nullptr; \
|
||||
} else { \
|
||||
valuePtr->LowerName##ArrayDefault = \
|
||||
new std::vector<ArrCType>{defaultVal.begin(), defaultVal.end()}; \
|
||||
} \
|
||||
valuePtr->hasDefault = true; \
|
||||
} \
|
||||
assert(valuePtr->LowerName##Array); \
|
||||
return *valuePtr->LowerName##Array; \
|
||||
}
|
||||
|
||||
DEFUN(Int, int, int, int, int)
|
||||
DEFUN(Int64, int64, int64_t, int64_t, int64_t)
|
||||
DEFUN(Bool, bool, bool, bool, int)
|
||||
DEFUN(Float, float, float, float, float)
|
||||
DEFUN(Double, double, double, double, double)
|
||||
DEFUN(String, string, std::string, std::string_view, std::string)
|
||||
|
||||
Storage& Storage::GetChild(std::string_view label_id) {
|
||||
auto [label, id] = wpi::split(label_id, "###");
|
||||
if (id.empty()) {
|
||||
id = label;
|
||||
}
|
||||
auto& childPtr = m_values[id];
|
||||
if (!childPtr) {
|
||||
childPtr = std::make_unique<Value>();
|
||||
}
|
||||
if (childPtr->type != Value::kChild) {
|
||||
childPtr->type = Value::kChild;
|
||||
childPtr->child = new Storage;
|
||||
}
|
||||
return *childPtr->child;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Storage>>& Storage::GetChildArray(
|
||||
std::string_view key) {
|
||||
auto& valuePtr = m_values[key];
|
||||
if (!valuePtr) {
|
||||
valuePtr = std::make_unique<Value>(Value::kChildArray);
|
||||
valuePtr->childArray = new std::vector<std::unique_ptr<Storage>>();
|
||||
} else if (valuePtr->type != Value::kChildArray) {
|
||||
valuePtr->Reset(Value::kChildArray);
|
||||
valuePtr->childArray = new std::vector<std::unique_ptr<Storage>>();
|
||||
}
|
||||
|
||||
return *valuePtr->childArray;
|
||||
}
|
||||
|
||||
std::unique_ptr<Storage::Value> Storage::Erase(std::string_view key) {
|
||||
auto it = m_values.find(key);
|
||||
if (it != m_values.end()) {
|
||||
auto rv = std::move(it->getValue());
|
||||
m_values.erase(it);
|
||||
return rv;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Storage::EraseChildren() {
|
||||
for (auto&& kv : m_values) {
|
||||
if (kv.getValue()->type == Value::kChild) {
|
||||
m_values.remove(&kv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::json& jarr,
|
||||
const char* filename) {
|
||||
auto& arr = jarr.get_ref<const wpi::json::array_t&>();
|
||||
if (arr.empty()) {
|
||||
ImGui::LogText("empty array in %s, ignoring", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// guess array type from first element
|
||||
switch (arr[0].type()) {
|
||||
case wpi::json::value_t::boolean:
|
||||
if (valuePtr->type != Storage::Value::kBoolArray) {
|
||||
valuePtr->Reset(Storage::Value::kBoolArray);
|
||||
valuePtr->boolArray = new std::vector<int>();
|
||||
valuePtr->boolArrayDefault = nullptr;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::number_float:
|
||||
if (valuePtr->type != Storage::Value::kDoubleArray) {
|
||||
valuePtr->Reset(Storage::Value::kDoubleArray);
|
||||
valuePtr->doubleArray = new std::vector<double>();
|
||||
valuePtr->doubleArrayDefault = nullptr;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::number_integer:
|
||||
case wpi::json::value_t::number_unsigned:
|
||||
if (valuePtr->type != Storage::Value::kInt64Array) {
|
||||
valuePtr->Reset(Storage::Value::kInt64Array);
|
||||
valuePtr->int64Array = new std::vector<int64_t>();
|
||||
valuePtr->int64ArrayDefault = nullptr;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::string:
|
||||
if (valuePtr->type != Storage::Value::kStringArray) {
|
||||
valuePtr->Reset(Storage::Value::kStringArray);
|
||||
valuePtr->stringArray = new std::vector<std::string>();
|
||||
valuePtr->stringArrayDefault = nullptr;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::object:
|
||||
if (valuePtr->type != Storage::Value::kChildArray) {
|
||||
valuePtr->Reset(Storage::Value::kChildArray);
|
||||
valuePtr->childArray = new std::vector<std::unique_ptr<Storage>>();
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::array:
|
||||
ImGui::LogText("nested array in %s, ignoring", filename);
|
||||
return false;
|
||||
default:
|
||||
ImGui::LogText("null value in %s, ignoring", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// loop over array to store elements
|
||||
for (auto jvalue : arr) {
|
||||
switch (jvalue.type()) {
|
||||
case wpi::json::value_t::boolean:
|
||||
if (valuePtr->type == Storage::Value::kBoolArray) {
|
||||
valuePtr->boolArray->push_back(jvalue.get<bool>());
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::number_float:
|
||||
if (valuePtr->type == Storage::Value::kDoubleArray) {
|
||||
valuePtr->doubleArray->push_back(jvalue.get<double>());
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::number_integer:
|
||||
if (valuePtr->type == Storage::Value::kInt64Array) {
|
||||
valuePtr->int64Array->push_back(jvalue.get<int64_t>());
|
||||
} else if (valuePtr->type == Storage::Value::kDoubleArray) {
|
||||
valuePtr->doubleArray->push_back(jvalue.get<int64_t>());
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::number_unsigned:
|
||||
if (valuePtr->type == Storage::Value::kInt64Array) {
|
||||
valuePtr->int64Array->push_back(jvalue.get<uint64_t>());
|
||||
} else if (valuePtr->type == Storage::Value::kDoubleArray) {
|
||||
valuePtr->doubleArray->push_back(jvalue.get<uint64_t>());
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::string:
|
||||
if (valuePtr->type == Storage::Value::kStringArray) {
|
||||
valuePtr->stringArray->emplace_back(
|
||||
jvalue.get_ref<const std::string&>());
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::object:
|
||||
if (valuePtr->type == Storage::Value::kChildArray) {
|
||||
valuePtr->childArray->emplace_back(std::make_unique<Storage>());
|
||||
valuePtr->childArray->back()->FromJson(jvalue, filename);
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case wpi::json::value_t::array:
|
||||
ImGui::LogText("nested array in %s, ignoring", filename);
|
||||
return false;
|
||||
default:
|
||||
ImGui::LogText("null value in %s, ignoring", filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
error:
|
||||
ImGui::LogText("array with variant types in %s, ignoring", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::FromJson(const wpi::json& json, const char* filename) {
|
||||
if (m_fromJson) {
|
||||
return m_fromJson(json, filename);
|
||||
}
|
||||
|
||||
if (!json.is_object()) {
|
||||
ImGui::LogText("non-object in %s", filename);
|
||||
return false;
|
||||
}
|
||||
for (auto&& jkv : json.items()) {
|
||||
auto& valuePtr = m_values[jkv.key()];
|
||||
bool created = false;
|
||||
if (!valuePtr) {
|
||||
valuePtr = std::make_unique<Value>();
|
||||
created = true;
|
||||
}
|
||||
auto& jvalue = jkv.value();
|
||||
switch (jvalue.type()) {
|
||||
case wpi::json::value_t::boolean:
|
||||
valuePtr->Reset(Value::kBool);
|
||||
valuePtr->boolVal = jvalue.get<bool>();
|
||||
break;
|
||||
case wpi::json::value_t::number_float:
|
||||
valuePtr->Reset(Value::kDouble);
|
||||
valuePtr->doubleVal = jvalue.get<double>();
|
||||
break;
|
||||
case wpi::json::value_t::number_integer:
|
||||
valuePtr->Reset(Value::kInt64);
|
||||
valuePtr->int64Val = jvalue.get<int64_t>();
|
||||
break;
|
||||
case wpi::json::value_t::number_unsigned:
|
||||
valuePtr->Reset(Value::kInt64);
|
||||
valuePtr->int64Val = jvalue.get<uint64_t>();
|
||||
break;
|
||||
case wpi::json::value_t::string:
|
||||
valuePtr->Reset(Value::kString);
|
||||
valuePtr->stringVal = jvalue.get_ref<const std::string&>();
|
||||
break;
|
||||
case wpi::json::value_t::object:
|
||||
if (valuePtr->type != Value::kChild) {
|
||||
valuePtr->Reset(Value::kChild);
|
||||
valuePtr->child = new Storage;
|
||||
}
|
||||
valuePtr->child->FromJson(jvalue, filename); // recurse
|
||||
break;
|
||||
case wpi::json::value_t::array:
|
||||
if (!JsonArrayToStorage(valuePtr.get(), jvalue, filename)) {
|
||||
if (created) {
|
||||
m_values.erase(jkv.key());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ImGui::LogText("null value in %s, ignoring", filename);
|
||||
if (created) {
|
||||
m_values.erase(jkv.key());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static wpi::json StorageToJsonArray(const std::vector<T>& arr) {
|
||||
wpi::json jarr = wpi::json::array();
|
||||
for (auto&& v : arr) {
|
||||
jarr.emplace_back(v);
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
template <>
|
||||
wpi::json StorageToJsonArray<std::unique_ptr<Storage>>(
|
||||
const std::vector<std::unique_ptr<Storage>>& arr) {
|
||||
wpi::json jarr = wpi::json::array();
|
||||
for (auto&& v : arr) {
|
||||
jarr.emplace_back(v->ToJson());
|
||||
}
|
||||
// remove any trailing empty items
|
||||
while (!jarr.empty() && jarr.back().empty()) {
|
||||
jarr.get_ref<wpi::json::array_t&>().pop_back();
|
||||
}
|
||||
return jarr;
|
||||
}
|
||||
|
||||
wpi::json Storage::ToJson() const {
|
||||
if (m_toJson) {
|
||||
return m_toJson();
|
||||
}
|
||||
|
||||
wpi::json j = wpi::json::object();
|
||||
for (auto&& kv : m_values) {
|
||||
wpi::json jelem;
|
||||
auto& value = *kv.getValue();
|
||||
switch (value.type) {
|
||||
#define CASE(CapsName, LowerName) \
|
||||
case Value::k##CapsName: \
|
||||
if (value.hasDefault && \
|
||||
value.LowerName##Val == value.LowerName##Default) { \
|
||||
continue; \
|
||||
} \
|
||||
jelem = value.LowerName##Val; \
|
||||
break; \
|
||||
case Value::k##CapsName##Array: \
|
||||
if (value.hasDefault && \
|
||||
((!value.LowerName##ArrayDefault && \
|
||||
value.LowerName##Array->empty()) || \
|
||||
(value.LowerName##ArrayDefault && \
|
||||
*value.LowerName##Array == *value.LowerName##ArrayDefault))) { \
|
||||
continue; \
|
||||
} \
|
||||
jelem = StorageToJsonArray(*value.LowerName##Array); \
|
||||
break;
|
||||
|
||||
CASE(Int, int)
|
||||
CASE(Int64, int64)
|
||||
CASE(Bool, bool)
|
||||
CASE(Float, float)
|
||||
CASE(Double, double)
|
||||
CASE(String, string)
|
||||
|
||||
case Value::kChild:
|
||||
jelem = value.child->ToJson(); // recurse
|
||||
if (jelem.empty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case Value::kChildArray:
|
||||
jelem = StorageToJsonArray(*value.childArray);
|
||||
if (jelem.empty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
j.emplace(kv.getKey(), std::move(jelem));
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
void Storage::Clear() {
|
||||
if (m_clear) {
|
||||
return m_clear();
|
||||
}
|
||||
|
||||
ClearValues();
|
||||
}
|
||||
|
||||
void Storage::ClearValues() {
|
||||
for (auto&& kv : m_values) {
|
||||
auto& value = *kv.getValue();
|
||||
switch (value.type) {
|
||||
case Value::kInt:
|
||||
value.intVal = value.intDefault;
|
||||
break;
|
||||
case Value::kInt64:
|
||||
value.int64Val = value.int64Default;
|
||||
break;
|
||||
case Value::kBool:
|
||||
value.boolVal = value.boolDefault;
|
||||
break;
|
||||
case Value::kFloat:
|
||||
value.floatVal = value.floatDefault;
|
||||
break;
|
||||
case Value::kDouble:
|
||||
value.doubleVal = value.doubleDefault;
|
||||
break;
|
||||
case Value::kString:
|
||||
value.stringVal = value.stringDefault;
|
||||
break;
|
||||
case Value::kIntArray:
|
||||
*value.intArray = *value.intArrayDefault;
|
||||
break;
|
||||
case Value::kInt64Array:
|
||||
*value.int64Array = *value.int64ArrayDefault;
|
||||
break;
|
||||
case Value::kBoolArray:
|
||||
*value.boolArray = *value.boolArrayDefault;
|
||||
break;
|
||||
case Value::kFloatArray:
|
||||
*value.floatArray = *value.floatArrayDefault;
|
||||
break;
|
||||
case Value::kDoubleArray:
|
||||
*value.doubleArray = *value.doubleArrayDefault;
|
||||
break;
|
||||
case Value::kStringArray:
|
||||
*value.stringArray = *value.stringArrayDefault;
|
||||
break;
|
||||
case Value::kChild:
|
||||
value.child->Clear();
|
||||
break;
|
||||
case Value::kChildArray:
|
||||
for (auto&& child : *value.childArray) {
|
||||
child->Clear();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::Apply() {
|
||||
if (m_apply) {
|
||||
return m_apply();
|
||||
}
|
||||
|
||||
ApplyChildren();
|
||||
}
|
||||
|
||||
void Storage::ApplyChildren() {
|
||||
for (auto&& kv : m_values) {
|
||||
auto& value = *kv.getValue();
|
||||
switch (value.type) {
|
||||
case Value::kChild:
|
||||
value.child->Apply();
|
||||
break;
|
||||
case Value::kChildArray:
|
||||
for (auto&& child : *value.childArray) {
|
||||
child->Apply();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,23 +8,28 @@
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
Window::Window(Storage& storage, std::string_view id,
|
||||
Visibility defaultVisibility)
|
||||
: m_id{id},
|
||||
m_name{storage.GetString("name")},
|
||||
m_defaultName{id},
|
||||
m_visible{storage.GetBool("visible", defaultVisibility != kHide)},
|
||||
m_enabled{storage.GetBool("enabled", defaultVisibility != kDisabled)},
|
||||
m_defaultVisible{storage.GetValue("visible").boolDefault},
|
||||
m_defaultEnabled{storage.GetValue("enabled").boolDefault} {}
|
||||
|
||||
void Window::SetVisibility(Visibility visibility) {
|
||||
switch (visibility) {
|
||||
case kHide:
|
||||
m_visible = false;
|
||||
m_enabled = true;
|
||||
break;
|
||||
case kShow:
|
||||
m_visible = true;
|
||||
m_enabled = true;
|
||||
break;
|
||||
case kDisabled:
|
||||
m_enabled = false;
|
||||
break;
|
||||
}
|
||||
m_visible = visibility != kHide;
|
||||
m_enabled = visibility != kDisabled;
|
||||
}
|
||||
|
||||
void Window::SetDefaultVisibility(Visibility visibility) {
|
||||
m_defaultVisible = visibility != kHide;
|
||||
m_defaultEnabled = visibility != kDisabled;
|
||||
}
|
||||
|
||||
void Window::Display() {
|
||||
@@ -85,27 +90,3 @@ void Window::ScaleDefault(float scale) {
|
||||
m_size.y *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
void Window::IniReadLine(const char* line) {
|
||||
auto [name, value] = wpi::split(line, '=');
|
||||
name = wpi::trim(name);
|
||||
value = wpi::trim(value);
|
||||
|
||||
if (name == "name") {
|
||||
m_name = value;
|
||||
} else if (name == "visible") {
|
||||
if (auto num = wpi::parse_integer<int>(value, 10)) {
|
||||
m_visible = num.value();
|
||||
}
|
||||
} else if (name == "enabled") {
|
||||
if (auto num = wpi::parse_integer<int>(value, 10)) {
|
||||
m_enabled = num.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Window::IniWriteAll(const char* typeName, ImGuiTextBuffer* out_buf) {
|
||||
out_buf->appendf("[%s][%s]\nname=%s\nvisible=%d\nenabled=%d\n\n", typeName,
|
||||
m_id.c_str(), m_name.c_str(), m_visible ? 1 : 0,
|
||||
m_enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -10,30 +10,23 @@
|
||||
#include <fmt/format.h>
|
||||
#include <wpigui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
WindowManager::WindowManager(std::string_view iniName)
|
||||
: m_iniSaver{iniName, this} {}
|
||||
|
||||
// read/write open state to ini file
|
||||
void* WindowManager::IniSaver::IniReadOpen(const char* name) {
|
||||
return m_manager->GetOrAddWindow(name, true);
|
||||
}
|
||||
|
||||
void WindowManager::IniSaver::IniReadLine(void* entry, const char* lineStr) {
|
||||
static_cast<Window*>(entry)->IniReadLine(lineStr);
|
||||
}
|
||||
|
||||
void WindowManager::IniSaver::IniWriteAll(ImGuiTextBuffer* out_buf) {
|
||||
const char* typeName = GetTypeName();
|
||||
for (auto&& window : m_manager->m_windows) {
|
||||
window->IniWriteAll(typeName, out_buf);
|
||||
}
|
||||
WindowManager::WindowManager(Storage& storage) : m_storage{storage} {
|
||||
storage.SetCustomApply([this] {
|
||||
for (auto&& childIt : m_storage.GetChildren()) {
|
||||
GetOrAddWindow(childIt.key(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Window* WindowManager::AddWindow(std::string_view id,
|
||||
wpi::unique_function<void()> display) {
|
||||
auto win = GetOrAddWindow(id, false);
|
||||
wpi::unique_function<void()> display,
|
||||
Window::Visibility defaultVisibility) {
|
||||
auto win = GetOrAddWindow(id, false, defaultVisibility);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -46,8 +39,9 @@ Window* WindowManager::AddWindow(std::string_view id,
|
||||
}
|
||||
|
||||
Window* WindowManager::AddWindow(std::string_view id,
|
||||
std::unique_ptr<View> view) {
|
||||
auto win = GetOrAddWindow(id, false);
|
||||
std::unique_ptr<View> view,
|
||||
Window::Visibility defaultVisibility) {
|
||||
auto win = GetOrAddWindow(id, false, defaultVisibility);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -59,7 +53,8 @@ Window* WindowManager::AddWindow(std::string_view id,
|
||||
return win;
|
||||
}
|
||||
|
||||
Window* WindowManager::GetOrAddWindow(std::string_view id, bool duplicateOk) {
|
||||
Window* WindowManager::GetOrAddWindow(std::string_view id, bool duplicateOk,
|
||||
Window::Visibility defaultVisibility) {
|
||||
// binary search
|
||||
auto it = std::lower_bound(
|
||||
m_windows.begin(), m_windows.end(), id,
|
||||
@@ -72,7 +67,11 @@ Window* WindowManager::GetOrAddWindow(std::string_view id, bool duplicateOk) {
|
||||
return it->get();
|
||||
}
|
||||
// insert before (keeps sort)
|
||||
return m_windows.emplace(it, std::make_unique<Window>(id))->get();
|
||||
return m_windows
|
||||
.emplace(it, std::make_unique<Window>(
|
||||
m_storage.GetChild(id).GetChild("window"), id,
|
||||
defaultVisibility))
|
||||
->get();
|
||||
}
|
||||
|
||||
Window* WindowManager::GetWindow(std::string_view id) {
|
||||
@@ -86,8 +85,12 @@ Window* WindowManager::GetWindow(std::string_view id) {
|
||||
return it->get();
|
||||
}
|
||||
|
||||
void WindowManager::RemoveWindow(size_t index) {
|
||||
m_storage.Erase(m_windows[index]->GetId());
|
||||
m_windows.erase(m_windows.begin() + index);
|
||||
}
|
||||
|
||||
void WindowManager::GlobalInit() {
|
||||
wpi::gui::AddInit([this] { m_iniSaver.Initialize(); });
|
||||
wpi::gui::AddWindowScaler([this](float scale) {
|
||||
// scale default window positions
|
||||
for (auto&& window : m_windows) {
|
||||
@@ -104,7 +107,9 @@ void WindowManager::DisplayMenu() {
|
||||
}
|
||||
|
||||
void WindowManager::DisplayWindows() {
|
||||
PushStorageStack(m_storage);
|
||||
for (auto&& window : m_windows) {
|
||||
window->Display();
|
||||
}
|
||||
PopStorageStack();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -18,10 +19,10 @@ void glass::DisplayAnalogInput(AnalogInputModel* model, int index) {
|
||||
}
|
||||
|
||||
// build label
|
||||
std::string* name = GetStorage().GetStringRef("name");
|
||||
std::string& name = GetStorage().GetString("name");
|
||||
char label[128];
|
||||
if (!name->empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), index);
|
||||
if (!name.empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), index);
|
||||
} else {
|
||||
std::snprintf(label, sizeof(label), "In[%d]###name", index);
|
||||
}
|
||||
@@ -42,8 +43,8 @@ void glass::DisplayAnalogInput(AnalogInputModel* model, int index) {
|
||||
}
|
||||
|
||||
// context menu to change name
|
||||
if (PopupEditName("name", name)) {
|
||||
voltageData->SetName(name->c_str());
|
||||
if (PopupEditName("name", &name)) {
|
||||
voltageData->SetName(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/Storage.h"
|
||||
#include "glass/other/DeviceTree.h"
|
||||
|
||||
using namespace glass;
|
||||
@@ -26,10 +27,10 @@ void glass::DisplayAnalogOutputsDevice(AnalogOutputsModel* model) {
|
||||
PushID(i);
|
||||
|
||||
// build label
|
||||
std::string* name = GetStorage().GetStringRef("name");
|
||||
std::string& name = GetStorage().GetString("name");
|
||||
char label[128];
|
||||
if (!name->empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), i);
|
||||
if (!name.empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), i);
|
||||
} else {
|
||||
std::snprintf(label, sizeof(label), "Out[%d]###name", i);
|
||||
}
|
||||
@@ -37,9 +38,9 @@ void glass::DisplayAnalogOutputsDevice(AnalogOutputsModel* model) {
|
||||
double value = analogOutData->GetValue();
|
||||
DeviceDouble(label, true, &value, analogOutData);
|
||||
|
||||
if (PopupEditName("name", name)) {
|
||||
if (PopupEditName("name", &name)) {
|
||||
if (analogOutData) {
|
||||
analogOutData->SetName(name->c_str());
|
||||
analogOutData->SetName(name);
|
||||
}
|
||||
}
|
||||
PopID();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/hardware/Encoder.h"
|
||||
#include "glass/support/IniSaverInfo.h"
|
||||
#include "glass/support/NameSetting.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -28,17 +28,18 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
|
||||
auto dutyCycleData = dutyCycle ? dutyCycle->GetValueData() : nullptr;
|
||||
|
||||
bool exists = model->Exists();
|
||||
auto& info = dioData->GetNameInfo();
|
||||
NameSetting dioName{dioData->GetName()};
|
||||
char label[128];
|
||||
if (exists && dpwmData) {
|
||||
dpwmData->GetNameInfo().GetLabel(label, sizeof(label), "PWM", index);
|
||||
NameSetting{dpwmData->GetName()}.GetLabel(label, sizeof(label), "PWM",
|
||||
index);
|
||||
if (auto simDevice = dpwm->GetSimDevice()) {
|
||||
LabelSimDevice(label, simDevice);
|
||||
} else {
|
||||
dpwmData->LabelText(label, "%0.3f", dpwmData->GetValue());
|
||||
}
|
||||
} else if (exists && encoder) {
|
||||
info.GetLabel(label, sizeof(label), " In", index);
|
||||
dioName.GetLabel(label, sizeof(label), " In", index);
|
||||
if (auto simDevice = encoder->GetSimDevice()) {
|
||||
LabelSimDevice(label, simDevice);
|
||||
} else {
|
||||
@@ -48,7 +49,8 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
} else if (exists && dutyCycleData) {
|
||||
dutyCycleData->GetNameInfo().GetLabel(label, sizeof(label), "Dty", index);
|
||||
NameSetting{dutyCycleData->GetName()}.GetLabel(label, sizeof(label), "Dty",
|
||||
index);
|
||||
if (auto simDevice = dutyCycle->GetSimDevice()) {
|
||||
LabelSimDevice(label, simDevice);
|
||||
} else {
|
||||
@@ -60,10 +62,10 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
|
||||
} else {
|
||||
const char* name = model->GetName();
|
||||
if (name[0] != '\0') {
|
||||
info.GetLabel(label, sizeof(label), name);
|
||||
dioName.GetLabel(label, sizeof(label), name);
|
||||
} else {
|
||||
info.GetLabel(label, sizeof(label), model->IsInput() ? " In" : "Out",
|
||||
index);
|
||||
dioName.GetLabel(label, sizeof(label), model->IsInput() ? " In" : "Out",
|
||||
index);
|
||||
}
|
||||
if (auto simDevice = model->GetSimDevice()) {
|
||||
LabelSimDevice(label, simDevice);
|
||||
@@ -87,12 +89,12 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.PopupEditName(index)) {
|
||||
if (dioName.PopupEditName(index)) {
|
||||
if (dpwmData) {
|
||||
dpwmData->SetName(info.GetName());
|
||||
dpwmData->SetName(dioName.GetName());
|
||||
}
|
||||
if (dutyCycleData) {
|
||||
dutyCycleData->SetName(info.GetName());
|
||||
dutyCycleData->SetName(dioName.GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -66,21 +67,21 @@ void glass::DisplayEncoder(EncoderModel* model) {
|
||||
int chB = model->GetChannelB();
|
||||
|
||||
// build header label
|
||||
std::string* name = GetStorage().GetStringRef("name");
|
||||
std::string& name = GetStorage().GetString("name");
|
||||
char label[128];
|
||||
if (!name->empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d,%d]###name", name->c_str(), chA,
|
||||
chB);
|
||||
if (!name.empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d,%d]###header", name.c_str(),
|
||||
chA, chB);
|
||||
} else {
|
||||
std::snprintf(label, sizeof(label), "Encoder[%d,%d]###name", chA, chB);
|
||||
std::snprintf(label, sizeof(label), "Encoder[%d,%d]###header", chA, chB);
|
||||
}
|
||||
|
||||
// header
|
||||
bool open = CollapsingHeader(label);
|
||||
|
||||
// context menu to change name
|
||||
if (PopupEditName("name", name)) {
|
||||
model->SetName(name->c_str());
|
||||
if (PopupEditName("header", &name)) {
|
||||
model->SetName(name);
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/Storage.h"
|
||||
#include "glass/support/ExtraGuiWidgets.h"
|
||||
|
||||
using namespace glass;
|
||||
@@ -25,27 +26,27 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) {
|
||||
bool running = model->IsRunning();
|
||||
auto& storage = GetStorage();
|
||||
|
||||
int* numColumns = storage.GetIntRef("columns", 10);
|
||||
bool* serpentine = storage.GetBoolRef("serpentine", false);
|
||||
int* order = storage.GetIntRef("order", LEDConfig::RowMajor);
|
||||
int* start = storage.GetIntRef("start", LEDConfig::UpperLeft);
|
||||
int& numColumns = storage.GetInt("columns", 10);
|
||||
bool& serpentine = storage.GetBool("serpentine", false);
|
||||
int& order = storage.GetInt("order", LEDConfig::RowMajor);
|
||||
int& start = storage.GetInt("start", LEDConfig::UpperLeft);
|
||||
|
||||
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
|
||||
ImGui::LabelText("Length", "%d", length);
|
||||
ImGui::LabelText("Running", "%s", running ? "Yes" : "No");
|
||||
ImGui::InputInt("Columns", numColumns);
|
||||
ImGui::InputInt("Columns", &numColumns);
|
||||
{
|
||||
static const char* options[] = {"Row Major", "Column Major"};
|
||||
ImGui::Combo("Order", order, options, 2);
|
||||
ImGui::Combo("Order", &order, options, 2);
|
||||
}
|
||||
{
|
||||
static const char* options[] = {"Upper Left", "Lower Left", "Upper Right",
|
||||
"Lower Right"};
|
||||
ImGui::Combo("Start", start, options, 4);
|
||||
ImGui::Combo("Start", &start, options, 4);
|
||||
}
|
||||
ImGui::Checkbox("Serpentine", serpentine);
|
||||
if (*numColumns < 1) {
|
||||
*numColumns = 1;
|
||||
ImGui::Checkbox("Serpentine", &serpentine);
|
||||
if (numColumns < 1) {
|
||||
numColumns = 1;
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
@@ -74,12 +75,12 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) {
|
||||
}
|
||||
|
||||
LEDConfig config;
|
||||
config.serpentine = *serpentine;
|
||||
config.order = static_cast<LEDConfig::Order>(*order);
|
||||
config.start = static_cast<LEDConfig::Start>(*start);
|
||||
config.serpentine = serpentine;
|
||||
config.order = static_cast<LEDConfig::Order>(order);
|
||||
config.start = static_cast<LEDConfig::Start>(start);
|
||||
|
||||
DrawLEDs(iData->values.data(), length, *numColumns, iData->colors.data(), 0,
|
||||
0, config);
|
||||
DrawLEDs(iData->values.data(), length, numColumns, iData->colors.data(), 0, 0,
|
||||
config);
|
||||
}
|
||||
|
||||
void glass::DisplayLEDDisplays(LEDDisplaysModel* model) {
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/Storage.h"
|
||||
#include "glass/other/DeviceTree.h"
|
||||
#include "glass/support/ExtraGuiWidgets.h"
|
||||
#include "glass/support/IniSaverInfo.h"
|
||||
#include "glass/support/NameSetting.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -42,18 +43,19 @@ bool glass::DisplayPCMSolenoids(PCMModel* model, int index,
|
||||
}
|
||||
|
||||
// build header label
|
||||
std::string* name = GetStorage().GetStringRef("name");
|
||||
std::string& name = GetStorage().GetString("name");
|
||||
char label[128];
|
||||
if (!name->empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), index);
|
||||
if (!name.empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###header", name.c_str(),
|
||||
index);
|
||||
} else {
|
||||
std::snprintf(label, sizeof(label), "PCM[%d]###name", index);
|
||||
std::snprintf(label, sizeof(label), "PCM[%d]###header", index);
|
||||
}
|
||||
|
||||
// header
|
||||
bool open = CollapsingHeader(label);
|
||||
|
||||
PopupEditName("name", name);
|
||||
PopupEditName("header", &name);
|
||||
|
||||
ImGui::SetItemAllowOverlap();
|
||||
ImGui::SameLine();
|
||||
@@ -68,11 +70,11 @@ bool glass::DisplayPCMSolenoids(PCMModel* model, int index,
|
||||
model->ForEachSolenoid([&](SolenoidModel& solenoid, int j) {
|
||||
if (auto data = solenoid.GetOutputData()) {
|
||||
PushID(j);
|
||||
char solenoidName[64];
|
||||
auto& info = data->GetNameInfo();
|
||||
info.GetLabel(solenoidName, sizeof(solenoidName), "Solenoid", j);
|
||||
data->LabelText(solenoidName, "%s", channels[j] == 1 ? "On" : "Off");
|
||||
info.PopupEditName(j);
|
||||
char label[64];
|
||||
NameSetting name{data->GetName()};
|
||||
name.GetLabel(label, sizeof(label), "Solenoid", j);
|
||||
data->LabelText(label, "%s", channels[j] == 1 ? "On" : "Off");
|
||||
name.PopupEditName(j);
|
||||
PopID();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -18,10 +19,10 @@ void glass::DisplayPWM(PWMModel* model, int index, bool outputsEnabled) {
|
||||
}
|
||||
|
||||
// build label
|
||||
std::string* name = GetStorage().GetStringRef("name");
|
||||
std::string& name = GetStorage().GetString("name");
|
||||
char label[128];
|
||||
if (!name->empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), index);
|
||||
if (!name.empty()) {
|
||||
std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), index);
|
||||
} else {
|
||||
std::snprintf(label, sizeof(label), "PWM[%d]###name", index);
|
||||
}
|
||||
@@ -35,8 +36,8 @@ void glass::DisplayPWM(PWMModel* model, int index, bool outputsEnabled) {
|
||||
float val = outputsEnabled ? data->GetValue() : 0;
|
||||
data->LabelText(label, "%0.3f", val);
|
||||
}
|
||||
if (PopupEditName("name", name)) {
|
||||
data->SetName(name->c_str());
|
||||
if (PopupEditName("name", &name)) {
|
||||
data->SetName(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/support/IniSaverInfo.h"
|
||||
#include "glass/support/NameSetting.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -19,16 +19,16 @@ static float DisplayChannel(PowerDistributionModel& pdp, int channel) {
|
||||
float width = 0;
|
||||
if (auto currentData = pdp.GetCurrentData(channel)) {
|
||||
ImGui::PushID(channel);
|
||||
auto& leftInfo = currentData->GetNameInfo();
|
||||
NameSetting leftName{currentData->GetName()};
|
||||
char name[64];
|
||||
leftInfo.GetLabel(name, sizeof(name), "", channel);
|
||||
leftName.GetLabel(name, sizeof(name), "", channel);
|
||||
double val = currentData->GetValue();
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
|
||||
if (currentData->InputDouble(name, &val, 0, 0, "%.3f")) {
|
||||
pdp.SetCurrent(channel, val);
|
||||
}
|
||||
width = ImGui::GetItemRectSize().x;
|
||||
leftInfo.PopupEditName(channel);
|
||||
leftName.PopupEditName(channel);
|
||||
ImGui::PopID();
|
||||
}
|
||||
return width;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/Storage.h"
|
||||
#include "glass/support/ExtraGuiWidgets.h"
|
||||
|
||||
using namespace glass;
|
||||
@@ -31,20 +32,20 @@ void glass::DisplayRelay(RelayModel* model, int index, bool outputsEnabled) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string* name = GetStorage().GetStringRef("name");
|
||||
std::string& name = GetStorage().GetString("name");
|
||||
ImGui::PushID("name");
|
||||
if (!name->empty()) {
|
||||
ImGui::Text("%s [%d]", name->c_str(), index);
|
||||
if (!name.empty()) {
|
||||
ImGui::Text("%s [%d]", name.c_str(), index);
|
||||
} else {
|
||||
ImGui::Text("Relay[%d]", index);
|
||||
}
|
||||
ImGui::PopID();
|
||||
if (PopupEditName("name", name)) {
|
||||
if (PopupEditName("name", &name)) {
|
||||
if (forwardData) {
|
||||
forwardData->SetName(name->c_str());
|
||||
forwardData->SetName(name);
|
||||
}
|
||||
if (reverseData) {
|
||||
reverseData->SetName(name->c_str());
|
||||
reverseData->SetName(name);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
@@ -51,13 +51,13 @@ bool glass::BeginDevice(const char* id, ImGuiTreeNodeFlags flags) {
|
||||
PushID(id);
|
||||
|
||||
// build label
|
||||
std::string* name = GetStorage().GetStringRef("name");
|
||||
std::string& name = GetStorage().GetString("name");
|
||||
char label[128];
|
||||
std::snprintf(label, sizeof(label), "%s###name",
|
||||
name->empty() ? id : name->c_str());
|
||||
std::snprintf(label, sizeof(label), "%s###header",
|
||||
name.empty() ? id : name.c_str());
|
||||
|
||||
bool open = CollapsingHeader(label, flags);
|
||||
PopupEditName("name", name);
|
||||
PopupEditName("header", &name);
|
||||
|
||||
if (!open) {
|
||||
PopID();
|
||||
|
||||
@@ -90,11 +90,20 @@ void glass::DisplayDrive(DriveModel* m) {
|
||||
double a1 = 0.0;
|
||||
double a2 = wpi::numbers::pi / 2 * rotation;
|
||||
|
||||
draw->PathArcTo(center, radius, a1, a2, 20);
|
||||
draw->PathStroke(color, false);
|
||||
draw->PathArcTo(center, radius, a1 + wpi::numbers::pi,
|
||||
a2 + wpi::numbers::pi, 20);
|
||||
draw->PathStroke(color, false);
|
||||
// PathArcTo requires a_min <= a_max, and rotation can be negative
|
||||
if (a1 > a2) {
|
||||
draw->PathArcTo(center, radius, a2, a1, 20);
|
||||
draw->PathStroke(color, false);
|
||||
draw->PathArcTo(center, radius, a2 + wpi::numbers::pi,
|
||||
a1 + wpi::numbers::pi, 20);
|
||||
draw->PathStroke(color, false);
|
||||
} else {
|
||||
draw->PathArcTo(center, radius, a1, a2, 20);
|
||||
draw->PathStroke(color, false);
|
||||
draw->PathArcTo(center, radius, a1 + wpi::numbers::pi,
|
||||
a2 + wpi::numbers::pi, 20);
|
||||
draw->PathStroke(color, false);
|
||||
}
|
||||
|
||||
double adder = rotation < 0 ? wpi::numbers::pi : 0;
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#include <wpigui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/Storage.h"
|
||||
#include "glass/support/ColorSetting.h"
|
||||
#include "glass/support/EnumSetting.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -114,12 +117,14 @@ struct DisplayOptions {
|
||||
|
||||
static constexpr Style kDefaultStyle = kBoxImage;
|
||||
static constexpr float kDefaultWeight = 4.0f;
|
||||
static constexpr float kDefaultColorFloat[] = {255, 0, 0, 255};
|
||||
static constexpr ImU32 kDefaultColor = IM_COL32(255, 0, 0, 255);
|
||||
static constexpr auto kDefaultWidth = 0.6858_m;
|
||||
static constexpr auto kDefaultLength = 0.8204_m;
|
||||
static constexpr bool kDefaultArrows = true;
|
||||
static constexpr int kDefaultArrowSize = 50;
|
||||
static constexpr float kDefaultArrowWeight = 4.0f;
|
||||
static constexpr float kDefaultArrowColorFloat[] = {0, 255, 0, 255};
|
||||
static constexpr ImU32 kDefaultArrowColor = IM_COL32(0, 255, 0, 255);
|
||||
static constexpr bool kDefaultSelectable = true;
|
||||
|
||||
@@ -180,7 +185,7 @@ class PoseFrameData {
|
||||
|
||||
class ObjectInfo {
|
||||
public:
|
||||
ObjectInfo();
|
||||
explicit ObjectInfo(Storage& storage);
|
||||
|
||||
DisplayOptions GetDisplayOptions() const;
|
||||
void DisplaySettings();
|
||||
@@ -191,26 +196,26 @@ class ObjectInfo {
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
bool LoadImageImpl(const char* fn);
|
||||
bool LoadImageImpl(const std::string& fn);
|
||||
|
||||
std::unique_ptr<pfd::open_file> m_fileOpener;
|
||||
|
||||
// in meters
|
||||
float* m_pWidth;
|
||||
float* m_pLength;
|
||||
float& m_width;
|
||||
float& m_length;
|
||||
|
||||
int* m_pStyle; // DisplayOptions::Style
|
||||
float* m_pWeight;
|
||||
int* m_pColor;
|
||||
EnumSetting m_style; // DisplayOptions::Style
|
||||
float& m_weight;
|
||||
ColorSetting m_color;
|
||||
|
||||
bool* m_pArrows;
|
||||
int* m_pArrowSize;
|
||||
float* m_pArrowWeight;
|
||||
int* m_pArrowColor;
|
||||
bool& m_arrows;
|
||||
int& m_arrowSize;
|
||||
float& m_arrowWeight;
|
||||
ColorSetting m_arrowColor;
|
||||
|
||||
bool* m_pSelectable;
|
||||
bool& m_selectable;
|
||||
|
||||
std::string* m_pFilename;
|
||||
std::string& m_filename;
|
||||
gui::Texture m_texture;
|
||||
};
|
||||
|
||||
@@ -219,7 +224,7 @@ class FieldInfo {
|
||||
static constexpr auto kDefaultWidth = 15.98_m;
|
||||
static constexpr auto kDefaultHeight = 8.21_m;
|
||||
|
||||
FieldInfo();
|
||||
explicit FieldInfo(Storage& storage);
|
||||
|
||||
void DisplaySettings();
|
||||
|
||||
@@ -231,25 +236,25 @@ class FieldInfo {
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
bool LoadImageImpl(const char* fn);
|
||||
bool LoadImageImpl(const std::string& fn);
|
||||
void LoadJson(std::string_view jsonfile);
|
||||
|
||||
std::unique_ptr<pfd::open_file> m_fileOpener;
|
||||
|
||||
std::string* m_pFilename;
|
||||
std::string& m_filename;
|
||||
gui::Texture m_texture;
|
||||
|
||||
// in meters
|
||||
float* m_pWidth;
|
||||
float* m_pHeight;
|
||||
float& m_width;
|
||||
float& m_height;
|
||||
|
||||
// in image pixels
|
||||
int m_imageWidth;
|
||||
int m_imageHeight;
|
||||
int* m_pTop;
|
||||
int* m_pLeft;
|
||||
int* m_pBottom;
|
||||
int* m_pRight;
|
||||
int& m_top;
|
||||
int& m_left;
|
||||
int& m_bottom;
|
||||
int& m_right;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -334,16 +339,14 @@ static bool InputPose(frc::Pose2d* pose) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
FieldInfo::FieldInfo() {
|
||||
auto& storage = GetStorage();
|
||||
m_pFilename = storage.GetStringRef("image");
|
||||
m_pTop = storage.GetIntRef("top", 0);
|
||||
m_pLeft = storage.GetIntRef("left", 0);
|
||||
m_pBottom = storage.GetIntRef("bottom", -1);
|
||||
m_pRight = storage.GetIntRef("right", -1);
|
||||
m_pWidth = storage.GetFloatRef("width", kDefaultWidth.to<float>());
|
||||
m_pHeight = storage.GetFloatRef("height", kDefaultHeight.to<float>());
|
||||
}
|
||||
FieldInfo::FieldInfo(Storage& storage)
|
||||
: m_filename{storage.GetString("image")},
|
||||
m_width{storage.GetFloat("width", kDefaultWidth.to<float>())},
|
||||
m_height{storage.GetFloat("height", kDefaultHeight.to<float>())},
|
||||
m_top{storage.GetInt("top", 0)},
|
||||
m_left{storage.GetInt("left", 0)},
|
||||
m_bottom{storage.GetInt("bottom", -1)},
|
||||
m_right{storage.GetInt("right", -1)} {}
|
||||
|
||||
void FieldInfo::DisplaySettings() {
|
||||
if (ImGui::Button("Choose image...")) {
|
||||
@@ -357,23 +360,23 @@ void FieldInfo::DisplaySettings() {
|
||||
if (ImGui::Button("Reset image")) {
|
||||
Reset();
|
||||
}
|
||||
InputFloatLength("Field Width", m_pWidth);
|
||||
InputFloatLength("Field Height", m_pHeight);
|
||||
// ImGui::InputInt("Field Top", m_pTop);
|
||||
// ImGui::InputInt("Field Left", m_pLeft);
|
||||
// ImGui::InputInt("Field Right", m_pRight);
|
||||
// ImGui::InputInt("Field Bottom", m_pBottom);
|
||||
InputFloatLength("Field Width", &m_width);
|
||||
InputFloatLength("Field Height", &m_height);
|
||||
// ImGui::InputInt("Field Top", &m_top);
|
||||
// ImGui::InputInt("Field Left", &m_left);
|
||||
// ImGui::InputInt("Field Right", &m_right);
|
||||
// ImGui::InputInt("Field Bottom", &m_bottom);
|
||||
}
|
||||
|
||||
void FieldInfo::Reset() {
|
||||
m_texture = gui::Texture{};
|
||||
m_pFilename->clear();
|
||||
m_filename.clear();
|
||||
m_imageWidth = 0;
|
||||
m_imageHeight = 0;
|
||||
*m_pTop = 0;
|
||||
*m_pLeft = 0;
|
||||
*m_pBottom = -1;
|
||||
*m_pRight = -1;
|
||||
m_top = 0;
|
||||
m_left = 0;
|
||||
m_bottom = -1;
|
||||
m_right = -1;
|
||||
}
|
||||
|
||||
void FieldInfo::LoadImage() {
|
||||
@@ -384,17 +387,17 @@ void FieldInfo::LoadImage() {
|
||||
LoadJson(result[0]);
|
||||
} else {
|
||||
LoadImageImpl(result[0].c_str());
|
||||
*m_pTop = 0;
|
||||
*m_pLeft = 0;
|
||||
*m_pBottom = -1;
|
||||
*m_pRight = -1;
|
||||
m_top = 0;
|
||||
m_left = 0;
|
||||
m_bottom = -1;
|
||||
m_right = -1;
|
||||
}
|
||||
}
|
||||
m_fileOpener.reset();
|
||||
}
|
||||
if (!m_texture && !m_pFilename->empty()) {
|
||||
if (!LoadImageImpl(m_pFilename->c_str())) {
|
||||
m_pFilename->clear();
|
||||
if (!m_texture && !m_filename.empty()) {
|
||||
if (!LoadImageImpl(m_filename)) {
|
||||
m_filename.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,18 +481,18 @@ void FieldInfo::LoadJson(std::string_view jsonfile) {
|
||||
}
|
||||
|
||||
// save to field info
|
||||
*m_pFilename = pathname;
|
||||
*m_pTop = top;
|
||||
*m_pLeft = left;
|
||||
*m_pBottom = bottom;
|
||||
*m_pRight = right;
|
||||
*m_pWidth = width;
|
||||
*m_pHeight = height;
|
||||
m_filename = pathname;
|
||||
m_top = top;
|
||||
m_left = left;
|
||||
m_bottom = bottom;
|
||||
m_right = right;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
}
|
||||
|
||||
bool FieldInfo::LoadImageImpl(const char* fn) {
|
||||
bool FieldInfo::LoadImageImpl(const std::string& fn) {
|
||||
fmt::print("GUI: loading field image '{}'\n", fn);
|
||||
auto texture = gui::Texture::CreateFromFile(fn);
|
||||
auto texture = gui::Texture::CreateFromFile(fn.c_str());
|
||||
if (!texture) {
|
||||
std::puts("GUI: could not read field image");
|
||||
return false;
|
||||
@@ -497,7 +500,7 @@ bool FieldInfo::LoadImageImpl(const char* fn) {
|
||||
m_texture = std::move(texture);
|
||||
m_imageWidth = m_texture.GetWidth();
|
||||
m_imageHeight = m_texture.GetHeight();
|
||||
*m_pFilename = fn;
|
||||
m_filename = fn;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -512,19 +515,19 @@ FieldFrameData FieldInfo::GetFrameData(ImVec2 min, ImVec2 max) const {
|
||||
ffd.imageMax = max;
|
||||
|
||||
// size down the box by the image corners (if any)
|
||||
if (*m_pBottom > 0 && *m_pRight > 0) {
|
||||
min.x += *m_pLeft * (max.x - min.x) / m_imageWidth;
|
||||
min.y += *m_pTop * (max.y - min.y) / m_imageHeight;
|
||||
max.x -= (m_imageWidth - *m_pRight) * (max.x - min.x) / m_imageWidth;
|
||||
max.y -= (m_imageHeight - *m_pBottom) * (max.y - min.y) / m_imageHeight;
|
||||
if (m_bottom > 0 && m_right > 0) {
|
||||
min.x += m_left * (max.x - min.x) / m_imageWidth;
|
||||
min.y += m_top * (max.y - min.y) / m_imageHeight;
|
||||
max.x -= (m_imageWidth - m_right) * (max.x - min.x) / m_imageWidth;
|
||||
max.y -= (m_imageHeight - m_bottom) * (max.y - min.y) / m_imageHeight;
|
||||
}
|
||||
|
||||
// draw the field "active area" as a yellow boundary box
|
||||
gui::MaxFit(&min, &max, *m_pWidth, *m_pHeight);
|
||||
gui::MaxFit(&min, &max, m_width, m_height);
|
||||
|
||||
ffd.min = min;
|
||||
ffd.max = max;
|
||||
ffd.scale = (max.x - min.x) / *m_pWidth;
|
||||
ffd.scale = (max.x - min.x) / m_width;
|
||||
return ffd;
|
||||
}
|
||||
|
||||
@@ -537,48 +540,47 @@ void FieldInfo::Draw(ImDrawList* drawList, const FieldFrameData& ffd) const {
|
||||
drawList->AddRect(ffd.min, ffd.max, IM_COL32(255, 255, 0, 255));
|
||||
}
|
||||
|
||||
ObjectInfo::ObjectInfo() {
|
||||
auto& storage = GetStorage();
|
||||
m_pFilename = storage.GetStringRef("image");
|
||||
m_pWidth =
|
||||
storage.GetFloatRef("width", DisplayOptions::kDefaultWidth.to<float>());
|
||||
m_pLength =
|
||||
storage.GetFloatRef("length", DisplayOptions::kDefaultLength.to<float>());
|
||||
m_pStyle = storage.GetIntRef("style", DisplayOptions::kDefaultStyle);
|
||||
m_pWeight = storage.GetFloatRef("weight", DisplayOptions::kDefaultWeight);
|
||||
m_pColor = storage.GetIntRef("color", DisplayOptions::kDefaultColor);
|
||||
m_pArrows = storage.GetBoolRef("arrows", DisplayOptions::kDefaultArrows);
|
||||
m_pArrowSize =
|
||||
storage.GetIntRef("arrowSize", DisplayOptions::kDefaultArrowSize);
|
||||
m_pArrowWeight =
|
||||
storage.GetFloatRef("arrowWeight", DisplayOptions::kDefaultArrowWeight);
|
||||
m_pArrowColor =
|
||||
storage.GetIntRef("arrowColor", DisplayOptions::kDefaultArrowColor);
|
||||
m_pSelectable =
|
||||
storage.GetBoolRef("selectable", DisplayOptions::kDefaultSelectable);
|
||||
}
|
||||
ObjectInfo::ObjectInfo(Storage& storage)
|
||||
: m_width{storage.GetFloat("width",
|
||||
DisplayOptions::kDefaultWidth.to<float>())},
|
||||
m_length{storage.GetFloat("length",
|
||||
DisplayOptions::kDefaultLength.to<float>())},
|
||||
m_style{storage.GetString("style"),
|
||||
DisplayOptions::kDefaultStyle,
|
||||
{"Box/Image", "Line", "Line (Closed)", "Track"}},
|
||||
m_weight{storage.GetFloat("weight", DisplayOptions::kDefaultWeight)},
|
||||
m_color{
|
||||
storage.GetFloatArray("color", DisplayOptions::kDefaultColorFloat)},
|
||||
m_arrows{storage.GetBool("arrows", DisplayOptions::kDefaultArrows)},
|
||||
m_arrowSize{
|
||||
storage.GetInt("arrowSize", DisplayOptions::kDefaultArrowSize)},
|
||||
m_arrowWeight{
|
||||
storage.GetFloat("arrowWeight", DisplayOptions::kDefaultArrowWeight)},
|
||||
m_arrowColor{storage.GetFloatArray(
|
||||
"arrowColor", DisplayOptions::kDefaultArrowColorFloat)},
|
||||
m_selectable{
|
||||
storage.GetBool("selectable", DisplayOptions::kDefaultSelectable)},
|
||||
m_filename{storage.GetString("image")} {}
|
||||
|
||||
DisplayOptions ObjectInfo::GetDisplayOptions() const {
|
||||
DisplayOptions rv{m_texture};
|
||||
rv.style = static_cast<DisplayOptions::Style>(*m_pStyle);
|
||||
rv.weight = *m_pWeight;
|
||||
rv.color = *m_pColor;
|
||||
rv.width = units::meter_t{*m_pWidth};
|
||||
rv.length = units::meter_t{*m_pLength};
|
||||
rv.arrows = *m_pArrows;
|
||||
rv.arrowSize = *m_pArrowSize;
|
||||
rv.arrowWeight = *m_pArrowWeight;
|
||||
rv.arrowColor = *m_pArrowColor;
|
||||
rv.selectable = *m_pSelectable;
|
||||
rv.style = static_cast<DisplayOptions::Style>(m_style.GetValue());
|
||||
rv.weight = m_weight;
|
||||
rv.color = ImGui::ColorConvertFloat4ToU32(m_color.GetColor());
|
||||
rv.width = units::meter_t{m_width};
|
||||
rv.length = units::meter_t{m_length};
|
||||
rv.arrows = m_arrows;
|
||||
rv.arrowSize = m_arrowSize;
|
||||
rv.arrowWeight = m_arrowWeight;
|
||||
rv.arrowColor = ImGui::ColorConvertFloat4ToU32(m_arrowColor.GetColor());
|
||||
rv.selectable = m_selectable;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void ObjectInfo::DisplaySettings() {
|
||||
static const char* styleChoices[] = {"Box/Image", "Line", "Line (Closed)",
|
||||
"Track"};
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::Combo("Style", m_pStyle, styleChoices, IM_ARRAYSIZE(styleChoices));
|
||||
switch (*m_pStyle) {
|
||||
m_style.Combo("Style");
|
||||
switch (m_style.GetValue()) {
|
||||
case DisplayOptions::kBoxImage:
|
||||
if (ImGui::Button("Choose image...")) {
|
||||
m_fileOpener = std::make_unique<pfd::open_file>(
|
||||
@@ -591,35 +593,27 @@ void ObjectInfo::DisplaySettings() {
|
||||
if (ImGui::Button("Reset image")) {
|
||||
Reset();
|
||||
}
|
||||
InputFloatLength("Width", m_pWidth);
|
||||
InputFloatLength("Length", m_pLength);
|
||||
InputFloatLength("Width", &m_width);
|
||||
InputFloatLength("Length", &m_length);
|
||||
break;
|
||||
case DisplayOptions::kTrack:
|
||||
InputFloatLength("Width", m_pWidth);
|
||||
InputFloatLength("Width", &m_width);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::InputFloat("Line Weight", m_pWeight);
|
||||
ImColor col(*m_pColor);
|
||||
if (ImGui::ColorEdit3("Line Color", &col.Value.x,
|
||||
ImGuiColorEditFlags_NoInputs)) {
|
||||
*m_pColor = col;
|
||||
}
|
||||
ImGui::Checkbox("Arrows", m_pArrows);
|
||||
if (*m_pArrows) {
|
||||
ImGui::SliderInt("Arrow Size", m_pArrowSize, 0, 100, "%d%%",
|
||||
ImGui::InputFloat("Line Weight", &m_weight);
|
||||
m_color.ColorEdit3("Line Color", ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::Checkbox("Arrows", &m_arrows);
|
||||
if (m_arrows) {
|
||||
ImGui::SliderInt("Arrow Size", &m_arrowSize, 0, 100, "%d%%",
|
||||
ImGuiSliderFlags_AlwaysClamp);
|
||||
ImGui::InputFloat("Arrow Weight", m_pArrowWeight);
|
||||
ImColor col(*m_pArrowColor);
|
||||
if (ImGui::ColorEdit3("Arrow Color", &col.Value.x,
|
||||
ImGuiColorEditFlags_NoInputs)) {
|
||||
*m_pArrowColor = col;
|
||||
}
|
||||
ImGui::InputFloat("Arrow Weight", &m_arrowWeight);
|
||||
m_arrowColor.ColorEdit3("Arrow Color", ImGuiColorEditFlags_NoInputs);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Selectable", m_pSelectable);
|
||||
ImGui::Checkbox("Selectable", &m_selectable);
|
||||
}
|
||||
|
||||
void ObjectInfo::DrawLine(ImDrawList* drawList,
|
||||
@@ -629,10 +623,12 @@ void ObjectInfo::DrawLine(ImDrawList* drawList,
|
||||
}
|
||||
|
||||
if (points.size() == 1) {
|
||||
drawList->AddCircleFilled(points.front(), *m_pWeight, *m_pWeight);
|
||||
drawList->AddCircleFilled(points.front(), m_weight, m_weight);
|
||||
return;
|
||||
}
|
||||
|
||||
ImU32 color = ImGui::ColorConvertFloat4ToU32(m_color.GetColor());
|
||||
|
||||
// PolyLine doesn't handle acute angles well; workaround from
|
||||
// https://github.com/ocornut/imgui/issues/3366
|
||||
size_t i = 0;
|
||||
@@ -651,18 +647,18 @@ void ObjectInfo::DrawLine(ImDrawList* drawList,
|
||||
++nlin;
|
||||
}
|
||||
|
||||
drawList->AddPolyline(&points[i], nlin, *m_pColor, false, *m_pWeight);
|
||||
drawList->AddPolyline(&points[i], nlin, color, false, m_weight);
|
||||
i += nlin - 1;
|
||||
}
|
||||
|
||||
if (points.size() > 2 && *m_pStyle == DisplayOptions::kLineClosed) {
|
||||
drawList->AddLine(points.back(), points.front(), *m_pColor, *m_pWeight);
|
||||
if (points.size() > 2 && m_style.GetValue() == DisplayOptions::kLineClosed) {
|
||||
drawList->AddLine(points.back(), points.front(), color, m_weight);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectInfo::Reset() {
|
||||
m_texture = gui::Texture{};
|
||||
m_pFilename->clear();
|
||||
m_filename.clear();
|
||||
}
|
||||
|
||||
void ObjectInfo::LoadImage() {
|
||||
@@ -673,22 +669,22 @@ void ObjectInfo::LoadImage() {
|
||||
}
|
||||
m_fileOpener.reset();
|
||||
}
|
||||
if (!m_texture && !m_pFilename->empty()) {
|
||||
if (!LoadImageImpl(m_pFilename->c_str())) {
|
||||
m_pFilename->clear();
|
||||
if (!m_texture && !m_filename.empty()) {
|
||||
if (!LoadImageImpl(m_filename)) {
|
||||
m_filename.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectInfo::LoadImageImpl(const char* fn) {
|
||||
bool ObjectInfo::LoadImageImpl(const std::string& fn) {
|
||||
fmt::print("GUI: loading object image '{}'\n", fn);
|
||||
auto texture = gui::Texture::CreateFromFile(fn);
|
||||
auto texture = gui::Texture::CreateFromFile(fn.c_str());
|
||||
if (!texture) {
|
||||
std::fputs("GUI: could not read object image\n", stderr);
|
||||
return false;
|
||||
}
|
||||
m_texture = std::move(texture);
|
||||
*m_pFilename = fn;
|
||||
m_filename = fn;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -857,15 +853,16 @@ void glass::DisplayField2DSettings(Field2DModel* model) {
|
||||
auto& storage = GetStorage();
|
||||
auto field = storage.GetData<FieldInfo>();
|
||||
if (!field) {
|
||||
storage.SetData(std::make_shared<FieldInfo>());
|
||||
storage.SetData(std::make_shared<FieldInfo>(storage));
|
||||
field = storage.GetData<FieldInfo>();
|
||||
}
|
||||
|
||||
static const char* unitNames[] = {"meters", "feet", "inches"};
|
||||
int* pDisplayUnits = GetStorage().GetIntRef("units", kDisplayMeters);
|
||||
EnumSetting displayUnits{GetStorage().GetString("units"),
|
||||
kDisplayMeters,
|
||||
{"meters", "feet", "inches"}};
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::Combo("Units", pDisplayUnits, unitNames, IM_ARRAYSIZE(unitNames));
|
||||
gDisplayUnits = static_cast<DisplayUnits>(*pDisplayUnits);
|
||||
displayUnits.Combo("Units");
|
||||
gDisplayUnits = static_cast<DisplayUnits>(displayUnits.GetValue());
|
||||
|
||||
ImGui::PushItemWidth(ImGui::GetFontSize() * 4);
|
||||
if (ImGui::CollapsingHeader("Field")) {
|
||||
@@ -881,7 +878,7 @@ void glass::DisplayField2DSettings(Field2DModel* model) {
|
||||
PushID(name);
|
||||
auto& objRef = field->m_objects[name];
|
||||
if (!objRef) {
|
||||
objRef = std::make_unique<ObjectInfo>();
|
||||
objRef = std::make_unique<ObjectInfo>(GetStorage());
|
||||
}
|
||||
auto obj = objRef.get();
|
||||
|
||||
@@ -1025,7 +1022,7 @@ void FieldDisplay::DisplayObject(FieldObjectModel& model,
|
||||
PushID(name);
|
||||
auto& objRef = m_field->m_objects[name];
|
||||
if (!objRef) {
|
||||
objRef = std::make_unique<ObjectInfo>();
|
||||
objRef = std::make_unique<ObjectInfo>(GetStorage());
|
||||
}
|
||||
auto obj = objRef.get();
|
||||
obj->LoadImage();
|
||||
@@ -1205,7 +1202,7 @@ void glass::DisplayField2D(Field2DModel* model, const ImVec2& contentSize) {
|
||||
auto& storage = GetStorage();
|
||||
auto field = storage.GetData<FieldInfo>();
|
||||
if (!field) {
|
||||
storage.SetData(std::make_shared<FieldInfo>());
|
||||
storage.SetData(std::make_shared<FieldInfo>(storage));
|
||||
field = storage.GetData<FieldInfo>();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <wpigui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -61,7 +62,7 @@ struct FrameData {
|
||||
|
||||
class BackgroundInfo {
|
||||
public:
|
||||
BackgroundInfo();
|
||||
explicit BackgroundInfo(Storage& storage);
|
||||
|
||||
void DisplaySettings();
|
||||
|
||||
@@ -72,11 +73,11 @@ class BackgroundInfo {
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
bool LoadImageImpl(const char* fn);
|
||||
bool LoadImageImpl(const std::string& fn);
|
||||
|
||||
std::unique_ptr<pfd::open_file> m_fileOpener;
|
||||
|
||||
std::string* m_pFilename;
|
||||
std::string& m_filename;
|
||||
gui::Texture m_texture;
|
||||
|
||||
// in image pixels
|
||||
@@ -86,10 +87,8 @@ class BackgroundInfo {
|
||||
|
||||
} // namespace
|
||||
|
||||
BackgroundInfo::BackgroundInfo() {
|
||||
auto& storage = GetStorage();
|
||||
m_pFilename = storage.GetStringRef("image");
|
||||
}
|
||||
BackgroundInfo::BackgroundInfo(Storage& storage)
|
||||
: m_filename{storage.GetString("image")} {}
|
||||
|
||||
void BackgroundInfo::DisplaySettings() {
|
||||
if (ImGui::Button("Choose image...")) {
|
||||
@@ -106,7 +105,7 @@ void BackgroundInfo::DisplaySettings() {
|
||||
|
||||
void BackgroundInfo::Reset() {
|
||||
m_texture = gui::Texture{};
|
||||
m_pFilename->clear();
|
||||
m_filename.clear();
|
||||
m_imageWidth = 0;
|
||||
m_imageHeight = 0;
|
||||
}
|
||||
@@ -119,16 +118,16 @@ void BackgroundInfo::LoadImage() {
|
||||
}
|
||||
m_fileOpener.reset();
|
||||
}
|
||||
if (!m_texture && !m_pFilename->empty()) {
|
||||
if (!LoadImageImpl(m_pFilename->c_str())) {
|
||||
m_pFilename->clear();
|
||||
if (!m_texture && !m_filename.empty()) {
|
||||
if (!LoadImageImpl(m_filename)) {
|
||||
m_filename.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BackgroundInfo::LoadImageImpl(const char* fn) {
|
||||
bool BackgroundInfo::LoadImageImpl(const std::string& fn) {
|
||||
fmt::print("GUI: loading background image '{}'\n", fn);
|
||||
auto texture = gui::Texture::CreateFromFile(fn);
|
||||
auto texture = gui::Texture::CreateFromFile(fn.c_str());
|
||||
if (!texture) {
|
||||
std::puts("GUI: could not read background image");
|
||||
return false;
|
||||
@@ -136,7 +135,7 @@ bool BackgroundInfo::LoadImageImpl(const char* fn) {
|
||||
m_texture = std::move(texture);
|
||||
m_imageWidth = m_texture.GetWidth();
|
||||
m_imageHeight = m_texture.GetHeight();
|
||||
*m_pFilename = fn;
|
||||
m_filename = fn;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -175,7 +174,7 @@ void glass::DisplayMechanism2DSettings(Mechanism2DModel* model) {
|
||||
auto& storage = GetStorage();
|
||||
auto bg = storage.GetData<BackgroundInfo>();
|
||||
if (!bg) {
|
||||
storage.SetData(std::make_shared<BackgroundInfo>());
|
||||
storage.SetData(std::make_shared<BackgroundInfo>(storage));
|
||||
bg = storage.GetData<BackgroundInfo>();
|
||||
}
|
||||
bg->DisplaySettings();
|
||||
@@ -208,7 +207,7 @@ void glass::DisplayMechanism2D(Mechanism2DModel* model,
|
||||
auto& storage = GetStorage();
|
||||
auto bg = storage.GetData<BackgroundInfo>();
|
||||
if (!bg) {
|
||||
storage.SetData(std::make_shared<BackgroundInfo>());
|
||||
storage.SetData(std::make_shared<BackgroundInfo>(storage));
|
||||
bg = storage.GetData<BackgroundInfo>();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
11
glass/src/lib/native/cpp/support/ColorSetting.cpp
Normal file
11
glass/src/lib/native/cpp/support/ColorSetting.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
// 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 "glass/support/ColorSetting.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
ColorSetting::ColorSetting(std::vector<float>& color) : m_color{color} {
|
||||
m_color.resize(4);
|
||||
}
|
||||
40
glass/src/lib/native/cpp/support/EnumSetting.cpp
Normal file
40
glass/src/lib/native/cpp/support/EnumSetting.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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 "glass/support/EnumSetting.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
EnumSetting::EnumSetting(std::string& str, int defaultValue,
|
||||
std::initializer_list<const char*> choices)
|
||||
: m_str{str}, m_choices{choices}, m_value{defaultValue} {
|
||||
// override default value if str is one of the choices
|
||||
int i = 0;
|
||||
for (auto choice : choices) {
|
||||
if (str == choice) {
|
||||
m_value = i;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void EnumSetting::SetValue(int value) {
|
||||
m_value = value;
|
||||
m_str = m_choices[m_value];
|
||||
}
|
||||
|
||||
bool EnumSetting::Combo(const char* label, int numOptions,
|
||||
int popup_max_height_in_items) {
|
||||
if (ImGui::Combo(
|
||||
label, &m_value, m_choices.data(),
|
||||
numOptions < 0 ? m_choices.size() : static_cast<size_t>(numOptions),
|
||||
popup_max_height_in_items)) {
|
||||
m_str = m_choices[m_value]; // update stored string
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -160,17 +160,17 @@ bool DeleteButton(ImGuiID id, const ImVec2& pos) {
|
||||
bool HeaderDeleteButton(const char* label) {
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiLastItemDataBackup last_item_backup;
|
||||
ImGuiLastItemData last_item_backup = g.LastItemData;
|
||||
ImGuiID id = window->GetID(label);
|
||||
float button_size = g.FontSize;
|
||||
float button_x = ImMax(window->DC.LastItemRect.Min.x,
|
||||
window->DC.LastItemRect.Max.x -
|
||||
g.Style.FramePadding.x * 2.0f - button_size);
|
||||
float button_y = window->DC.LastItemRect.Min.y;
|
||||
float button_x = ImMax(
|
||||
g.LastItemData.Rect.Min.x,
|
||||
g.LastItemData.Rect.Max.x - g.Style.FramePadding.x * 2.0f - button_size);
|
||||
float button_y = g.LastItemData.Rect.Min.y;
|
||||
bool rv = DeleteButton(
|
||||
window->GetID(reinterpret_cast<void*>(static_cast<intptr_t>(id) + 1)),
|
||||
ImVec2(button_x, button_y));
|
||||
last_item_backup.Restore();
|
||||
g.LastItemData = last_item_backup;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +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 "glass/support/IniSaverBase.h"
|
||||
|
||||
#include <imgui_internal.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
namespace {
|
||||
class ImGuiSaver : public IniSaverBackend {
|
||||
public:
|
||||
void Register(IniSaverBase* iniSaver) override;
|
||||
void Unregister(IniSaverBase* iniSaver) override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void ImGuiSaver::Register(IniSaverBase* iniSaver) {
|
||||
// hook ini handler to save settings
|
||||
ImGuiSettingsHandler iniHandler;
|
||||
iniHandler.TypeName = iniSaver->GetTypeName();
|
||||
iniHandler.TypeHash = ImHashStr(iniHandler.TypeName);
|
||||
iniHandler.ReadOpenFn = [](ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
const char* name) {
|
||||
return static_cast<IniSaverBase*>(handler->UserData)->IniReadOpen(name);
|
||||
};
|
||||
iniHandler.ReadLineFn = [](ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
void* entry, const char* line) {
|
||||
static_cast<IniSaverBase*>(handler->UserData)->IniReadLine(entry, line);
|
||||
};
|
||||
iniHandler.WriteAllFn = [](ImGuiContext* ctx, ImGuiSettingsHandler* handler,
|
||||
ImGuiTextBuffer* out_buf) {
|
||||
static_cast<IniSaverBase*>(handler->UserData)->IniWriteAll(out_buf);
|
||||
};
|
||||
iniHandler.UserData = iniSaver;
|
||||
ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler);
|
||||
}
|
||||
|
||||
void ImGuiSaver::Unregister(IniSaverBase* iniSaver) {
|
||||
if (auto ctx = ImGui::GetCurrentContext()) {
|
||||
auto& handlers = ctx->SettingsHandlers;
|
||||
for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) {
|
||||
if (it->UserData == iniSaver) {
|
||||
handlers.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ImGuiSaver* GetSaverInstance() {
|
||||
static ImGuiSaver* inst = new ImGuiSaver;
|
||||
return inst;
|
||||
}
|
||||
|
||||
IniSaverBase::IniSaverBase(std::string_view typeName, IniSaverBackend* backend)
|
||||
: m_typeName(typeName), m_backend{backend ? backend : GetSaverInstance()} {}
|
||||
|
||||
IniSaverBase::~IniSaverBase() {
|
||||
m_backend->Unregister(this);
|
||||
}
|
||||
@@ -1,168 +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 "glass/support/IniSaverInfo.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <imgui_internal.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
void NameInfo::SetName(std::string_view name) {
|
||||
size_t len = (std::min)(name.size(), sizeof(m_name) - 1);
|
||||
std::memcpy(m_name, name.data(), len);
|
||||
m_name[len] = '\0';
|
||||
}
|
||||
|
||||
void NameInfo::GetName(char* buf, size_t size, const char* defaultName) const {
|
||||
if (m_name[0] != '\0') {
|
||||
std::snprintf(buf, size, "%s", m_name);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s", defaultName);
|
||||
}
|
||||
}
|
||||
|
||||
void NameInfo::GetName(char* buf, size_t size, const char* defaultName,
|
||||
int index) const {
|
||||
if (m_name[0] != '\0') {
|
||||
std::snprintf(buf, size, "%s [%d]", m_name, index);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d]", defaultName, index);
|
||||
}
|
||||
}
|
||||
|
||||
void NameInfo::GetName(char* buf, size_t size, const char* defaultName,
|
||||
int index, int index2) const {
|
||||
if (m_name[0] != '\0') {
|
||||
std::snprintf(buf, size, "%s [%d,%d]", m_name, index, index2);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d,%d]", defaultName, index, index2);
|
||||
}
|
||||
}
|
||||
|
||||
void NameInfo::GetLabel(char* buf, size_t size, const char* defaultName) const {
|
||||
if (m_name[0] != '\0') {
|
||||
std::snprintf(buf, size, "%s###Name%s", m_name, defaultName);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s###Name%s", defaultName, defaultName);
|
||||
}
|
||||
}
|
||||
|
||||
void NameInfo::GetLabel(char* buf, size_t size, const char* defaultName,
|
||||
int index) const {
|
||||
if (m_name[0] != '\0') {
|
||||
std::snprintf(buf, size, "%s [%d]###Name%d", m_name, index, index);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d]###Name%d", defaultName, index, index);
|
||||
}
|
||||
}
|
||||
|
||||
void NameInfo::GetLabel(char* buf, size_t size, const char* defaultName,
|
||||
int index, int index2) const {
|
||||
if (m_name[0] != '\0') {
|
||||
std::snprintf(buf, size, "%s [%d,%d]###Name%d", m_name, index, index2,
|
||||
index);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d,%d]###Name%d", defaultName, index, index2,
|
||||
index);
|
||||
}
|
||||
}
|
||||
|
||||
bool NameInfo::ReadIni(std::string_view name, std::string_view value) {
|
||||
if (name != "name") {
|
||||
return false;
|
||||
}
|
||||
size_t len = (std::min)(value.size(), sizeof(m_name) - 1);
|
||||
std::memcpy(m_name, value.data(), len);
|
||||
m_name[len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
void NameInfo::WriteIni(ImGuiTextBuffer* out) {
|
||||
out->appendf("name=%s\n", m_name);
|
||||
}
|
||||
|
||||
void NameInfo::PushEditNameId(int index) {
|
||||
char id[64];
|
||||
std::snprintf(id, sizeof(id), "Name%d", index);
|
||||
ImGui::PushID(id);
|
||||
}
|
||||
|
||||
void NameInfo::PushEditNameId(const char* name) {
|
||||
char id[128];
|
||||
std::snprintf(id, sizeof(id), "Name%s", name);
|
||||
ImGui::PushID(id);
|
||||
}
|
||||
|
||||
bool NameInfo::PopupEditName(int index) {
|
||||
bool rv = false;
|
||||
char id[64];
|
||||
std::snprintf(id, sizeof(id), "Name%d", index);
|
||||
if (ImGui::BeginPopupContextItem(id)) {
|
||||
ImGui::Text("Edit name:");
|
||||
if (InputTextName("##edit")) {
|
||||
rv = true;
|
||||
}
|
||||
if (ImGui::Button("Close") || ImGui::IsKeyPressedMap(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool NameInfo::PopupEditName(const char* name) {
|
||||
bool rv = false;
|
||||
char id[128];
|
||||
std::snprintf(id, sizeof(id), "Name%s", name);
|
||||
if (ImGui::BeginPopupContextItem(id)) {
|
||||
ImGui::Text("Edit name:");
|
||||
if (InputTextName("##edit")) {
|
||||
rv = true;
|
||||
}
|
||||
if (ImGui::Button("Close") || ImGui::IsKeyPressedMap(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool NameInfo::InputTextName(const char* label_id, ImGuiInputTextFlags flags) {
|
||||
return ImGui::InputText(label_id, m_name, sizeof(m_name), flags);
|
||||
}
|
||||
|
||||
bool OpenInfo::ReadIni(std::string_view name, std::string_view value) {
|
||||
if (name != "open") {
|
||||
return false;
|
||||
}
|
||||
if (auto num = wpi::parse_integer<int>(value, 10)) {
|
||||
m_open = num.value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenInfo::WriteIni(ImGuiTextBuffer* out) {
|
||||
out->appendf("open=%d\n", m_open ? 1 : 0);
|
||||
}
|
||||
|
||||
bool NameOpenInfo::ReadIni(std::string_view name, std::string_view value) {
|
||||
if (NameInfo::ReadIni(name, value)) {
|
||||
return true;
|
||||
}
|
||||
if (OpenInfo::ReadIni(name, value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NameOpenInfo::WriteIni(ImGuiTextBuffer* out) {
|
||||
NameInfo::WriteIni(out);
|
||||
OpenInfo::WriteIni(out);
|
||||
}
|
||||
123
glass/src/lib/native/cpp/support/NameSetting.cpp
Normal file
123
glass/src/lib/native/cpp/support/NameSetting.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
// 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 "glass/support/NameSetting.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
using namespace glass;
|
||||
|
||||
void NameSetting::GetName(char* buf, size_t size,
|
||||
const char* defaultName) const {
|
||||
if (!m_name.empty()) {
|
||||
std::snprintf(buf, size, "%s", m_name.c_str());
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s", defaultName);
|
||||
}
|
||||
}
|
||||
|
||||
void NameSetting::GetName(char* buf, size_t size, const char* defaultName,
|
||||
int index) const {
|
||||
if (!m_name.empty()) {
|
||||
std::snprintf(buf, size, "%s [%d]", m_name.c_str(), index);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d]", defaultName, index);
|
||||
}
|
||||
}
|
||||
|
||||
void NameSetting::GetName(char* buf, size_t size, const char* defaultName,
|
||||
int index, int index2) const {
|
||||
if (!m_name.empty()) {
|
||||
std::snprintf(buf, size, "%s [%d,%d]", m_name.c_str(), index, index2);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d,%d]", defaultName, index, index2);
|
||||
}
|
||||
}
|
||||
|
||||
void NameSetting::GetLabel(char* buf, size_t size,
|
||||
const char* defaultName) const {
|
||||
if (!m_name.empty()) {
|
||||
std::snprintf(buf, size, "%s###Name%s", m_name.c_str(), defaultName);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s###Name%s", defaultName, defaultName);
|
||||
}
|
||||
}
|
||||
|
||||
void NameSetting::GetLabel(char* buf, size_t size, const char* defaultName,
|
||||
int index) const {
|
||||
if (!m_name.empty()) {
|
||||
std::snprintf(buf, size, "%s [%d]###Name%d", m_name.c_str(), index, index);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d]###Name%d", defaultName, index, index);
|
||||
}
|
||||
}
|
||||
|
||||
void NameSetting::GetLabel(char* buf, size_t size, const char* defaultName,
|
||||
int index, int index2) const {
|
||||
if (!m_name.empty()) {
|
||||
std::snprintf(buf, size, "%s [%d,%d]###Name%d", m_name.c_str(), index,
|
||||
index2, index);
|
||||
} else {
|
||||
std::snprintf(buf, size, "%s[%d,%d]###Name%d", defaultName, index, index2,
|
||||
index);
|
||||
}
|
||||
}
|
||||
|
||||
void NameSetting::PushEditNameId(int index) {
|
||||
char id[64];
|
||||
std::snprintf(id, sizeof(id), "Name%d", index);
|
||||
ImGui::PushID(id);
|
||||
}
|
||||
|
||||
void NameSetting::PushEditNameId(const char* name) {
|
||||
char id[128];
|
||||
std::snprintf(id, sizeof(id), "Name%s", name);
|
||||
ImGui::PushID(id);
|
||||
}
|
||||
|
||||
bool NameSetting::PopupEditName(int index) {
|
||||
bool rv = false;
|
||||
char id[64];
|
||||
std::snprintf(id, sizeof(id), "Name%d", index);
|
||||
if (ImGui::BeginPopupContextItem(id)) {
|
||||
ImGui::Text("Edit name:");
|
||||
if (InputTextName("##edit")) {
|
||||
rv = true;
|
||||
}
|
||||
if (ImGui::Button("Close") || ImGui::IsKeyPressedMap(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool NameSetting::PopupEditName(const char* name) {
|
||||
bool rv = false;
|
||||
char id[128];
|
||||
std::snprintf(id, sizeof(id), "Name%s", name);
|
||||
if (ImGui::BeginPopupContextItem(id)) {
|
||||
ImGui::Text("Edit name:");
|
||||
if (InputTextName("##edit")) {
|
||||
rv = true;
|
||||
}
|
||||
if (ImGui::Button("Close") || ImGui::IsKeyPressedMap(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool NameSetting::InputTextName(const char* label_id,
|
||||
ImGuiInputTextFlags flags) {
|
||||
return ImGui::InputText(label_id, &m_name, flags);
|
||||
}
|
||||
@@ -4,17 +4,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace glass {
|
||||
|
||||
struct Context;
|
||||
class Context;
|
||||
class Storage;
|
||||
|
||||
Context* CreateContext();
|
||||
void DestroyContext(Context* ctx = nullptr);
|
||||
@@ -32,86 +31,135 @@ void ResetTime();
|
||||
uint64_t GetZeroTime();
|
||||
|
||||
/**
|
||||
* Storage provides both persistent and non-persistent key/value storage for
|
||||
* widgets.
|
||||
*
|
||||
* Keys are always strings. The storage also provides non-persistent arbitrary
|
||||
* data storage (via std::shared_ptr<void>).
|
||||
*
|
||||
* Storage is automatically indexed internally by the ID stack. Note it is
|
||||
* necessary to use the glass wrappers for PushID et al to preserve naming in
|
||||
* the save file (unnamed values are still stored, but this is non-ideal for
|
||||
* users trying to hand-edit the save file).
|
||||
* Resets the workspace (all storage except window storage).
|
||||
* Operates effectively like calling LoadStorage() on a path with no existing
|
||||
* storage files. Note this will result in auto-saving of the reset state to
|
||||
* storage.
|
||||
*/
|
||||
class Storage {
|
||||
public:
|
||||
struct Value {
|
||||
Value() = default;
|
||||
explicit Value(std::string_view str) : stringVal{str} {}
|
||||
void WorkspaceReset();
|
||||
|
||||
enum Type { kNone, kInt, kInt64, kBool, kFloat, kDouble, kString };
|
||||
Type type = kNone;
|
||||
union {
|
||||
int intVal;
|
||||
int64_t int64Val;
|
||||
bool boolVal;
|
||||
float floatVal;
|
||||
double doubleVal;
|
||||
};
|
||||
std::string stringVal;
|
||||
};
|
||||
/**
|
||||
* Adds function to be called during workspace (storage) initialization/load.
|
||||
* This should set up any initial default state, restore stored
|
||||
* settings/windows, etc. This will be called after the storage is initialized.
|
||||
* This must be called prior to WorkspaceInit() for proper automatic startup
|
||||
* loading.
|
||||
*
|
||||
* @param init initialization function
|
||||
*/
|
||||
void AddWorkspaceInit(std::function<void()> init);
|
||||
|
||||
int GetInt(std::string_view key, int defaultVal = 0) const;
|
||||
int64_t GetInt64(std::string_view key, int64_t defaultVal = 0) const;
|
||||
bool GetBool(std::string_view key, bool defaultVal = false) const;
|
||||
float GetFloat(std::string_view key, float defaultVal = 0.0f) const;
|
||||
double GetDouble(std::string_view key, double defaultVal = 0.0) const;
|
||||
std::string GetString(std::string_view key,
|
||||
std::string_view defaultVal = {}) const;
|
||||
/**
|
||||
* Adds function to be called during workspace (storage) reset. This should
|
||||
* bring back the state to startup state (e.g. remove any storage references,
|
||||
* destroy windows, etc). This will be called prior to the storage being
|
||||
* destroyed.
|
||||
*
|
||||
* @param reset reset function
|
||||
*/
|
||||
void AddWorkspaceReset(std::function<void()> reset);
|
||||
|
||||
void SetInt(std::string_view key, int val);
|
||||
void SetInt64(std::string_view key, int64_t val);
|
||||
void SetBool(std::string_view key, bool val);
|
||||
void SetFloat(std::string_view key, float val);
|
||||
void SetDouble(std::string_view key, double val);
|
||||
void SetString(std::string_view key, std::string_view val);
|
||||
/**
|
||||
* Sets storage load and auto-save name.
|
||||
* Call this prior to calling wpi::gui::Initialize() for automatic startup
|
||||
* loading.
|
||||
*
|
||||
* @param name base name, suffix will be generated
|
||||
*/
|
||||
void SetStorageName(std::string_view name);
|
||||
|
||||
int* GetIntRef(std::string_view key, int defaultVal = 0);
|
||||
int64_t* GetInt64Ref(std::string_view key, int64_t defaultVal = 0);
|
||||
bool* GetBoolRef(std::string_view key, bool defaultVal = false);
|
||||
float* GetFloatRef(std::string_view key, float defaultVal = 0.0f);
|
||||
double* GetDoubleRef(std::string_view key, double defaultVal = 0.0);
|
||||
std::string* GetStringRef(std::string_view key,
|
||||
std::string_view defaultVal = {});
|
||||
/**
|
||||
* Sets storage load and auto-save directory. For more customized behavior, set
|
||||
* Context::storageLoadPath and Context::storageAutoSavePath directly.
|
||||
* Call this prior to calling wpi::gui::Initialize() for automatic startup
|
||||
* loading.
|
||||
*
|
||||
* @param dir path to directory
|
||||
*/
|
||||
void SetStorageDir(std::string_view dir);
|
||||
|
||||
Value& GetValue(std::string_view key);
|
||||
/**
|
||||
* Gets storage auto-save directory.
|
||||
*
|
||||
* @return Path to directory
|
||||
*/
|
||||
std::string GetStorageDir();
|
||||
|
||||
void SetData(std::shared_ptr<void>&& data) { m_data = std::move(data); }
|
||||
/**
|
||||
* Explicitly load storage. Set Context::storageLoadDir prior to calling
|
||||
* wpi::gui::Initialize() for automatic startup loading.
|
||||
*
|
||||
* Non-empty root names are not loaded unless GetStorageRoot() is called during
|
||||
* initialization (or before this function is called).
|
||||
*
|
||||
* @param dir path to directory
|
||||
*/
|
||||
bool LoadStorage(std::string_view dir);
|
||||
|
||||
template <typename T>
|
||||
T* GetData() const {
|
||||
return static_cast<T*>(m_data.get());
|
||||
}
|
||||
/**
|
||||
* Save storage to automatic on-change save location.
|
||||
*/
|
||||
bool SaveStorage();
|
||||
|
||||
Storage() = default;
|
||||
Storage(const Storage&) = delete;
|
||||
Storage& operator=(const Storage&) = delete;
|
||||
/**
|
||||
* Explicitly save storage. Set Context::storageAutoSaveDir prior to calling
|
||||
* wpi::gui::Initialize() for automatic on-change saving.
|
||||
*
|
||||
* @param dir path to directory
|
||||
*/
|
||||
bool SaveStorage(std::string_view dir);
|
||||
|
||||
std::vector<std::string>& GetKeys() { return m_keys; }
|
||||
const std::vector<std::string>& GetKeys() const { return m_keys; }
|
||||
std::vector<std::unique_ptr<Value>>& GetValues() { return m_values; }
|
||||
const std::vector<std::unique_ptr<Value>>& GetValues() const {
|
||||
return m_values;
|
||||
}
|
||||
/**
|
||||
* Gets the storage root for the current ID stack (e.g. the last call to
|
||||
* ResetStorageStack).
|
||||
*
|
||||
* @return Storage object
|
||||
*/
|
||||
Storage& GetCurStorageRoot();
|
||||
|
||||
private:
|
||||
mutable std::vector<std::string> m_keys;
|
||||
mutable std::vector<std::unique_ptr<Value>> m_values;
|
||||
std::shared_ptr<void> m_data;
|
||||
};
|
||||
/**
|
||||
* Gets an arbitrary storage root.
|
||||
*
|
||||
* Non-empty root names are saved but not loaded unless GetStorageRoot()
|
||||
* is called during initialization (or before LoadStorage is called).
|
||||
*
|
||||
* @param rootName root name
|
||||
* @return Storage object
|
||||
*/
|
||||
Storage& GetStorageRoot(std::string_view rootName = {});
|
||||
|
||||
/**
|
||||
* Resets storage stack. Should only be called at top level.
|
||||
*
|
||||
* @param rootName root name
|
||||
*/
|
||||
void ResetStorageStack(std::string_view rootName = {});
|
||||
|
||||
/**
|
||||
* Gets the storage object for the current point in the ID stack.
|
||||
*
|
||||
* @return Storage object
|
||||
*/
|
||||
Storage& GetStorage();
|
||||
Storage& GetStorage(std::string_view id);
|
||||
|
||||
/**
|
||||
* Pushes label/ID onto the storage stack, without pushing the imgui ID stack.
|
||||
*
|
||||
* @param label_id label or label###id
|
||||
*/
|
||||
void PushStorageStack(std::string_view label_id);
|
||||
|
||||
/**
|
||||
* Pushes specific storage onto the storage stack.
|
||||
*
|
||||
* @param storage storage
|
||||
*/
|
||||
void PushStorageStack(Storage& storage);
|
||||
|
||||
/**
|
||||
* Pops storage stack, without popping the imgui ID stack.
|
||||
*/
|
||||
void PopStorageStack();
|
||||
|
||||
bool Begin(const char* name, bool* p_open = nullptr,
|
||||
ImGuiWindowFlags flags = 0);
|
||||
|
||||
@@ -6,42 +6,40 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/support/IniSaverInfo.h"
|
||||
#include "glass/support/IniSaverString.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
class DataSource;
|
||||
|
||||
class DataSourceName {
|
||||
class Context {
|
||||
public:
|
||||
DataSourceName() = default;
|
||||
explicit DataSourceName(DataSource* source) : source{source} {}
|
||||
Context();
|
||||
Context(const Context&) = delete;
|
||||
Context& operator=(const Context&) = delete;
|
||||
~Context();
|
||||
|
||||
bool ReadIni(std::string_view name_, std::string_view value) {
|
||||
return name->ReadIni(name_, value);
|
||||
}
|
||||
void WriteIni(ImGuiTextBuffer* out) { name->WriteIni(out); }
|
||||
|
||||
std::unique_ptr<NameInfo> name{new NameInfo};
|
||||
DataSource* source = nullptr;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
wpi::SmallString<128> curId;
|
||||
wpi::SmallVector<size_t, 32> idStack;
|
||||
wpi::StringMap<std::unique_ptr<Storage>> storage;
|
||||
std::vector<std::function<void()>> workspaceInit;
|
||||
std::vector<std::function<void()>> workspaceReset;
|
||||
std::string storageLoadDir = ".";
|
||||
std::string storageAutoSaveDir = ".";
|
||||
std::string storageName = "imgui";
|
||||
wpi::SmallVector<Storage*, 32> storageStack;
|
||||
wpi::StringMap<std::unique_ptr<Storage>> storageRoots;
|
||||
wpi::StringMap<bool> deviceHidden;
|
||||
IniSaverString<DataSourceName> sources{"Data Sources"};
|
||||
wpi::StringMap<DataSource*> sources;
|
||||
Storage& sourceNameStorage;
|
||||
uint64_t zeroTime = 0;
|
||||
bool isPlatformSaveDir = false;
|
||||
};
|
||||
|
||||
extern Context* gContext;
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
namespace glass {
|
||||
|
||||
class NameInfo;
|
||||
|
||||
/**
|
||||
* A data source for numeric/boolean data.
|
||||
*/
|
||||
@@ -33,15 +31,9 @@ class DataSource {
|
||||
|
||||
const char* GetId() const { return m_id.c_str(); }
|
||||
|
||||
void SetName(std::string_view name);
|
||||
const char* GetName() const;
|
||||
NameInfo& GetNameInfo() { return *m_name; }
|
||||
|
||||
void PushEditNameId(int index);
|
||||
void PushEditNameId(const char* name);
|
||||
bool PopupEditName(int index);
|
||||
bool PopupEditName(const char* name);
|
||||
bool InputTextName(const char* label_id, ImGuiInputTextFlags flags = 0);
|
||||
void SetName(std::string_view name) { m_name = name; }
|
||||
std::string& GetName() { return m_name; }
|
||||
const std::string& GetName() const { return m_name; }
|
||||
|
||||
void SetDigital(bool digital) { m_digital = digital; }
|
||||
bool IsDigital() const { return m_digital; }
|
||||
@@ -53,8 +45,9 @@ class DataSource {
|
||||
double GetValue() const { return m_value; }
|
||||
|
||||
// drag source helpers
|
||||
void LabelText(const char* label, const char* fmt, ...) const;
|
||||
void LabelTextV(const char* label, const char* fmt, va_list args) const;
|
||||
void LabelText(const char* label, const char* fmt, ...) const IM_FMTARGS(3);
|
||||
void LabelTextV(const char* label, const char* fmt, va_list args) const
|
||||
IM_FMTLIST(3);
|
||||
bool Combo(const char* label, int* current_item, const char* const items[],
|
||||
int items_count, int popup_max_height_in_items = -1) const;
|
||||
bool SliderFloat(const char* label, float* v, float v_min, float v_max,
|
||||
@@ -74,7 +67,7 @@ class DataSource {
|
||||
|
||||
private:
|
||||
std::string m_id;
|
||||
NameInfo* m_name;
|
||||
std::string& m_name;
|
||||
bool m_digital = false;
|
||||
std::atomic<double> m_value = 0;
|
||||
};
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <portable-file-dialogs.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace glass {
|
||||
|
||||
class WindowManager;
|
||||
|
||||
/**
|
||||
* GUI main menu bar.
|
||||
*/
|
||||
@@ -21,6 +22,11 @@ class MainMenuBar {
|
||||
*/
|
||||
void Display();
|
||||
|
||||
/**
|
||||
* Displays workspace menu. Called by Display().
|
||||
*/
|
||||
void WorkspaceMenu();
|
||||
|
||||
/**
|
||||
* Adds to GUI's main menu bar. The menu function is called from within a
|
||||
* ImGui::BeginMainMenuBar()/EndMainMenuBar() block. Usually it's only
|
||||
@@ -43,6 +49,8 @@ class MainMenuBar {
|
||||
private:
|
||||
std::vector<std::function<void()>> m_optionMenus;
|
||||
std::vector<std::function<void()>> m_menus;
|
||||
std::unique_ptr<pfd::select_folder> m_openFolder;
|
||||
std::unique_ptr<pfd::select_folder> m_saveFolder;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
namespace glass {
|
||||
|
||||
class Storage;
|
||||
|
||||
namespace detail {
|
||||
struct ProviderFunctions {
|
||||
using Exists = std::function<bool()>;
|
||||
@@ -49,9 +51,9 @@ class Provider : public WindowManager {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param iniName Group name to use in ini file
|
||||
* @param storage Storage
|
||||
*/
|
||||
explicit Provider(std::string_view iniName) : WindowManager{iniName} {}
|
||||
explicit Provider(Storage& storage) : WindowManager{storage} {}
|
||||
|
||||
Provider(const Provider&) = delete;
|
||||
Provider& operator=(const Provider&) = delete;
|
||||
@@ -133,6 +135,7 @@ class Provider : public WindowManager {
|
||||
ModelEntry* modelEntry;
|
||||
ViewExistsFunc exists;
|
||||
CreateViewFunc createView;
|
||||
bool showDefault = false;
|
||||
Window* window = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ void Provider<Functions>::ShowDefault(std::string_view name) {
|
||||
if (it == m_viewEntries.end() || (*it)->name != name) {
|
||||
return;
|
||||
}
|
||||
this->Show(it->get(), (*it)->window);
|
||||
(*it)->showDefault = true;
|
||||
}
|
||||
|
||||
template <typename Functions>
|
||||
|
||||
293
glass/src/lib/native/include/glass/Storage.h
Normal file
293
glass/src/lib/native/include/glass/Storage.h
Normal file
@@ -0,0 +1,293 @@
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/StringMap.h>
|
||||
#include <wpi/iterator_range.h>
|
||||
#include <wpi/span.h>
|
||||
|
||||
namespace wpi {
|
||||
class json;
|
||||
} // namespace wpi
|
||||
|
||||
namespace glass {
|
||||
|
||||
namespace detail {
|
||||
template <typename IteratorType>
|
||||
class ChildIterator;
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Storage provides both persistent and non-persistent nested key/value storage
|
||||
* for widgets.
|
||||
*
|
||||
* Keys are always strings. The storage also provides non-persistent arbitrary
|
||||
* data storage (via std::shared_ptr<void>).
|
||||
*
|
||||
* Storage is automatically indexed internally by the ID stack. Note it is
|
||||
* necessary to use the glass wrappers for PushID et al to preserve naming in
|
||||
* the save file (unnamed values are still stored, but this is non-ideal for
|
||||
* users trying to hand-edit the save file).
|
||||
*/
|
||||
class Storage {
|
||||
public:
|
||||
struct Value {
|
||||
enum Type {
|
||||
kNone,
|
||||
kInt,
|
||||
kInt64,
|
||||
kBool,
|
||||
kFloat,
|
||||
kDouble,
|
||||
kString,
|
||||
kChild,
|
||||
kIntArray,
|
||||
kInt64Array,
|
||||
kBoolArray,
|
||||
kFloatArray,
|
||||
kDoubleArray,
|
||||
kStringArray,
|
||||
kChildArray
|
||||
};
|
||||
|
||||
Value() = default;
|
||||
explicit Value(Type type) : type{type} {}
|
||||
Value(const Value&) = delete;
|
||||
Value& operator=(const Value&) = delete;
|
||||
~Value() { Reset(kNone); }
|
||||
|
||||
Type type = kNone;
|
||||
union {
|
||||
int intVal;
|
||||
int64_t int64Val;
|
||||
bool boolVal;
|
||||
float floatVal;
|
||||
double doubleVal;
|
||||
Storage* child;
|
||||
std::vector<int>* intArray;
|
||||
std::vector<int64_t>* int64Array;
|
||||
std::vector<int>* boolArray;
|
||||
std::vector<float>* floatArray;
|
||||
std::vector<double>* doubleArray;
|
||||
std::vector<std::string>* stringArray;
|
||||
std::vector<std::unique_ptr<Storage>>* childArray;
|
||||
};
|
||||
std::string stringVal;
|
||||
|
||||
union {
|
||||
int intDefault;
|
||||
int64_t int64Default;
|
||||
bool boolDefault;
|
||||
float floatDefault;
|
||||
double doubleDefault;
|
||||
// pointers may be nullptr to indicate empty
|
||||
std::vector<int>* intArrayDefault;
|
||||
std::vector<int64_t>* int64ArrayDefault;
|
||||
std::vector<int>* boolArrayDefault;
|
||||
std::vector<float>* floatArrayDefault;
|
||||
std::vector<double>* doubleArrayDefault;
|
||||
std::vector<std::string>* stringArrayDefault;
|
||||
};
|
||||
std::string stringDefault;
|
||||
|
||||
bool hasDefault = false;
|
||||
|
||||
void Reset(Type newType);
|
||||
};
|
||||
|
||||
using ValueMap = wpi::StringMap<std::unique_ptr<Value>>;
|
||||
template <typename Iterator>
|
||||
using ChildIterator = detail::ChildIterator<Iterator>;
|
||||
|
||||
// The "Read" functions don't create or overwrite the value
|
||||
int ReadInt(std::string_view key, int defaultVal = 0) const;
|
||||
int64_t ReadInt64(std::string_view key, int64_t defaultVal = 0) const;
|
||||
bool ReadBool(std::string_view key, bool defaultVal = false) const;
|
||||
float ReadFloat(std::string_view key, float defaultVal = 0.0f) const;
|
||||
double ReadDouble(std::string_view key, double defaultVal = 0.0) const;
|
||||
std::string ReadString(std::string_view key,
|
||||
std::string_view defaultVal = {}) const;
|
||||
|
||||
void SetInt(std::string_view key, int val);
|
||||
void SetInt64(std::string_view key, int64_t val);
|
||||
void SetBool(std::string_view key, bool val);
|
||||
void SetFloat(std::string_view key, float val);
|
||||
void SetDouble(std::string_view key, double val);
|
||||
void SetString(std::string_view key, std::string_view val);
|
||||
|
||||
// The "Get" functions create or override the current value type.
|
||||
// If the value is not set, it is set to the provided default.
|
||||
int& GetInt(std::string_view key, int defaultVal = 0);
|
||||
int64_t& GetInt64(std::string_view key, int64_t defaultVal = 0);
|
||||
bool& GetBool(std::string_view key, bool defaultVal = false);
|
||||
float& GetFloat(std::string_view key, float defaultVal = 0.0f);
|
||||
double& GetDouble(std::string_view key, double defaultVal = 0.0);
|
||||
std::string& GetString(std::string_view key,
|
||||
std::string_view defaultVal = {});
|
||||
|
||||
std::vector<int>& GetIntArray(std::string_view key,
|
||||
wpi::span<const int> defaultVal = {});
|
||||
std::vector<int64_t>& GetInt64Array(std::string_view key,
|
||||
wpi::span<const int64_t> defaultVal = {});
|
||||
std::vector<int>& GetBoolArray(std::string_view key,
|
||||
wpi::span<const int> defaultVal = {});
|
||||
std::vector<float>& GetFloatArray(std::string_view key,
|
||||
wpi::span<const float> defaultVal = {});
|
||||
std::vector<double>& GetDoubleArray(std::string_view key,
|
||||
wpi::span<const double> defaultVal = {});
|
||||
std::vector<std::string>& GetStringArray(
|
||||
std::string_view key, wpi::span<const std::string> defaultVal = {});
|
||||
std::vector<std::unique_ptr<Storage>>& GetChildArray(std::string_view key);
|
||||
|
||||
Value* FindValue(std::string_view key);
|
||||
Value& GetValue(std::string_view key);
|
||||
Storage& GetChild(std::string_view label_id);
|
||||
|
||||
void SetData(std::shared_ptr<void>&& data) { m_data = std::move(data); }
|
||||
|
||||
template <typename T>
|
||||
T* GetData() const {
|
||||
return static_cast<T*>(m_data.get());
|
||||
}
|
||||
|
||||
Storage() = default;
|
||||
Storage(const Storage&) = delete;
|
||||
Storage& operator=(const Storage&) = delete;
|
||||
|
||||
void Insert(std::string_view key, std::unique_ptr<Value> value) {
|
||||
m_values[key] = std::move(value);
|
||||
}
|
||||
|
||||
std::unique_ptr<Value> Erase(std::string_view key);
|
||||
|
||||
void EraseAll() { m_values.clear(); }
|
||||
|
||||
ValueMap& GetValues() { return m_values; }
|
||||
const ValueMap& GetValues() const { return m_values; }
|
||||
|
||||
wpi::iterator_range<ChildIterator<ValueMap::iterator>> GetChildren();
|
||||
|
||||
/**
|
||||
* Erases all children (at top level).
|
||||
*/
|
||||
void EraseChildren();
|
||||
|
||||
bool FromJson(const wpi::json& json, const char* filename);
|
||||
wpi::json ToJson() const;
|
||||
|
||||
/**
|
||||
* Clear settings (set to default). Calls custom clear function (if set),
|
||||
* otherwise calls ClearValues().
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* Clear values (and values of children) only (set to default). Does not
|
||||
* call custom clear function.
|
||||
*/
|
||||
void ClearValues();
|
||||
|
||||
/**
|
||||
* Apply settings (called after all settings have been loaded). Calls
|
||||
* custom apply function (if set), otherwise calls ApplyChildren().
|
||||
*/
|
||||
void Apply();
|
||||
|
||||
/**
|
||||
* Apply settings to children. Does not call custom apply function.
|
||||
*/
|
||||
void ApplyChildren();
|
||||
|
||||
/**
|
||||
* Sets custom JSON handlers (replaces FromJson and ToJson).
|
||||
*
|
||||
* @param fromJson replacement for FromJson()
|
||||
* @param toJson replacement for ToJson()
|
||||
*/
|
||||
void SetCustomJson(
|
||||
std::function<bool(const wpi::json& json, const char* filename)> fromJson,
|
||||
std::function<wpi::json()> toJson) {
|
||||
m_fromJson = std::move(fromJson);
|
||||
m_toJson = std::move(toJson);
|
||||
}
|
||||
|
||||
void SetCustomClear(std::function<void()> clear) {
|
||||
m_clear = std::move(clear);
|
||||
}
|
||||
|
||||
void SetCustomApply(std::function<void()> apply) {
|
||||
m_apply = std::move(apply);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable ValueMap m_values;
|
||||
std::shared_ptr<void> m_data;
|
||||
std::function<bool(const wpi::json& json, const char* filename)> m_fromJson;
|
||||
std::function<wpi::json()> m_toJson;
|
||||
std::function<void()> m_clear;
|
||||
std::function<void()> m_apply;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// proxy class for the GetChildren() function
|
||||
template <typename IteratorType>
|
||||
class ChildIterator {
|
||||
private:
|
||||
/// the iterator
|
||||
IteratorType anchor;
|
||||
IteratorType end;
|
||||
|
||||
public:
|
||||
ChildIterator(IteratorType it, IteratorType end) noexcept
|
||||
: anchor(it), end(end) {
|
||||
while (anchor != end &&
|
||||
anchor->getValue()->type != Storage::Value::kChild) {
|
||||
++anchor;
|
||||
}
|
||||
}
|
||||
|
||||
/// dereference operator (needed for range-based for)
|
||||
ChildIterator& operator*() { return *this; }
|
||||
|
||||
/// increment operator (needed for range-based for)
|
||||
ChildIterator& operator++() {
|
||||
++anchor;
|
||||
while (anchor != end &&
|
||||
anchor->getValue()->type != Storage::Value::kChild) {
|
||||
++anchor;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// inequality operator (needed for range-based for)
|
||||
bool operator!=(const ChildIterator& o) const noexcept {
|
||||
return anchor != o.anchor;
|
||||
}
|
||||
|
||||
/// return key of the iterator
|
||||
std::string_view key() const { return anchor->getKey(); }
|
||||
|
||||
/// return value of the iterator
|
||||
Storage& value() const { return *anchor->getValue()->child; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline auto Storage::GetChildren()
|
||||
-> wpi::iterator_range<ChildIterator<ValueMap::iterator>> {
|
||||
return {{m_values.begin(), m_values.end()}, {m_values.end(), m_values.end()}};
|
||||
}
|
||||
|
||||
} // namespace glass
|
||||
@@ -15,19 +15,21 @@
|
||||
|
||||
namespace glass {
|
||||
|
||||
class Storage;
|
||||
|
||||
/**
|
||||
* Managed window information.
|
||||
* A Window owns the View that displays the window's contents.
|
||||
*/
|
||||
class Window {
|
||||
public:
|
||||
Window() = default;
|
||||
explicit Window(std::string_view id) : m_id{id}, m_defaultName{id} {}
|
||||
enum Visibility { kHide = 0, kShow, kDisabled };
|
||||
|
||||
Window(Storage& storage, std::string_view id,
|
||||
Visibility defaultVisibility = kShow);
|
||||
|
||||
std::string_view GetId() const { return m_id; }
|
||||
|
||||
enum Visibility { kHide = 0, kShow, kDisabled };
|
||||
|
||||
bool HasView() { return static_cast<bool>(m_view); }
|
||||
|
||||
void SetView(std::unique_ptr<View> view) { m_view = std::move(view); }
|
||||
@@ -59,6 +61,13 @@ class Window {
|
||||
*/
|
||||
void SetVisibility(Visibility visibility);
|
||||
|
||||
/**
|
||||
* Sets default visibility of window.
|
||||
*
|
||||
* @param visibility 0=hide, 1=show, 2=disabled (force-hide)
|
||||
*/
|
||||
void SetDefaultVisibility(Visibility visibility);
|
||||
|
||||
/**
|
||||
* Sets default position of window.
|
||||
*
|
||||
@@ -109,17 +118,16 @@ class Window {
|
||||
*/
|
||||
void ScaleDefault(float scale);
|
||||
|
||||
void IniReadLine(const char* lineStr);
|
||||
void IniWriteAll(const char* typeName, ImGuiTextBuffer* out_buf);
|
||||
|
||||
private:
|
||||
std::string m_id;
|
||||
std::string m_name;
|
||||
std::string& m_name;
|
||||
std::string m_defaultName;
|
||||
std::unique_ptr<View> m_view;
|
||||
ImGuiWindowFlags m_flags = 0;
|
||||
bool m_visible = true;
|
||||
bool m_enabled = true;
|
||||
bool& m_visible;
|
||||
bool& m_enabled;
|
||||
bool& m_defaultVisible;
|
||||
bool& m_defaultEnabled;
|
||||
bool m_renamePopupEnabled = true;
|
||||
ImGuiCond m_posCond = 0;
|
||||
ImGuiCond m_sizeCond = 0;
|
||||
|
||||
@@ -4,20 +4,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wpi/FunctionExtras.h>
|
||||
|
||||
#include "glass/Window.h"
|
||||
#include "glass/support/IniSaverBase.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
class Storage;
|
||||
|
||||
/**
|
||||
* Window manager.
|
||||
*
|
||||
@@ -31,9 +29,9 @@ class WindowManager {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param iniName Group name to use in ini file
|
||||
* @param storage Storage for window information
|
||||
*/
|
||||
explicit WindowManager(std::string_view iniName);
|
||||
explicit WindowManager(Storage& storage);
|
||||
virtual ~WindowManager() = default;
|
||||
|
||||
WindowManager(const WindowManager&) = delete;
|
||||
@@ -65,8 +63,10 @@ class WindowManager {
|
||||
*
|
||||
* @param id unique identifier of the window (title bar)
|
||||
* @param display window contents display function
|
||||
* @param defaultVisibility default window visibility
|
||||
*/
|
||||
Window* AddWindow(std::string_view id, wpi::unique_function<void()> display);
|
||||
Window* AddWindow(std::string_view id, wpi::unique_function<void()> display,
|
||||
Window::Visibility defaultVisibility = Window::kShow);
|
||||
|
||||
/**
|
||||
* Adds window to GUI. The view's display function is called from within a
|
||||
@@ -82,9 +82,11 @@ class WindowManager {
|
||||
*
|
||||
* @param id unique identifier of the window (title bar)
|
||||
* @param view view object
|
||||
* @param defaultVisibility default window visibility
|
||||
* @return Window, or nullptr on duplicate window
|
||||
*/
|
||||
Window* AddWindow(std::string_view id, std::unique_ptr<View> view);
|
||||
Window* AddWindow(std::string_view id, std::unique_ptr<View> view,
|
||||
Window::Visibility defaultVisibility = Window::kShow);
|
||||
|
||||
/**
|
||||
* Adds window to GUI. A View must be assigned to the returned Window
|
||||
@@ -99,9 +101,12 @@ class WindowManager {
|
||||
* every frame in the gui::AddExecute() function.
|
||||
*
|
||||
* @param id unique identifier of the window (default title bar)
|
||||
* @param duplicateOk if false, warn on duplicates
|
||||
* @param defaultVisibility default window visibility
|
||||
* @return Window, or nullptr on duplicate window
|
||||
*/
|
||||
Window* GetOrAddWindow(std::string_view id, bool duplicateOk = false);
|
||||
Window* GetOrAddWindow(std::string_view id, bool duplicateOk = false,
|
||||
Window::Visibility defaultVisibility = Window::kShow);
|
||||
|
||||
/**
|
||||
* Gets existing window. If none exists, returns nullptr.
|
||||
@@ -111,27 +116,26 @@ class WindowManager {
|
||||
*/
|
||||
Window* GetWindow(std::string_view id);
|
||||
|
||||
/**
|
||||
* Erases all windows.
|
||||
*/
|
||||
void EraseWindows() { m_windows.clear(); }
|
||||
|
||||
protected:
|
||||
virtual void DisplayWindows();
|
||||
/**
|
||||
* Removes existing window (by index)
|
||||
*
|
||||
* @param index index of window in m_windows
|
||||
*/
|
||||
void RemoveWindow(size_t index);
|
||||
|
||||
// kept sorted by id
|
||||
std::vector<std::unique_ptr<Window>> m_windows;
|
||||
|
||||
Storage& m_storage;
|
||||
|
||||
private:
|
||||
class IniSaver : public IniSaverBase {
|
||||
public:
|
||||
explicit IniSaver(std::string_view typeName, WindowManager* manager)
|
||||
: IniSaverBase{typeName}, m_manager{manager} {}
|
||||
|
||||
void* IniReadOpen(const char* name) override;
|
||||
void IniReadLine(void* entry, const char* lineStr) override;
|
||||
void IniWriteAll(ImGuiTextBuffer* out_buf) override;
|
||||
|
||||
private:
|
||||
WindowManager* m_manager;
|
||||
};
|
||||
|
||||
IniSaver m_iniSaver;
|
||||
void DisplayWindows();
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
|
||||
@@ -4,19 +4,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "glass/WindowManager.h"
|
||||
#include "glass/support/IniSaverBase.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
class PlotProvider : private WindowManager {
|
||||
public:
|
||||
explicit PlotProvider(std::string_view iniName);
|
||||
explicit PlotProvider(Storage& storage);
|
||||
~PlotProvider() override;
|
||||
|
||||
void GlobalInit() override;
|
||||
using WindowManager::GlobalInit;
|
||||
|
||||
/**
|
||||
* Pauses or unpauses all plots.
|
||||
@@ -33,24 +30,6 @@ class PlotProvider : private WindowManager {
|
||||
void DisplayMenu() override;
|
||||
|
||||
private:
|
||||
void DisplayWindows() override;
|
||||
|
||||
class IniSaver : public IniSaverBase {
|
||||
public:
|
||||
explicit IniSaver(std::string_view typeName, PlotProvider* provider,
|
||||
bool forSeries);
|
||||
|
||||
void* IniReadOpen(const char* name) override;
|
||||
void IniReadLine(void* entry, const char* lineStr) override;
|
||||
void IniWriteAll(ImGuiTextBuffer* out_buf) override;
|
||||
|
||||
private:
|
||||
PlotProvider* m_provider;
|
||||
bool m_forSeries;
|
||||
};
|
||||
|
||||
IniSaver m_plotSaver;
|
||||
IniSaver m_seriesSaver;
|
||||
bool m_paused = false;
|
||||
};
|
||||
|
||||
|
||||
53
glass/src/lib/native/include/glass/support/ColorSetting.h
Normal file
53
glass/src/lib/native/include/glass/support/ColorSetting.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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 <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace glass {
|
||||
|
||||
class ColorSetting {
|
||||
public:
|
||||
explicit ColorSetting(std::vector<float>& color);
|
||||
|
||||
ImVec4 GetColor() const {
|
||||
return {m_color[0], m_color[1], m_color[2], m_color[3]};
|
||||
}
|
||||
|
||||
float* GetColorFloat() { return m_color.data(); }
|
||||
const float* GetColorFloat() const { return m_color.data(); }
|
||||
|
||||
void SetColor(const ImVec4& color) {
|
||||
m_color[0] = color.x;
|
||||
m_color[1] = color.y;
|
||||
m_color[2] = color.z;
|
||||
m_color[3] = color.w;
|
||||
}
|
||||
|
||||
// updates internal value, returns true on change
|
||||
bool ColorEdit3(const char* label, ImGuiColorEditFlags flags = 0) {
|
||||
return ImGui::ColorEdit3(label, m_color.data(), flags);
|
||||
}
|
||||
|
||||
bool ColorEdit4(const char* label, ImGuiColorEditFlags flags = 0) {
|
||||
return ImGui::ColorEdit4(label, m_color.data(), flags);
|
||||
}
|
||||
|
||||
bool ColorPicker3(const char* label, ImGuiColorEditFlags flags = 0) {
|
||||
return ImGui::ColorPicker3(label, m_color.data(), flags);
|
||||
}
|
||||
|
||||
bool ColorPicker4(const char* label, ImGuiColorEditFlags flags = 0,
|
||||
const float* ref_col = nullptr) {
|
||||
return ImGui::ColorPicker4(label, m_color.data(), flags, ref_col);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<float>& m_color;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
31
glass/src/lib/native/include/glass/support/EnumSetting.h
Normal file
31
glass/src/lib/native/include/glass/support/EnumSetting.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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 <string>
|
||||
|
||||
#include <wpi/SmallVector.h>
|
||||
|
||||
namespace glass {
|
||||
|
||||
class EnumSetting {
|
||||
public:
|
||||
EnumSetting(std::string& str, int defaultValue,
|
||||
std::initializer_list<const char*> choices);
|
||||
|
||||
int GetValue() const { return m_value; }
|
||||
void SetValue(int value);
|
||||
|
||||
// updates internal value, returns true on change
|
||||
bool Combo(const char* label, int numOptions = -1,
|
||||
int popup_max_height_in_items = -1);
|
||||
|
||||
private:
|
||||
std::string& m_str;
|
||||
wpi::SmallVector<const char*, 8> m_choices;
|
||||
int m_value;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
@@ -1,44 +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 <string_view>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
|
||||
#include "glass/support/IniSaverBase.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
template <typename Info>
|
||||
class IniSaver : public IniSaverBase {
|
||||
public:
|
||||
explicit IniSaver(std::string_view typeName,
|
||||
IniSaverBackend* backend = nullptr)
|
||||
: IniSaverBase(typeName, backend) {}
|
||||
|
||||
// pass through useful functions to map
|
||||
Info& operator[](int index) { return m_map[index]; }
|
||||
|
||||
auto begin() { return m_map.begin(); }
|
||||
auto end() { return m_map.end(); }
|
||||
auto find(int index) { return m_map.find(index); }
|
||||
|
||||
auto begin() const { return m_map.begin(); }
|
||||
auto end() const { return m_map.end(); }
|
||||
auto find(int index) const { return m_map.find(index); }
|
||||
|
||||
private:
|
||||
void* IniReadOpen(const char* name) override;
|
||||
void IniReadLine(void* entry, const char* lineStr) override;
|
||||
void IniWriteAll(ImGuiTextBuffer* out_buf) override;
|
||||
|
||||
wpi::DenseMap<int, Info> m_map;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
|
||||
#include "IniSaver.inc"
|
||||
@@ -1,40 +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 <string_view>
|
||||
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "glass/support/IniSaver.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
template <typename Info>
|
||||
void* IniSaver<Info>::IniReadOpen(const char* name) {
|
||||
if (auto num = wpi::parse_integer<int>(name, 10)) {
|
||||
return &m_map[num.value()];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaver<Info>::IniReadLine(void* entry, const char* line) {
|
||||
auto element = static_cast<Info*>(entry);
|
||||
auto [name, value] = wpi::split(line, '=');
|
||||
element->ReadIni(wpi::trim(name), wpi::trim(value));
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaver<Info>::IniWriteAll(ImGuiTextBuffer* out_buf) {
|
||||
for (auto&& it : m_map) {
|
||||
out_buf->appendf("[%s][%d]\n", GetTypeName(), it.first);
|
||||
it.second.WriteIni(out_buf);
|
||||
out_buf->append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace glass
|
||||
@@ -1,46 +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 <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace glass {
|
||||
|
||||
class IniSaverBase;
|
||||
|
||||
class IniSaverBackend {
|
||||
public:
|
||||
virtual ~IniSaverBackend() = default;
|
||||
virtual void Register(IniSaverBase* iniSaver) = 0;
|
||||
virtual void Unregister(IniSaverBase* iniSaver) = 0;
|
||||
};
|
||||
|
||||
class IniSaverBase {
|
||||
public:
|
||||
explicit IniSaverBase(std::string_view typeName,
|
||||
IniSaverBackend* backend = nullptr);
|
||||
virtual ~IniSaverBase();
|
||||
|
||||
void Initialize() { m_backend->Register(this); }
|
||||
|
||||
const char* GetTypeName() const { return m_typeName.c_str(); }
|
||||
IniSaverBackend* GetBackend() const { return m_backend; }
|
||||
|
||||
IniSaverBase(const IniSaverBase&) = delete;
|
||||
IniSaverBase& operator=(const IniSaverBase&) = delete;
|
||||
|
||||
virtual void* IniReadOpen(const char* name) = 0;
|
||||
virtual void IniReadLine(void* entry, const char* lineStr) = 0;
|
||||
virtual void IniWriteAll(ImGuiTextBuffer* out_buf) = 0;
|
||||
|
||||
private:
|
||||
std::string m_typeName;
|
||||
IniSaverBackend* m_backend;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
@@ -1,55 +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 <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
#include "glass/support/IniSaverBase.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
template <typename Info>
|
||||
class IniSaverString : public IniSaverBase {
|
||||
public:
|
||||
explicit IniSaverString(std::string_view typeName,
|
||||
IniSaverBackend* backend = nullptr)
|
||||
: IniSaverBase(typeName, backend) {}
|
||||
|
||||
// pass through useful functions to map
|
||||
Info& operator[](std::string_view key) { return m_map[key]; }
|
||||
|
||||
template <typename... ArgsTy>
|
||||
auto try_emplace(std::string_view key, ArgsTy&&... args) {
|
||||
return m_map.try_emplace(key, std::forward<ArgsTy>(args)...);
|
||||
}
|
||||
|
||||
void erase(typename wpi::StringMap<Info>::iterator it) { m_map.erase(it); }
|
||||
auto erase(std::string_view key) { return m_map.erase(key); }
|
||||
|
||||
auto begin() { return m_map.begin(); }
|
||||
auto end() { return m_map.end(); }
|
||||
auto find(std::string_view key) { return m_map.find(key); }
|
||||
|
||||
auto begin() const { return m_map.begin(); }
|
||||
auto end() const { return m_map.end(); }
|
||||
auto find(std::string_view key) const { return m_map.find(key); }
|
||||
|
||||
bool empty() const { return m_map.empty(); }
|
||||
|
||||
private:
|
||||
void* IniReadOpen(const char* name) override;
|
||||
void IniReadLine(void* entry, const char* lineStr) override;
|
||||
void IniWriteAll(ImGuiTextBuffer* out_buf) override;
|
||||
|
||||
wpi::StringMap<Info> m_map;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
|
||||
#include "IniSaverString.inc"
|
||||
@@ -1,36 +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 <string_view>
|
||||
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "glass/support/IniSaverString.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
template <typename Info>
|
||||
void* IniSaverString<Info>::IniReadOpen(const char* name) {
|
||||
return &m_map[name];
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaverString<Info>::IniReadLine(void* entry, const char* line) {
|
||||
auto element = static_cast<Info*>(entry);
|
||||
auto [name, value] = wpi::split(line, '=');
|
||||
element->ReadIni(wpi::trim(name), wpi::trim(value));
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaverString<Info>::IniWriteAll(ImGuiTextBuffer* out_buf) {
|
||||
for (auto&& it : m_map) {
|
||||
out_buf->appendf("[%s][%s]\n", GetTypeName(), it.getKey().data());
|
||||
it.second.WriteIni(out_buf);
|
||||
out_buf->append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace glass
|
||||
@@ -1,31 +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 <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "glass/support/IniSaverBase.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
template <typename Info>
|
||||
class IniSaverVector : public std::vector<Info>, public IniSaverBase {
|
||||
public:
|
||||
explicit IniSaverVector(std::string_view typeName,
|
||||
IniSaverBackend* backend = nullptr)
|
||||
: IniSaverBase(typeName, backend) {}
|
||||
|
||||
private:
|
||||
void* IniReadOpen(const char* name) override;
|
||||
void IniReadLine(void* entry, const char* lineStr) override;
|
||||
void IniWriteAll(ImGuiTextBuffer* out_buf) override;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
|
||||
#include "IniSaverVector.inc"
|
||||
@@ -1,43 +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 <string_view>
|
||||
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "glass/support/IniSaverVector.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
template <typename Info>
|
||||
void* IniSaverVector<Info>::IniReadOpen(const char* name) {
|
||||
if (auto num = wpi::parse_integer<unsigned int>(name, 10)) {
|
||||
if (num.value() >= this->size()) {
|
||||
this->resize(num.value() + 1);
|
||||
}
|
||||
return &(*this)[num.value()];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaverVector<Info>::IniReadLine(void* entry, const char* line) {
|
||||
auto element = static_cast<Info*>(entry);
|
||||
auto [name, value] = wpi::split(line, '=');
|
||||
element->ReadIni(wpi::trim(name), wpi::trim(value));
|
||||
}
|
||||
|
||||
template <typename Info>
|
||||
void IniSaverVector<Info>::IniWriteAll(ImGuiTextBuffer* out_buf) {
|
||||
for (size_t i = 0; i < this->size(); ++i) {
|
||||
out_buf->appendf("[%s][%d]\n", GetTypeName(), static_cast<int>(i));
|
||||
(*this)[i].WriteIni(out_buf);
|
||||
out_buf->append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace glass
|
||||
@@ -4,19 +4,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace glass {
|
||||
|
||||
class NameInfo {
|
||||
class NameSetting {
|
||||
public:
|
||||
NameInfo() { m_name[0] = '\0'; }
|
||||
explicit NameSetting(std::string& str) : m_name{str} {}
|
||||
|
||||
bool HasName() const { return m_name[0] != '\0'; }
|
||||
void SetName(std::string_view name);
|
||||
const char* GetName() const { return m_name; }
|
||||
bool HasName() const { return !m_name.empty(); }
|
||||
void SetName(std::string_view name) { m_name = name; }
|
||||
std::string& GetName() { return m_name; }
|
||||
const std::string& GetName() const { return m_name; }
|
||||
void GetName(char* buf, size_t size, const char* defaultName) const;
|
||||
void GetName(char* buf, size_t size, const char* defaultName,
|
||||
int index) const;
|
||||
@@ -28,8 +30,6 @@ class NameInfo {
|
||||
void GetLabel(char* buf, size_t size, const char* defaultName, int index,
|
||||
int index2) const;
|
||||
|
||||
bool ReadIni(std::string_view name, std::string_view value);
|
||||
void WriteIni(ImGuiTextBuffer* out);
|
||||
void PushEditNameId(int index);
|
||||
void PushEditNameId(const char* name);
|
||||
bool PopupEditName(int index);
|
||||
@@ -37,27 +37,7 @@ class NameInfo {
|
||||
bool InputTextName(const char* label_id, ImGuiInputTextFlags flags = 0);
|
||||
|
||||
private:
|
||||
char m_name[64];
|
||||
};
|
||||
|
||||
class OpenInfo {
|
||||
public:
|
||||
OpenInfo() = default;
|
||||
explicit OpenInfo(bool open) : m_open(open) {}
|
||||
|
||||
bool IsOpen() const { return m_open; }
|
||||
void SetOpen(bool open) { m_open = open; }
|
||||
bool ReadIni(std::string_view name, std::string_view value);
|
||||
void WriteIni(ImGuiTextBuffer* out);
|
||||
|
||||
private:
|
||||
bool m_open = false;
|
||||
};
|
||||
|
||||
class NameOpenInfo : public NameInfo, public OpenInfo {
|
||||
public:
|
||||
bool ReadIni(std::string_view name, std::string_view value);
|
||||
void WriteIni(ImGuiTextBuffer* out);
|
||||
std::string& m_name;
|
||||
};
|
||||
|
||||
} // namespace glass
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -731,15 +732,15 @@ void glass::DisplayNetworkTables(NetworkTablesModel* model,
|
||||
void NetworkTablesFlagsSettings::Update() {
|
||||
if (!m_pTreeView) {
|
||||
auto& storage = GetStorage();
|
||||
m_pTreeView = storage.GetBoolRef(
|
||||
"tree", m_defaultFlags & NetworkTablesFlags_TreeView);
|
||||
m_pShowConnections = storage.GetBoolRef(
|
||||
m_pTreeView =
|
||||
&storage.GetBool("tree", m_defaultFlags & NetworkTablesFlags_TreeView);
|
||||
m_pShowConnections = &storage.GetBool(
|
||||
"connections", m_defaultFlags & NetworkTablesFlags_ShowConnections);
|
||||
m_pShowFlags = storage.GetBoolRef(
|
||||
m_pShowFlags = &storage.GetBool(
|
||||
"flags", m_defaultFlags & NetworkTablesFlags_ShowFlags);
|
||||
m_pShowTimestamp = storage.GetBoolRef(
|
||||
m_pShowTimestamp = &storage.GetBool(
|
||||
"timestamp", m_defaultFlags & NetworkTablesFlags_ShowTimestamp);
|
||||
m_pCreateNoncanonicalKeys = storage.GetBoolRef(
|
||||
m_pCreateNoncanonicalKeys = &storage.GetBool(
|
||||
"createNonCanonical",
|
||||
m_defaultFlags & NetworkTablesFlags_CreateNoncanonicalKeys);
|
||||
}
|
||||
|
||||
@@ -12,23 +12,53 @@
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpigui.h>
|
||||
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
NetworkTablesProvider::NetworkTablesProvider(std::string_view iniName)
|
||||
: NetworkTablesProvider{iniName, nt::GetDefaultInstance()} {}
|
||||
NetworkTablesProvider::NetworkTablesProvider(Storage& storage)
|
||||
: NetworkTablesProvider{storage, nt::GetDefaultInstance()} {}
|
||||
|
||||
NetworkTablesProvider::NetworkTablesProvider(std::string_view iniName,
|
||||
NT_Inst inst)
|
||||
: Provider{fmt::format("{}Window", iniName)},
|
||||
NetworkTablesProvider::NetworkTablesProvider(Storage& storage, NT_Inst inst)
|
||||
: Provider{storage.GetChild("windows")},
|
||||
m_nt{inst},
|
||||
m_typeCache{iniName} {
|
||||
m_nt.AddListener("", NT_NOTIFY_LOCAL | NT_NOTIFY_NEW | NT_NOTIFY_DELETE |
|
||||
NT_NOTIFY_IMMEDIATE);
|
||||
}
|
||||
m_typeCache{storage.GetChild("types")} {
|
||||
storage.SetCustomApply([this] {
|
||||
m_listener =
|
||||
m_nt.AddListener("", NT_NOTIFY_LOCAL | NT_NOTIFY_NEW |
|
||||
NT_NOTIFY_DELETE | NT_NOTIFY_IMMEDIATE);
|
||||
for (auto&& childIt : m_storage.GetChildren()) {
|
||||
auto id = childIt.key();
|
||||
auto typePtr = m_typeCache.FindValue(id);
|
||||
if (!typePtr || typePtr->type != Storage::Value::kString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
void NetworkTablesProvider::GlobalInit() {
|
||||
Provider::GlobalInit();
|
||||
wpi::gui::AddInit([this] { m_typeCache.Initialize(); });
|
||||
// only handle ones where we have a builder
|
||||
auto builderIt = m_typeMap.find(typePtr->stringVal);
|
||||
if (builderIt == m_typeMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto entry = GetOrCreateView(
|
||||
builderIt->second,
|
||||
nt::GetEntry(m_nt.GetInstance(), fmt::format("{}/.type", id)), id);
|
||||
if (entry) {
|
||||
Show(entry, nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
storage.SetCustomClear([this, &storage] {
|
||||
nt::RemoveEntryListener(m_listener);
|
||||
m_listener = 0;
|
||||
for (auto&& modelEntry : m_modelEntries) {
|
||||
modelEntry->model.reset();
|
||||
}
|
||||
m_viewEntries.clear();
|
||||
m_windows.clear();
|
||||
m_typeCache.EraseAll();
|
||||
storage.ClearValues();
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkTablesProvider::DisplayMenu() {
|
||||
@@ -98,33 +128,7 @@ void NetworkTablesProvider::Update() {
|
||||
} else if (event.flags & NT_NOTIFY_NEW) {
|
||||
GetOrCreateView(builderIt->second, event.entry, tableName);
|
||||
// cache the type
|
||||
m_typeCache[tableName].SetName(event.value->GetString());
|
||||
}
|
||||
}
|
||||
|
||||
// check for visible windows that need displays (typically this is due to
|
||||
// file loading)
|
||||
for (auto&& window : m_windows) {
|
||||
if (!window->IsVisible() || window->HasView()) {
|
||||
continue;
|
||||
}
|
||||
auto id = window->GetId();
|
||||
auto typeIt = m_typeCache.find(id);
|
||||
if (typeIt == m_typeCache.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only handle ones where we have a builder
|
||||
auto builderIt = m_typeMap.find(typeIt->second.GetName());
|
||||
if (builderIt == m_typeMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto entry = GetOrCreateView(
|
||||
builderIt->second,
|
||||
nt::GetEntry(m_nt.GetInstance(), fmt::format("{}/.type", id)), id);
|
||||
if (entry) {
|
||||
Show(entry, window.get());
|
||||
m_typeCache.SetString(tableName, event.value->GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,7 +157,7 @@ void NetworkTablesProvider::Show(ViewEntry* entry, Window* window) {
|
||||
|
||||
// the window might exist and we're just not associated to it yet
|
||||
if (!window) {
|
||||
window = GetOrAddWindow(entry->name, true);
|
||||
window = GetOrAddWindow(entry->name, true, Window::kHide);
|
||||
}
|
||||
if (!window) {
|
||||
return;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/Storage.h"
|
||||
|
||||
using namespace glass;
|
||||
|
||||
@@ -81,15 +82,12 @@ void NetworkTablesSettings::Thread::Main() {
|
||||
}
|
||||
}
|
||||
|
||||
NetworkTablesSettings::NetworkTablesSettings(NT_Inst inst,
|
||||
const char* storageName) {
|
||||
auto& storage = glass::GetStorage(storageName);
|
||||
m_pMode = storage.GetIntRef("mode");
|
||||
m_pIniName = storage.GetStringRef("iniName", "networktables.ini");
|
||||
m_pServerTeam = storage.GetStringRef("serverTeam");
|
||||
m_pListenAddress = storage.GetStringRef("listenAddress");
|
||||
m_pDsClient = storage.GetBoolRef("dsClient", true);
|
||||
|
||||
NetworkTablesSettings::NetworkTablesSettings(Storage& storage, NT_Inst inst)
|
||||
: m_mode{storage.GetString("mode"), 0, {"Disabled", "Client", "Server"}},
|
||||
m_iniName{storage.GetString("iniName", "networktables.ini")},
|
||||
m_serverTeam{storage.GetString("serverTeam")},
|
||||
m_listenAddress{storage.GetString("listenAddress")},
|
||||
m_dsClient{storage.GetBool("dsClient", true)} {
|
||||
m_thread.Start(inst);
|
||||
}
|
||||
|
||||
@@ -102,25 +100,24 @@ void NetworkTablesSettings::Update() {
|
||||
// do actual operation on thread
|
||||
auto thr = m_thread.GetThread();
|
||||
thr->m_restart = true;
|
||||
thr->m_mode = *m_pMode;
|
||||
thr->m_iniName = *m_pIniName;
|
||||
thr->m_serverTeam = *m_pServerTeam;
|
||||
thr->m_listenAddress = *m_pListenAddress;
|
||||
thr->m_dsClient = *m_pDsClient;
|
||||
thr->m_mode = m_mode.GetValue();
|
||||
thr->m_iniName = m_iniName;
|
||||
thr->m_serverTeam = m_serverTeam;
|
||||
thr->m_listenAddress = m_listenAddress;
|
||||
thr->m_dsClient = m_dsClient;
|
||||
thr->m_cond.notify_one();
|
||||
}
|
||||
|
||||
bool NetworkTablesSettings::Display() {
|
||||
static const char* modeOptions[] = {"Disabled", "Client", "Server"};
|
||||
ImGui::Combo("Mode", m_pMode, modeOptions, m_serverOption ? 3 : 2);
|
||||
switch (*m_pMode) {
|
||||
m_mode.Combo("Mode", m_serverOption ? 3 : 2);
|
||||
switch (m_mode.GetValue()) {
|
||||
case 1:
|
||||
ImGui::InputText("Team/IP", m_pServerTeam);
|
||||
ImGui::Checkbox("Get Address from DS", m_pDsClient);
|
||||
ImGui::InputText("Team/IP", &m_serverTeam);
|
||||
ImGui::Checkbox("Get Address from DS", &m_dsClient);
|
||||
break;
|
||||
case 2:
|
||||
ImGui::InputText("Listen Address", m_pListenAddress);
|
||||
ImGui::InputText("ini Filename", m_pIniName);
|
||||
ImGui::InputText("Listen Address", &m_listenAddress);
|
||||
ImGui::InputText("ini Filename", &m_iniName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <ntcore_c.h>
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/StringMap.h>
|
||||
|
||||
#include "glass/Model.h"
|
||||
#include "glass/Provider.h"
|
||||
#include "glass/networktables/NetworkTablesHelper.h"
|
||||
#include "glass/support/IniSaverInfo.h"
|
||||
#include "glass/support/IniSaverString.h"
|
||||
|
||||
namespace glass {
|
||||
|
||||
@@ -41,8 +40,8 @@ class NetworkTablesProvider : private Provider<detail::NTProviderFunctions> {
|
||||
using Provider::CreateModelFunc;
|
||||
using Provider::CreateViewFunc;
|
||||
|
||||
explicit NetworkTablesProvider(std::string_view iniName);
|
||||
NetworkTablesProvider(std::string_view iniName, NT_Inst inst);
|
||||
explicit NetworkTablesProvider(Storage& storage);
|
||||
NetworkTablesProvider(Storage& storage, NT_Inst inst);
|
||||
|
||||
/**
|
||||
* Get the NetworkTables instance being used for this provider.
|
||||
@@ -55,7 +54,7 @@ class NetworkTablesProvider : private Provider<detail::NTProviderFunctions> {
|
||||
* Perform global initialization. This should be called prior to
|
||||
* wpi::gui::Initialize().
|
||||
*/
|
||||
void GlobalInit() override;
|
||||
void GlobalInit() override { Provider::GlobalInit(); }
|
||||
|
||||
/**
|
||||
* Displays menu contents as a tree of available NetworkTables views.
|
||||
@@ -72,15 +71,14 @@ class NetworkTablesProvider : private Provider<detail::NTProviderFunctions> {
|
||||
void Register(std::string_view typeName, CreateModelFunc createModel,
|
||||
CreateViewFunc createView);
|
||||
|
||||
using WindowManager::AddWindow;
|
||||
|
||||
private:
|
||||
void Update() override;
|
||||
|
||||
NetworkTablesHelper m_nt;
|
||||
NT_EntryListener m_listener{0};
|
||||
|
||||
// cached mapping from table name to type string
|
||||
IniSaverString<NameInfo> m_typeCache;
|
||||
Storage& m_typeCache;
|
||||
|
||||
struct Builder {
|
||||
CreateModelFunc createModel;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/SafeThread.h>
|
||||
|
||||
#include "glass/support/EnumSetting.h"
|
||||
|
||||
namespace wpi {
|
||||
template <typename T>
|
||||
class SmallVectorImpl;
|
||||
@@ -16,11 +18,12 @@ class SmallVectorImpl;
|
||||
|
||||
namespace glass {
|
||||
|
||||
class Storage;
|
||||
|
||||
class NetworkTablesSettings {
|
||||
public:
|
||||
explicit NetworkTablesSettings(
|
||||
NT_Inst inst = nt::GetDefaultInstance(),
|
||||
const char* storageName = "NetworkTables Settings");
|
||||
explicit NetworkTablesSettings(Storage& storage,
|
||||
NT_Inst inst = nt::GetDefaultInstance());
|
||||
|
||||
/**
|
||||
* Enables or disables the server option. Default is enabled.
|
||||
@@ -33,11 +36,11 @@ class NetworkTablesSettings {
|
||||
private:
|
||||
bool m_restart = true;
|
||||
bool m_serverOption = true;
|
||||
int* m_pMode;
|
||||
std::string* m_pIniName;
|
||||
std::string* m_pServerTeam;
|
||||
std::string* m_pListenAddress;
|
||||
bool* m_pDsClient;
|
||||
EnumSetting m_mode;
|
||||
std::string& m_iniName;
|
||||
std::string& m_serverTeam;
|
||||
std::string& m_listenAddress;
|
||||
bool& m_dsClient;
|
||||
|
||||
class Thread : public wpi::SafeThread {
|
||||
public:
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
org.gradle.jvmargs=-Xmx1g
|
||||
# The --add-exports flags work around a bug with spotless and JDK 17
|
||||
# https://github.com/diffplug/spotless/issues/834
|
||||
org.gradle.jvmargs=-Xmx1g \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
269
gradlew
vendored
269
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,67 +17,101 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MSYS* | MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -106,80 +140,95 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
@@ -28,6 +28,20 @@ public class ControlWord {
|
||||
m_dsAttached = dsAttached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates from an existing word.
|
||||
*
|
||||
* @param word word to update from
|
||||
*/
|
||||
public void update(ControlWord word) {
|
||||
m_enabled = word.m_enabled;
|
||||
m_autonomous = word.m_autonomous;
|
||||
m_test = word.m_test;
|
||||
m_emergencyStop = word.m_emergencyStop;
|
||||
m_fmsAttached = word.m_fmsAttached;
|
||||
m_dsAttached = word.m_dsAttached;
|
||||
}
|
||||
|
||||
public boolean getEnabled() {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@ package edu.wpi.first.hal;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class CounterJNI extends JNIWrapper {
|
||||
public static final int TWO_PULSE = 0;
|
||||
public static final int SEMI_PERIOD = 1;
|
||||
public static final int PULSE_LENGTH = 2;
|
||||
public static final int EXTERNAL_DIRECTION = 3;
|
||||
|
||||
public static native int initializeCounter(int mode, IntBuffer index);
|
||||
|
||||
public static native void freeCounter(int counterHandle);
|
||||
|
||||
@@ -196,8 +196,8 @@ public final class HAL extends JNIWrapper {
|
||||
@SuppressWarnings("MissingJavadocMethod")
|
||||
public static native boolean waitForDSDataTimeout(double timeout);
|
||||
|
||||
public static int kMaxJoystickAxes = 12;
|
||||
public static int kMaxJoystickPOVs = 12;
|
||||
public static final int kMaxJoystickAxes = 12;
|
||||
public static final int kMaxJoystickPOVs = 12;
|
||||
|
||||
public static native short getJoystickAxes(byte joystickNum, float[] axesArray);
|
||||
|
||||
|
||||
@@ -55,4 +55,6 @@ public class JNIWrapper {
|
||||
loader.loadLibrary();
|
||||
libraryLoaded = true;
|
||||
}
|
||||
|
||||
public static void suppressUnused(Object object) {}
|
||||
}
|
||||
|
||||
123
hal/src/main/java/edu/wpi/first/hal/PowerDistributionFaults.java
Normal file
123
hal/src/main/java/edu/wpi/first/hal/PowerDistributionFaults.java
Normal file
@@ -0,0 +1,123 @@
|
||||
// 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.
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
public class PowerDistributionFaults {
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel0BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel1BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel2BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel3BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel4BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel5BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel6BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel7BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel8BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel9BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel10BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel11BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel12BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel13BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel14BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel15BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel16BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel17BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel18BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel19BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel20BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel21BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel22BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel23BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Brownout;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CanWarning;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean HardwareFault;
|
||||
|
||||
/**
|
||||
* Constructs from a bitfield.
|
||||
*
|
||||
* @param faults faults
|
||||
*/
|
||||
public PowerDistributionFaults(int faults) {
|
||||
Channel0BreakerFault = (faults & 0x1) != 0;
|
||||
Channel1BreakerFault = (faults & 0x2) != 0;
|
||||
Channel2BreakerFault = (faults & 0x4) != 0;
|
||||
Channel3BreakerFault = (faults & 0x8) != 0;
|
||||
Channel4BreakerFault = (faults & 0x10) != 0;
|
||||
Channel5BreakerFault = (faults & 0x20) != 0;
|
||||
Channel6BreakerFault = (faults & 0x40) != 0;
|
||||
Channel7BreakerFault = (faults & 0x80) != 0;
|
||||
Channel8BreakerFault = (faults & 0x100) != 0;
|
||||
Channel9BreakerFault = (faults & 0x200) != 0;
|
||||
Channel10BreakerFault = (faults & 0x400) != 0;
|
||||
Channel11BreakerFault = (faults & 0x800) != 0;
|
||||
Channel12BreakerFault = (faults & 0x1000) != 0;
|
||||
Channel13BreakerFault = (faults & 0x2000) != 0;
|
||||
Channel14BreakerFault = (faults & 0x4000) != 0;
|
||||
Channel15BreakerFault = (faults & 0x8000) != 0;
|
||||
Channel16BreakerFault = (faults & 0x10000) != 0;
|
||||
Channel17BreakerFault = (faults & 0x20000) != 0;
|
||||
Channel18BreakerFault = (faults & 0x40000) != 0;
|
||||
Channel19BreakerFault = (faults & 0x80000) != 0;
|
||||
Channel20BreakerFault = (faults & 0x100000) != 0;
|
||||
Channel21BreakerFault = (faults & 0x200000) != 0;
|
||||
Channel22BreakerFault = (faults & 0x400000) != 0;
|
||||
Channel23BreakerFault = (faults & 0x800000) != 0;
|
||||
Brownout = (faults & 0x1000000) != 0;
|
||||
CanWarning = (faults & 0x2000000) != 0;
|
||||
HardwareFault = (faults & 0x4000000) != 0;
|
||||
}
|
||||
}
|
||||
@@ -56,4 +56,18 @@ public class PowerDistributionJNI extends JNIWrapper {
|
||||
public static native boolean getSwitchableChannelNoError(int handle);
|
||||
|
||||
public static native void setSwitchableChannelNoError(int handle, boolean enabled);
|
||||
|
||||
public static native int getFaultsNative(int handle);
|
||||
|
||||
public static PowerDistributionFaults getFaults(int handle) {
|
||||
return new PowerDistributionFaults(getFaultsNative(handle));
|
||||
}
|
||||
|
||||
public static native int getStickyFaultsNative(int handle);
|
||||
|
||||
public static PowerDistributionStickyFaults getStickyFaults(int handle) {
|
||||
return new PowerDistributionStickyFaults(getStickyFaultsNative(handle));
|
||||
}
|
||||
|
||||
public static native PowerDistributionVersion getVersion(int handle);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// 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.
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
public class PowerDistributionStickyFaults {
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel0BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel1BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel2BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel3BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel4BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel5BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel6BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel7BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel8BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel9BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel10BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel11BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel12BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel13BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel14BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel15BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel16BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel17BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel18BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel19BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel20BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel21BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel22BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel23BreakerFault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Brownout;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CanWarning;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CanBusOff;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean HasReset;
|
||||
|
||||
/**
|
||||
* Constructs from a bitfield.
|
||||
*
|
||||
* @param faults faults
|
||||
*/
|
||||
public PowerDistributionStickyFaults(int faults) {
|
||||
Channel0BreakerFault = (faults & 0x1) != 0;
|
||||
Channel1BreakerFault = (faults & 0x2) != 0;
|
||||
Channel2BreakerFault = (faults & 0x4) != 0;
|
||||
Channel3BreakerFault = (faults & 0x8) != 0;
|
||||
Channel4BreakerFault = (faults & 0x10) != 0;
|
||||
Channel5BreakerFault = (faults & 0x20) != 0;
|
||||
Channel6BreakerFault = (faults & 0x40) != 0;
|
||||
Channel7BreakerFault = (faults & 0x80) != 0;
|
||||
Channel8BreakerFault = (faults & 0x100) != 0;
|
||||
Channel9BreakerFault = (faults & 0x200) != 0;
|
||||
Channel10BreakerFault = (faults & 0x400) != 0;
|
||||
Channel11BreakerFault = (faults & 0x800) != 0;
|
||||
Channel12BreakerFault = (faults & 0x1000) != 0;
|
||||
Channel13BreakerFault = (faults & 0x2000) != 0;
|
||||
Channel14BreakerFault = (faults & 0x4000) != 0;
|
||||
Channel15BreakerFault = (faults & 0x8000) != 0;
|
||||
Channel16BreakerFault = (faults & 0x10000) != 0;
|
||||
Channel17BreakerFault = (faults & 0x20000) != 0;
|
||||
Channel18BreakerFault = (faults & 0x40000) != 0;
|
||||
Channel19BreakerFault = (faults & 0x80000) != 0;
|
||||
Channel20BreakerFault = (faults & 0x100000) != 0;
|
||||
Channel21BreakerFault = (faults & 0x200000) != 0;
|
||||
Channel22BreakerFault = (faults & 0x400000) != 0;
|
||||
Channel23BreakerFault = (faults & 0x800000) != 0;
|
||||
Brownout = (faults & 0x1000000) != 0;
|
||||
CanWarning = (faults & 0x2000000) != 0;
|
||||
CanBusOff = (faults & 0x4000000) != 0;
|
||||
HasReset = (faults & 0x8000000) != 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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.
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
public class PowerDistributionVersion {
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int firmwareMajor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int firmwareMinor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int firmwareFix;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int hardwareMinor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int hardwareMajor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int uniqueId;
|
||||
|
||||
/**
|
||||
* Constructs a power distribution version (Called from the HAL).
|
||||
*
|
||||
* @param firmwareMajor firmware major
|
||||
* @param firmwareMinor firmware minor
|
||||
* @param firmwareFix firmware fix
|
||||
* @param hardwareMinor hardware minor
|
||||
* @param hardwareMajor hardware major
|
||||
* @param uniqueId unique id
|
||||
*/
|
||||
public PowerDistributionVersion(
|
||||
int firmwareMajor,
|
||||
int firmwareMinor,
|
||||
int firmwareFix,
|
||||
int hardwareMinor,
|
||||
int hardwareMajor,
|
||||
int uniqueId) {
|
||||
this.firmwareMajor = firmwareMajor;
|
||||
this.firmwareMinor = firmwareMinor;
|
||||
this.firmwareFix = firmwareFix;
|
||||
this.hardwareMinor = hardwareMinor;
|
||||
this.hardwareMajor = hardwareMajor;
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
}
|
||||
104
hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
Normal file
104
hal/src/main/java/edu/wpi/first/hal/REVPHFaults.java
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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.
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class REVPHFaults {
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel0Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel1Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel2Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel3Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel4Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel5Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel6Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel7Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel8Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel9Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel10Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel11Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel12Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel13Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel14Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Channel15Fault;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CompressorOverCurrent;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CompressorOpen;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean SolenoidOverCurrent;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Brownout;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CanWarning;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean HardwareFault;
|
||||
|
||||
/**
|
||||
* Called from HAL to construct.
|
||||
*
|
||||
* @param faults the fault bitfields
|
||||
*/
|
||||
public REVPHFaults(int faults) {
|
||||
Channel0Fault = (faults & 0x1) != 0;
|
||||
Channel1Fault = (faults & 0x2) != 0;
|
||||
Channel2Fault = (faults & 0x4) != 0;
|
||||
Channel3Fault = (faults & 0x8) != 0;
|
||||
Channel4Fault = (faults & 0x10) != 0;
|
||||
Channel5Fault = (faults & 0x20) != 0;
|
||||
Channel6Fault = (faults & 0x40) != 0;
|
||||
Channel7Fault = (faults & 0x80) != 0;
|
||||
Channel8Fault = (faults & 0x100) != 0;
|
||||
Channel9Fault = (faults & 0x200) != 0;
|
||||
Channel10Fault = (faults & 0x400) != 0;
|
||||
Channel11Fault = (faults & 0x800) != 0;
|
||||
Channel12Fault = (faults & 0x1000) != 0;
|
||||
Channel13Fault = (faults & 0x2000) != 0;
|
||||
Channel14Fault = (faults & 0x4000) != 0;
|
||||
Channel15Fault = (faults & 0x8000) != 0;
|
||||
CompressorOverCurrent = (faults & 0x8000) != 0;
|
||||
CompressorOpen = (faults & 0x10000) != 0;
|
||||
SolenoidOverCurrent = (faults & 0x20000) != 0;
|
||||
Brownout = (faults & 0x40000) != 0;
|
||||
CanWarning = (faults & 0x80000) != 0;
|
||||
HardwareFault = (faults & 0x100000) != 0;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,11 @@ package edu.wpi.first.hal;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class REVPHJNI extends JNIWrapper {
|
||||
public static final int COMPRESSOR_CONFIG_TYPE_DISABLED = 0;
|
||||
public static final int COMPRESSOR_CONFIG_TYPE_DIGITAL = 1;
|
||||
public static final int COMPRESSOR_CONFIG_TYPE_ANALOG = 2;
|
||||
public static final int COMPRESSOR_CONFIG_TYPE_HYBRID = 3;
|
||||
|
||||
public static native int initialize(int module);
|
||||
|
||||
public static native void free(int handle);
|
||||
@@ -14,13 +19,28 @@ public class REVPHJNI extends JNIWrapper {
|
||||
|
||||
public static native boolean getCompressor(int handle);
|
||||
|
||||
public static native void setClosedLoopControl(int handle, boolean enabled);
|
||||
public static native void setCompressorConfig(
|
||||
int handle,
|
||||
double minAnalogVoltage,
|
||||
double maxAnalogVoltage,
|
||||
boolean forceDisable,
|
||||
boolean useDigital);
|
||||
|
||||
public static native boolean getClosedLoopControl(int handle);
|
||||
public static native void setClosedLoopControlDisabled(int handle);
|
||||
|
||||
public static native void setClosedLoopControlDigital(int handle);
|
||||
|
||||
public static native void setClosedLoopControlAnalog(
|
||||
int handle, double minAnalogVoltage, double maxAnalogVoltage);
|
||||
|
||||
public static native void setClosedLoopControlHybrid(
|
||||
int handle, double minAnalogVoltage, double maxAnalogVoltage);
|
||||
|
||||
public static native int getCompressorConfig(int handle);
|
||||
|
||||
public static native boolean getPressureSwitch(int handle);
|
||||
|
||||
public static native double getAnalogPressure(int handle, int channel);
|
||||
public static native double getAnalogVoltage(int handle, int channel);
|
||||
|
||||
public static native double getCompressorCurrent(int handle);
|
||||
|
||||
@@ -29,4 +49,28 @@ public class REVPHJNI extends JNIWrapper {
|
||||
public static native void setSolenoids(int handle, int mask, int values);
|
||||
|
||||
public static native void fireOneShot(int handle, int index, int durMs);
|
||||
|
||||
public static native void clearStickyFaults(int handle);
|
||||
|
||||
public static native double getInputVoltage(int handle);
|
||||
|
||||
public static native double get5VVoltage(int handle);
|
||||
|
||||
public static native double getSolenoidCurrent(int handle);
|
||||
|
||||
public static native double getSolenoidVoltage(int handle);
|
||||
|
||||
public static native int getStickyFaultsNative(int handle);
|
||||
|
||||
public static REVPHStickyFaults getStickyFaults(int handle) {
|
||||
return new REVPHStickyFaults(getStickyFaultsNative(handle));
|
||||
}
|
||||
|
||||
public static native int getFaultsNative(int handle);
|
||||
|
||||
public static REVPHFaults getFaults(int handle) {
|
||||
return new REVPHFaults(getFaultsNative(handle));
|
||||
}
|
||||
|
||||
public static native REVPHVersion getVersion(int handle);
|
||||
}
|
||||
|
||||
44
hal/src/main/java/edu/wpi/first/hal/REVPHStickyFaults.java
Normal file
44
hal/src/main/java/edu/wpi/first/hal/REVPHStickyFaults.java
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class REVPHStickyFaults {
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CompressorOverCurrent;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CompressorOpen;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean SolenoidOverCurrent;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean Brownout;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CanWarning;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean CanBusOff;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final boolean HasReset;
|
||||
|
||||
/**
|
||||
* Called from HAL.
|
||||
*
|
||||
* @param faults sticky fault bit mask
|
||||
*/
|
||||
public REVPHStickyFaults(int faults) {
|
||||
CompressorOverCurrent = (faults & 0x1) != 0;
|
||||
CompressorOpen = (faults & 0x2) != 0;
|
||||
SolenoidOverCurrent = (faults & 0x4) != 0;
|
||||
Brownout = (faults & 0x8) != 0;
|
||||
CanWarning = (faults & 0x10) != 0;
|
||||
CanBusOff = (faults & 0x20) != 0;
|
||||
HasReset = (faults & 0x40) != 0;
|
||||
}
|
||||
}
|
||||
51
hal/src/main/java/edu/wpi/first/hal/REVPHVersion.java
Normal file
51
hal/src/main/java/edu/wpi/first/hal/REVPHVersion.java
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class REVPHVersion {
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int firmwareMajor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int firmwareMinor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int firmwareFix;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int hardwareMinor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int hardwareMajor;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int uniqueId;
|
||||
|
||||
/**
|
||||
* Constructs a revph version (Called from the HAL).
|
||||
*
|
||||
* @param firmwareMajor firmware major
|
||||
* @param firmwareMinor firmware minor
|
||||
* @param firmwareFix firmware fix
|
||||
* @param hardwareMinor hardware minor
|
||||
* @param hardwareMajor hardware major
|
||||
* @param uniqueId unique id
|
||||
*/
|
||||
public REVPHVersion(
|
||||
int firmwareMajor,
|
||||
int firmwareMinor,
|
||||
int firmwareFix,
|
||||
int hardwareMinor,
|
||||
int hardwareMajor,
|
||||
int uniqueId) {
|
||||
this.firmwareMajor = firmwareMajor;
|
||||
this.firmwareMinor = firmwareMinor;
|
||||
this.firmwareFix = firmwareFix;
|
||||
this.hardwareMinor = hardwareMinor;
|
||||
this.hardwareMajor = hardwareMajor;
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,17 @@ public final class CANExceptionFactory {
|
||||
static final int ERR_CANSessionMux_NotAllowed = -44088;
|
||||
static final int ERR_CANSessionMux_NotInitialized = -44089;
|
||||
|
||||
@SuppressWarnings("MissingJavadocMethod")
|
||||
public static void checkStatus(int status, int messageID)
|
||||
throws CANInvalidBufferException, CANMessageNotAllowedException, CANNotInitializedException,
|
||||
UncleanStatusException {
|
||||
/**
|
||||
* Checks the status of a CAN message with the given message ID.
|
||||
*
|
||||
* @param status The CAN status.
|
||||
* @param messageID The CAN message ID.
|
||||
* @throws CANInvalidBufferException if the buffer is invalid.
|
||||
* @throws CANMessageNotAllowedException if the message isn't allowed.
|
||||
* @throws CANNotInitializedException if the CAN bus isn't initialized.
|
||||
* @throws UncleanStatusException if the status code passed in reports an error.
|
||||
*/
|
||||
public static void checkStatus(int status, int messageID) {
|
||||
switch (status) {
|
||||
case NIRioStatus.kRioStatusSuccess:
|
||||
// Everything is ok... don't throw.
|
||||
|
||||
@@ -35,14 +35,14 @@ public class REVPHDataJNI extends JNIWrapper {
|
||||
|
||||
public static native void setCompressorOn(int index, boolean compressorOn);
|
||||
|
||||
public static native int registerClosedLoopEnabledCallback(
|
||||
public static native int registerCompressorConfigTypeCallback(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void cancelClosedLoopEnabledCallback(int index, int uid);
|
||||
public static native void cancelCompressorConfigTypeCallback(int index, int uid);
|
||||
|
||||
public static native boolean getClosedLoopEnabled(int index);
|
||||
public static native int getCompressorConfigType(int index);
|
||||
|
||||
public static native void setClosedLoopEnabled(int index, boolean closeLoopEnabled);
|
||||
public static native void setCompressorConfigType(int index, int configType);
|
||||
|
||||
public static native int registerPressureSwitchCallback(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
@@ -54,6 +54,10 @@ void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status) {
|
||||
}
|
||||
|
||||
if (port == HAL_I2C_kOnboard) {
|
||||
HAL_SendError(0, 0, 0,
|
||||
"Onboard I2C port is subject to system lockups. See Known "
|
||||
"Issues page for details",
|
||||
"", "", true);
|
||||
std::scoped_lock lock(digitalI2COnBoardMutex);
|
||||
i2COnboardObjCount++;
|
||||
if (i2COnboardObjCount > 1) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "hal/PowerDistribution.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
#include "CTREPDP.h"
|
||||
@@ -55,7 +56,7 @@ HAL_PowerDistributionHandle HAL_InitializePowerDistribution(
|
||||
HAL_CleanPDP(pdpHandle);
|
||||
}
|
||||
*status = 0;
|
||||
auto pdhHandle = HAL_REV_InitializePDH(1, allocationLocation, status);
|
||||
auto pdhHandle = HAL_InitializeREVPDH(1, allocationLocation, status);
|
||||
return static_cast<HAL_PowerDistributionHandle>(pdhHandle);
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ HAL_PowerDistributionHandle HAL_InitializePowerDistribution(
|
||||
moduleNumber = 1;
|
||||
}
|
||||
return static_cast<HAL_PowerDistributionHandle>(
|
||||
HAL_REV_InitializePDH(moduleNumber, allocationLocation, status));
|
||||
HAL_InitializeREVPDH(moduleNumber, allocationLocation, status));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ void HAL_CleanPowerDistribution(HAL_PowerDistributionHandle handle) {
|
||||
if (IsCtre(handle)) {
|
||||
HAL_CleanPDP(handle);
|
||||
} else {
|
||||
HAL_REV_FreePDH(handle);
|
||||
HAL_FreeREVPDH(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ int32_t HAL_GetPowerDistributionModuleNumber(HAL_PowerDistributionHandle handle,
|
||||
if (IsCtre(handle)) {
|
||||
return HAL_GetPDPModuleNumber(handle, status);
|
||||
} else {
|
||||
return HAL_REV_GetPDHModuleNumber(handle, status);
|
||||
return HAL_GetREVPDHModuleNumber(handle, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +99,7 @@ HAL_Bool HAL_CheckPowerDistributionChannel(HAL_PowerDistributionHandle handle,
|
||||
if (IsCtre(handle)) {
|
||||
return HAL_CheckPDPChannel(channel);
|
||||
} else {
|
||||
return HAL_REV_CheckPDHChannelNumber(channel);
|
||||
return HAL_CheckREVPDHChannelNumber(channel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ HAL_Bool HAL_CheckPowerDistributionModule(int32_t module,
|
||||
if (type == HAL_PowerDistributionType::HAL_PowerDistributionType_kCTRE) {
|
||||
return HAL_CheckPDPModule(module);
|
||||
} else {
|
||||
return HAL_REV_CheckPDHModuleNumber(module);
|
||||
return HAL_CheckREVPDHModuleNumber(module);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +143,7 @@ double HAL_GetPowerDistributionVoltage(HAL_PowerDistributionHandle handle,
|
||||
if (IsCtre(handle)) {
|
||||
return HAL_GetPDPVoltage(handle, status);
|
||||
} else {
|
||||
return HAL_REV_GetPDHSupplyVoltage(handle, status);
|
||||
return HAL_GetREVPDHVoltage(handle, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +152,7 @@ double HAL_GetPowerDistributionChannelCurrent(
|
||||
if (IsCtre(handle)) {
|
||||
return HAL_GetPDPChannelCurrent(handle, channel, status);
|
||||
} else {
|
||||
return HAL_REV_GetPDHChannelCurrent(handle, channel, status);
|
||||
return HAL_GetREVPDHChannelCurrent(handle, channel, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +172,7 @@ void HAL_GetPowerDistributionAllChannelCurrents(
|
||||
SetLastError(status, "Output array not large enough");
|
||||
return;
|
||||
}
|
||||
return HAL_REV_GetPDHAllChannelCurrents(handle, currents, status);
|
||||
return HAL_GetREVPDHAllChannelCurrents(handle, currents, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +181,7 @@ double HAL_GetPowerDistributionTotalCurrent(HAL_PowerDistributionHandle handle,
|
||||
if (IsCtre(handle)) {
|
||||
return HAL_GetPDPTotalCurrent(handle, status);
|
||||
} else {
|
||||
return HAL_REV_GetPDHTotalCurrent(handle, status);
|
||||
return HAL_GetREVPDHTotalCurrent(handle, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +219,7 @@ void HAL_ClearPowerDistributionStickyFaults(HAL_PowerDistributionHandle handle,
|
||||
if (IsCtre(handle)) {
|
||||
HAL_ClearPDPStickyFaults(handle, status);
|
||||
} else {
|
||||
HAL_REV_ClearPDHFaults(handle, status);
|
||||
HAL_ClearREVPDHStickyFaults(handle, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +229,7 @@ void HAL_SetPowerDistributionSwitchableChannel(
|
||||
// No-op on CTRE
|
||||
return;
|
||||
} else {
|
||||
HAL_REV_SetPDHSwitchableChannel(handle, enabled, status);
|
||||
HAL_SetREVPDHSwitchableChannel(handle, enabled, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +239,37 @@ HAL_Bool HAL_GetPowerDistributionSwitchableChannel(
|
||||
// No-op on CTRE
|
||||
return false;
|
||||
} else {
|
||||
return HAL_REV_GetPDHSwitchableChannelState(handle, status);
|
||||
return HAL_GetREVPDHSwitchableChannelState(handle, status);
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_GetPowerDistributionVersion(HAL_PowerDistributionHandle handle,
|
||||
HAL_PowerDistributionVersion* version,
|
||||
int32_t* status) {
|
||||
if (IsCtre(handle)) {
|
||||
std::memset(version, 0, sizeof(*version));
|
||||
} else {
|
||||
HAL_GetREVPDHVersion(handle, version, status);
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_GetPowerDistributionFaults(HAL_PowerDistributionHandle handle,
|
||||
HAL_PowerDistributionFaults* faults,
|
||||
int32_t* status) {
|
||||
if (IsCtre(handle)) {
|
||||
std::memset(faults, 0, sizeof(*faults));
|
||||
} else {
|
||||
HAL_GetREVPDHFaults(handle, faults, status);
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_GetPowerDistributionStickyFaults(
|
||||
HAL_PowerDistributionHandle handle,
|
||||
HAL_PowerDistributionStickyFaults* stickyFaults, int32_t* status) {
|
||||
if (IsCtre(handle)) {
|
||||
std::memset(stickyFaults, 0, sizeof(*stickyFaults));
|
||||
} else {
|
||||
HAL_GetREVPDHStickyFaults(handle, stickyFaults, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <hal/handles/IndexedHandleResource.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -35,6 +36,7 @@ struct REV_PDHObj {
|
||||
int32_t controlPeriod;
|
||||
HAL_CANHandle hcan;
|
||||
std::string previousAllocation;
|
||||
HAL_PowerDistributionVersion versionInfo;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -43,32 +45,26 @@ static constexpr uint32_t APIFromExtId(uint32_t extId) {
|
||||
return (extId >> 6) & 0x3FF;
|
||||
}
|
||||
|
||||
static constexpr uint32_t PDH_SWITCH_CHANNEL_SET_FRAME_API =
|
||||
APIFromExtId(PDH_SWITCH_CHANNEL_SET_FRAME_ID);
|
||||
static constexpr uint32_t PDH_SET_SWITCH_CHANNEL_FRAME_API =
|
||||
APIFromExtId(PDH_SET_SWITCH_CHANNEL_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PDH_STATUS0_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS0_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS1_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS1_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS2_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS2_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS3_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS3_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS4_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS4_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS_0_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS_0_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS_1_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS_1_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS_2_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS_2_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS_3_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS_3_FRAME_ID);
|
||||
static constexpr uint32_t PDH_STATUS_4_FRAME_API =
|
||||
APIFromExtId(PDH_STATUS_4_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PDH_CLEAR_FAULTS_FRAME_API =
|
||||
APIFromExtId(PDH_CLEAR_FAULTS_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PDH_IDENTIFY_FRAME_API =
|
||||
APIFromExtId(PDH_IDENTIFY_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PDH_VERSION_FRAME_API =
|
||||
APIFromExtId(PDH_VERSION_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PDH_CONFIGURE_HR_CHANNEL_FRAME_API =
|
||||
APIFromExtId(PDH_CONFIGURE_HR_CHANNEL_FRAME_ID);
|
||||
|
||||
static constexpr int32_t kPDHFrameStatus0Timeout = 20;
|
||||
static constexpr int32_t kPDHFrameStatus1Timeout = 20;
|
||||
static constexpr int32_t kPDHFrameStatus2Timeout = 20;
|
||||
@@ -89,97 +85,97 @@ void InitializeREVPDH() {
|
||||
|
||||
extern "C" {
|
||||
|
||||
static PDH_status0_t HAL_REV_ReadPDHStatus0(HAL_CANHandle hcan,
|
||||
static PDH_status_0_t HAL_ReadREVPDHStatus0(HAL_CANHandle hcan,
|
||||
int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PDH_status0_t result = {};
|
||||
PDH_status_0_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS0_FRAME_API, packedData, &length,
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS_0_FRAME_API, packedData, &length,
|
||||
×tamp, kPDHFrameStatus0Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PDH_status0_unpack(&result, packedData, PDH_STATUS0_LENGTH);
|
||||
PDH_status_0_unpack(&result, packedData, PDH_STATUS_0_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PDH_status1_t HAL_REV_ReadPDHStatus1(HAL_CANHandle hcan,
|
||||
static PDH_status_1_t HAL_ReadREVPDHStatus1(HAL_CANHandle hcan,
|
||||
int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PDH_status1_t result = {};
|
||||
PDH_status_1_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS1_FRAME_API, packedData, &length,
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS_1_FRAME_API, packedData, &length,
|
||||
×tamp, kPDHFrameStatus1Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PDH_status1_unpack(&result, packedData, PDH_STATUS1_LENGTH);
|
||||
PDH_status_1_unpack(&result, packedData, PDH_STATUS_1_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PDH_status2_t HAL_REV_ReadPDHStatus2(HAL_CANHandle hcan,
|
||||
static PDH_status_2_t HAL_ReadREVPDHStatus2(HAL_CANHandle hcan,
|
||||
int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PDH_status2_t result = {};
|
||||
PDH_status_2_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS2_FRAME_API, packedData, &length,
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS_2_FRAME_API, packedData, &length,
|
||||
×tamp, kPDHFrameStatus2Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PDH_status2_unpack(&result, packedData, PDH_STATUS2_LENGTH);
|
||||
PDH_status_2_unpack(&result, packedData, PDH_STATUS_2_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PDH_status3_t HAL_REV_ReadPDHStatus3(HAL_CANHandle hcan,
|
||||
static PDH_status_3_t HAL_ReadREVPDHStatus3(HAL_CANHandle hcan,
|
||||
int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PDH_status3_t result = {};
|
||||
PDH_status_3_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS3_FRAME_API, packedData, &length,
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS_3_FRAME_API, packedData, &length,
|
||||
×tamp, kPDHFrameStatus3Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PDH_status3_unpack(&result, packedData, PDH_STATUS3_LENGTH);
|
||||
PDH_status_3_unpack(&result, packedData, PDH_STATUS_3_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PDH_status4_t HAL_REV_ReadPDHStatus4(HAL_CANHandle hcan,
|
||||
static PDH_status_4_t HAL_ReadREVPDHStatus4(HAL_CANHandle hcan,
|
||||
int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PDH_status4_t result = {};
|
||||
PDH_status_4_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS4_FRAME_API, packedData, &length,
|
||||
HAL_ReadCANPacketTimeout(hcan, PDH_STATUS_4_FRAME_API, packedData, &length,
|
||||
×tamp, kPDHFrameStatus4Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PDH_status4_unpack(&result, packedData, PDH_STATUS4_LENGTH);
|
||||
PDH_status_4_unpack(&result, packedData, PDH_STATUS_4_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -187,23 +183,23 @@ static PDH_status4_t HAL_REV_ReadPDHStatus4(HAL_CANHandle hcan,
|
||||
/**
|
||||
* Helper function for the individual getter functions for status 4
|
||||
*/
|
||||
PDH_status4_t HAL_REV_GetPDHStatus4(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status4_t statusFrame = {};
|
||||
PDH_status_4_t HAL_GetREVPDHStatus4(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status_4_t statusFrame = {};
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return statusFrame;
|
||||
}
|
||||
|
||||
statusFrame = HAL_REV_ReadPDHStatus4(hpdh->hcan, status);
|
||||
statusFrame = HAL_ReadREVPDHStatus4(hpdh->hcan, status);
|
||||
return statusFrame;
|
||||
}
|
||||
|
||||
HAL_REVPDHHandle HAL_REV_InitializePDH(int32_t module,
|
||||
const char* allocationLocation,
|
||||
int32_t* status) {
|
||||
HAL_REVPDHHandle HAL_InitializeREVPDH(int32_t module,
|
||||
const char* allocationLocation,
|
||||
int32_t* status) {
|
||||
hal::init::CheckInit();
|
||||
if (!HAL_REV_CheckPDHModuleNumber(module)) {
|
||||
if (!HAL_CheckREVPDHModuleNumber(module)) {
|
||||
*status = RESOURCE_OUT_OF_RANGE;
|
||||
return HAL_kInvalidHandle;
|
||||
}
|
||||
@@ -232,11 +228,12 @@ HAL_REVPDHHandle HAL_REV_InitializePDH(int32_t module,
|
||||
hpdh->previousAllocation = allocationLocation ? allocationLocation : "";
|
||||
hpdh->hcan = hcan;
|
||||
hpdh->controlPeriod = kDefaultControlPeriod;
|
||||
std::memset(&hpdh->versionInfo, 0, sizeof(hpdh->versionInfo));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void HAL_REV_FreePDH(HAL_REVPDHHandle handle) {
|
||||
void HAL_FreeREVPDH(HAL_REVPDHHandle handle) {
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
return;
|
||||
@@ -247,27 +244,27 @@ void HAL_REV_FreePDH(HAL_REVPDHHandle handle) {
|
||||
REVPDHHandles->Free(handle);
|
||||
}
|
||||
|
||||
int32_t HAL_REV_GetPDHModuleNumber(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
int32_t HAL_GetREVPDHModuleNumber(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
return hal::getHandleIndex(handle);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHModuleNumber(int32_t module) {
|
||||
HAL_Bool HAL_CheckREVPDHModuleNumber(int32_t module) {
|
||||
return ((module >= 1) && (module < kNumREVPDHModules)) ? 1 : 0;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHChannelNumber(int32_t channel) {
|
||||
HAL_Bool HAL_CheckREVPDHChannelNumber(int32_t channel) {
|
||||
return ((channel >= 0) && (channel < kNumREVPDHChannels)) ? 1 : 0;
|
||||
}
|
||||
|
||||
double HAL_REV_GetPDHChannelCurrent(HAL_REVPDHHandle handle, int32_t channel,
|
||||
int32_t* status) {
|
||||
double HAL_GetREVPDHChannelCurrent(HAL_REVPDHHandle handle, int32_t channel,
|
||||
int32_t* status) {
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!HAL_REV_CheckPDHChannelNumber(channel)) {
|
||||
if (!HAL_CheckREVPDHChannelNumber(channel)) {
|
||||
*status = RESOURCE_OUT_OF_RANGE;
|
||||
return 0;
|
||||
}
|
||||
@@ -275,174 +272,101 @@ double HAL_REV_GetPDHChannelCurrent(HAL_REVPDHHandle handle, int32_t channel,
|
||||
// Determine what periodic status the channel is in
|
||||
if (channel < 6) {
|
||||
// Periodic status 0
|
||||
PDH_status0_t statusFrame = HAL_REV_ReadPDHStatus0(hpdh->hcan, status);
|
||||
PDH_status_0_t statusFrame = HAL_ReadREVPDHStatus0(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 0:
|
||||
return PDH_status0_channel_0_current_decode(
|
||||
return PDH_status_0_channel_0_current_decode(
|
||||
statusFrame.channel_0_current);
|
||||
case 1:
|
||||
return PDH_status0_channel_1_current_decode(
|
||||
return PDH_status_0_channel_1_current_decode(
|
||||
statusFrame.channel_1_current);
|
||||
case 2:
|
||||
return PDH_status0_channel_2_current_decode(
|
||||
return PDH_status_0_channel_2_current_decode(
|
||||
statusFrame.channel_2_current);
|
||||
case 3:
|
||||
return PDH_status0_channel_3_current_decode(
|
||||
return PDH_status_0_channel_3_current_decode(
|
||||
statusFrame.channel_3_current);
|
||||
case 4:
|
||||
return PDH_status0_channel_4_current_decode(
|
||||
return PDH_status_0_channel_4_current_decode(
|
||||
statusFrame.channel_4_current);
|
||||
case 5:
|
||||
return PDH_status0_channel_5_current_decode(
|
||||
return PDH_status_0_channel_5_current_decode(
|
||||
statusFrame.channel_5_current);
|
||||
}
|
||||
} else if (channel < 12) {
|
||||
// Periodic status 1
|
||||
PDH_status1_t statusFrame = HAL_REV_ReadPDHStatus1(hpdh->hcan, status);
|
||||
PDH_status_1_t statusFrame = HAL_ReadREVPDHStatus1(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 6:
|
||||
return PDH_status1_channel_6_current_decode(
|
||||
return PDH_status_1_channel_6_current_decode(
|
||||
statusFrame.channel_6_current);
|
||||
case 7:
|
||||
return PDH_status1_channel_7_current_decode(
|
||||
return PDH_status_1_channel_7_current_decode(
|
||||
statusFrame.channel_7_current);
|
||||
case 8:
|
||||
return PDH_status1_channel_8_current_decode(
|
||||
return PDH_status_1_channel_8_current_decode(
|
||||
statusFrame.channel_8_current);
|
||||
case 9:
|
||||
return PDH_status1_channel_9_current_decode(
|
||||
return PDH_status_1_channel_9_current_decode(
|
||||
statusFrame.channel_9_current);
|
||||
case 10:
|
||||
return PDH_status1_channel_10_current_decode(
|
||||
return PDH_status_1_channel_10_current_decode(
|
||||
statusFrame.channel_10_current);
|
||||
case 11:
|
||||
return PDH_status1_channel_11_current_decode(
|
||||
return PDH_status_1_channel_11_current_decode(
|
||||
statusFrame.channel_11_current);
|
||||
}
|
||||
} else if (channel < 18) {
|
||||
// Periodic status 2
|
||||
PDH_status2_t statusFrame = HAL_REV_ReadPDHStatus2(hpdh->hcan, status);
|
||||
PDH_status_2_t statusFrame = HAL_ReadREVPDHStatus2(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 12:
|
||||
return PDH_status2_channel_12_current_decode(
|
||||
return PDH_status_2_channel_12_current_decode(
|
||||
statusFrame.channel_12_current);
|
||||
case 13:
|
||||
return PDH_status2_channel_13_current_decode(
|
||||
return PDH_status_2_channel_13_current_decode(
|
||||
statusFrame.channel_13_current);
|
||||
case 14:
|
||||
return PDH_status2_channel_14_current_decode(
|
||||
return PDH_status_2_channel_14_current_decode(
|
||||
statusFrame.channel_14_current);
|
||||
case 15:
|
||||
return PDH_status2_channel_15_current_decode(
|
||||
return PDH_status_2_channel_15_current_decode(
|
||||
statusFrame.channel_15_current);
|
||||
case 16:
|
||||
return PDH_status2_channel_16_current_decode(
|
||||
return PDH_status_2_channel_16_current_decode(
|
||||
statusFrame.channel_16_current);
|
||||
case 17:
|
||||
return PDH_status2_channel_17_current_decode(
|
||||
return PDH_status_2_channel_17_current_decode(
|
||||
statusFrame.channel_17_current);
|
||||
}
|
||||
} else if (channel < 24) {
|
||||
// Periodic status 3
|
||||
PDH_status3_t statusFrame = HAL_REV_ReadPDHStatus3(hpdh->hcan, status);
|
||||
PDH_status_3_t statusFrame = HAL_ReadREVPDHStatus3(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 18:
|
||||
return PDH_status3_channel_18_current_decode(
|
||||
return PDH_status_3_channel_18_current_decode(
|
||||
statusFrame.channel_18_current);
|
||||
case 19:
|
||||
return PDH_status3_channel_19_current_decode(
|
||||
return PDH_status_3_channel_19_current_decode(
|
||||
statusFrame.channel_19_current);
|
||||
case 20:
|
||||
return PDH_status3_channel_20_current_decode(
|
||||
return PDH_status_3_channel_20_current_decode(
|
||||
statusFrame.channel_20_current);
|
||||
case 21:
|
||||
return PDH_status3_channel_21_current_decode(
|
||||
return PDH_status_3_channel_21_current_decode(
|
||||
statusFrame.channel_21_current);
|
||||
case 22:
|
||||
return PDH_status3_channel_22_current_decode(
|
||||
return PDH_status_3_channel_22_current_decode(
|
||||
statusFrame.channel_22_current);
|
||||
case 23:
|
||||
return PDH_status3_channel_23_current_decode(
|
||||
return PDH_status_3_channel_23_current_decode(
|
||||
statusFrame.channel_23_current);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HAL_REV_GetPDHAllChannelCurrents(HAL_REVPDHHandle handle, double* currents,
|
||||
int32_t* status) {
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
PDH_status0_t statusFrame0 = HAL_REV_ReadPDHStatus0(hpdh->hcan, status);
|
||||
PDH_status1_t statusFrame1 = HAL_REV_ReadPDHStatus1(hpdh->hcan, status);
|
||||
PDH_status2_t statusFrame2 = HAL_REV_ReadPDHStatus2(hpdh->hcan, status);
|
||||
PDH_status3_t statusFrame3 = HAL_REV_ReadPDHStatus3(hpdh->hcan, status);
|
||||
|
||||
currents[0] =
|
||||
PDH_status0_channel_0_current_decode(statusFrame0.channel_0_current);
|
||||
currents[1] =
|
||||
PDH_status0_channel_1_current_decode(statusFrame0.channel_1_current);
|
||||
currents[2] =
|
||||
PDH_status0_channel_2_current_decode(statusFrame0.channel_2_current);
|
||||
currents[3] =
|
||||
PDH_status0_channel_3_current_decode(statusFrame0.channel_3_current);
|
||||
currents[4] =
|
||||
PDH_status0_channel_4_current_decode(statusFrame0.channel_4_current);
|
||||
currents[5] =
|
||||
PDH_status0_channel_5_current_decode(statusFrame0.channel_5_current);
|
||||
currents[6] =
|
||||
PDH_status1_channel_6_current_decode(statusFrame1.channel_6_current);
|
||||
currents[7] =
|
||||
PDH_status1_channel_7_current_decode(statusFrame1.channel_7_current);
|
||||
currents[8] =
|
||||
PDH_status1_channel_8_current_decode(statusFrame1.channel_8_current);
|
||||
currents[9] =
|
||||
PDH_status1_channel_9_current_decode(statusFrame1.channel_9_current);
|
||||
currents[10] =
|
||||
PDH_status1_channel_10_current_decode(statusFrame1.channel_10_current);
|
||||
currents[11] =
|
||||
PDH_status1_channel_11_current_decode(statusFrame1.channel_11_current);
|
||||
currents[12] =
|
||||
PDH_status2_channel_12_current_decode(statusFrame2.channel_12_current);
|
||||
currents[13] =
|
||||
PDH_status2_channel_13_current_decode(statusFrame2.channel_13_current);
|
||||
currents[14] =
|
||||
PDH_status2_channel_14_current_decode(statusFrame2.channel_14_current);
|
||||
currents[15] =
|
||||
PDH_status2_channel_15_current_decode(statusFrame2.channel_15_current);
|
||||
currents[16] =
|
||||
PDH_status2_channel_16_current_decode(statusFrame2.channel_16_current);
|
||||
currents[17] =
|
||||
PDH_status2_channel_17_current_decode(statusFrame2.channel_17_current);
|
||||
currents[18] =
|
||||
PDH_status3_channel_18_current_decode(statusFrame3.channel_18_current);
|
||||
currents[19] =
|
||||
PDH_status3_channel_19_current_decode(statusFrame3.channel_19_current);
|
||||
currents[20] =
|
||||
PDH_status3_channel_20_current_decode(statusFrame3.channel_20_current);
|
||||
currents[21] =
|
||||
PDH_status3_channel_21_current_decode(statusFrame3.channel_21_current);
|
||||
currents[22] =
|
||||
PDH_status3_channel_22_current_decode(statusFrame3.channel_22_current);
|
||||
currents[23] =
|
||||
PDH_status3_channel_23_current_decode(statusFrame3.channel_23_current);
|
||||
}
|
||||
|
||||
uint16_t HAL_REV_GetPDHTotalCurrent(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PDH_status4_total_current_decode(statusFrame.total_current);
|
||||
}
|
||||
|
||||
void HAL_REV_SetPDHSwitchableChannel(HAL_REVPDHHandle handle, HAL_Bool enabled,
|
||||
void HAL_GetREVPDHAllChannelCurrents(HAL_REVPDHHandle handle, double* currents,
|
||||
int32_t* status) {
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
@@ -450,291 +374,115 @@ void HAL_REV_SetPDHSwitchableChannel(HAL_REVPDHHandle handle, HAL_Bool enabled,
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t packedData[8] = {0};
|
||||
PDH_switch_channel_set_t frame;
|
||||
frame.output_set_value = enabled;
|
||||
frame.use_system_enable = false;
|
||||
PDH_switch_channel_set_pack(packedData, &frame, 1);
|
||||
PDH_status_0_t statusFrame0 = HAL_ReadREVPDHStatus0(hpdh->hcan, status);
|
||||
PDH_status_1_t statusFrame1 = HAL_ReadREVPDHStatus1(hpdh->hcan, status);
|
||||
PDH_status_2_t statusFrame2 = HAL_ReadREVPDHStatus2(hpdh->hcan, status);
|
||||
PDH_status_3_t statusFrame3 = HAL_ReadREVPDHStatus3(hpdh->hcan, status);
|
||||
|
||||
HAL_WriteCANPacket(hpdh->hcan, packedData, PDH_SWITCH_CHANNEL_SET_LENGTH,
|
||||
PDH_SWITCH_CHANNEL_SET_FRAME_API, status);
|
||||
currents[0] =
|
||||
PDH_status_0_channel_0_current_decode(statusFrame0.channel_0_current);
|
||||
currents[1] =
|
||||
PDH_status_0_channel_1_current_decode(statusFrame0.channel_1_current);
|
||||
currents[2] =
|
||||
PDH_status_0_channel_2_current_decode(statusFrame0.channel_2_current);
|
||||
currents[3] =
|
||||
PDH_status_0_channel_3_current_decode(statusFrame0.channel_3_current);
|
||||
currents[4] =
|
||||
PDH_status_0_channel_4_current_decode(statusFrame0.channel_4_current);
|
||||
currents[5] =
|
||||
PDH_status_0_channel_5_current_decode(statusFrame0.channel_5_current);
|
||||
currents[6] =
|
||||
PDH_status_1_channel_6_current_decode(statusFrame1.channel_6_current);
|
||||
currents[7] =
|
||||
PDH_status_1_channel_7_current_decode(statusFrame1.channel_7_current);
|
||||
currents[8] =
|
||||
PDH_status_1_channel_8_current_decode(statusFrame1.channel_8_current);
|
||||
currents[9] =
|
||||
PDH_status_1_channel_9_current_decode(statusFrame1.channel_9_current);
|
||||
currents[10] =
|
||||
PDH_status_1_channel_10_current_decode(statusFrame1.channel_10_current);
|
||||
currents[11] =
|
||||
PDH_status_1_channel_11_current_decode(statusFrame1.channel_11_current);
|
||||
currents[12] =
|
||||
PDH_status_2_channel_12_current_decode(statusFrame2.channel_12_current);
|
||||
currents[13] =
|
||||
PDH_status_2_channel_13_current_decode(statusFrame2.channel_13_current);
|
||||
currents[14] =
|
||||
PDH_status_2_channel_14_current_decode(statusFrame2.channel_14_current);
|
||||
currents[15] =
|
||||
PDH_status_2_channel_15_current_decode(statusFrame2.channel_15_current);
|
||||
currents[16] =
|
||||
PDH_status_2_channel_16_current_decode(statusFrame2.channel_16_current);
|
||||
currents[17] =
|
||||
PDH_status_2_channel_17_current_decode(statusFrame2.channel_17_current);
|
||||
currents[18] =
|
||||
PDH_status_3_channel_18_current_decode(statusFrame3.channel_18_current);
|
||||
currents[19] =
|
||||
PDH_status_3_channel_19_current_decode(statusFrame3.channel_19_current);
|
||||
currents[20] =
|
||||
PDH_status_3_channel_20_current_decode(statusFrame3.channel_20_current);
|
||||
currents[21] =
|
||||
PDH_status_3_channel_21_current_decode(statusFrame3.channel_21_current);
|
||||
currents[22] =
|
||||
PDH_status_3_channel_22_current_decode(statusFrame3.channel_22_current);
|
||||
currents[23] =
|
||||
PDH_status_3_channel_23_current_decode(statusFrame3.channel_23_current);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_GetPDHSwitchableChannelState(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
uint16_t HAL_GetREVPDHTotalCurrent(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status_4_t statusFrame = HAL_GetREVPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PDH_status4_sw_state_decode(statusFrame.sw_state);
|
||||
return PDH_status_4_total_current_decode(statusFrame.total_current);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHChannelBrownout(HAL_REVPDHHandle handle,
|
||||
int32_t channel, int32_t* status) {
|
||||
void HAL_SetREVPDHSwitchableChannel(HAL_REVPDHHandle handle, HAL_Bool enabled,
|
||||
int32_t* status) {
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HAL_REV_CheckPDHChannelNumber(channel)) {
|
||||
*status = RESOURCE_OUT_OF_RANGE;
|
||||
return 0;
|
||||
}
|
||||
uint8_t packedData[8] = {0};
|
||||
PDH_set_switch_channel_t frame;
|
||||
frame.output_set_value = enabled;
|
||||
PDH_set_switch_channel_pack(packedData, &frame,
|
||||
PDH_SET_SWITCH_CHANNEL_LENGTH);
|
||||
|
||||
// Determine what periodic status the channel is in
|
||||
if (channel < 4) {
|
||||
// Periodic status 0
|
||||
PDH_status0_t statusFrame = HAL_REV_ReadPDHStatus0(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 0:
|
||||
return PDH_status0_channel_0_brownout_decode(
|
||||
statusFrame.channel_0_brownout);
|
||||
case 1:
|
||||
return PDH_status0_channel_1_brownout_decode(
|
||||
statusFrame.channel_1_brownout);
|
||||
case 2:
|
||||
return PDH_status0_channel_2_brownout_decode(
|
||||
statusFrame.channel_2_brownout);
|
||||
case 3:
|
||||
return PDH_status0_channel_3_brownout_decode(
|
||||
statusFrame.channel_3_brownout);
|
||||
}
|
||||
} else if (channel < 8) {
|
||||
// Periodic status 1
|
||||
PDH_status1_t statusFrame = HAL_REV_ReadPDHStatus1(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 4:
|
||||
return PDH_status1_channel_4_brownout_decode(
|
||||
statusFrame.channel_4_brownout);
|
||||
case 5:
|
||||
return PDH_status1_channel_5_brownout_decode(
|
||||
statusFrame.channel_5_brownout);
|
||||
case 6:
|
||||
return PDH_status1_channel_6_brownout_decode(
|
||||
statusFrame.channel_6_brownout);
|
||||
case 7:
|
||||
return PDH_status1_channel_7_brownout_decode(
|
||||
statusFrame.channel_7_brownout);
|
||||
}
|
||||
} else if (channel < 12) {
|
||||
// Periodic status 2
|
||||
PDH_status2_t statusFrame = HAL_REV_ReadPDHStatus2(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 8:
|
||||
return PDH_status2_channel_8_brownout_decode(
|
||||
statusFrame.channel_8_brownout);
|
||||
case 9:
|
||||
return PDH_status2_channel_9_brownout_decode(
|
||||
statusFrame.channel_9_brownout);
|
||||
case 10:
|
||||
return PDH_status2_channel_10_brownout_decode(
|
||||
statusFrame.channel_10_brownout);
|
||||
case 11:
|
||||
return PDH_status2_channel_11_brownout_decode(
|
||||
statusFrame.channel_11_brownout);
|
||||
}
|
||||
} else if (channel < 24) {
|
||||
// Periodic status 3
|
||||
PDH_status3_t statusFrame = HAL_REV_ReadPDHStatus3(hpdh->hcan, status);
|
||||
switch (channel) {
|
||||
case 12:
|
||||
return PDH_status3_channel_12_brownout_decode(
|
||||
statusFrame.channel_12_brownout);
|
||||
case 13:
|
||||
return PDH_status3_channel_13_brownout_decode(
|
||||
statusFrame.channel_13_brownout);
|
||||
case 14:
|
||||
return PDH_status3_channel_14_brownout_decode(
|
||||
statusFrame.channel_14_brownout);
|
||||
case 15:
|
||||
return PDH_status3_channel_15_brownout_decode(
|
||||
statusFrame.channel_15_brownout);
|
||||
case 16:
|
||||
return PDH_status3_channel_16_brownout_decode(
|
||||
statusFrame.channel_16_brownout);
|
||||
case 17:
|
||||
return PDH_status3_channel_17_brownout_decode(
|
||||
statusFrame.channel_17_brownout);
|
||||
case 18:
|
||||
return PDH_status3_channel_18_brownout_decode(
|
||||
statusFrame.channel_18_brownout);
|
||||
case 19:
|
||||
return PDH_status3_channel_19_brownout_decode(
|
||||
statusFrame.channel_19_brownout);
|
||||
case 20:
|
||||
return PDH_status3_channel_20_brownout_decode(
|
||||
statusFrame.channel_20_brownout);
|
||||
case 21:
|
||||
return PDH_status3_channel_21_brownout_decode(
|
||||
statusFrame.channel_21_brownout);
|
||||
case 22:
|
||||
return PDH_status3_channel_22_brownout_decode(
|
||||
statusFrame.channel_22_brownout);
|
||||
case 23:
|
||||
return PDH_status3_channel_23_brownout_decode(
|
||||
statusFrame.channel_23_brownout);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
HAL_WriteCANPacket(hpdh->hcan, packedData, PDH_SET_SWITCH_CHANNEL_LENGTH,
|
||||
PDH_SET_SWITCH_CHANNEL_FRAME_API, status);
|
||||
}
|
||||
|
||||
double HAL_REV_GetPDHSupplyVoltage(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_v_bus_decode(statusFrame.v_bus);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_IsPDHEnabled(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return PDH_status4_system_enable_decode(statusFrame.system_enable);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHBrownout(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return PDH_status4_brownout_decode(statusFrame.brownout);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHCANWarning(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_can_warning_decode(statusFrame.can_warning);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHHardwareFault(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_hardware_fault_decode(statusFrame.hardware_fault);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHStickyBrownout(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_sticky_brownout_decode(statusFrame.sticky_brownout);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHStickyCANWarning(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_sticky_can_warning_decode(statusFrame.sticky_can_warning);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHStickyCANBusOff(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_sticky_can_bus_off_decode(statusFrame.sticky_can_bus_off);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHStickyHardwareFault(HAL_REVPDHHandle handle,
|
||||
HAL_Bool HAL_GetREVPDHSwitchableChannelState(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
PDH_status_4_t statusFrame = HAL_GetREVPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_sticky_hardware_fault_decode(
|
||||
statusFrame.sticky_hardware_fault);
|
||||
return PDH_status_4_switch_channel_state_decode(
|
||||
statusFrame.switch_channel_state);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHStickyFirmwareFault(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
double HAL_GetREVPDHVoltage(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_status_4_t statusFrame = HAL_GetREVPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_sticky_firmware_fault_decode(
|
||||
statusFrame.sticky_firmware_fault);
|
||||
return PDH_status_4_v_bus_decode(statusFrame.v_bus);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHStickyChannelBrownout(HAL_REVPDHHandle handle,
|
||||
int32_t channel,
|
||||
int32_t* status) {
|
||||
if (channel < 20 || channel > 23) {
|
||||
*status = RESOURCE_OUT_OF_RANGE;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
switch (channel) {
|
||||
case 20:
|
||||
return PDH_status4_sticky_ch20_brownout_decode(
|
||||
statusFrame.sticky_ch20_brownout);
|
||||
case 21:
|
||||
return PDH_status4_sticky_ch21_brownout_decode(
|
||||
statusFrame.sticky_ch21_brownout);
|
||||
case 22:
|
||||
return PDH_status4_sticky_ch22_brownout_decode(
|
||||
statusFrame.sticky_ch22_brownout);
|
||||
case 23:
|
||||
return PDH_status4_sticky_ch23_brownout_decode(
|
||||
statusFrame.sticky_ch23_brownout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_REV_CheckPDHStickyHasReset(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
PDH_status4_t statusFrame = HAL_REV_GetPDHStatus4(handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return PDH_status4_sticky_has_reset_decode(statusFrame.sticky_has_reset);
|
||||
}
|
||||
|
||||
REV_PDH_Version HAL_REV_GetPDHVersion(HAL_REVPDHHandle handle,
|
||||
int32_t* status) {
|
||||
REV_PDH_Version version;
|
||||
std::memset(&version, 0, sizeof(version));
|
||||
void HAL_GetREVPDHVersion(HAL_REVPDHHandle handle,
|
||||
HAL_PowerDistributionVersion* version,
|
||||
int32_t* status) {
|
||||
std::memset(version, 0, sizeof(*version));
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
@@ -742,36 +490,141 @@ REV_PDH_Version HAL_REV_GetPDHVersion(HAL_REVPDHHandle handle,
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return version;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hpdh->versionInfo.firmwareMajor > 0) {
|
||||
version->firmwareMajor = hpdh->versionInfo.firmwareMajor;
|
||||
version->firmwareMinor = hpdh->versionInfo.firmwareMinor;
|
||||
version->firmwareFix = hpdh->versionInfo.firmwareFix;
|
||||
version->hardwareMajor = hpdh->versionInfo.hardwareMajor;
|
||||
version->hardwareMinor = hpdh->versionInfo.hardwareMinor;
|
||||
version->uniqueId = hpdh->versionInfo.uniqueId;
|
||||
|
||||
*status = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
HAL_WriteCANRTRFrame(hpdh->hcan, PDH_VERSION_LENGTH, PDH_VERSION_FRAME_API,
|
||||
status);
|
||||
|
||||
if (*status != 0) {
|
||||
return version;
|
||||
return;
|
||||
}
|
||||
|
||||
HAL_ReadCANPacketTimeout(hpdh->hcan, PDH_VERSION_FRAME_API, packedData,
|
||||
&length, ×tamp, kDefaultControlPeriod * 2,
|
||||
status);
|
||||
uint32_t timeoutMs = 100;
|
||||
for (uint32_t i = 0; i <= timeoutMs; i++) {
|
||||
HAL_ReadCANPacketNew(hpdh->hcan, PDH_VERSION_FRAME_API, packedData, &length,
|
||||
×tamp, status);
|
||||
if (*status == 0) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
if (*status != 0) {
|
||||
return version;
|
||||
return;
|
||||
}
|
||||
|
||||
PDH_version_unpack(&result, packedData, PDH_VERSION_LENGTH);
|
||||
|
||||
version.firmwareMajor = result.firmware_year;
|
||||
version.firmwareMinor = result.firmware_minor;
|
||||
version.firmwareFix = result.firmware_fix;
|
||||
version.hardwareRev = result.hardware_code;
|
||||
version.uniqueId = result.unique_id;
|
||||
version->firmwareMajor = result.firmware_year;
|
||||
version->firmwareMinor = result.firmware_minor;
|
||||
version->firmwareFix = result.firmware_fix;
|
||||
version->hardwareMinor = result.hardware_minor;
|
||||
version->hardwareMajor = result.hardware_major;
|
||||
version->uniqueId = result.unique_id;
|
||||
|
||||
return version;
|
||||
hpdh->versionInfo = *version;
|
||||
}
|
||||
|
||||
void HAL_REV_ClearPDHFaults(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
void HAL_GetREVPDHFaults(HAL_REVPDHHandle handle,
|
||||
HAL_PowerDistributionFaults* faults, int32_t* status) {
|
||||
std::memset(faults, 0, sizeof(*faults));
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
PDH_status_0_t status0 = HAL_ReadREVPDHStatus0(hpdh->hcan, status);
|
||||
PDH_status_1_t status1 = HAL_ReadREVPDHStatus1(hpdh->hcan, status);
|
||||
PDH_status_2_t status2 = HAL_ReadREVPDHStatus2(hpdh->hcan, status);
|
||||
PDH_status_3_t status3 = HAL_ReadREVPDHStatus3(hpdh->hcan, status);
|
||||
PDH_status_4_t status4 = HAL_ReadREVPDHStatus4(hpdh->hcan, status);
|
||||
|
||||
faults->channel0BreakerFault = status0.channel_0_breaker_fault;
|
||||
faults->channel1BreakerFault = status0.channel_1_breaker_fault;
|
||||
faults->channel2BreakerFault = status0.channel_2_breaker_fault;
|
||||
faults->channel3BreakerFault = status0.channel_3_breaker_fault;
|
||||
faults->channel4BreakerFault = status1.channel_4_breaker_fault;
|
||||
faults->channel5BreakerFault = status1.channel_5_breaker_fault;
|
||||
faults->channel6BreakerFault = status1.channel_6_breaker_fault;
|
||||
faults->channel7BreakerFault = status1.channel_7_breaker_fault;
|
||||
faults->channel8BreakerFault = status2.channel_8_breaker_fault;
|
||||
faults->channel9BreakerFault = status2.channel_9_breaker_fault;
|
||||
faults->channel10BreakerFault = status2.channel_10_breaker_fault;
|
||||
faults->channel11BreakerFault = status2.channel_11_breaker_fault;
|
||||
faults->channel12BreakerFault = status3.channel_12_breaker_fault;
|
||||
faults->channel13BreakerFault = status3.channel_13_breaker_fault;
|
||||
faults->channel14BreakerFault = status3.channel_14_breaker_fault;
|
||||
faults->channel15BreakerFault = status3.channel_15_breaker_fault;
|
||||
faults->channel16BreakerFault = status3.channel_16_breaker_fault;
|
||||
faults->channel17BreakerFault = status3.channel_17_breaker_fault;
|
||||
faults->channel18BreakerFault = status3.channel_18_breaker_fault;
|
||||
faults->channel19BreakerFault = status3.channel_19_breaker_fault;
|
||||
faults->channel20BreakerFault = status3.channel_20_breaker_fault;
|
||||
faults->channel21BreakerFault = status3.channel_21_breaker_fault;
|
||||
faults->channel22BreakerFault = status3.channel_22_breaker_fault;
|
||||
faults->channel23BreakerFault = status3.channel_23_breaker_fault;
|
||||
faults->brownout = status4.brownout_fault;
|
||||
faults->canWarning = status4.can_warning_fault;
|
||||
faults->hardwareFault = status4.hardware_fault;
|
||||
}
|
||||
|
||||
void HAL_GetREVPDHStickyFaults(HAL_REVPDHHandle handle,
|
||||
HAL_PowerDistributionStickyFaults* stickyFaults,
|
||||
int32_t* status) {
|
||||
std::memset(stickyFaults, 0, sizeof(*stickyFaults));
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
PDH_status_4_t status4 = HAL_ReadREVPDHStatus4(hpdh->hcan, status);
|
||||
|
||||
stickyFaults->channel0BreakerFault = status4.sticky_ch0_breaker_fault;
|
||||
stickyFaults->channel1BreakerFault = status4.sticky_ch1_breaker_fault;
|
||||
stickyFaults->channel2BreakerFault = status4.sticky_ch2_breaker_fault;
|
||||
stickyFaults->channel3BreakerFault = status4.sticky_ch3_breaker_fault;
|
||||
stickyFaults->channel4BreakerFault = status4.sticky_ch4_breaker_fault;
|
||||
stickyFaults->channel5BreakerFault = status4.sticky_ch5_breaker_fault;
|
||||
stickyFaults->channel6BreakerFault = status4.sticky_ch6_breaker_fault;
|
||||
stickyFaults->channel7BreakerFault = status4.sticky_ch7_breaker_fault;
|
||||
stickyFaults->channel8BreakerFault = status4.sticky_ch8_breaker_fault;
|
||||
stickyFaults->channel9BreakerFault = status4.sticky_ch9_breaker_fault;
|
||||
stickyFaults->channel10BreakerFault = status4.sticky_ch10_breaker_fault;
|
||||
stickyFaults->channel11BreakerFault = status4.sticky_ch11_breaker_fault;
|
||||
stickyFaults->channel12BreakerFault = status4.sticky_ch12_breaker_fault;
|
||||
stickyFaults->channel13BreakerFault = status4.sticky_ch13_breaker_fault;
|
||||
stickyFaults->channel14BreakerFault = status4.sticky_ch14_breaker_fault;
|
||||
stickyFaults->channel15BreakerFault = status4.sticky_ch15_breaker_fault;
|
||||
stickyFaults->channel16BreakerFault = status4.sticky_ch16_breaker_fault;
|
||||
stickyFaults->channel17BreakerFault = status4.sticky_ch17_breaker_fault;
|
||||
stickyFaults->channel18BreakerFault = status4.sticky_ch18_breaker_fault;
|
||||
stickyFaults->channel19BreakerFault = status4.sticky_ch19_breaker_fault;
|
||||
stickyFaults->channel20BreakerFault = status4.sticky_ch20_breaker_fault;
|
||||
stickyFaults->channel21BreakerFault = status4.sticky_ch21_breaker_fault;
|
||||
stickyFaults->channel22BreakerFault = status4.sticky_ch22_breaker_fault;
|
||||
stickyFaults->channel23BreakerFault = status4.sticky_ch23_breaker_fault;
|
||||
stickyFaults->brownout = status4.sticky_brownout_fault;
|
||||
stickyFaults->canWarning = status4.sticky_can_warning_fault;
|
||||
stickyFaults->canBusOff = status4.sticky_can_bus_off_fault;
|
||||
stickyFaults->hasReset = status4.sticky_has_reset_fault;
|
||||
}
|
||||
|
||||
void HAL_ClearREVPDHStickyFaults(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
@@ -783,16 +636,4 @@ void HAL_REV_ClearPDHFaults(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
PDH_CLEAR_FAULTS_FRAME_API, status);
|
||||
}
|
||||
|
||||
void HAL_REV_IdentifyPDH(HAL_REVPDHHandle handle, int32_t* status) {
|
||||
auto hpdh = REVPDHHandles->Get(handle);
|
||||
if (hpdh == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t packedData[8] = {0};
|
||||
HAL_WriteCANPacket(hpdh->hcan, packedData, PDH_IDENTIFY_LENGTH,
|
||||
PDH_IDENTIFY_FRAME_API, status);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal/PowerDistribution.h"
|
||||
#include "hal/Types.h"
|
||||
|
||||
/**
|
||||
@@ -14,14 +15,6 @@
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct REV_PDH_Version {
|
||||
uint32_t firmwareMajor;
|
||||
uint32_t firmwareMinor;
|
||||
uint32_t firmwareFix;
|
||||
uint32_t hardwareRev;
|
||||
uint32_t uniqueId;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -32,21 +25,21 @@ extern "C" {
|
||||
* @param module the device CAN ID (1 .. 63)
|
||||
* @return the created PDH handle
|
||||
*/
|
||||
HAL_REVPDHHandle HAL_REV_InitializePDH(int32_t module,
|
||||
const char* allocationLocation,
|
||||
int32_t* status);
|
||||
HAL_REVPDHHandle HAL_InitializeREVPDH(int32_t module,
|
||||
const char* allocationLocation,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Frees a PDH device handle.
|
||||
*
|
||||
* @param handle the previously created PDH handle
|
||||
*/
|
||||
void HAL_REV_FreePDH(HAL_REVPDHHandle handle);
|
||||
void HAL_FreeREVPDH(HAL_REVPDHHandle handle);
|
||||
|
||||
/**
|
||||
* Gets the module number for a pdh.
|
||||
*/
|
||||
int32_t HAL_REV_GetPDHModuleNumber(HAL_REVPDHHandle handle, int32_t* status);
|
||||
int32_t HAL_GetREVPDHModuleNumber(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if a PDH module number is valid.
|
||||
@@ -56,34 +49,34 @@ int32_t HAL_REV_GetPDHModuleNumber(HAL_REVPDHHandle handle, int32_t* status);
|
||||
* @param module module number (1 .. 63)
|
||||
* @return 1 if the module number is valid; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHModuleNumber(int32_t module);
|
||||
HAL_Bool HAL_CheckREVPDHModuleNumber(int32_t module);
|
||||
|
||||
/**
|
||||
* Checks if a PDH channel number is valid.
|
||||
*
|
||||
* @param module channel number (0 .. HAL_REV_PDH_NUM_CHANNELS)
|
||||
* @param module channel number (0 .. kNumREVPDHChannels)
|
||||
* @return 1 if the channel number is valid; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHChannelNumber(int32_t channel);
|
||||
HAL_Bool HAL_CheckREVPDHChannelNumber(int32_t channel);
|
||||
|
||||
/**
|
||||
* Gets the current of a PDH channel in Amps.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
* @param channel the channel to retrieve the current of (0 ..
|
||||
* HAL_REV_PDH_NUM_CHANNELS)
|
||||
* kNumREVPDHChannels)
|
||||
*
|
||||
* @return the current of the PDH channel in Amps
|
||||
*/
|
||||
double HAL_REV_GetPDHChannelCurrent(HAL_REVPDHHandle handle, int32_t channel,
|
||||
int32_t* status);
|
||||
double HAL_GetREVPDHChannelCurrent(HAL_REVPDHHandle handle, int32_t channel,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* @param handle PDH handle
|
||||
* @param currents array of currents
|
||||
*/
|
||||
void HAL_REV_GetPDHAllChannelCurrents(HAL_REVPDHHandle handle, double* currents,
|
||||
int32_t* status);
|
||||
void HAL_GetREVPDHAllChannelCurrents(HAL_REVPDHHandle handle, double* currents,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Gets the total current of the PDH in Amps, measured to the nearest even
|
||||
@@ -93,7 +86,7 @@ void HAL_REV_GetPDHAllChannelCurrents(HAL_REVPDHHandle handle, double* currents,
|
||||
*
|
||||
* @return the total current of the PDH in Amps
|
||||
*/
|
||||
uint16_t HAL_REV_GetPDHTotalCurrent(HAL_REVPDHHandle handle, int32_t* status);
|
||||
uint16_t HAL_GetREVPDHTotalCurrent(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Sets the state of the switchable channel on a PDH device.
|
||||
@@ -102,8 +95,8 @@ uint16_t HAL_REV_GetPDHTotalCurrent(HAL_REVPDHHandle handle, int32_t* status);
|
||||
* @param enabled 1 if the switchable channel should be enabled; 0
|
||||
* otherwise
|
||||
*/
|
||||
void HAL_REV_SetPDHSwitchableChannel(HAL_REVPDHHandle handle, HAL_Bool enabled,
|
||||
int32_t* status);
|
||||
void HAL_SetREVPDHSwitchableChannel(HAL_REVPDHHandle handle, HAL_Bool enabled,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Gets the current state of the switchable channel on a PDH device.
|
||||
@@ -114,174 +107,9 @@ void HAL_REV_SetPDHSwitchableChannel(HAL_REVPDHHandle handle, HAL_Bool enabled,
|
||||
* @param handle PDH handle
|
||||
* @return 1 if the switchable channel is enabled; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_GetPDHSwitchableChannelState(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if a PDH channel is currently experiencing a brownout condition.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
* @param channel the channel to retrieve the brownout status of
|
||||
*
|
||||
* @return 1 if the channel is experiencing a brownout; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHChannelBrownout(HAL_REVPDHHandle handle,
|
||||
int32_t channel, int32_t* status);
|
||||
|
||||
/**
|
||||
* Gets the voltage being supplied to a PDH device.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return the voltage at the input of the PDH in Volts
|
||||
*/
|
||||
double HAL_REV_GetPDHSupplyVoltage(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if a PDH device is currently enabled.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the PDH is enabled; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_IsPDHEnabled(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if the input voltage on a PDH device is currently below the minimum
|
||||
* voltage.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the PDH is experiencing a brownout; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHBrownout(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if the CAN RX or TX error levels on a PDH device have exceeded the
|
||||
* warning threshold.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device has exceeded the warning threshold; 0
|
||||
* otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHCANWarning(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if a PDH device is currently malfunctioning.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device is in a hardware fault state; 0
|
||||
* otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHHardwareFault(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if the input voltage on a PDH device has gone below the specified
|
||||
* minimum voltage.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device has had a brownout; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHStickyBrownout(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if the CAN RX or TX error levels on a PDH device have exceeded the
|
||||
* warning threshold.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device has exceeded the CAN warning threshold;
|
||||
* 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHStickyCANWarning(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if the CAN bus on a PDH device has previously experienced a 'Bus Off'
|
||||
* event.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device has experienced a 'Bus Off' event; 0
|
||||
* otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHStickyCANBusOff(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if a PDH device has malfunctioned.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device has had a malfunction; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHStickyHardwareFault(HAL_REVPDHHandle handle,
|
||||
HAL_Bool HAL_GetREVPDHSwitchableChannelState(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if the firmware on a PDH device has malfunctioned and reset during
|
||||
* operation.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device has had a malfunction and reset; 0
|
||||
* otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHStickyFirmwareFault(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if a brownout has happened on channels 20-23 of a PDH device while it
|
||||
* was enabled.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
* @param channel PDH channel to retrieve sticky brownout status (20 ..
|
||||
* 23)
|
||||
*
|
||||
*
|
||||
* @return 1 if the channel has had a brownout; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHStickyChannelBrownout(HAL_REVPDHHandle handle,
|
||||
int32_t channel,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Checks if a PDH device has reset.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return 1 if the device has reset; 0 otherwise
|
||||
*/
|
||||
HAL_Bool HAL_REV_CheckPDHStickyHasReset(HAL_REVPDHHandle handle,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Gets the firmware and hardware versions of a PDH device.
|
||||
*
|
||||
@@ -289,25 +117,46 @@ HAL_Bool HAL_REV_CheckPDHStickyHasReset(HAL_REVPDHHandle handle,
|
||||
*
|
||||
* @return version information
|
||||
*/
|
||||
REV_PDH_Version HAL_REV_GetPDHVersion(HAL_REVPDHHandle handle, int32_t* status);
|
||||
void HAL_GetREVPDHVersion(HAL_REVPDHHandle handle,
|
||||
HAL_PowerDistributionVersion* version,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Gets the voltage being supplied to a PDH device.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return the voltage at the input of the PDH in Volts
|
||||
*/
|
||||
double HAL_GetREVPDHVoltage(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Gets the faults of a PDH device.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return the faults of the PDH
|
||||
*/
|
||||
void HAL_GetREVPDHFaults(HAL_REVPDHHandle handle,
|
||||
HAL_PowerDistributionFaults* faults, int32_t* status);
|
||||
|
||||
/**
|
||||
* Gets the sticky faults of a PDH device.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*
|
||||
* @return the sticky faults of the PDH
|
||||
*/
|
||||
void HAL_GetREVPDHStickyFaults(HAL_REVPDHHandle handle,
|
||||
HAL_PowerDistributionStickyFaults* stickyFaults,
|
||||
int32_t* status);
|
||||
|
||||
/**
|
||||
* Clears the sticky faults on a PDH device.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*/
|
||||
void HAL_REV_ClearPDHFaults(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
/**
|
||||
* Identifies a PDH device by blinking its LED.
|
||||
*
|
||||
* NOTE: Not implemented in firmware as of 2021-04-23.
|
||||
*
|
||||
* @param handle PDH handle
|
||||
*/
|
||||
void HAL_REV_IdentifyPDH(HAL_REVPDHHandle handle, int32_t* status);
|
||||
void HAL_ClearREVPDHStickyFaults(HAL_REVPDHHandle handle, int32_t* status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "hal/REVPH.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "HALInitializer.h"
|
||||
@@ -23,28 +25,34 @@ static constexpr HAL_CANDeviceType deviceType =
|
||||
HAL_CANDeviceType::HAL_CAN_Dev_kPneumatics;
|
||||
|
||||
static constexpr int32_t kDefaultControlPeriod = 20;
|
||||
// static constexpr uint8_t kDefaultSensorMask = (1 <<
|
||||
// HAL_REV_PHSENSOR_DIGITAL);
|
||||
static constexpr uint8_t kDefaultCompressorDuty = 255;
|
||||
static constexpr uint8_t kDefaultPressureTarget = 120;
|
||||
static constexpr uint8_t kDefaultPressureHysteresis = 60;
|
||||
|
||||
#define HAL_REV_MAX_PULSE_TIME 65534
|
||||
#define HAL_REV_MAX_PRESSURE_TARGET 120
|
||||
#define HAL_REV_MAX_PRESSURE_HYSTERESIS HAL_REV_MAX_PRESSURE_TARGET
|
||||
#define HAL_REVPH_MAX_PULSE_TIME 65534
|
||||
|
||||
static constexpr uint32_t APIFromExtId(uint32_t extId) {
|
||||
return (extId >> 6) & 0x3FF;
|
||||
}
|
||||
|
||||
static constexpr uint32_t PH_STATUS_0_FRAME_API =
|
||||
APIFromExtId(PH_STATUS_0_FRAME_ID);
|
||||
static constexpr uint32_t PH_STATUS_1_FRAME_API =
|
||||
APIFromExtId(PH_STATUS_1_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PH_SET_ALL_FRAME_API =
|
||||
APIFromExtId(PH_SET_ALL_FRAME_ID);
|
||||
static constexpr uint32_t PH_PULSE_ONCE_FRAME_API =
|
||||
APIFromExtId(PH_PULSE_ONCE_FRAME_ID);
|
||||
static constexpr uint32_t PH_STATUS0_FRAME_API =
|
||||
APIFromExtId(PH_STATUS0_FRAME_ID);
|
||||
static constexpr uint32_t PH_STATUS1_FRAME_API =
|
||||
APIFromExtId(PH_STATUS1_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PH_COMPRESSOR_CONFIG_API =
|
||||
APIFromExtId(PH_COMPRESSOR_CONFIG_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PH_CLEAR_FAULTS_FRAME_API =
|
||||
APIFromExtId(PH_CLEAR_FAULTS_FRAME_ID);
|
||||
|
||||
static constexpr uint32_t PH_VERSION_FRAME_API =
|
||||
APIFromExtId(PH_VERSION_FRAME_ID);
|
||||
|
||||
static constexpr int32_t kPHFrameStatus0Timeout = 50;
|
||||
static constexpr int32_t kPHFrameStatus1Timeout = 50;
|
||||
@@ -57,6 +65,7 @@ struct REV_PHObj {
|
||||
wpi::mutex solenoidLock;
|
||||
HAL_CANHandle hcan;
|
||||
std::string previousAllocation;
|
||||
HAL_REVPHVersion versionInfo;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -73,38 +82,38 @@ void InitializeREVPH() {
|
||||
}
|
||||
} // namespace hal::init
|
||||
|
||||
static PH_status0_t HAL_REV_ReadPHStatus0(HAL_CANHandle hcan, int32_t* status) {
|
||||
static PH_status_0_t HAL_ReadREVPHStatus0(HAL_CANHandle hcan, int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PH_status0_t result = {};
|
||||
PH_status_0_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PH_STATUS0_FRAME_API, packedData, &length,
|
||||
HAL_ReadCANPacketTimeout(hcan, PH_STATUS_0_FRAME_API, packedData, &length,
|
||||
×tamp, kPHFrameStatus0Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PH_status0_unpack(&result, packedData, PH_STATUS0_LENGTH);
|
||||
PH_status_0_unpack(&result, packedData, PH_STATUS_0_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PH_status1_t HAL_REV_ReadPHStatus1(HAL_CANHandle hcan, int32_t* status) {
|
||||
static PH_status_1_t HAL_ReadREVPHStatus1(HAL_CANHandle hcan, int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PH_status1_t result = {};
|
||||
PH_status_1_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PH_STATUS1_FRAME_API, packedData, &length,
|
||||
HAL_ReadCANPacketTimeout(hcan, PH_STATUS_1_FRAME_API, packedData, &length,
|
||||
×tamp, kPHFrameStatus1Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PH_status1_unpack(&result, packedData, PH_STATUS1_LENGTH);
|
||||
PH_status_1_unpack(&result, packedData, PH_STATUS_1_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -115,9 +124,9 @@ enum REV_SolenoidState {
|
||||
kSolenoidControlledViaPulse
|
||||
};
|
||||
|
||||
static void HAL_REV_UpdateDesiredPHSolenoidState(REV_PHObj* hph,
|
||||
int32_t solenoid,
|
||||
REV_SolenoidState state) {
|
||||
static void HAL_UpdateDesiredREVPHSolenoidState(REV_PHObj* hph,
|
||||
int32_t solenoid,
|
||||
REV_SolenoidState state) {
|
||||
switch (solenoid) {
|
||||
case 0:
|
||||
hph->desiredSolenoidsState.channel_0 = state;
|
||||
@@ -170,15 +179,15 @@ static void HAL_REV_UpdateDesiredPHSolenoidState(REV_PHObj* hph,
|
||||
}
|
||||
}
|
||||
|
||||
static void HAL_REV_SendSolenoidsState(REV_PHObj* hph, int32_t* status) {
|
||||
static void HAL_SendREVPHSolenoidsState(REV_PHObj* hph, int32_t* status) {
|
||||
uint8_t packedData[PH_SET_ALL_LENGTH] = {0};
|
||||
PH_set_all_pack(packedData, &(hph->desiredSolenoidsState), PH_SET_ALL_LENGTH);
|
||||
HAL_WriteCANPacketRepeating(hph->hcan, packedData, PH_SET_ALL_LENGTH,
|
||||
PH_SET_ALL_FRAME_API, hph->controlPeriod, status);
|
||||
}
|
||||
|
||||
static HAL_Bool HAL_REV_CheckPHPulseTime(int32_t time) {
|
||||
return ((time > 0) && (time <= HAL_REV_MAX_PULSE_TIME)) ? 1 : 0;
|
||||
static HAL_Bool HAL_CheckREVPHPulseTime(int32_t time) {
|
||||
return ((time > 0) && (time <= HAL_REVPH_MAX_PULSE_TIME)) ? 1 : 0;
|
||||
}
|
||||
|
||||
HAL_REVPHHandle HAL_InitializeREVPH(int32_t module,
|
||||
@@ -215,9 +224,12 @@ HAL_REVPHHandle HAL_InitializeREVPH(int32_t module,
|
||||
hph->previousAllocation = allocationLocation ? allocationLocation : "";
|
||||
hph->hcan = hcan;
|
||||
hph->controlPeriod = kDefaultControlPeriod;
|
||||
std::memset(&hph->desiredSolenoidsState, 0,
|
||||
sizeof(hph->desiredSolenoidsState));
|
||||
std::memset(&hph->versionInfo, 0, sizeof(hph->versionInfo));
|
||||
|
||||
// Start closed-loop compressor control by starting solenoid state updates
|
||||
HAL_REV_SendSolenoidsState(hph.get(), status);
|
||||
HAL_SendREVPHSolenoidsState(hph.get(), status);
|
||||
|
||||
return handle;
|
||||
}
|
||||
@@ -247,7 +259,7 @@ HAL_Bool HAL_GetREVPHCompressor(HAL_REVPHHandle handle, int32_t* status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
PH_status_0_t status0 = HAL_ReadREVPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return false;
|
||||
@@ -256,14 +268,86 @@ HAL_Bool HAL_GetREVPHCompressor(HAL_REVPHHandle handle, int32_t* status) {
|
||||
return status0.compressor_on;
|
||||
}
|
||||
|
||||
void HAL_SetREVPHClosedLoopControl(HAL_REVPHHandle handle, HAL_Bool enabled,
|
||||
int32_t* status) {
|
||||
// TODO
|
||||
void HAL_SetREVPHCompressorConfig(HAL_REVPHHandle handle,
|
||||
const HAL_REVPHCompressorConfig* config,
|
||||
int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
PH_compressor_config_t frameData;
|
||||
frameData.minimum_tank_pressure =
|
||||
PH_compressor_config_minimum_tank_pressure_encode(
|
||||
config->minAnalogVoltage);
|
||||
frameData.maximum_tank_pressure =
|
||||
PH_compressor_config_maximum_tank_pressure_encode(
|
||||
config->maxAnalogVoltage);
|
||||
frameData.force_disable = config->forceDisable;
|
||||
frameData.use_digital = config->useDigital;
|
||||
|
||||
uint8_t packedData[PH_COMPRESSOR_CONFIG_LENGTH] = {0};
|
||||
PH_compressor_config_pack(packedData, &frameData,
|
||||
PH_COMPRESSOR_CONFIG_LENGTH);
|
||||
HAL_WriteCANPacket(ph->hcan, packedData, PH_COMPRESSOR_CONFIG_LENGTH,
|
||||
PH_COMPRESSOR_CONFIG_API, status);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHClosedLoopControl(HAL_REVPHHandle handle,
|
||||
int32_t* status) {
|
||||
return false; // TODO
|
||||
void HAL_SetREVPHClosedLoopControlDisabled(HAL_REVPHHandle handle,
|
||||
int32_t* status) {
|
||||
HAL_REVPHCompressorConfig config = {0, 0, 0, 0};
|
||||
config.forceDisable = true;
|
||||
|
||||
HAL_SetREVPHCompressorConfig(handle, &config, status);
|
||||
}
|
||||
|
||||
void HAL_SetREVPHClosedLoopControlDigital(HAL_REVPHHandle handle,
|
||||
int32_t* status) {
|
||||
HAL_REVPHCompressorConfig config = {0, 0, 0, 0};
|
||||
config.useDigital = true;
|
||||
|
||||
HAL_SetREVPHCompressorConfig(handle, &config, status);
|
||||
}
|
||||
|
||||
void HAL_SetREVPHClosedLoopControlAnalog(HAL_REVPHHandle handle,
|
||||
double minAnalogVoltage,
|
||||
double maxAnalogVoltage,
|
||||
int32_t* status) {
|
||||
HAL_REVPHCompressorConfig config = {0, 0, 0, 0};
|
||||
config.minAnalogVoltage = minAnalogVoltage;
|
||||
config.maxAnalogVoltage = maxAnalogVoltage;
|
||||
|
||||
HAL_SetREVPHCompressorConfig(handle, &config, status);
|
||||
}
|
||||
|
||||
void HAL_SetREVPHClosedLoopControlHybrid(HAL_REVPHHandle handle,
|
||||
double minAnalogVoltage,
|
||||
double maxAnalogVoltage,
|
||||
int32_t* status) {
|
||||
HAL_REVPHCompressorConfig config = {0, 0, 0, 0};
|
||||
config.minAnalogVoltage = minAnalogVoltage;
|
||||
config.maxAnalogVoltage = maxAnalogVoltage;
|
||||
config.useDigital = true;
|
||||
|
||||
HAL_SetREVPHCompressorConfig(handle, &config, status);
|
||||
}
|
||||
|
||||
HAL_REVPHCompressorConfigType HAL_GetREVPHCompressorConfig(
|
||||
HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return HAL_REVPHCompressorConfigType_kDisabled;
|
||||
}
|
||||
|
||||
PH_status_0_t status0 = HAL_ReadREVPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return HAL_REVPHCompressorConfigType_kDisabled;
|
||||
}
|
||||
|
||||
return static_cast<HAL_REVPHCompressorConfigType>(status0.compressor_config);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHPressureSwitch(HAL_REVPHHandle handle, int32_t* status) {
|
||||
@@ -273,7 +357,7 @@ HAL_Bool HAL_GetREVPHPressureSwitch(HAL_REVPHHandle handle, int32_t* status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
PH_status_0_t status0 = HAL_ReadREVPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return false;
|
||||
@@ -289,17 +373,17 @@ double HAL_GetREVPHCompressorCurrent(HAL_REVPHHandle handle, int32_t* status) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status1_t status1 = HAL_REV_ReadPHStatus1(ph->hcan, status);
|
||||
PH_status_1_t status1 = HAL_ReadREVPHStatus1(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PH_status1_compressor_current_decode(status1.compressor_current);
|
||||
return PH_status_1_compressor_current_decode(status1.compressor_current);
|
||||
}
|
||||
|
||||
double HAL_GetREVPHAnalogPressure(HAL_REVPHHandle handle, int32_t channel,
|
||||
int32_t* status) {
|
||||
double HAL_GetREVPHAnalogVoltage(HAL_REVPHHandle handle, int32_t channel,
|
||||
int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
@@ -313,16 +397,138 @@ double HAL_GetREVPHAnalogPressure(HAL_REVPHHandle handle, int32_t channel,
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
PH_status_0_t status0 = HAL_ReadREVPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (channel == 0) {
|
||||
return PH_status0_analog_0_decode(status0.analog_0);
|
||||
return PH_status_0_analog_0_decode(status0.analog_0);
|
||||
}
|
||||
return PH_status0_analog_1_decode(status0.analog_1);
|
||||
return PH_status_0_analog_1_decode(status0.analog_1);
|
||||
}
|
||||
|
||||
double HAL_GetREVPHVoltage(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status_1_t status1 = HAL_ReadREVPHStatus1(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PH_status_1_v_bus_decode(status1.v_bus);
|
||||
}
|
||||
|
||||
double HAL_GetREVPH5VVoltage(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status_1_t status1 = HAL_ReadREVPHStatus1(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PH_status_1_supply_voltage_5_v_decode(status1.supply_voltage_5_v);
|
||||
}
|
||||
|
||||
double HAL_GetREVPHSolenoidCurrent(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status_1_t status1 = HAL_ReadREVPHStatus1(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PH_status_1_solenoid_current_decode(status1.solenoid_current);
|
||||
}
|
||||
|
||||
double HAL_GetREVPHSolenoidVoltage(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status_1_t status1 = HAL_ReadREVPHStatus1(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PH_status_1_solenoid_voltage_decode(status1.solenoid_voltage);
|
||||
}
|
||||
|
||||
void HAL_GetREVPHVersion(HAL_REVPHHandle handle, HAL_REVPHVersion* version,
|
||||
int32_t* status) {
|
||||
std::memset(version, 0, sizeof(*version));
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PH_version_t result = {};
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ph->versionInfo.firmwareMajor > 0) {
|
||||
version->firmwareMajor = ph->versionInfo.firmwareMajor;
|
||||
version->firmwareMinor = ph->versionInfo.firmwareMinor;
|
||||
version->firmwareFix = ph->versionInfo.firmwareFix;
|
||||
version->hardwareMajor = ph->versionInfo.hardwareMajor;
|
||||
version->hardwareMinor = ph->versionInfo.hardwareMinor;
|
||||
version->uniqueId = ph->versionInfo.uniqueId;
|
||||
|
||||
*status = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
HAL_WriteCANRTRFrame(ph->hcan, PH_VERSION_LENGTH, PH_VERSION_FRAME_API,
|
||||
status);
|
||||
|
||||
if (*status != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t timeoutMs = 100;
|
||||
for (uint32_t i = 0; i <= timeoutMs; i++) {
|
||||
HAL_ReadCANPacketNew(ph->hcan, PH_VERSION_FRAME_API, packedData, &length,
|
||||
×tamp, status);
|
||||
if (*status == 0) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
if (*status != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PH_version_unpack(&result, packedData, PH_VERSION_LENGTH);
|
||||
|
||||
version->firmwareMajor = result.firmware_year;
|
||||
version->firmwareMinor = result.firmware_minor;
|
||||
version->firmwareFix = result.firmware_fix;
|
||||
version->hardwareMinor = result.hardware_minor;
|
||||
version->hardwareMajor = result.hardware_major;
|
||||
version->uniqueId = result.unique_id;
|
||||
|
||||
ph->versionInfo = *version;
|
||||
}
|
||||
|
||||
int32_t HAL_GetREVPHSolenoids(HAL_REVPHHandle handle, int32_t* status) {
|
||||
@@ -332,7 +538,7 @@ int32_t HAL_GetREVPHSolenoids(HAL_REVPHHandle handle, int32_t* status) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
PH_status_0_t status0 = HAL_ReadREVPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
@@ -372,11 +578,11 @@ void HAL_SetREVPHSolenoids(HAL_REVPHHandle handle, int32_t mask, int32_t values,
|
||||
// The mask bit for the solenoid is set, so we update the solenoid state
|
||||
REV_SolenoidState desiredSolenoidState =
|
||||
values & (1 << solenoid) ? kSolenoidEnabled : kSolenoidDisabled;
|
||||
HAL_REV_UpdateDesiredPHSolenoidState(ph.get(), solenoid,
|
||||
desiredSolenoidState);
|
||||
HAL_UpdateDesiredREVPHSolenoidState(ph.get(), solenoid,
|
||||
desiredSolenoidState);
|
||||
}
|
||||
}
|
||||
HAL_REV_SendSolenoidsState(ph.get(), status);
|
||||
HAL_SendREVPHSolenoidsState(ph.get(), status);
|
||||
}
|
||||
|
||||
void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
|
||||
@@ -395,7 +601,7 @@ void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HAL_REV_CheckPHPulseTime(durMs)) {
|
||||
if (!HAL_CheckREVPHPulseTime(durMs)) {
|
||||
*status = PARAMETER_OUT_OF_RANGE;
|
||||
hal::SetLastError(
|
||||
status,
|
||||
@@ -406,9 +612,9 @@ void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
|
||||
|
||||
{
|
||||
std::scoped_lock lock{ph->solenoidLock};
|
||||
HAL_REV_UpdateDesiredPHSolenoidState(ph.get(), index,
|
||||
kSolenoidControlledViaPulse);
|
||||
HAL_REV_SendSolenoidsState(ph.get(), status);
|
||||
HAL_UpdateDesiredREVPHSolenoidState(ph.get(), index,
|
||||
kSolenoidControlledViaPulse);
|
||||
HAL_SendREVPHSolenoidsState(ph.get(), status);
|
||||
}
|
||||
|
||||
if (*status != 0) {
|
||||
@@ -478,3 +684,69 @@ void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
|
||||
HAL_WriteCANPacket(ph->hcan, packedData, PH_PULSE_ONCE_LENGTH,
|
||||
PH_PULSE_ONCE_FRAME_API, status);
|
||||
}
|
||||
|
||||
void HAL_GetREVPHFaults(HAL_REVPHHandle handle, HAL_REVPHFaults* faults,
|
||||
int32_t* status) {
|
||||
std::memset(faults, 0, sizeof(*faults));
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
PH_status_0_t status0 = HAL_ReadREVPHStatus0(ph->hcan, status);
|
||||
faults->channel0Fault = status0.channel_0_fault;
|
||||
faults->channel1Fault = status0.channel_1_fault;
|
||||
faults->channel2Fault = status0.channel_2_fault;
|
||||
faults->channel3Fault = status0.channel_3_fault;
|
||||
faults->channel4Fault = status0.channel_4_fault;
|
||||
faults->channel5Fault = status0.channel_5_fault;
|
||||
faults->channel6Fault = status0.channel_6_fault;
|
||||
faults->channel7Fault = status0.channel_7_fault;
|
||||
faults->channel8Fault = status0.channel_8_fault;
|
||||
faults->channel9Fault = status0.channel_9_fault;
|
||||
faults->channel10Fault = status0.channel_10_fault;
|
||||
faults->channel11Fault = status0.channel_11_fault;
|
||||
faults->channel12Fault = status0.channel_12_fault;
|
||||
faults->channel13Fault = status0.channel_13_fault;
|
||||
faults->channel14Fault = status0.channel_14_fault;
|
||||
faults->channel15Fault = status0.channel_15_fault;
|
||||
faults->compressorOverCurrent = status0.compressor_oc_fault;
|
||||
faults->compressorOpen = status0.compressor_open_fault;
|
||||
faults->solenoidOverCurrent = status0.solenoid_oc_fault;
|
||||
faults->brownout = status0.brownout_fault;
|
||||
faults->canWarning = status0.can_warning_fault;
|
||||
faults->hardwareFault = status0.hardware_fault;
|
||||
}
|
||||
|
||||
void HAL_GetREVPHStickyFaults(HAL_REVPHHandle handle,
|
||||
HAL_REVPHStickyFaults* stickyFaults,
|
||||
int32_t* status) {
|
||||
std::memset(stickyFaults, 0, sizeof(*stickyFaults));
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
PH_status_1_t status1 = HAL_ReadREVPHStatus1(ph->hcan, status);
|
||||
stickyFaults->compressorOverCurrent = status1.sticky_compressor_oc_fault;
|
||||
stickyFaults->compressorOpen = status1.sticky_compressor_open_fault;
|
||||
stickyFaults->solenoidOverCurrent = status1.sticky_solenoid_oc_fault;
|
||||
stickyFaults->brownout = status1.sticky_brownout_fault;
|
||||
stickyFaults->canWarning = status1.sticky_can_warning_fault;
|
||||
stickyFaults->canBusOff = status1.sticky_can_bus_off_fault;
|
||||
stickyFaults->hasReset = status1.sticky_has_reset_fault;
|
||||
}
|
||||
|
||||
void HAL_ClearREVPHStickyFaults(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t packedData[8] = {0};
|
||||
HAL_WriteCANPacket(ph->hcan, packedData, PH_CLEAR_FAULTS_LENGTH,
|
||||
PH_CLEAR_FAULTS_FRAME_API, status);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user