Merge "Scouting: Update dead screen and dropped"
diff --git a/.bazelrc b/.bazelrc
index 9ff6c55..a69ccad 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -96,11 +96,25 @@
build --spawn_strategy=sandboxed
build --sandbox_default_allow_network=false
+build --incompatible_exclusive_test_sandboxed
build --strategy=TsProject=sandboxed
build --strategy=CopyFile=standalone
build --strategy=CopyDirectory=standalone
+# Honor the setting of `skipLibCheck` in the tsconfig.json file.
+# https://www.typescriptlang.org/tsconfig#skipLibCheck
+build --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig
+fetch --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig
+query --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig
+
+# Use "tsc" as the transpiler when ts_project has no `transpiler` set.
+# For now this is an acceptable default, but it would be nice to switch to swc in the future.
+# https://docs.aspect.build/rulesets/aspect_rules_ts/docs/transpiler/
+build --@aspect_rules_ts//ts:default_to_tsc_transpiler
+fetch --@aspect_rules_ts//ts:default_to_tsc_transpiler
+query --@aspect_rules_ts//ts:default_to_tsc_transpiler
+
# Use our hermetic JDK.
# Note that this doesn't quite work fully, but it should. See
# https://github.com/bazelbuild/bazel/issues/6341 for ongoing discussion with
diff --git a/Cargo.Bazel.lock b/Cargo.Bazel.lock
index e70a4ca..6bf8948 100644
--- a/Cargo.Bazel.lock
+++ b/Cargo.Bazel.lock
@@ -1,5 +1,5 @@
{
- "checksum": "7305dfa1e83bfd0222912e22f8294905db9c35e036a34182aaaf7bafa13c28ac",
+ "checksum": "6c02572fd1f0170376a09b2b84af516c934a7918e2fa5f5842e6a3cbccb0cb31",
"crates": {
"addr2line 0.20.0": {
"name": "addr2line",
@@ -233,6 +233,76 @@
},
"license": "MIT"
},
+ "anstream 0.6.11": {
+ "name": "anstream",
+ "version": "0.6.11",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/anstream/0.6.11/download",
+ "sha256": "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "anstream",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "anstream",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "auto",
+ "default",
+ "wincon"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "anstyle 1.0.1",
+ "target": "anstyle"
+ },
+ {
+ "id": "anstyle-parse 0.2.3",
+ "target": "anstyle_parse"
+ },
+ {
+ "id": "anstyle-query 1.0.2",
+ "target": "anstyle_query"
+ },
+ {
+ "id": "colorchoice 1.0.0",
+ "target": "colorchoice"
+ },
+ {
+ "id": "utf8parse 0.2.1",
+ "target": "utf8parse"
+ }
+ ],
+ "selects": {
+ "cfg(windows)": [
+ {
+ "id": "anstyle-wincon 3.0.2",
+ "target": "anstyle_wincon"
+ }
+ ]
+ }
+ },
+ "edition": "2021",
+ "version": "0.6.11"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"anstyle 1.0.1": {
"name": "anstyle",
"version": "1.0.1",
@@ -270,6 +340,139 @@
},
"license": "MIT OR Apache-2.0"
},
+ "anstyle-parse 0.2.3": {
+ "name": "anstyle-parse",
+ "version": "0.2.3",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/anstyle-parse/0.2.3/download",
+ "sha256": "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "anstyle_parse",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "anstyle_parse",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "default",
+ "utf8"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "utf8parse 0.2.1",
+ "target": "utf8parse"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.2.3"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
+ "anstyle-query 1.0.2": {
+ "name": "anstyle-query",
+ "version": "1.0.2",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/anstyle-query/1.0.2/download",
+ "sha256": "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "anstyle_query",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "anstyle_query",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [],
+ "selects": {
+ "cfg(windows)": [
+ {
+ "id": "windows-sys 0.52.0",
+ "target": "windows_sys"
+ }
+ ]
+ }
+ },
+ "edition": "2021",
+ "version": "1.0.2"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
+ "anstyle-wincon 3.0.2": {
+ "name": "anstyle-wincon",
+ "version": "3.0.2",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/anstyle-wincon/3.0.2/download",
+ "sha256": "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "anstyle_wincon",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "anstyle_wincon",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "anstyle 1.0.1",
+ "target": "anstyle"
+ }
+ ],
+ "selects": {
+ "cfg(windows)": [
+ {
+ "id": "windows-sys 0.52.0",
+ "target": "windows_sys"
+ }
+ ]
+ }
+ },
+ "edition": "2021",
+ "version": "3.0.2"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"anyhow 1.0.71": {
"name": "anyhow",
"version": "1.0.71",
@@ -385,6 +588,43 @@
},
"license": "MIT"
},
+ "arrayvec 0.7.4": {
+ "name": "arrayvec",
+ "version": "0.7.4",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/arrayvec/0.7.4/download",
+ "sha256": "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "arrayvec",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "arrayvec",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "default",
+ "std"
+ ],
+ "selects": {}
+ },
+ "edition": "2018",
+ "version": "0.7.4"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"assert_cmd 2.0.12": {
"name": "assert_cmd",
"version": "2.0.12",
@@ -863,7 +1103,8 @@
"common": [
{
"id": "clap 3.2.25",
- "target": "clap"
+ "target": "clap",
+ "alias": "clap3"
},
{
"id": "env_logger 0.9.3",
@@ -1981,6 +2222,188 @@
},
"license": "MIT OR Apache-2.0"
},
+ "clap 4.4.18": {
+ "name": "clap",
+ "version": "4.4.18",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/clap/4.4.18/download",
+ "sha256": "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "clap",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "clap",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "color",
+ "default",
+ "derive",
+ "error-context",
+ "help",
+ "std",
+ "string",
+ "suggestions",
+ "usage"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "clap_builder 4.4.18",
+ "target": "clap_builder"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "proc_macro_deps": {
+ "common": [
+ {
+ "id": "clap_derive 4.4.7",
+ "target": "clap_derive"
+ }
+ ],
+ "selects": {}
+ },
+ "version": "4.4.18"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
+ "clap_builder 4.4.18": {
+ "name": "clap_builder",
+ "version": "4.4.18",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/clap_builder/4.4.18/download",
+ "sha256": "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "clap_builder",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "clap_builder",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "color",
+ "error-context",
+ "help",
+ "std",
+ "string",
+ "suggestions",
+ "usage"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "anstream 0.6.11",
+ "target": "anstream"
+ },
+ {
+ "id": "anstyle 1.0.1",
+ "target": "anstyle"
+ },
+ {
+ "id": "clap_lex 0.6.0",
+ "target": "clap_lex"
+ },
+ {
+ "id": "strsim 0.10.0",
+ "target": "strsim"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "4.4.18"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
+ "clap_derive 4.4.7": {
+ "name": "clap_derive",
+ "version": "4.4.7",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/clap_derive/4.4.7/download",
+ "sha256": "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+ }
+ },
+ "targets": [
+ {
+ "ProcMacro": {
+ "crate_name": "clap_derive",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "clap_derive",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "default"
+ ],
+ "selects": {}
+ },
+ "deps": {
+ "common": [
+ {
+ "id": "heck 0.4.1",
+ "target": "heck"
+ },
+ {
+ "id": "proc-macro2 1.0.63",
+ "target": "proc_macro2"
+ },
+ {
+ "id": "quote 1.0.29",
+ "target": "quote"
+ },
+ {
+ "id": "syn 2.0.28",
+ "target": "syn"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "4.4.7"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"clap_lex 0.2.4": {
"name": "clap_lex",
"version": "0.2.4",
@@ -2009,7 +2432,7 @@
"deps": {
"common": [
{
- "id": "os_str_bytes 6.5.1",
+ "id": "os_str_bytes 6.6.1",
"target": "os_str_bytes"
}
],
@@ -2020,6 +2443,36 @@
},
"license": "MIT OR Apache-2.0"
},
+ "clap_lex 0.6.0": {
+ "name": "clap_lex",
+ "version": "0.6.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/clap_lex/0.6.0/download",
+ "sha256": "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "clap_lex",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "clap_lex",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "edition": "2021",
+ "version": "0.6.0"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"codespan-reporting 0.11.1": {
"name": "codespan-reporting",
"version": "0.11.1",
@@ -2063,6 +2516,36 @@
},
"license": "Apache-2.0"
},
+ "colorchoice 1.0.0": {
+ "name": "colorchoice",
+ "version": "1.0.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/colorchoice/1.0.0/download",
+ "sha256": "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "colorchoice",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "colorchoice",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "edition": "2021",
+ "version": "1.0.0"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"compile_with_bazel 0.0.0": {
"name": "compile_with_bazel",
"version": "0.0.0",
@@ -2098,6 +2581,10 @@
"target": "bitflags"
},
{
+ "id": "clap 4.4.18",
+ "target": "clap"
+ },
+ {
"id": "cxx 1.0.97",
"target": "cxx"
},
@@ -2805,6 +3292,10 @@
"deps": {
"common": [
{
+ "id": "arrayvec 0.7.4",
+ "target": "arrayvec"
+ },
+ {
"id": "bitflags 1.3.2",
"target": "bitflags"
},
@@ -3554,6 +4045,42 @@
},
"license": "MIT OR Apache-2.0"
},
+ "heck 0.4.1": {
+ "name": "heck",
+ "version": "0.4.1",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/heck/0.4.1/download",
+ "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "heck",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "heck",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "default"
+ ],
+ "selects": {}
+ },
+ "edition": "2018",
+ "version": "0.4.1"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"hermit-abi 0.1.19": {
"name": "hermit-abi",
"version": "0.1.19",
@@ -5059,13 +5586,13 @@
},
"license": "MIT OR Apache-2.0"
},
- "os_str_bytes 6.5.1": {
+ "os_str_bytes 6.6.1": {
"name": "os_str_bytes",
- "version": "6.5.1",
+ "version": "6.6.1",
"repository": {
"Http": {
- "url": "https://crates.io/api/v1/crates/os_str_bytes/6.5.1/download",
- "sha256": "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
+ "url": "https://crates.io/api/v1/crates/os_str_bytes/6.6.1/download",
+ "sha256": "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
}
},
"targets": [
@@ -5091,7 +5618,7 @@
"selects": {}
},
"edition": "2021",
- "version": "6.5.1"
+ "version": "6.6.1"
},
"license": "MIT OR Apache-2.0"
},
@@ -7928,6 +8455,42 @@
},
"license": "MIT/Apache-2.0"
},
+ "utf8parse 0.2.1": {
+ "name": "utf8parse",
+ "version": "0.2.1",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/utf8parse/0.2.1/download",
+ "sha256": "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "utf8parse",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "utf8parse",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "crate_features": {
+ "common": [
+ "default"
+ ],
+ "selects": {}
+ },
+ "edition": "2018",
+ "version": "0.2.1"
+ },
+ "license": "Apache-2.0 OR MIT"
+ },
"uuid 1.4.0": {
"name": "uuid",
"version": "1.4.0",
@@ -8437,6 +9000,45 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows-sys 0.52.0": {
+ "name": "windows-sys",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows-sys/0.52.0/download",
+ "sha256": "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_sys",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_sys",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows-targets 0.52.0",
+ "target": "windows_targets"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows-targets 0.48.1": {
"name": "windows-targets",
"version": "0.48.1",
@@ -8514,6 +9116,83 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows-targets 0.52.0": {
+ "name": "windows-targets",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows-targets/0.52.0/download",
+ "sha256": "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_targets",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_targets",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [],
+ "selects": {
+ "aarch64-pc-windows-gnullvm": [
+ {
+ "id": "windows_aarch64_gnullvm 0.52.0",
+ "target": "windows_aarch64_gnullvm"
+ }
+ ],
+ "cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))": [
+ {
+ "id": "windows_aarch64_msvc 0.52.0",
+ "target": "windows_aarch64_msvc"
+ }
+ ],
+ "cfg(all(target_arch = \"x86\", target_env = \"gnu\", not(windows_raw_dylib)))": [
+ {
+ "id": "windows_i686_gnu 0.52.0",
+ "target": "windows_i686_gnu"
+ }
+ ],
+ "cfg(all(target_arch = \"x86\", target_env = \"msvc\", not(windows_raw_dylib)))": [
+ {
+ "id": "windows_i686_msvc 0.52.0",
+ "target": "windows_i686_msvc"
+ }
+ ],
+ "cfg(all(target_arch = \"x86_64\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))": [
+ {
+ "id": "windows_x86_64_gnu 0.52.0",
+ "target": "windows_x86_64_gnu"
+ }
+ ],
+ "cfg(all(target_arch = \"x86_64\", target_env = \"msvc\", not(windows_raw_dylib)))": [
+ {
+ "id": "windows_x86_64_msvc 0.52.0",
+ "target": "windows_x86_64_msvc"
+ }
+ ],
+ "x86_64-pc-windows-gnullvm": [
+ {
+ "id": "windows_x86_64_gnullvm 0.52.0",
+ "target": "windows_x86_64_gnullvm"
+ }
+ ]
+ }
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows_aarch64_gnullvm 0.48.0": {
"name": "windows_aarch64_gnullvm",
"version": "0.48.0",
@@ -8567,6 +9246,59 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows_aarch64_gnullvm 0.52.0": {
+ "name": "windows_aarch64_gnullvm",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows_aarch64_gnullvm/0.52.0/download",
+ "sha256": "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_aarch64_gnullvm",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ },
+ {
+ "BuildScript": {
+ "crate_name": "build_script_build",
+ "crate_root": "build.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_aarch64_gnullvm",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows_aarch64_gnullvm 0.52.0",
+ "target": "build_script_build"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "build_script_attrs": {
+ "data_glob": [
+ "**"
+ ]
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows_aarch64_msvc 0.48.0": {
"name": "windows_aarch64_msvc",
"version": "0.48.0",
@@ -8620,6 +9352,59 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows_aarch64_msvc 0.52.0": {
+ "name": "windows_aarch64_msvc",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows_aarch64_msvc/0.52.0/download",
+ "sha256": "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_aarch64_msvc",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ },
+ {
+ "BuildScript": {
+ "crate_name": "build_script_build",
+ "crate_root": "build.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_aarch64_msvc",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows_aarch64_msvc 0.52.0",
+ "target": "build_script_build"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "build_script_attrs": {
+ "data_glob": [
+ "**"
+ ]
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows_i686_gnu 0.48.0": {
"name": "windows_i686_gnu",
"version": "0.48.0",
@@ -8673,6 +9458,59 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows_i686_gnu 0.52.0": {
+ "name": "windows_i686_gnu",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows_i686_gnu/0.52.0/download",
+ "sha256": "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_i686_gnu",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ },
+ {
+ "BuildScript": {
+ "crate_name": "build_script_build",
+ "crate_root": "build.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_i686_gnu",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows_i686_gnu 0.52.0",
+ "target": "build_script_build"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "build_script_attrs": {
+ "data_glob": [
+ "**"
+ ]
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows_i686_msvc 0.48.0": {
"name": "windows_i686_msvc",
"version": "0.48.0",
@@ -8726,6 +9564,59 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows_i686_msvc 0.52.0": {
+ "name": "windows_i686_msvc",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows_i686_msvc/0.52.0/download",
+ "sha256": "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_i686_msvc",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ },
+ {
+ "BuildScript": {
+ "crate_name": "build_script_build",
+ "crate_root": "build.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_i686_msvc",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows_i686_msvc 0.52.0",
+ "target": "build_script_build"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "build_script_attrs": {
+ "data_glob": [
+ "**"
+ ]
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows_x86_64_gnu 0.48.0": {
"name": "windows_x86_64_gnu",
"version": "0.48.0",
@@ -8779,6 +9670,59 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows_x86_64_gnu 0.52.0": {
+ "name": "windows_x86_64_gnu",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows_x86_64_gnu/0.52.0/download",
+ "sha256": "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_x86_64_gnu",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ },
+ {
+ "BuildScript": {
+ "crate_name": "build_script_build",
+ "crate_root": "build.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_x86_64_gnu",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows_x86_64_gnu 0.52.0",
+ "target": "build_script_build"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "build_script_attrs": {
+ "data_glob": [
+ "**"
+ ]
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows_x86_64_gnullvm 0.48.0": {
"name": "windows_x86_64_gnullvm",
"version": "0.48.0",
@@ -8832,6 +9776,59 @@
},
"license": "MIT OR Apache-2.0"
},
+ "windows_x86_64_gnullvm 0.52.0": {
+ "name": "windows_x86_64_gnullvm",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows_x86_64_gnullvm/0.52.0/download",
+ "sha256": "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_x86_64_gnullvm",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ },
+ {
+ "BuildScript": {
+ "crate_name": "build_script_build",
+ "crate_root": "build.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_x86_64_gnullvm",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows_x86_64_gnullvm 0.52.0",
+ "target": "build_script_build"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "build_script_attrs": {
+ "data_glob": [
+ "**"
+ ]
+ },
+ "license": "MIT OR Apache-2.0"
+ },
"windows_x86_64_msvc 0.48.0": {
"name": "windows_x86_64_msvc",
"version": "0.48.0",
@@ -8884,6 +9881,59 @@
]
},
"license": "MIT OR Apache-2.0"
+ },
+ "windows_x86_64_msvc 0.52.0": {
+ "name": "windows_x86_64_msvc",
+ "version": "0.52.0",
+ "repository": {
+ "Http": {
+ "url": "https://crates.io/api/v1/crates/windows_x86_64_msvc/0.52.0/download",
+ "sha256": "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+ }
+ },
+ "targets": [
+ {
+ "Library": {
+ "crate_name": "windows_x86_64_msvc",
+ "crate_root": "src/lib.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ },
+ {
+ "BuildScript": {
+ "crate_name": "build_script_build",
+ "crate_root": "build.rs",
+ "srcs": [
+ "**/*.rs"
+ ]
+ }
+ }
+ ],
+ "library_target_name": "windows_x86_64_msvc",
+ "common_attrs": {
+ "compile_data_glob": [
+ "**"
+ ],
+ "deps": {
+ "common": [
+ {
+ "id": "windows_x86_64_msvc 0.52.0",
+ "target": "build_script_build"
+ }
+ ],
+ "selects": {}
+ },
+ "edition": "2021",
+ "version": "0.52.0"
+ },
+ "build_script_attrs": {
+ "data_glob": [
+ "**"
+ ]
+ },
+ "license": "MIT OR Apache-2.0"
}
},
"binary_crates": [],
diff --git a/Cargo.lock b/Cargo.lock
index 5fcc5eb..3829492 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -47,12 +47,54 @@
]
[[package]]
+name = "anstream"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
name = "anstyle"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -72,6 +114,12 @@
]
[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
name = "assert_cmd"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -385,7 +433,7 @@
dependencies = [
"atty",
"bitflags 1.3.2",
- "clap_lex",
+ "clap_lex 0.2.4",
"indexmap",
"once_cell",
"strsim 0.10.0",
@@ -394,6 +442,40 @@
]
[[package]]
+name = "clap"
+version = "4.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex 0.6.0",
+ "strsim 0.10.0",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+]
+
+[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -403,6 +485,12 @@
]
[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -413,12 +501,19 @@
]
[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
name = "compile_with_bazel"
version = "0.0.0"
dependencies = [
"anyhow",
"bindgen",
"bitflags 1.3.2",
+ "clap 4.4.18",
"cxx",
"cxxbridge-macro",
"futures",
@@ -524,7 +619,7 @@
dependencies = [
"errno-dragonfly",
"libc",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -550,6 +645,7 @@
name = "flatbuffers"
version = "22.10.26"
dependencies = [
+ "arrayvec",
"bitflags 1.3.2",
"rustc_version",
"serde",
@@ -677,6 +773,12 @@
]
[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -731,7 +833,7 @@
dependencies = [
"hermit-abi 0.3.2",
"libc",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -742,7 +844,7 @@
dependencies = [
"hermit-abi 0.3.2",
"rustix 0.38.7",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -939,9 +1041,9 @@
[[package]]
name = "os_str_bytes"
-version = "6.5.1"
+version = "6.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
[[package]]
name = "owo-colors"
@@ -1129,7 +1231,7 @@
"io-lifetimes",
"libc",
"linux-raw-sys 0.3.8",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1142,7 +1244,7 @@
"errno",
"libc",
"linux-raw-sys 0.4.5",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1300,7 +1402,7 @@
"fastrand",
"redox_syscall",
"rustix 0.37.23",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1432,6 +1534,12 @@
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
name = "uuid"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1521,7 +1629,16 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
]
[[package]]
@@ -1530,13 +1647,28 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
]
[[package]]
@@ -1546,37 +1678,79 @@
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
diff --git a/Cargo.toml b/Cargo.toml
index 8d07945..993491a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@
]
[dependencies]
+clap = { version = "4.4", features = ["derive", "string"] }
cxx = "1.0"
cxxbridge-macro = "1.0"
uuid = "1.0"
diff --git a/WORKSPACE b/WORKSPACE
index 7809594..82a0dd4 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -31,15 +31,17 @@
http_archive(
name = "aspect_bazel_lib",
- sha256 = "80897b673c2b506d21f861ae316689aa8abcc3e56947580a41bf9e68ff13af58",
- strip_prefix = "bazel-lib-1.27.1",
- url = "https://github.com/aspect-build/bazel-lib/releases/download/v1.27.1/bazel-lib-v1.27.1.tar.gz",
+ sha256 = "979667bb7276ee8fcf2c114c9be9932b9a3052a64a647e0dcaacfb9c0016f0a3",
+ strip_prefix = "bazel-lib-2.4.1",
+ url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.4.1/bazel-lib-v2.4.1.tar.gz",
)
-load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies", "register_jq_toolchains")
+load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies", "aspect_bazel_lib_register_toolchains", "register_jq_toolchains")
aspect_bazel_lib_dependencies()
+aspect_bazel_lib_register_toolchains()
+
register_jq_toolchains()
http_archive(
@@ -575,6 +577,23 @@
)
http_archive(
+ name = "ffmpeg",
+ build_file_content = """
+load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
+
+native_binary(
+ name = "ffmpeg",
+ src = "ffmpeg-6.0.1-amd64-static/ffmpeg",
+ out = "ffmpeg",
+ visibility = ["//visibility:public"],
+ target_compatible_with = ["@platforms//cpu:x86_64", "@platforms//os:linux"],
+)
+ """,
+ sha256 = "28268bf402f1083833ea269331587f60a242848880073be8016501d864bd07a5",
+ url = "https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-6.0.1-amd64-static.tar.xz",
+)
+
+http_archive(
name = "apache2",
build_file = "@//debian:apache2.BUILD",
sha256 = "98b0ad6d911751ba0aa486429e6278f995e7bbabd928c7d3d44c888fa2bf371b",
@@ -853,25 +872,28 @@
http_archive(
name = "aspect_rules_js",
- sha256 = "9fadde0ae6e0101755b8aedabf7d80b166491a8de297c60f6a5179cd0d0fea58",
- strip_prefix = "rules_js-1.20.0",
- url = "https://github.com/aspect-build/rules_js/releases/download/v1.20.0/rules_js-v1.20.0.tar.gz",
+ sha256 = "630a71aba66c4023a5b16ab3efafaeed8b1a2865ccd168a34611eb73876b3fc4",
+ strip_prefix = "rules_js-1.37.1",
+ url = "https://github.com/aspect-build/rules_js/releases/download/v1.37.1/rules_js-v1.37.1.tar.gz",
)
-load("@aspect_rules_js//npm:npm_import.bzl", "npm_translate_lock", "pnpm_repository")
-
-pnpm_repository(name = "pnpm")
-
load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies")
rules_js_dependencies()
+load("@bazel_features//:deps.bzl", "bazel_features_deps")
+
+bazel_features_deps()
+
+load("@aspect_rules_js//npm:npm_import.bzl", "npm_translate_lock", "pnpm_repository")
+
+pnpm_repository(name = "pnpm")
+
http_archive(
name = "aspect_rules_esbuild",
- sha256 = "b98cde83e9e6a006d8300e88e2f09da56b5a6c18166465a224cfe36bdcbc03e0",
- strip_prefix = "aspect-build-rules_esbuild-110b94c",
- type = "tar.gz",
- url = "https://github.com/aspect-build/rules_esbuild/tarball/110b94c7f16f328a0eab8aa0b862030055b86564",
+ sha256 = "999349afef62875301f45ec8515189ceaf2e85b1e67a17e2d28b95b30e1d6c0b",
+ strip_prefix = "rules_esbuild-0.18.0",
+ url = "https://github.com/aspect-build/rules_esbuild/releases/download/v0.18.0/rules_esbuild-v0.18.0.tar.gz",
)
load("@aspect_rules_esbuild//esbuild:dependencies.bzl", "rules_esbuild_dependencies")
@@ -916,7 +938,7 @@
verify_node_modules_ignored = "//:.bazelignore",
)
-load("@aspect_rules_esbuild//esbuild:repositories.bzl", "esbuild_register_toolchains", LATEST_ESBUILD_VERSION = "LATEST_VERSION")
+load("@aspect_rules_esbuild//esbuild:repositories.bzl", "LATEST_ESBUILD_VERSION", "esbuild_register_toolchains")
esbuild_register_toolchains(
name = "esbuild",
@@ -931,65 +953,34 @@
patches = [
"//third_party:rules_rollup/0001-Fix-resolving-files.patch",
],
- sha256 = "4c43d20ce377b93cd43a3553e6159a17b85ce80c36a564b55051c2320d32b777",
- strip_prefix = "rules_rollup-0.13.1",
- url = "https://github.com/aspect-build/rules_rollup/releases/download/v0.13.1/rules_rollup-v0.13.1.tar.gz",
+ sha256 = "a0433a0b0206a45d362749d71bc1e4e0dacf5ca2a572b059328f9753392bca80",
+ strip_prefix = "rules_rollup-1.0.0",
+ url = "https://github.com/aspect-build/rules_rollup/releases/download/v1.0.0/rules_rollup-v1.0.0.tar.gz",
)
-load("@aspect_rules_rollup//rollup:dependencies.bzl", "rules_rollup_dependencies")
-
-# Fetches the rules_rollup dependencies.
-# If you want to have a different version of some dependency,
-# you should fetch it *before* calling this.
-# Alternatively, you can skip calling this function, so long as you've
-# already fetched all the dependencies.
-rules_rollup_dependencies()
-
-load("@aspect_rules_rollup//rollup:repositories.bzl", "rollup_repositories")
-
-rollup_repositories(name = "rollup")
-
-load("@rollup//:npm_repositories.bzl", rollup_npm_repositories = "npm_repositories")
-
-rollup_npm_repositories()
-
http_archive(
name = "aspect_rules_terser",
- sha256 = "918e7ac036eca1402cae4d4ddba75ecdcdd886ac35bc0624d9f1ebc7527e369b",
- strip_prefix = "rules_terser-0.13.0",
- url = "https://github.com/aspect-build/rules_terser/archive/refs/tags/v0.13.0.tar.gz",
+ sha256 = "8424b4c064d0e490e5b6f215b993712ef641b77e03b68fdc64221edf48d14add",
+ strip_prefix = "rules_terser-1.0.0",
+ url = "https://github.com/aspect-build/rules_terser/releases/download/v1.0.0/rules_terser-v1.0.0.tar.gz",
)
load("@aspect_rules_terser//terser:dependencies.bzl", "rules_terser_dependencies")
rules_terser_dependencies()
-# Fetch and register a nodejs interpreter, if you haven't already
-
-nodejs_register_toolchains(
- name = "node",
- node_version = DEFAULT_NODE_VERSION,
-)
-
-# Fetch and register the terser tool
-load("@aspect_rules_terser//terser:repositories.bzl", "terser_repositories")
-
-terser_repositories(name = "terser")
-
-load("@terser//:npm_repositories.bzl", terser_npm_repositories = "npm_repositories")
-
-terser_npm_repositories()
-
http_archive(
name = "aspect_rules_ts",
- sha256 = "db77d904284d21121ae63dbaaadfd8c75ff6d21ad229f92038b415c1ad5019cc",
- strip_prefix = "rules_ts-1.3.0",
- url = "https://github.com/aspect-build/rules_ts/releases/download/v1.3.0/rules_ts-v1.3.0.tar.gz",
+ sha256 = "6ad28b5bac2bb5a74e737925fbc3f62ce1edabe5a48d61a9980c491ef4cedfb7",
+ strip_prefix = "rules_ts-2.1.1",
+ url = "https://github.com/aspect-build/rules_ts/releases/download/v2.1.1/rules_ts-v2.1.1.tar.gz",
)
load("@aspect_rules_ts//ts:repositories.bzl", "rules_ts_dependencies")
-rules_ts_dependencies(ts_version_from = "//:package.json")
+rules_ts_dependencies(
+ ts_version_from = "//:package.json",
+)
load("@npm//:repositories.bzl", "npm_repositories")
@@ -997,11 +988,10 @@
http_archive(
name = "aspect_rules_cypress",
- sha256 = "06d70a2960108607d2e70f9bc6863af6b82317fdfcf7a5a30fd226a5abc46782",
- strip_prefix = "aspect-build-rules_cypress-3db1b74",
- type = "tar.gz",
+ sha256 = "76947778d8e855eee3c15931e1fcdc1c2a25d56d6c0edd110b2227c05b794d08",
+ strip_prefix = "rules_cypress-0.3.2",
urls = [
- "https://github.com/aspect-build/rules_cypress/tarball/3db1b74818ac4ce1b9d489a6e0065b36c1076761",
+ "https://github.com/aspect-build/rules_cypress/archive/refs/tags/v0.3.2.tar.gz",
],
)
@@ -1433,6 +1423,14 @@
)
http_archive(
+ name = "com_github_nghttp2_nghttp2",
+ build_file = "//debian:BUILD.nghttp2.bazel",
+ sha256 = "7da19947b33a07ddcf97b9791331bfee8a8545e6b394275a9971f43cae9d636b",
+ strip_prefix = "nghttp2-1.58.0",
+ url = "https://github.com/nghttp2/nghttp2/archive/refs/tags/v1.58.0.tar.gz",
+)
+
+http_archive(
# No official name exists. Names used in our external dependencies include
# zlib, madler_zlib, com_github_madler_zlib.
name = "zlib",
@@ -1521,8 +1519,8 @@
# This contains the *compiled* foxglove studio. This can be reproduced by:
# 1. Cloning https://github.com/foxglove/studio
-# 2. Building the code (yarn web:build:prod)
-# 3. tar'ing the web/.webpack folder
+# 2. Building the code (yarn install; yarn web:build:prod)
+# 3. tar'ing the web/.webpack folder (e.g., tar czvf foxglove-1456f4a4cb6f4c6c7e50e020ba9918dba9e04b96.tar.gz --directory=web/.webpack/ .)
# These files can be hosted locally to provide an offline foxglove server.
# Foxglove may be served on any port and may be nested at a subpath
# (e.g., at hostname:8000/foxglove behind a proxy).
@@ -1534,9 +1532,9 @@
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)""",
- sha256 = "68513024efb60dcdfc9b130d3e0466d194a8599fbd85f8e99d01c148d03e5887",
+ sha256 = "d02f4ca629e6dcf2b65557a0353871ce0025e70715214de4e6ec7e9f862de420",
url =
- "https://software.frc971.org/Build-Dependencies/foxglove-9857d637e90dfeecd63ad47fa760046791f8d43c.tar.gz",
+ "https://software.frc971.org/Build-Dependencies/foxglove-1456f4a4cb6f4c6c7e50e020ba9918dba9e04b96.tar.gz",
)
#
@@ -1577,6 +1575,20 @@
url = "https://software.frc971.org/Build-Dependencies/orin_large_gs_apriltag.bfbs",
)
+http_file(
+ name = "orin_capture_24_04",
+ downloaded_file_path = "orin_capture_24_04.bfbs",
+ sha256 = "719edb1d1394c13c1b55d02cf35c277e1d4c2111f4eb4220b28addc08634488a",
+ url = "https://software.frc971.org/Build-Dependencies/orin-capture-24-04-2024.02.14.bfbs",
+)
+
+http_file(
+ name = "orin_capture_24_04_side",
+ downloaded_file_path = "orin_capture_24_04_side.bfbs",
+ sha256 = "4747cc98f8794d6570cb12a3171d7984e358581914a28b43fb6bb8b9bd7a10ac",
+ url = "https://software.frc971.org/Build-Dependencies/orin-capture-24-04-side-2024.02.17.bfbs",
+)
+
http_archive(
name = "libedgetpu",
build_file = "//third_party:libedgetpu/libedgetpu.BUILD",
@@ -1633,10 +1645,11 @@
# https://github.com/hedronvision/bazel-compile-commands-extractor
http_archive(
name = "hedron_compile_commands",
- strip_prefix = "bazel-compile-commands-extractor-daae6f40adfa5fdb7c89684cbe4d88b691c63b2d",
# Replace the commit hash (daae6f40adfa5fdb7c89684cbe4d88b691c63b2d) in both places (below) with the latest (https://github.com/hedronvision/bazel-compile-commands-extractor/commits/main), rather than using the stale one here.
# Even better, set up Renovate and let it do the work for you (see "Suggestion: Updates" in the README).
+ sha256 = "43451a32bf271e7ba4635a07f7996d535501f066c0fe8feab04fb0c91dd5986e",
+ strip_prefix = "bazel-compile-commands-extractor-daae6f40adfa5fdb7c89684cbe4d88b691c63b2d",
url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/daae6f40adfa5fdb7c89684cbe4d88b691c63b2d.tar.gz",
# When you first run this tool, it'll recommend a sha256 hash to put here with a message like: "DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = ..."
)
diff --git a/aos/BUILD b/aos/BUILD
index 4d041ca..67e5b29 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -1,9 +1,9 @@
-load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library", "flatbuffer_py_library", "flatbuffer_rust_library")
+load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library", "flatbuffer_py_library")
load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
load("@com_github_google_flatbuffers//:typescript.bzl", "flatbuffer_ts_library")
-load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
load("//aos:flatbuffers.bzl", "cc_static_flatbuffer")
load("//tools/build_rules:autocxx.bzl", "autocxx_library")
+load("//tools/rust:defs.bzl", "flatbuffer_rust_library", "rust_library")
exports_files(["aos_dump_autocomplete.sh"])
@@ -184,19 +184,36 @@
],
)
+cc_library(
+ name = "init_for_rust",
+ srcs = [
+ "init_for_rust.cc",
+ ],
+ hdrs = [
+ "init_for_rust.h",
+ ],
+ deps = [
+ ":for_rust",
+ ":init",
+ "//aos/logging",
+ "@com_github_gflags_gflags//:gflags",
+ "@crate_index//:cxx_cc",
+ ],
+)
+
autocxx_library(
name = "init_rs",
srcs = ["init.rs"],
crate_name = "aos_init",
libs = [
- ":init",
+ ":init_for_rust",
],
override_cc_toolchain = "@llvm_toolchain//:cc-clang-x86_64-linux",
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
visibility = ["//visibility:public"],
+ deps = [
+ "@crate_index//:clap",
+ "@crate_index//:env_logger",
+ ],
)
autocxx_library(
@@ -204,17 +221,15 @@
testonly = True,
srcs = ["test_init.rs"],
crate_name = "aos_test_init",
+ gen_docs = False,
libs = [
"//aos/testing:tmpdir",
],
override_cc_toolchain = "@llvm_toolchain//:cc-clang-x86_64-linux",
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
visibility = ["//visibility:public"],
deps = [
":init_rs",
+ "@crate_index//:env_logger",
],
)
@@ -239,6 +254,7 @@
srcs = ["configuration.fbs"],
target_compatible_with = ["@platforms//os:linux"],
visibility = ["//visibility:public"],
+ deps = ["//aos/flatbuffers/reflection:reflection_fbs"],
)
cc_static_flatbuffer(
@@ -274,10 +290,6 @@
name = "configuration_rust_fbs",
srcs = ["configuration.fbs"],
crate_name = "aos_configuration_fbs",
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
visibility = ["//visibility:public"],
)
@@ -331,10 +343,9 @@
":configuration_fbs",
],
override_cc_toolchain = "@llvm_toolchain//:cc-clang-x86_64-linux",
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
+ test_data = [
+ "//aos/testdata:test_configs",
+ ],
visibility = ["//visibility:public"],
deps = [
":configuration_rust_fbs",
@@ -343,17 +354,6 @@
],
)
-rust_test(
- name = "configuration_rs_test",
- crate = ":configuration_rs",
- data = [
- "//aos/testdata:test_configs",
- ],
- # TODO: Make Rust play happy with pic vs nopic. Details at:
- # https://github.com/bazelbuild/rules_rust/issues/118
- rustc_flags = ["-Crelocation-model=static"],
-)
-
flatbuffer_ts_library(
name = "json_to_flatbuffer_fbs_ts",
srcs = ["json_to_flatbuffer.fbs"],
@@ -373,10 +373,6 @@
name = "json_to_flatbuffer_rust_fbs",
srcs = ["json_to_flatbuffer.fbs"],
crate_name = "aos_json_to_flatbuffer_fbs",
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
visibility = ["//aos:__subpackages__"],
)
@@ -517,24 +513,15 @@
name = "flatbuffers_rs",
srcs = ["flatbuffers.rs"],
crate_name = "aos_flatbuffers",
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
+ test_deps = [
+ ":json_to_flatbuffer_rust_fbs",
+ ],
visibility = ["//visibility:public"],
deps = [
"@com_github_google_flatbuffers//rust",
],
)
-rust_test(
- name = "flatbuffers_rs_test",
- crate = ":flatbuffers_rs",
- deps = [
- ":json_to_flatbuffer_rust_fbs",
- ],
-)
-
cc_test(
name = "configuration_test",
srcs = [
@@ -765,10 +752,6 @@
rs_deps = [
"@crate_index//:uuid",
],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
visibility = ["//visibility:public"],
)
@@ -825,3 +808,19 @@
"//aos/testing:tmpdir",
],
)
+
+rust_library(
+ name = "aos_rs",
+ srcs = ["aos.rs"],
+ crate_name = "aos",
+ visibility = ["//visibility:public"],
+ deps = [
+ ":configuration_rs",
+ ":flatbuffers_rs",
+ ":init_rs",
+ ":uuid_rs",
+ "//aos/events:event_loop_runtime",
+ "//aos/events:shm_event_loop_rs",
+ "//aos/events:simulated_event_loop_rs",
+ ],
+)
diff --git a/aos/aos.rs b/aos/aos.rs
new file mode 100644
index 0000000..3a13c4d
--- /dev/null
+++ b/aos/aos.rs
@@ -0,0 +1,14 @@
+//! All of AOS into a single, easy to use library.
+
+pub use aos_configuration as configuration;
+pub use aos_init as init;
+pub use aos_uuid as uuid;
+
+pub use aos_flatbuffers as flatbuffers;
+
+/// The essentials for working with the AOS event loop.
+pub mod events {
+ pub use aos_events_event_loop_runtime as event_loop_runtime;
+ pub use aos_events_shm_event_loop as shm_event_loop;
+ pub use aos_events_simulated_event_loop as simulated_event_loop;
+}
diff --git a/aos/configuration.fbs b/aos/configuration.fbs
index 0b42f5b..b2b34c1 100644
--- a/aos/configuration.fbs
+++ b/aos/configuration.fbs
@@ -169,6 +169,10 @@
// If set, this is the memory limit to enforce in bytes for the application
// (and it's children)
memory_limit:uint64 = 0 (id: 8);
+
+ // If set, this is the number of nanoseconds the application has to stop. If the application
+ // doesn't stop within the specified time, then it is killed.
+ stop_time:int64 = 1000000000 (id: 9);
}
// Per node data and connection information.
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 9910fc6..e82fdde 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -1,10 +1,9 @@
-load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_rust_library")
load("@com_github_google_flatbuffers//:typescript.bzl", "flatbuffer_ts_library")
load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
load("//aos:flatbuffers.bzl", "cc_static_flatbuffer")
load("//aos:config.bzl", "aos_config")
load("//tools/build_rules:autocxx.bzl", "autocxx_library")
-load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_doc", "rust_doc_test", "rust_test")
+load("//tools/rust:defs.bzl", "flatbuffer_rust_library", "rust_binary", "rust_library", "rust_test")
package(default_visibility = ["//visibility:public"])
@@ -46,19 +45,11 @@
flatbuffer_rust_library(
name = "ping_rust_fbs",
srcs = ["ping.fbs"],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
)
flatbuffer_rust_library(
name = "pong_rust_fbs",
srcs = ["pong.fbs"],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
)
static_flatbuffer(
@@ -155,6 +146,10 @@
name = "event_loop_runtime",
srcs = ["event_loop_runtime.rs"],
crate_name = "aos_events_event_loop_runtime",
+ doctest_deps = [
+ ":pong_rust_fbs",
+ ],
+ gen_tests = False,
libs = [
":event_loop_runtime_cc",
],
@@ -165,10 +160,6 @@
"@crate_index//:futures",
"@crate_index//:thiserror",
],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
visibility = ["//visibility:public"],
deps = [
"//aos:configuration_rs",
@@ -176,37 +167,19 @@
],
)
-rust_doc(
- name = "event_loop_runtime_doc",
- crate = ":event_loop_runtime",
-)
-
-rust_doc_test(
- name = "event_loop_runtime_doc_test",
- crate = ":event_loop_runtime",
- target_compatible_with = ["@platforms//cpu:x86_64"],
- deps = [
- ":pong_rust_fbs",
- ],
-)
-
autocxx_library(
name = "event_loop_runtime_test_lib_rs",
testonly = True,
srcs = ["event_loop_runtime_test_lib.rs"],
+ gen_docs = False,
libs = [
":event_loop",
],
- override_cc_toolchain = "@llvm_toolchain//:cc-clang-x86_64-linux",
rs_deps = [
":event_loop_runtime",
":ping_rust_fbs",
":pong_rust_fbs",
],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
)
cc_test(
@@ -259,6 +232,28 @@
],
)
+rust_test(
+ name = "pingpong_test_rs",
+ srcs = [
+ "pingpong_test.rs",
+ ],
+ data = [":pingpong_config"],
+ # TODO(adam.snaider): Remove later. For now we need this because when
+ # a rust test crashes inside of C++, the output gets captured which makes
+ # it pretty much impossible to figure out what happened.
+ env = {"RUST_TEST_NOCAPTURE": "1"},
+ deps = [
+ ":ping_lib_rs",
+ ":ping_rust_fbs",
+ ":pong_lib_rs",
+ ":pong_rust_fbs",
+ "//aos/testing:aos_rs",
+ "@com_github_google_flatbuffers//rust",
+ "@crate_index//:futures",
+ "@rules_rust//tools/runfiles",
+ ],
+)
+
rust_binary(
name = "ping_rs",
srcs = [
@@ -267,22 +262,27 @@
data = [
":pingpong_config",
],
- rustc_flags = ["-Crelocation-model=static"],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
deps = [
- ":event_loop_runtime",
+ ":ping_lib_rs",
+ ":shm_event_loop_rs",
+ "//aos:aos_rs",
+ "@crate_index//:clap",
+ ],
+)
+
+rust_library(
+ name = "ping_lib_rs",
+ srcs = [
+ "ping_lib.rs",
+ ],
+ crate_name = "ping_lib",
+ deps = [
":ping_rust_fbs",
":pong_rust_fbs",
- ":shm_event_loop_rs",
- "//aos:configuration_rs",
- "//aos:configuration_rust_fbs",
- "//aos:flatbuffers_rs",
- "//aos:init_rs",
+ "//aos:aos_rs",
"@com_github_google_flatbuffers//rust",
"@crate_index//:futures",
+ "@crate_index//:log",
],
)
@@ -294,22 +294,27 @@
data = [
":pingpong_config",
],
- rustc_flags = ["-Crelocation-model=static"],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
deps = [
- ":event_loop_runtime",
+ ":pong_lib_rs",
+ ":shm_event_loop_rs",
+ "//aos:aos_rs",
+ "@crate_index//:clap",
+ ],
+)
+
+rust_library(
+ name = "pong_lib_rs",
+ srcs = [
+ "pong_lib.rs",
+ ],
+ crate_name = "pong_lib",
+ deps = [
":ping_rust_fbs",
":pong_rust_fbs",
- ":shm_event_loop_rs",
- "//aos:configuration_rs",
- "//aos:configuration_rust_fbs",
- "//aos:flatbuffers_rs",
- "//aos:init_rs",
+ "//aos:aos_rs",
"@com_github_google_flatbuffers//rust",
"@crate_index//:futures",
+ "@crate_index//:log",
],
)
@@ -599,6 +604,15 @@
"//aos:flatbuffers_rs",
"@crate_index//:futures",
],
+ test_data = [
+ ":multinode_pingpong_test_combined_config",
+ ],
+ test_deps = [
+ ":ping_rust_fbs",
+ "//aos:test_init_rs",
+ "@crate_index//:futures",
+ "@rules_rust//tools/runfiles",
+ ],
visibility = ["//visibility:public"],
deps = [
":event_loop_runtime",
@@ -607,27 +621,14 @@
],
)
-rust_test(
- name = "simulated_event_loop_rs_test",
- crate = ":simulated_event_loop_rs",
- data = [
- ":multinode_pingpong_test_combined_config",
- ],
- # TODO: Make Rust play happy with pic vs nopic. Details at:
- # https://github.com/bazelbuild/rules_rust/issues/118
- rustc_flags = ["-Crelocation-model=static"],
- deps = [
- ":ping_rust_fbs",
- "//aos:test_init_rs",
- "@crate_index//:futures",
- "@rules_rust//tools/runfiles",
- ],
-)
-
autocxx_library(
name = "shm_event_loop_rs",
srcs = ["shm_event_loop.rs"],
crate_name = "aos_events_shm_event_loop",
+ doctest_deps = [
+ ":ping_rust_fbs",
+ ":pong_rust_fbs",
+ ],
libs = [
":shm_event_loop",
":shm_event_loop_for_rust",
@@ -638,49 +639,17 @@
"//aos:configuration_rust_fbs",
"//aos:flatbuffers_rs",
],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
- visibility = ["//visibility:public"],
- deps = [
- ":event_loop_runtime",
- "//aos:configuration_rs",
- ],
-)
-
-rust_doc(
- name = "shm_event_loop_rs_doc",
- crate = ":shm_event_loop_rs",
- target_compatible_with = ["@platforms//cpu:x86_64"],
-)
-
-rust_test(
- name = "shm_event_loop_rs_test",
- crate = ":shm_event_loop_rs",
- data = [":pingpong_config"],
- # TODO: Make Rust play happy with pic vs nopic. Details at:
- # https://github.com/bazelbuild/rules_rust/issues/118
- rustc_flags = ["-Crelocation-model=static"],
- target_compatible_with = select({
- "//conditions:default": ["//tools/platforms/rust:has_support"],
- "//tools:has_msan": ["@platforms//:incompatible"],
- }),
- deps = [
+ test_data = [":pingpong_config"],
+ test_deps = [
":ping_rust_fbs",
"//aos:test_init_rs",
"@crate_index//:futures",
"@rules_rust//tools/runfiles",
],
-)
-
-rust_doc_test(
- name = "shm_event_loop_rs_doc_test",
- crate = ":shm_event_loop_rs",
- target_compatible_with = ["@platforms//cpu:x86_64"],
+ visibility = ["//visibility:public"],
deps = [
- ":ping_rust_fbs",
- ":pong_rust_fbs",
+ ":event_loop_runtime",
+ "//aos:configuration_rs",
],
)
diff --git a/aos/events/event_loop_runtime.cc b/aos/events/event_loop_runtime.cc
index e3d73c1..790fe9e 100644
--- a/aos/events/event_loop_runtime.cc
+++ b/aos/events/event_loop_runtime.cc
@@ -2,13 +2,15 @@
namespace aos {
-OnRunForRust::OnRunForRust(EventLoopRuntime *runtime) : runtime_(runtime) {
+OnRunForRust::OnRunForRust(const EventLoopRuntime *runtime)
+ : runtime_(runtime) {
++runtime->child_count_;
}
OnRunForRust::~OnRunForRust() { --runtime_->child_count_; }
bool OnRunForRust::is_running() const { return runtime_->is_running(); }
-std::unique_ptr<TimerForRust> TimerForRust::Make(EventLoopRuntime *runtime) {
+std::unique_ptr<TimerForRust> TimerForRust::Make(
+ const EventLoopRuntime *runtime) {
auto handler = std::unique_ptr<TimerForRust>(new TimerForRust());
TimerForRust *inner = handler.get();
handler->timer_ = runtime->event_loop()->AddTimer([inner, runtime] {
diff --git a/aos/events/event_loop_runtime.h b/aos/events/event_loop_runtime.h
index 7cc551f..0505852 100644
--- a/aos/events/event_loop_runtime.h
+++ b/aos/events/event_loop_runtime.h
@@ -131,18 +131,18 @@
class OnRunForRust {
public:
- OnRunForRust(EventLoopRuntime *runtime);
+ OnRunForRust(const EventLoopRuntime *runtime);
~OnRunForRust();
bool is_running() const;
private:
- EventLoopRuntime *const runtime_;
+ const EventLoopRuntime *const runtime_;
};
class TimerForRust {
public:
- static std::unique_ptr<TimerForRust> Make(EventLoopRuntime *runtime);
+ static std::unique_ptr<TimerForRust> Make(const EventLoopRuntime *runtime);
TimerForRust(const TimerForRust &) = delete;
TimerForRust(TimerForRust &&) = delete;
@@ -177,7 +177,16 @@
class EventLoopRuntime {
public:
- EventLoopRuntime(EventLoop *event_loop) : event_loop_(event_loop) {}
+ EventLoopRuntime(const EventLoop *event_loop)
+ // SAFETY: A &EventLoop in Rust becomes a const EventLoop*. While
+ // that's generally a reasonable convention, they are semantically
+ // different. In Rust, a &mut EventLoop is very restrictive as it enforces
+ // uniqueness of the reference. Additionally, a &EventLoop doesn't convey
+ // const-ness in the C++ sense. So to make the FFI boundary more
+ // ergonomic, we allow a &EventLoop passed from rust to be translated into
+ // an EventLoop* in C++. This is safe so long as EventLoop is !Sync and no
+ // &mut EventLoop references are constructed in Rust.
+ : event_loop_(const_cast<EventLoop *>(event_loop)) {}
~EventLoopRuntime() {
// Do this first, because it may hold child objects.
task_.reset();
@@ -185,9 +194,9 @@
<< ": Some child objects were not destroyed first";
}
- EventLoop *event_loop() { return event_loop_; }
+ EventLoop *event_loop() const { return event_loop_; }
- void Spawn(std::unique_ptr<ApplicationFuture> task) {
+ void Spawn(std::unique_ptr<ApplicationFuture> task) const {
CHECK(!task_) << ": May only call Spawn once";
task_ = std::move(task);
DoPoll();
@@ -219,30 +228,40 @@
rust::Str name() const { return StringViewToRustStr(event_loop_->name()); }
- WatcherForRust MakeWatcher(const Channel *channel) {
+ WatcherForRust MakeWatcher(const Channel *channel) const {
event_loop_->MakeRawNoArgWatcher(channel,
[this](const Context &) { DoPoll(); });
return WatcherForRust(event_loop_->MakeRawFetcher(channel));
}
- SenderForRust MakeSender(const Channel *channel) {
+ SenderForRust MakeSender(const Channel *channel) const {
return SenderForRust(event_loop_->MakeRawSender(channel));
}
- FetcherForRust MakeFetcher(const Channel *channel) {
+ FetcherForRust MakeFetcher(const Channel *channel) const {
return FetcherForRust(event_loop_->MakeRawFetcher(channel));
}
- OnRunForRust MakeOnRun() { return OnRunForRust(this); }
+ OnRunForRust MakeOnRun() const { return OnRunForRust(this); }
- std::unique_ptr<TimerForRust> AddTimer() { return TimerForRust::Make(this); }
+ std::unique_ptr<TimerForRust> AddTimer() const {
+ return TimerForRust::Make(this);
+ }
+
+ void SetRuntimeRealtimePriority(int priority) const {
+ event_loop_->SetRuntimeRealtimePriority(priority);
+ }
+
+ void SetRuntimeAffinity(const cpu_set_t &cpuset) const {
+ event_loop_->SetRuntimeAffinity(cpuset);
+ }
private:
friend class OnRunForRust;
friend class TimerForRust;
// Polls the top-level future once. This is what all the callbacks should do.
- void DoPoll() {
+ void DoPoll() const {
if (task_) {
CHECK(task_->Poll()) << ": Rust panic, aborting";
}
@@ -250,9 +269,20 @@
EventLoop *const event_loop_;
- std::unique_ptr<ApplicationFuture> task_;
+ // For Rust's EventLoopRuntime to be semantically equivelant to C++'s event
+ // loop, we need the ability to have shared references (&EventLoopRuntime) on
+ // the Rust side. Without that, the API would be overly restrictive to be
+ // usable. In order for the generated code to use &self references on methods,
+ // they need to be marked `const` on the C++ side. We use the `mutable`
+ // keyword to allow mutation through `const` methods.
+ //
+ // SAFETY:
+ // * The event loop runtime must be `!Sync` in the Rust side (default).
+ // * We can't expose exclusive references (&mut) to either of the mutable
+ // fields on the Rust side from a shared reference (&).
+ mutable std::unique_ptr<ApplicationFuture> task_;
- int child_count_ = 0;
+ mutable int child_count_ = 0;
};
} // namespace aos
diff --git a/aos/events/event_loop_runtime.rs b/aos/events/event_loop_runtime.rs
index 35a4225..18d743e 100644
--- a/aos/events/event_loop_runtime.rs
+++ b/aos/events/event_loop_runtime.rs
@@ -22,7 +22,7 @@
//! future".
//! 3. Multiple applications are better suited to multiple `EventLoopRuntime`s, on separate
//! `aos::EventLoop`s. Otherwise they can't send messages to each other, among other
-//! restrictions. https://github.com/frc971/971-Robot-Code/issues/12 covers creating an adapter
+//! restrictions. <https://github.com/frc971/971-Robot-Code/issues/12> covers creating an adapter
//! that provides multiple `EventLoop`s on top of a single underlying implementation.
//!
//! ## Design
@@ -31,7 +31,7 @@
//! considerations in arriving at this design include:
//! * `EventLoop` implementations alias the objects they're returning from C++, which means
//! creating Rust unique references to them is unsound. See
-//! https://github.com/google/autocxx/issues/1146 for details.
+//! <https://github.com/google/autocxx/issues/1146> for details.
//! * For various reasons autocxx can't directly wrap APIs using types ergonomic for C++. This and
//! the previous point mean we wrap all of the C++ objects specifically for this class.
//! * Rust's lifetimes are only flexible enough to track everything with a single big lifetime.
@@ -40,14 +40,13 @@
//! * We can't use [`futures::stream::Stream`] and all of its nice [`futures::stream::StreamExt`]
//! helpers for watchers because we need lifetime-generic `Item` types. Effectively we're making
//! a lending stream. This is very close to lending iterators, which is one of the motivating
-//! examples for generic associated types (https://github.com/rust-lang/rust/issues/44265).
+//! examples for generic associated types (<https://github.com/rust-lang/rust/issues/44265>).
use std::{
fmt,
future::Future,
marker::PhantomData,
- mem::ManuallyDrop,
- ops::Add,
+ ops::{Add, Deref, DerefMut},
panic::{catch_unwind, AssertUnwindSafe},
pin::Pin,
slice,
@@ -60,7 +59,9 @@
WithinBox,
};
use cxx::UniquePtr;
-use flatbuffers::{root_unchecked, Follow, FollowWith, FullyQualifiedName};
+use flatbuffers::{
+ root_unchecked, Allocator, FlatBufferBuilder, Follow, FollowWith, FullyQualifiedName,
+};
use futures::{future::pending, future::FusedFuture, never::Never};
use thiserror::Error;
use uuid::Uuid;
@@ -69,6 +70,7 @@
use aos_configuration::{ChannelLookupError, ConfigurationExt};
pub use aos_uuid::UUID;
+pub use ffi::aos::EventLoop as CppEventLoop;
pub use ffi::aos::EventLoopRuntime as CppEventLoopRuntime;
pub use ffi::aos::ExitHandle as CppExitHandle;
@@ -95,8 +97,6 @@
extern_cpp_type!("aos::UUID", crate::UUID)
);
-pub type EventLoop = ffi::aos::EventLoop;
-
/// A marker type which is invariant with respect to the given lifetime.
///
/// When interacting with functions that take and return things with a given lifetime, the lifetime
@@ -161,35 +161,29 @@
///
/// # Safety
///
-/// Objects implementing this trait *must* have mostly-exclusive (except for running it) ownership
-/// of the `aos::EventLoop` *for its entire lifetime*, which *must* be dropped when this object is.
-/// See [`EventLoopRuntime.new`]'s safety requirements for why this can be important and details of
-/// mostly-exclusive. In other words, nothing else may mutate it in any way except processing events
-/// (including dropping, because this object has to be the one to drop it).
+/// Objects implementing this trait must guarantee that the underlying event loop (as returned
+/// from [`EventLoopHolder::as_raw`]), must be valid for as long as this object is. One way to do
+/// this may be by managing ownership of the event loop with Rust's ownership semantics. However,
+/// this is not strictly necessary.
///
-/// This also implies semantics similar to `Pin<&mut ffi::aos::EventLoop>` for the underlying object.
-/// Implementations of this trait must have exclusive ownership of it, and the underlying object
-/// must not be moved.
+/// This also implies semantics similar to `Pin<&mut CppEventLoop>` for the underlying object.
+/// Implementations of this trait must guarantee that the underlying object must not be moved while
+/// this object exists.
pub unsafe trait EventLoopHolder {
- /// Converts this holder into a raw C++ pointer. This may be fed through other Rust and C++
- /// code, and eventually passed back to [`from_raw`].
- fn into_raw(self) -> *mut ffi::aos::EventLoop;
-
- /// Converts a raw C++ pointer back to a holder object.
+ /// Returns the raw C++ pointer of the underlying event loop.
///
- /// # Safety
- ///
- /// `raw` must be the result of [`into_raw`] on an instance of this same type. These raw
- /// pointers *are not* interchangeable between implementations of this trait.
- unsafe fn from_raw(raw: *mut ffi::aos::EventLoop) -> Self;
+ /// Caller can only assume this pointer is valid while `self` is still alive.
+ fn as_raw(&self) -> *const CppEventLoop;
}
/// Owns an [`EventLoopRuntime`] and its underlying `aos::EventLoop`, with safe management of the
/// associated Rust lifetimes.
-pub struct EventLoopRuntimeHolder<T: EventLoopHolder>(
- ManuallyDrop<Pin<Box<CppEventLoopRuntime>>>,
- PhantomData<T>,
-);
+pub struct EventLoopRuntimeHolder<T: EventLoopHolder> {
+ // NOTE: `runtime` must get dropped first, so we declare it before the event_loop:
+ // https://doc.rust-lang.org/reference/destructors.html
+ _runtime: Pin<Box<CppEventLoopRuntime>>,
+ _event_loop: T,
+}
impl<T: EventLoopHolder> EventLoopRuntimeHolder<T> {
/// Creates a new [`EventLoopRuntime`] and runs an initialization function on it. This is a
@@ -235,59 +229,41 @@
/// ```
pub fn new<F>(event_loop: T, fun: F) -> Self
where
- F: for<'event_loop> FnOnce(&mut EventLoopRuntime<'event_loop>),
+ F: for<'event_loop> FnOnce(EventLoopRuntime<'event_loop>),
{
- // SAFETY: The EventLoopRuntime never escapes this function, which means the only code that
- // observes its lifetime is `fun`. `fun` must be generic across any value of its
- // `'event_loop` lifetime parameter, which means we can choose any lifetime here, which
- // satisfies the safety requirements.
- //
- // This is a similar pattern as `std::thread::scope`, `ghost-cell`, etc. Note that unlike
- // `std::thread::scope`, our inner functions (the async ones) are definitely not allowed to
- // capture things from the calling scope of this function, so there's no `'env` equivalent.
- // `ghost-cell` ends up looking very similar despite doing different things with the
- // pattern, while `std::thread::scope` has a lot of additional complexity to achieve a
- // similar result.
- //
- // `EventLoopHolder`s safety requirements prevent anybody else from touching the underlying
- // `aos::EventLoop`.
- let mut runtime = unsafe { EventLoopRuntime::new(event_loop.into_raw()) };
- fun(&mut runtime);
- Self(ManuallyDrop::new(runtime.into_cpp()), PhantomData)
+ // SAFETY: The event loop pointer produced by as_raw must be valid and it will get dropped
+ // first (see https://doc.rust-lang.org/reference/destructors.html)
+ let runtime = unsafe { CppEventLoopRuntime::new(event_loop.as_raw()).within_box() };
+ EventLoopRuntime::with(&runtime, fun);
+ Self {
+ _runtime: runtime,
+ _event_loop: event_loop,
+ }
}
}
-impl<T: EventLoopHolder> Drop for EventLoopRuntimeHolder<T> {
- fn drop(&mut self) {
- let event_loop = self.0.as_mut().event_loop();
- // SAFETY: We're not going to touch this field again. The underlying EventLoop will not be
- // run again because we're going to drop it next.
- unsafe { ManuallyDrop::drop(&mut self.0) };
- // SAFETY: We took this from `into_raw`, and we just dropped the runtime which may contain
- // Rust references to it.
- unsafe { drop(T::from_raw(event_loop)) };
- }
-}
-
+/// Manages the Rust interface to a *single* `aos::EventLoop`.
+///
+/// This is intended to be used by a single application.
+#[derive(Copy, Clone)]
pub struct EventLoopRuntime<'event_loop>(
- Pin<Box<ffi::aos::EventLoopRuntime>>,
+ &'event_loop CppEventLoopRuntime,
// See documentation of [`new`] for details.
InvariantLifetime<'event_loop>,
);
-/// Manages the Rust interface to a *single* `aos::EventLoop`. This is intended to be used by a
-/// single application.
impl<'event_loop> EventLoopRuntime<'event_loop> {
- /// Creates a new runtime. This must be the only user of the underlying `aos::EventLoop`.
+ /// Creates a new runtime for the underlying event loop.
///
/// Consider using [`EventLoopRuntimeHolder.new`] instead, if you're working with an
- /// `aos::EventLoop` owned (indirectly) by Rust code.
+ /// `aos::EventLoop` owned (indirectly) by Rust code or using [`EventLoopRuntime::with`] as a safe
+ /// alternative.
///
- /// One common pattern is calling this in the constructor of an object whose lifetime is managed
- /// by C++; C++ doesn't inherit the Rust lifetime but we do have a lot of C++ code that obeys
- /// these rules implicitly.
+ /// One common pattern is wrapping the lifetime behind a higher-rank trait bound (such as
+ /// [`FnOnce`]). This would constraint the lifetime to `'static` and objects with `'event_loop`
+ /// returned by this runtime.
///
- /// Call [`spawn`] to respond to events. The non-event-driven APIs may be used without calling
+ /// Call [`EventLoopRuntime::spawn`] to respond to events. The non-event-driven APIs may be used without calling
/// this.
///
/// This is an async runtime, but it's a somewhat unusual one. See the module-level
@@ -299,14 +275,11 @@
/// together. It all boils down to choosing `'event_loop` correctly, which is very complicated.
/// Here are the rules:
///
- /// 1. The `aos::EventLoop` APIs, and any other consumer-facing APIs, of the underlying
- /// `aos::EventLoop` *must* be exclusively used by this object, and things it calls, for
- /// `'event_loop`.
- /// 2. `'event_loop` extends until after the last time the underlying `aos::EventLoop` is run.
- /// This is often beyond the lifetime of this Rust `EventLoopRuntime` object.
- /// 3. `'event_loop` must outlive this object, because this object stores references to the
+ /// 1. `'event_loop` extends until after the last time the underlying `aos::EventLoop` is run.
+ /// **This is often beyond the lifetime of this Rust `EventLoopRuntime` object**.
+ /// 2. `'event_loop` must outlive this object, because this object stores references to the
/// underlying `aos::EventLoop`.
- /// 4. Any other references stored in the underlying `aos::EventLoop` must be valid for
+ /// 3. Any other references stored in the underlying `aos::EventLoop` must be valid for
/// `'event_loop`. The easiest way to ensure this is by not using the `aos::EventLoop` before
/// passing it to this object.
///
@@ -314,13 +287,15 @@
///
/// 1. The underlying `aos::EventLoop` must be dropped after this object.
/// 2. This object will store various references valid for `'event_loop` with a duration of
- /// `'event_loop`, which is safe as long as they're both the same `'event_loop`. Note that
- /// this requires this type to be invariant with respect to `'event_loop`.
- /// 3. `event_loop` (the pointer being passed in) is effectively `Pin`, which is also implied
- /// by the underlying `aos::EventLoop` C++ type.
- /// 4. You cannot create multiple `EventLoopRuntime`s from the same underlying `aos::EventLoop`
- /// or otherwise use it from a different application. The first one may create
- /// mutable Rust references while the second one expects exclusive ownership, for example.
+ /// `'event_loop`, which is safe as long as
+ ///
+ /// * `'event_loop` outlives the underlying event loop, and
+ /// * `'event_loop` references are not used once the event loop is destroyed
+ ///
+ /// Note that this requires this type to be invariant with respect to `'event_loop`. This can
+ /// be achieved by using [`EventLoopRuntime::with`] since `'event_loop` referenes can't leave
+ /// `fun` and the runtime holding `'event_loop` references will be destroyed before the event
+ /// loop.
///
/// `aos::EventLoop`'s public API is exclusively for consumers of the event loop. Some
/// subclasses extend this API. Additionally, all useful implementations of `aos::EventLoop`
@@ -330,18 +305,6 @@
/// loop functions independently of the consuming functions in every way except lifetime of the
/// `aos::EventLoop`, and may be used independently of `'event_loop`.
///
- /// ## Discussion of the rules
- ///
- /// Rule 1 is similar to rule 3 (they're both similar to mutable borrowing), but rule 1 extends
- /// for the entire lifetime of the object instead of being limited to the lifetime of an
- /// individual borrow by an instance of this type. This is similar to the way [`Pin`]'s
- /// estrictions extend for the entire lifetime of the object, until it is dropped.
- ///
- /// Rule 2 and corollaries 2 and 3 go together, and are essential for making [`spawn`]ed tasks
- /// useful. The `aos::EventLoop` is full of indirect circular references, both within itself
- /// and via all of the callbacks. This is sound if all of these references have the *exact
- /// same* Rust lifetime, which is `'event_loop`.
- ///
/// ## Alternatives and why they don't work
///
/// Making the argument `Pin<&'event_loop mut EventLoop>` would express some (but not all) of
@@ -350,7 +313,7 @@
/// same object from C++, which is a common operation. See the module-level documentation for
/// details.
///
- /// [`spawn`]ed tasks need to hold `&'event_loop` references to things like channels. Using a
+ /// spawned tasks need to hold `&'event_loop` references to things like channels. Using a
/// separate `'config` lifetime wouldn't change much; the tasks still need to do things which
/// require them to not outlive something they don't control. This is fundamental to
/// self-referential objects, which `aos::EventLoop` is based around, but Rust requires unsafe
@@ -358,49 +321,39 @@
///
/// ## Final cautions
///
- /// Following these rules is very tricky. Be very cautious calling this function. It exposes an
- /// unbound lifetime, which means you should wrap it directly in a function that attaches a
- /// correct lifetime.
- pub unsafe fn new(event_loop: *mut ffi::aos::EventLoop) -> Self {
- Self(
- // SAFETY: We push all the validity requirements for this up to our caller.
- unsafe { ffi::aos::EventLoopRuntime::new(event_loop) }.within_box(),
- InvariantLifetime::default(),
- )
+ /// Following these rules is very tricky. Be very cautious calling this function. The
+ /// exposed lifetime doesn't actually convey all the rules to the compiler. To the compiler,
+ /// `'event_loop` ends when this object is dropped which is not the case!
+ pub unsafe fn new(event_loop: &'event_loop CppEventLoopRuntime) -> Self {
+ Self(event_loop, InvariantLifetime::default())
}
- /// Creates a Rust wrapper from the underlying C++ object, with an unbound lifetime.
+ /// Safely builds a "constrained" EventLoopRuntime with `fun`.
///
- /// This may never be useful, but it's here for this big scary comment to explain why it's not
- /// useful.
- ///
- /// # Safety
- ///
- /// See [`new`] for safety restrictions on `'event_loop` when calling this. In particular, see
- /// the note about how tricky doing this correctly is, and remember that for this function the
- /// event loop in question isn't even an argument to this function so it's even trickier. Also
- /// note that you cannot call this on the result of [`into_cpp`] without violating those
- /// restrictions.
- pub unsafe fn from_cpp(cpp: Pin<Box<ffi::aos::EventLoopRuntime>>) -> Self {
- Self(cpp, InvariantLifetime::default())
- }
-
- /// Extracts the underlying C++ object, without the corresponding Rust lifetime. This is useful
- /// to stop the propagation of Rust lifetimes without destroying the underlying object which
- /// contains all the state.
- ///
- /// Note that you *cannot* call [`from_cpp`] on the result of this, because that will violate
- /// [`from_cpp`]'s safety requirements.
- pub fn into_cpp(self) -> Pin<Box<ffi::aos::EventLoopRuntime>> {
- self.0
+ /// We constrain the scope of the `[EventLoopRuntime]` by tying it to **any** `'a` lifetime. The
+ /// idea is that the only things that satisfy this lifetime are either ``static` or produced by
+ /// the event loop itself with a '`event_loop` runtime.
+ pub fn with<F>(event_loop: &'event_loop CppEventLoopRuntime, fun: F)
+ where
+ F: for<'a> FnOnce(EventLoopRuntime<'a>),
+ {
+ // SAFETY: We satisfy the event loop lifetime constraint by scoping it inside of a higher-
+ // rank lifetime in FnOnce. This is similar to what is done in std::thread::scope, and the
+ // point is that `fun` can only assume that `'static` and types produced by this type with a
+ // 'event_loop lifetime are the only lifetimes that will satisfy `'a`. This is possible due
+ // to this type's invariance over its lifetime, otherwise, one could easily make a Subtype
+ // that, due to its shorter lifetime, would include things from its outer scope.
+ unsafe {
+ fun(Self::new(event_loop));
+ }
}
/// Returns the pointer passed into the constructor.
///
/// The returned value should only be used for destroying it (_after_ `self` is dropped) or
/// calling other C++ APIs.
- pub fn raw_event_loop(&mut self) -> *mut ffi::aos::EventLoop {
- self.0.as_mut().event_loop()
+ pub fn raw_event_loop(&self) -> *mut CppEventLoop {
+ self.0.event_loop()
}
/// Returns a reference to the name of this EventLoop.
@@ -443,7 +396,7 @@
/// will never complete. `task` will not be polled after the underlying `aos::EventLoop` exits.
///
/// Note that task will be polled immediately, to give it a chance to initialize. If you want to
- /// defer work until the event loop starts running, await [`on_run`] in the task.
+ /// defer work until the event loop starts running, await [`EventLoopRuntime::on_run`] in the task.
///
/// # Panics
///
@@ -563,8 +516,8 @@
/// }});
/// # }
/// ```
- pub fn spawn(&mut self, task: impl Future<Output = Never> + 'event_loop) {
- self.0.as_mut().Spawn(RustApplicationFuture::new(task));
+ pub fn spawn(&self, task: impl Future<Output = Never> + 'event_loop) {
+ self.0.Spawn(RustApplicationFuture::new(task));
}
pub fn configuration(&self) -> &'event_loop Configuration {
@@ -590,10 +543,10 @@
/// # Panics
///
/// Dropping `self` before the returned object is dropped will panic.
- pub fn make_raw_watcher(&mut self, channel: &'event_loop Channel) -> RawWatcher {
+ pub fn make_raw_watcher(&self, channel: &'event_loop Channel) -> RawWatcher {
// SAFETY: `channel` is valid for the necessary lifetime, all other requirements fall under
// the usual autocxx heuristics.
- RawWatcher(unsafe { self.0.as_mut().MakeWatcher(channel) }.within_box())
+ RawWatcher(unsafe { self.0.MakeWatcher(channel) }.within_box())
}
/// Provides type-safe async blocking access to messages on a channel. `T` should be a
@@ -603,7 +556,7 @@
/// # Panics
///
/// Dropping `self` before the returned object is dropped will panic.
- pub fn make_watcher<T>(&mut self, channel_name: &str) -> Result<Watcher<T>, ChannelLookupError>
+ pub fn make_watcher<T>(&self, channel_name: &str) -> Result<Watcher<T>, ChannelLookupError>
where
for<'a> T: FollowWith<'a>,
for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
@@ -619,10 +572,10 @@
/// # Panics
///
/// Dropping `self` before the returned object is dropped will panic.
- pub fn make_raw_sender(&mut self, channel: &'event_loop Channel) -> RawSender {
+ pub fn make_raw_sender(&self, channel: &'event_loop Channel) -> RawSender {
// SAFETY: `channel` is valid for the necessary lifetime, all other requirements fall under
// the usual autocxx heuristics.
- RawSender(unsafe { self.0.as_mut().MakeSender(channel) }.within_box())
+ RawSender(unsafe { self.0.MakeSender(channel) }.within_box())
}
/// Allows sending messages on a channel with a type-safe API.
@@ -630,7 +583,7 @@
/// # Panics
///
/// Dropping `self` before the returned object is dropped will panic.
- pub fn make_sender<T>(&mut self, channel_name: &str) -> Result<Sender<T>, ChannelLookupError>
+ pub fn make_sender<T>(&self, channel_name: &str) -> Result<Sender<T>, ChannelLookupError>
where
for<'a> T: FollowWith<'a>,
for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
@@ -646,10 +599,10 @@
/// # Panics
///
/// Dropping `self` before the returned object is dropped will panic.
- pub fn make_raw_fetcher(&mut self, channel: &'event_loop Channel) -> RawFetcher {
+ pub fn make_raw_fetcher(&self, channel: &'event_loop Channel) -> RawFetcher {
// SAFETY: `channel` is valid for the necessary lifetime, all other requirements fall under
// the usual autocxx heuristics.
- RawFetcher(unsafe { self.0.as_mut().MakeFetcher(channel) }.within_box())
+ RawFetcher(unsafe { self.0.MakeFetcher(channel) }.within_box())
}
/// Provides type-safe access to messages on a channel, without the ability to wait for a new
@@ -659,7 +612,7 @@
/// # Panics
///
/// Dropping `self` before the returned object is dropped will panic.
- pub fn make_fetcher<T>(&mut self, channel_name: &str) -> Result<Fetcher<T>, ChannelLookupError>
+ pub fn make_fetcher<T>(&self, channel_name: &str) -> Result<Fetcher<T>, ChannelLookupError>
where
for<'a> T: FollowWith<'a>,
for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
@@ -676,8 +629,8 @@
/// subsequent code will have any realtime scheduling applied. This means it can rely on
/// consistent timing, but it can no longer create any EventLoop child objects or do anything
/// else non-realtime.
- pub fn on_run(&mut self) -> OnRun {
- OnRun(self.0.as_mut().MakeOnRun().within_box())
+ pub fn on_run(&self) -> OnRun {
+ OnRun(self.0.MakeOnRun().within_box())
}
pub fn is_running(&self) -> bool {
@@ -685,16 +638,21 @@
}
/// Returns an unarmed timer.
- pub fn add_timer(&mut self) -> Timer {
- Timer(self.0.as_mut().AddTimer())
+ pub fn add_timer(&self) -> Timer {
+ Timer(self.0.AddTimer())
}
/// Returns a timer that goes off every `duration`-long ticks.
- pub fn add_interval(&mut self, duration: Duration) -> Timer {
+ pub fn add_interval(&self, duration: Duration) -> Timer {
let mut timer = self.add_timer();
timer.setup(self.monotonic_now(), Some(duration));
timer
}
+
+ /// Sets the scheduler priority to run the event loop at.
+ pub fn set_realtime_priority(&self, priority: i32) {
+ self.0.SetRuntimeRealtimePriority(priority.into());
+ }
}
/// An event loop primitive that allows sleeping asynchronously.
@@ -817,7 +775,7 @@
///
/// We also run into some limitations in the borrow checker trying to implement `poll`, I think it's
/// the same one mentioned here:
-/// https://blog.rust-lang.org/2022/08/05/nll-by-default.html#looking-forward-what-can-we-expect-for-the-borrow-checker-of-the-future
+/// <https://blog.rust-lang.org/2022/08/05/nll-by-default.html#looking-forward-what-can-we-expect-for-the-borrow-checker-of-the-future>
/// We get around that one by moving the unbounded lifetime from the pointer dereference into the
/// function with the if statement.
// SAFETY: If this outlives the parent EventLoop, the C++ code will LOG(FATAL).
@@ -1160,11 +1118,6 @@
pub struct RawSender(Pin<Box<ffi::aos::SenderForRust>>);
impl RawSender {
- fn buffer(&mut self) -> &mut [u8] {
- // SAFETY: This is a valid slice, and `u8` doesn't have any alignment requirements.
- unsafe { slice::from_raw_parts_mut(self.0.as_mut().data(), self.0.as_mut().size()) }
- }
-
/// Returns an object which can be used to build a message.
///
/// # Examples
@@ -1187,6 +1140,7 @@
/// # unsafe {
/// let mut builder1 = sender.make_builder();
/// builder1.fbb();
+ /// drop(builder1);
/// let mut builder2 = sender.make_builder();
/// let pong = PongBuilder::new(builder2.fbb()).finish();
/// builder2.send(pong);
@@ -1206,11 +1160,14 @@
/// # }
/// ```
pub fn make_builder(&mut self) -> RawBuilder {
- // TODO(Brian): Actually use the provided buffer instead of just using its
- // size to allocate a separate one.
- //
- // See https://github.com/google/flatbuffers/issues/7385.
- let fbb = flatbuffers::FlatBufferBuilder::with_capacity(self.buffer().len());
+ // SAFETY: This is a valid slice, and `u8` doesn't have any alignment
+ // requirements. Additionally, the lifetime of the builder is tied to
+ // the lifetime of self so the buffer won't be accessible again until
+ // the builder is destroyed.
+ let allocator = ChannelPreallocatedAllocator::new(unsafe {
+ slice::from_raw_parts_mut(self.0.as_mut().data(), self.0.as_mut().size())
+ });
+ let fbb = FlatBufferBuilder::new_in(allocator);
RawBuilder {
raw_sender: self,
fbb,
@@ -1221,11 +1178,13 @@
/// Used for building a message. See [`RawSender::make_builder`] for details.
pub struct RawBuilder<'sender> {
raw_sender: &'sender mut RawSender,
- fbb: flatbuffers::FlatBufferBuilder<'sender>,
+ fbb: FlatBufferBuilder<'sender, ChannelPreallocatedAllocator<'sender>>,
}
impl<'sender> RawBuilder<'sender> {
- pub fn fbb(&mut self) -> &mut flatbuffers::FlatBufferBuilder<'sender> {
+ pub fn fbb(
+ &mut self,
+ ) -> &mut FlatBufferBuilder<'sender, ChannelPreallocatedAllocator<'sender>> {
&mut self.fbb
}
@@ -1238,12 +1197,7 @@
use ffi::aos::RawSender_Error as FfiError;
// SAFETY: This is a valid buffer we're passing.
- match unsafe {
- self.raw_sender
- .0
- .as_mut()
- .CopyAndSend(data.as_ptr(), data.len())
- } {
+ match self.raw_sender.0.as_mut().SendBuffer(data.len()) {
FfiError::kOk => Ok(()),
FfiError::kMessagesSentTooFast => Err(SendError::MessagesSentTooFast),
FfiError::kInvalidRedzone => Err(SendError::InvalidRedzone),
@@ -1287,6 +1241,7 @@
/// # fn compile_check(mut sender: aos_events_event_loop_runtime::Sender<Pong<'static>>) {
/// let mut builder1 = sender.make_builder();
/// builder1.fbb();
+ /// drop(builder1);
/// let mut builder2 = sender.make_builder();
/// let pong = PongBuilder::new(builder2.fbb()).finish();
/// builder2.send(pong);
@@ -1322,7 +1277,9 @@
for<'a> T: FollowWith<'a>,
for<'a> <T as FollowWith<'a>>::Inner: Follow<'a>,
{
- pub fn fbb(&mut self) -> &mut flatbuffers::FlatBufferBuilder<'sender> {
+ pub fn fbb(
+ &mut self,
+ ) -> &mut FlatBufferBuilder<'sender, ChannelPreallocatedAllocator<'sender>> {
self.0.fbb()
}
@@ -1415,7 +1372,7 @@
#[repr(transparent)]
pub struct OnRun(Pin<Box<ffi::aos::OnRunForRust>>);
-impl Future for OnRun {
+impl Future for &'_ OnRun {
type Output = ();
fn poll(self: Pin<&mut Self>, _: &mut std::task::Context) -> Poll<()> {
@@ -1461,6 +1418,12 @@
}
}
+impl From<MonotonicInstant> for i64 {
+ fn from(value: MonotonicInstant) -> Self {
+ value.0
+ }
+}
+
impl fmt::Debug for MonotonicInstant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.duration_since_epoch().fmt(f)
@@ -1489,6 +1452,12 @@
}
}
+impl From<RealtimeInstant> for i64 {
+ fn from(value: RealtimeInstant) -> Self {
+ value.0
+ }
+}
+
impl fmt::Debug for RealtimeInstant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.duration_since_epoch().fmt(f)
@@ -1527,8 +1496,8 @@
impl ExitHandle {
/// Exits the EventLoops represented by this handle. You probably want to immediately return
- /// from the context this is called in. Awaiting [`exit`] instead of using this function is an
- /// easy way to do that.
+ /// from the context this is called in. Awaiting [`ExitHandle::exit`] instead of using this
+ /// function is an easy way to do that.
pub fn exit_sync(mut self) {
self.0.as_mut().unwrap().Exit();
}
@@ -1547,3 +1516,44 @@
Self(inner)
}
}
+
+pub struct ChannelPreallocatedAllocator<'a> {
+ buffer: &'a mut [u8],
+}
+
+impl<'a> ChannelPreallocatedAllocator<'a> {
+ pub fn new(buffer: &'a mut [u8]) -> Self {
+ Self { buffer }
+ }
+}
+
+#[derive(Debug, Error)]
+#[error("Can't allocate more memory with a fixed size allocator")]
+pub struct OutOfMemory;
+
+// SAFETY: Allocator follows the required behavior.
+unsafe impl Allocator for ChannelPreallocatedAllocator<'_> {
+ type Error = OutOfMemory;
+ fn grow_downwards(&mut self) -> Result<(), Self::Error> {
+ // Fixed size allocator can't grow.
+ Err(OutOfMemory)
+ }
+
+ fn len(&self) -> usize {
+ self.buffer.len()
+ }
+}
+
+impl Deref for ChannelPreallocatedAllocator<'_> {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ self.buffer
+ }
+}
+
+impl DerefMut for ChannelPreallocatedAllocator<'_> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.buffer
+ }
+}
diff --git a/aos/events/event_loop_runtime_test_lib.rs b/aos/events/event_loop_runtime_test_lib.rs
index 56dc9ef..acc162d 100644
--- a/aos/events/event_loop_runtime_test_lib.rs
+++ b/aos/events/event_loop_runtime_test_lib.rs
@@ -1,14 +1,26 @@
//! These test helpers have to live in a separate file because autocxx only generates one set of
//! outputs per file, and that needs to be the non-`#[cfg(test)]` stuff.
-use aos_events_event_loop_runtime::{EventLoop, EventLoopRuntime, Fetcher, RawFetcher};
+use aos_events_event_loop_runtime::{CppEventLoop as EventLoop, Fetcher, RawFetcher};
use ping_rust_fbs::aos::examples::{root_as_ping, Ping};
use pong_rust_fbs::aos::examples::{Pong, PongBuilder};
mod tests {
+ use aos_events_event_loop_runtime::{EventLoopHolder, EventLoopRuntimeHolder};
+
use super::*;
- use std::cell::RefCell;
+ use std::{borrow::Borrow, cell::RefCell};
+
+ /// Represents a holder of the event loop that is managed in C++.
+ struct CppEventLoopHolder(*const EventLoop);
+
+ // SAFETY: We defer the requirement that the event loop is valid and won't move to C++.
+ unsafe impl EventLoopHolder for CppEventLoopHolder {
+ fn as_raw(&self) -> *const EventLoop {
+ self.0
+ }
+ }
#[derive(Debug, Default)]
struct GlobalState {
@@ -39,62 +51,65 @@
GLOBAL_STATE.with(|g| g.borrow().on_run_count)
}
- pub struct TestApplication<'event_loop> {
- _runtime: EventLoopRuntime<'event_loop>,
+ pub struct TestApplication {
+ _runtime: EventLoopRuntimeHolder<CppEventLoopHolder>,
raw_ping_fetcher: RawFetcher,
}
- impl<'event_loop> TestApplication<'event_loop> {
- fn new(mut runtime: EventLoopRuntime<'event_loop>) -> Self {
- let ping_channel = runtime
- .get_raw_channel("/test", "aos.examples.Ping")
- .expect("Should have Ping channel");
- let mut raw_ping_watcher = runtime.make_raw_watcher(ping_channel);
- let mut raw_pong_sender = runtime.make_raw_sender(
- runtime
- .get_raw_channel("/test", "aos.examples.Pong")
- .expect("Should have Pong channel"),
- );
- let on_run = runtime.on_run();
- runtime.spawn(async move {
- on_run.await;
- GLOBAL_STATE.with(|g| {
- let g = &mut *g.borrow_mut();
- assert_eq!(g.creation_count, g.drop_count + 1);
- assert_eq!(g.drop_count, g.on_run_count);
- assert_eq!(g.drop_count, g.before_count);
- assert_eq!(g.drop_count, g.watcher_count);
- assert_eq!(g.drop_count, g.after_count);
- g.on_run_count += 1;
- });
- loop {
- let context = raw_ping_watcher.next().await;
- assert!(!context.monotonic_event_time().is_min_time());
- assert!(!context.data().is_none());
+ impl TestApplication {
+ fn new(event_loop: CppEventLoopHolder) -> Self {
+ let mut raw_ping_fetcher = None;
+ let runtime = EventLoopRuntimeHolder::new(event_loop, |runtime| {
+ let ping_channel = runtime
+ .get_raw_channel("/test", "aos.examples.Ping")
+ .expect("Should have Ping channel");
+ let mut raw_ping_watcher = runtime.make_raw_watcher(ping_channel);
+ let mut raw_pong_sender = runtime.make_raw_sender(
+ runtime
+ .get_raw_channel("/test", "aos.examples.Pong")
+ .expect("Should have Pong channel"),
+ );
+ let on_run = runtime.on_run();
+ runtime.spawn(async move {
+ on_run.borrow().await;
GLOBAL_STATE.with(|g| {
let g = &mut *g.borrow_mut();
assert_eq!(g.creation_count, g.drop_count + 1);
- assert_eq!(g.creation_count, g.on_run_count);
- assert_eq!(g.creation_count, g.before_count);
+ assert_eq!(g.drop_count, g.on_run_count);
+ assert_eq!(g.drop_count, g.before_count);
assert_eq!(g.drop_count, g.watcher_count);
assert_eq!(g.drop_count, g.after_count);
- g.watcher_count += 1;
+ g.on_run_count += 1;
});
- let ping = root_as_ping(context.data().expect("should have the data"))
- .expect("Ping should be valid");
+ loop {
+ let context = raw_ping_watcher.next().await;
+ assert!(!context.monotonic_event_time().is_min_time());
+ assert!(!context.data().is_none());
+ GLOBAL_STATE.with(|g| {
+ let g = &mut *g.borrow_mut();
+ assert_eq!(g.creation_count, g.drop_count + 1);
+ assert_eq!(g.creation_count, g.on_run_count);
+ assert_eq!(g.creation_count, g.before_count);
+ assert_eq!(g.drop_count, g.watcher_count);
+ assert_eq!(g.drop_count, g.after_count);
+ g.watcher_count += 1;
+ });
+ let ping = root_as_ping(context.data().expect("should have the data"))
+ .expect("Ping should be valid");
- let mut builder = raw_pong_sender.make_builder();
- let mut pong = PongBuilder::new(builder.fbb());
- pong.add_value(ping.value());
- let pong = pong.finish();
- // SAFETY: We're sending the correct type here.
- unsafe { builder.send(pong) }.expect("send should succeed");
- }
+ let mut builder = raw_pong_sender.make_builder();
+ let mut pong = PongBuilder::new(builder.fbb());
+ pong.add_value(ping.value());
+ let pong = pong.finish();
+ // SAFETY: We're sending the correct type here.
+ unsafe { builder.send(pong) }.expect("send should succeed");
+ }
+ });
+ raw_ping_fetcher = Some(runtime.make_raw_fetcher(ping_channel));
});
- let raw_ping_fetcher = runtime.make_raw_fetcher(ping_channel);
Self {
_runtime: runtime,
- raw_ping_fetcher,
+ raw_ping_fetcher: raw_ping_fetcher.unwrap(),
}
}
@@ -137,7 +152,7 @@
}
}
- impl Drop for TestApplication<'_> {
+ impl Drop for TestApplication {
fn drop(&mut self) {
GLOBAL_STATE.with(|g| {
let g = &mut *g.borrow_mut();
@@ -151,61 +166,64 @@
}
}
- unsafe fn make_test_application(event_loop: *mut EventLoop) -> Box<TestApplication<'static>> {
+ unsafe fn make_test_application(event_loop: *mut EventLoop) -> Box<TestApplication> {
GLOBAL_STATE.with(|g| {
let g = &mut *g.borrow_mut();
g.creation_count += 1;
});
- Box::new(TestApplication::new(EventLoopRuntime::new(event_loop)))
+ Box::new(TestApplication::new(CppEventLoopHolder(event_loop)))
}
- pub struct TypedTestApplication<'event_loop> {
- _runtime: EventLoopRuntime<'event_loop>,
+ pub struct TypedTestApplication {
+ _runtime: EventLoopRuntimeHolder<CppEventLoopHolder>,
ping_fetcher: Fetcher<Ping<'static>>,
}
- impl<'event_loop> TypedTestApplication<'event_loop> {
- fn new(mut runtime: EventLoopRuntime<'event_loop>) -> Self {
- let mut ping_watcher = runtime.make_watcher::<Ping<'static>>("/test").unwrap();
- let mut pong_sender = runtime.make_sender::<Pong<'static>>("/test").unwrap();
- let on_run = runtime.on_run();
- runtime.spawn(async move {
- on_run.await;
- GLOBAL_STATE.with(|g| {
- let g = &mut *g.borrow_mut();
- assert_eq!(g.creation_count, g.drop_count + 1);
- assert_eq!(g.drop_count, g.on_run_count);
- assert_eq!(g.drop_count, g.before_count);
- assert_eq!(g.drop_count, g.watcher_count);
- assert_eq!(g.drop_count, g.after_count);
- g.on_run_count += 1;
- });
- loop {
- let context = ping_watcher.next().await;
- assert!(!context.monotonic_event_time().is_min_time());
- assert!(!context.message().is_none());
+ impl TypedTestApplication {
+ fn new(event_loop: CppEventLoopHolder) -> Self {
+ let mut ping_fetcher = None;
+ let runtime = EventLoopRuntimeHolder::new(event_loop, |runtime| {
+ let mut ping_watcher = runtime.make_watcher::<Ping<'static>>("/test").unwrap();
+ let mut pong_sender = runtime.make_sender::<Pong<'static>>("/test").unwrap();
+ let on_run = runtime.on_run();
+ runtime.spawn(async move {
+ on_run.borrow().await;
GLOBAL_STATE.with(|g| {
let g = &mut *g.borrow_mut();
assert_eq!(g.creation_count, g.drop_count + 1);
- assert_eq!(g.creation_count, g.on_run_count);
- assert_eq!(g.creation_count, g.before_count);
+ assert_eq!(g.drop_count, g.on_run_count);
+ assert_eq!(g.drop_count, g.before_count);
assert_eq!(g.drop_count, g.watcher_count);
assert_eq!(g.drop_count, g.after_count);
- g.watcher_count += 1;
+ g.on_run_count += 1;
});
- let ping: Ping<'_> = context.message().unwrap();
+ loop {
+ let context = ping_watcher.next().await;
+ assert!(!context.monotonic_event_time().is_min_time());
+ assert!(!context.message().is_none());
+ GLOBAL_STATE.with(|g| {
+ let g = &mut *g.borrow_mut();
+ assert_eq!(g.creation_count, g.drop_count + 1);
+ assert_eq!(g.creation_count, g.on_run_count);
+ assert_eq!(g.creation_count, g.before_count);
+ assert_eq!(g.drop_count, g.watcher_count);
+ assert_eq!(g.drop_count, g.after_count);
+ g.watcher_count += 1;
+ });
+ let ping: Ping<'_> = context.message().unwrap();
- let mut builder = pong_sender.make_builder();
- let mut pong = PongBuilder::new(builder.fbb());
- pong.add_value(ping.value());
- let pong = pong.finish();
- builder.send(pong).expect("send should succeed");
- }
+ let mut builder = pong_sender.make_builder();
+ let mut pong = PongBuilder::new(builder.fbb());
+ pong.add_value(ping.value());
+ let pong = pong.finish();
+ builder.send(pong).expect("send should succeed");
+ }
+ });
+ ping_fetcher = Some(runtime.make_fetcher("/test").unwrap());
});
- let ping_fetcher = runtime.make_fetcher("/test").unwrap();
Self {
_runtime: runtime,
- ping_fetcher,
+ ping_fetcher: ping_fetcher.unwrap(),
}
}
@@ -245,7 +263,7 @@
}
}
- impl Drop for TypedTestApplication<'_> {
+ impl Drop for TypedTestApplication {
fn drop(&mut self) {
GLOBAL_STATE.with(|g| {
let g = &mut *g.borrow_mut();
@@ -259,44 +277,46 @@
}
}
- unsafe fn make_typed_test_application(
- event_loop: *mut EventLoop,
- ) -> Box<TypedTestApplication<'static>> {
+ unsafe fn make_typed_test_application(event_loop: *mut EventLoop) -> Box<TypedTestApplication> {
GLOBAL_STATE.with(|g| {
let g = &mut *g.borrow_mut();
g.creation_count += 1;
});
- Box::new(TypedTestApplication::new(EventLoopRuntime::new(event_loop)))
+ Box::new(TypedTestApplication::new(CppEventLoopHolder(event_loop)))
}
- struct PanicApplication<'event_loop> {
- _runtime: EventLoopRuntime<'event_loop>,
+ struct PanicApplication {
+ _runtime: EventLoopRuntimeHolder<CppEventLoopHolder>,
}
- impl<'event_loop> PanicApplication<'event_loop> {
- fn new(mut runtime: EventLoopRuntime<'event_loop>) -> Self {
- runtime.spawn(async move {
- panic!("Test Rust panic");
+ impl PanicApplication {
+ fn new(event_loop: CppEventLoopHolder) -> Self {
+ let runtime = EventLoopRuntimeHolder::new(event_loop, |runtime| {
+ runtime.spawn(async move {
+ panic!("Test Rust panic");
+ });
});
Self { _runtime: runtime }
}
}
- unsafe fn make_panic_application(event_loop: *mut EventLoop) -> Box<PanicApplication<'static>> {
- Box::new(PanicApplication::new(EventLoopRuntime::new(event_loop)))
+ unsafe fn make_panic_application(event_loop: *mut EventLoop) -> Box<PanicApplication> {
+ Box::new(PanicApplication::new(CppEventLoopHolder(event_loop)))
}
- struct PanicOnRunApplication<'event_loop> {
- _runtime: EventLoopRuntime<'event_loop>,
+ struct PanicOnRunApplication {
+ _runtime: EventLoopRuntimeHolder<CppEventLoopHolder>,
}
- impl<'event_loop> PanicOnRunApplication<'event_loop> {
- fn new(mut runtime: EventLoopRuntime<'event_loop>) -> Self {
- let on_run = runtime.on_run();
- runtime.spawn(async move {
- on_run.await;
- panic!("Test Rust panic");
+ impl PanicOnRunApplication {
+ fn new(event_loop: CppEventLoopHolder) -> Self {
+ let runtime = EventLoopRuntimeHolder::new(event_loop, |runtime| {
+ let on_run = runtime.on_run();
+ runtime.spawn(async move {
+ on_run.borrow().await;
+ panic!("Test Rust panic");
+ });
});
Self { _runtime: runtime }
@@ -305,55 +325,49 @@
unsafe fn make_panic_on_run_application(
event_loop: *mut EventLoop,
- ) -> Box<PanicOnRunApplication<'static>> {
- Box::new(PanicOnRunApplication::new(EventLoopRuntime::new(
- event_loop,
- )))
+ ) -> Box<PanicOnRunApplication> {
+ Box::new(PanicOnRunApplication::new(CppEventLoopHolder(event_loop)))
}
#[cxx::bridge(namespace = "aos::events::testing")]
mod ffi_bridge {
extern "Rust" {
- unsafe fn make_test_application(
- event_loop: *mut EventLoop,
- ) -> Box<TestApplication<'static>>;
+ unsafe fn make_test_application(event_loop: *mut EventLoop) -> Box<TestApplication>;
unsafe fn make_typed_test_application(
event_loop: *mut EventLoop,
- ) -> Box<TypedTestApplication<'static>>;
+ ) -> Box<TypedTestApplication>;
- unsafe fn make_panic_application(
- event_loop: *mut EventLoop,
- ) -> Box<PanicApplication<'static>>;
+ unsafe fn make_panic_application(event_loop: *mut EventLoop) -> Box<PanicApplication>;
unsafe fn make_panic_on_run_application(
event_loop: *mut EventLoop,
- ) -> Box<PanicOnRunApplication<'static>>;
+ ) -> Box<PanicOnRunApplication>;
fn completed_test_count() -> u32;
fn started_test_count() -> u32;
}
extern "Rust" {
- type TestApplication<'a>;
+ type TestApplication;
fn before_sending(&mut self);
fn after_sending(&mut self);
}
extern "Rust" {
- type TypedTestApplication<'a>;
+ type TypedTestApplication;
fn before_sending(&mut self);
fn after_sending(&mut self);
}
extern "Rust" {
- type PanicApplication<'a>;
+ type PanicApplication;
}
extern "Rust" {
- type PanicOnRunApplication<'a>;
+ type PanicOnRunApplication;
}
unsafe extern "C++" {
diff --git a/aos/events/logging/log_backend_test.cc b/aos/events/logging/log_backend_test.cc
index d3c83cc..1e95c10 100644
--- a/aos/events/logging/log_backend_test.cc
+++ b/aos/events/logging/log_backend_test.cc
@@ -128,7 +128,17 @@
TEST(QueueAlignmentTest, Cases) {
QueueAligner aligner;
- uint8_t *start = nullptr;
+
+ // Get a 512-byte-aligned pointer to a buffer. That buffer needs to be at
+ // least 3 sectors big for the purposes of this test.
+ uint8_t buffer[FileHandler::kSector * 4];
+ void *aligned_start = buffer;
+ size_t size = sizeof(buffer);
+ ASSERT_TRUE(std::align(FileHandler::kSector, FileHandler::kSector * 3,
+ aligned_start, size) != nullptr);
+ ASSERT_GE(size, FileHandler::kSector * 3);
+
+ uint8_t *start = static_cast<uint8_t *>(aligned_start);
{
// Only prefix
std::vector<absl::Span<const uint8_t>> queue;
diff --git a/aos/events/logging/logfile_utils_out_of_space_test.sh b/aos/events/logging/logfile_utils_out_of_space_test.sh
index f412e0d..6a32347 100755
--- a/aos/events/logging/logfile_utils_out_of_space_test.sh
+++ b/aos/events/logging/logfile_utils_out_of_space_test.sh
@@ -23,7 +23,7 @@
rm -rf "${TMPFS}"
mkdir "${TMPFS}"
-function test {
+function run_test {
SIZE="$1"
echo "Running test with ${SIZE}..." >&2
unshare --mount --map-root-user bash <<END
@@ -37,10 +37,10 @@
}
# Run out of space exactly at the beginning of a block.
-test 81920
+run_test 81920
# Run out of space 1 byte into a block.
-test 81921
+run_test 81921
# Run out of space in the middle of a block.
-test 87040
+run_test 87040
diff --git a/aos/events/logging/multinode_logger_test_lib.h b/aos/events/logging/multinode_logger_test_lib.h
index 63604d6..8f64f66 100644
--- a/aos/events/logging/multinode_logger_test_lib.h
+++ b/aos/events/logging/multinode_logger_test_lib.h
@@ -76,13 +76,13 @@
};
constexpr std::string_view kCombinedConfigSha1() {
- return "32514f3a686e5f8936cc4651e7c81350112f7be8d80dcb8d4afaa29d233c5619";
+ return "71eb8341221fbabefb4ddde43bcebf794fd5855e3ad77786a1db0f9e27a39091";
}
constexpr std::string_view kSplitConfigSha1() {
- return "416da222c09d83325c6f453591d34c7ef12c12c2dd129ddeea657c4bec61b7fd";
+ return "f61d45dc0bda026e852e2da9b3e5c2c7f1c89c9f7958cfba3d02e2c960416f04";
}
constexpr std::string_view kReloggedSplitConfigSha1() {
- return "3fe428684a38298d3323ef087f44517574da3f07dd84b3740829156d6d870108";
+ return "3d8fd3d13955b517ee3d66a50b5e4dd7a13fd648f469d16910990418bcfc6beb";
}
LoggerState MakeLoggerState(NodeEventLoopFactory *node,
diff --git a/aos/events/ping.rs b/aos/events/ping.rs
index b9725b8..b285860 100644
--- a/aos/events/ping.rs
+++ b/aos/events/ping.rs
@@ -1,87 +1,24 @@
-use aos_configuration as config;
-use aos_events_event_loop_runtime::{EventLoopRuntime, Sender, Watcher};
-use aos_events_shm_event_loop::ShmEventLoop;
-use core::cell::Cell;
-use core::future::Future;
-use core::time::Duration;
-use futures::never::Never;
+use aos::configuration;
+use aos::events::shm_event_loop::ShmEventLoop;
+use aos::init::Init;
+use clap::Parser;
+use ping_lib::PingTask;
use std::path::Path;
-use ping_rust_fbs::aos::examples as ping;
-use pong_rust_fbs::aos::examples as pong;
+/// Ping portion of a ping/pong system.
+#[derive(Parser, Debug)]
+struct App {
+ /// Time to sleep between pings.
+ #[arg(long, default_value_t = 10000, value_name = "MICROS")]
+ sleep: u64,
+}
fn main() {
- aos_init::init();
- let config = config::read_config_from(Path::new("pingpong_config.json")).unwrap();
+ let app = App::init();
+ let config = configuration::read_config_from(Path::new("pingpong_config.json")).unwrap();
let ping = PingTask::new();
ShmEventLoop::new(&config).run_with(|runtime| {
- let task = ping.tasks(runtime);
- runtime.spawn(task);
+ runtime.set_realtime_priority(5);
+ runtime.spawn(ping.tasks(*runtime, app.sleep));
});
}
-
-#[derive(Debug)]
-struct PingTask {
- counter: Cell<i32>,
-}
-
-impl PingTask {
- pub fn new() -> Self {
- Self {
- counter: Cell::new(0),
- }
- }
-
- /// Returns a future with all the tasks for the ping process
- pub fn tasks(&self, event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> + '_ {
- let ping = self.ping(event_loop);
- let handle_pong = self.handle_pong(event_loop);
-
- async move {
- futures::join!(ping, handle_pong);
- unreachable!("Let's hope `never_type` gets stabilized soon :)");
- }
- }
-
- fn ping(&self, event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> + '_ {
- // The sender is used to send messages back to the pong channel.
- let mut ping_sender: Sender<ping::Ping> = event_loop.make_sender("/test").unwrap();
- let startup = event_loop.on_run();
-
- let mut interval = event_loop.add_interval(Duration::from_secs(1));
-
- async move {
- // Wait for startup.
- startup.await;
- loop {
- interval.tick().await;
- self.counter.set(self.counter.get() + 1);
- let mut builder = ping_sender.make_builder();
- let mut ping = ping::PingBuilder::new(builder.fbb());
- let iter = self.counter.get();
- ping.add_value(iter);
- let ping = ping.finish();
- builder.send(ping).expect("Can't send ping");
- }
- }
- }
-
- fn handle_pong(&self, event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> + '_ {
- // The watcher gives us incoming ping messages.
- let mut pong_watcher: Watcher<pong::Pong> = event_loop.make_watcher("/test").unwrap();
- let startup = event_loop.on_run();
-
- async move {
- // Wait for startup.
- startup.await;
- loop {
- let pong = dbg!(pong_watcher.next().await);
- assert_eq!(
- pong.message().unwrap().value(),
- self.counter.get(),
- "Missed a reply"
- );
- }
- }
- }
-}
diff --git a/aos/events/ping_lib.rs b/aos/events/ping_lib.rs
new file mode 100644
index 0000000..ce4d844
--- /dev/null
+++ b/aos/events/ping_lib.rs
@@ -0,0 +1,64 @@
+use aos::events::event_loop_runtime::{EventLoopRuntime, Sender, Watcher};
+use core::cell::Cell;
+use core::time::Duration;
+use futures::never::Never;
+use std::borrow::Borrow;
+
+use ping_rust_fbs::aos::examples as ping;
+use pong_rust_fbs::aos::examples as pong;
+
+#[derive(Debug)]
+pub struct PingTask {
+ counter: Cell<i32>,
+}
+
+impl PingTask {
+ pub fn new() -> Self {
+ Self {
+ counter: Cell::new(0),
+ }
+ }
+
+ /// Returns a future with all the tasks for the ping process
+ #[allow(unreachable_code)]
+ pub async fn tasks(&self, event_loop: EventLoopRuntime<'_>, sleep: u64) -> Never {
+ futures::join!(self.ping(&event_loop, sleep), self.handle_pong(&event_loop));
+ unreachable!("Let's hope `never_type` gets stabilized soon :)");
+ }
+
+ pub async fn ping(&self, event_loop: &EventLoopRuntime<'_>, sleep: u64) -> Never {
+ // The sender is used to send messages back to the pong channel.
+ let mut ping_sender: Sender<ping::Ping> = event_loop.make_sender("/test").unwrap();
+ let mut interval = event_loop.add_interval(Duration::from_micros(sleep));
+
+ let on_run = event_loop.on_run();
+ on_run.borrow().await;
+
+ loop {
+ interval.tick().await;
+ self.counter.set(self.counter.get() + 1);
+ let mut builder = ping_sender.make_builder();
+ let mut ping = ping::PingBuilder::new(builder.fbb());
+ let iter = self.counter.get();
+ log::trace!("Ping: {iter}");
+ ping.add_value(iter);
+ ping.add_send_time(event_loop.monotonic_now().into());
+ let ping = ping.finish();
+ builder.send(ping).expect("Can't send ping");
+ }
+ }
+
+ pub async fn handle_pong(&self, event_loop: &EventLoopRuntime<'_>) -> Never {
+ // The watcher gives us incoming ping messages.
+ let mut pong_watcher: Watcher<pong::Pong> = event_loop.make_watcher("/test").unwrap();
+
+ let on_run = event_loop.on_run();
+ on_run.borrow().await;
+ loop {
+ let pong = pong_watcher.next().await;
+ let pong = pong.message().unwrap();
+ log::trace!("Got pong: {}", pong.value());
+ assert_eq!(pong.value(), self.counter.get(), "Missed a reply");
+ }
+ }
+}
diff --git a/aos/events/pingpong_test.rs b/aos/events/pingpong_test.rs
new file mode 100644
index 0000000..70d8772
--- /dev/null
+++ b/aos/events/pingpong_test.rs
@@ -0,0 +1,114 @@
+#[cfg(test)]
+mod tests {
+ use std::{cell::Cell, time::Duration};
+
+ use aos::configuration::read_config_from;
+ use aos::events::event_loop_runtime::{EventLoopRuntimeHolder, Watcher};
+ use aos::events::simulated_event_loop::{SimulatedEventLoopFactory, SimulatedEventLoopHolder};
+ use aos::testing::init::test_init;
+ use ping_lib::PingTask;
+ use ping_rust_fbs::aos::examples as ping;
+ use pong_rust_fbs::aos::examples as pong;
+ use runfiles::Runfiles;
+
+ // We use this trait to simplify leaking memory. For now, the event loop only allows
+ // data with a `'static` lifetime. Until that restriction is lifted, we may leak
+ // some memory in tests.
+ trait Leak: Sized {
+ fn leak(self) -> &'static mut Self {
+ Box::leak(Box::new(self))
+ }
+ }
+
+ impl<T> Leak for T {}
+
+ #[allow(unused)]
+ struct PingPongTest {
+ ping_event_loop: EventLoopRuntimeHolder<SimulatedEventLoopHolder>,
+ pong_event_loop: EventLoopRuntimeHolder<SimulatedEventLoopHolder>,
+ event_loop_factory: SimulatedEventLoopFactory<'static>,
+ }
+
+ impl PingPongTest {
+ pub fn init() -> PingPongTest {
+ test_init();
+ let r = Runfiles::create().unwrap();
+ let config =
+ read_config_from(&r.rlocation("org_frc971/aos/events/pingpong_config.json"))
+ .unwrap()
+ .leak();
+ let mut event_loop_factory = SimulatedEventLoopFactory::new(config);
+
+ let ping_event_loop = event_loop_factory.make_runtime("ping", None, |runtime| {
+ let ping = PingTask::new();
+ runtime.spawn(async move { ping.tasks(runtime, 10000).await });
+ });
+
+ let pong_event_loop = event_loop_factory.make_runtime("pong", None, |runtime| {
+ runtime.spawn(async move { pong_lib::pong(runtime).await })
+ });
+ PingPongTest {
+ event_loop_factory,
+ ping_event_loop,
+ pong_event_loop,
+ }
+ }
+
+ pub fn event_loop(&mut self) -> &mut SimulatedEventLoopFactory<'static> {
+ &mut self.event_loop_factory
+ }
+ }
+
+ #[test]
+ fn starts() {
+ let mut pingpong = PingPongTest::init();
+ pingpong.event_loop().run_for(Duration::from_secs(10));
+ }
+
+ #[test]
+ fn always_replies() {
+ let mut pingpong = PingPongTest::init();
+
+ // For now, the simulated event loop requires all references in the tasks
+ // to be `'static`, so we leak them for now until the restriction is lifted.
+ let ping_count: &Cell<i32> = Cell::new(1).leak();
+ let pong_count: &Cell<i32> = Cell::new(1).leak();
+
+ let _test_runtime = pingpong.event_loop().make_runtime("test", None, |runtime| {
+ let count_pings = async move {
+ let mut ping_watcher: Watcher<ping::Ping> = runtime.make_watcher("/test").unwrap();
+ loop {
+ let ping = ping_watcher.next().await;
+ assert_eq!(ping.message().unwrap().value(), ping_count.get());
+ ping_count.set(ping_count.get() + 1);
+ }
+ };
+ let count_pongs = async move {
+ let mut pong_watcher: Watcher<pong::Pong> = runtime.make_watcher("/test").unwrap();
+ loop {
+ let pong = pong_watcher.next().await;
+ assert_eq!(pong.message().unwrap().value(), pong_count.get());
+ pong_count.set(pong_count.get() + 1);
+ }
+ };
+
+ runtime.spawn(async move {
+ futures::join!(count_pings, count_pongs);
+ unreachable!();
+ });
+ });
+
+ pingpong.event_loop().run_for(Duration::from_secs(10));
+
+ // We run at t=0 and t=10 seconds, which means we run 1 extra time (Note that we started
+ // the count at 1, not 0).
+ assert_eq!(ping_count.get(), 1002);
+ assert_eq!(pong_count.get(), 1002);
+ }
+}
+
+// TODO(adam.snaider): Remove once we don't leak.
+#[no_mangle]
+extern "C" fn __asan_default_options() -> *const u8 {
+ "detect_leaks=0\0".as_ptr()
+}
diff --git a/aos/events/pong.rs b/aos/events/pong.rs
index b817ac2..c2c316b 100644
--- a/aos/events/pong.rs
+++ b/aos/events/pong.rs
@@ -1,42 +1,16 @@
-use aos_configuration as config;
-use aos_events_event_loop_runtime::{EventLoopRuntime, Sender, Watcher};
-use aos_events_shm_event_loop::ShmEventLoop;
-use core::future::Future;
-use futures::never::Never;
+use aos::configuration;
+use aos::events::shm_event_loop::ShmEventLoop;
+use aos::init::{DefaultApp, Init};
use std::path::Path;
-use ping_rust_fbs::aos::examples as ping;
-use pong_rust_fbs::aos::examples as pong;
+use pong_lib::pong;
fn main() {
- aos_init::init();
- let config = config::read_config_from(Path::new("pingpong_config.json")).unwrap();
+ let _ = DefaultApp::init();
+ let config = configuration::read_config_from(Path::new("pingpong_config.json")).unwrap();
ShmEventLoop::new(&config).run_with(|runtime| {
- let task = pong(runtime);
+ let task = pong(*runtime);
+ runtime.set_realtime_priority(5);
runtime.spawn(task);
});
}
-
-/// Responds to ping messages with an equivalent pong.
-fn pong(event_loop: &mut EventLoopRuntime) -> impl Future<Output = Never> {
- // The watcher gives us incoming ping messages.
- let mut ping_watcher: Watcher<ping::Ping> = event_loop.make_watcher("/test").unwrap();
-
- // The sender is used to send messages back to the pong channel.
- let mut pong_sender: Sender<pong::Pong> = event_loop.make_sender("/test").unwrap();
- // Wait for startup.
- let startup = event_loop.on_run();
-
- async move {
- startup.await;
- loop {
- let ping = dbg!(ping_watcher.next().await);
-
- let mut builder = pong_sender.make_builder();
- let mut pong = pong::PongBuilder::new(builder.fbb());
- pong.add_value(ping.message().unwrap().value());
- let pong = pong.finish();
- builder.send(pong).expect("Can't send pong reponse");
- }
- }
-}
diff --git a/aos/events/pong_lib.rs b/aos/events/pong_lib.rs
new file mode 100644
index 0000000..8a0587a
--- /dev/null
+++ b/aos/events/pong_lib.rs
@@ -0,0 +1,30 @@
+use aos::events::event_loop_runtime::{EventLoopRuntime, Sender, Watcher};
+use futures::never::Never;
+use std::borrow::Borrow;
+
+use ping_rust_fbs::aos::examples as ping;
+use pong_rust_fbs::aos::examples as pong;
+
+/// Responds to ping messages with an equivalent pong.
+pub async fn pong(event_loop: EventLoopRuntime<'_>) -> Never {
+ // The watcher gives us incoming ping messages.
+ let mut ping_watcher: Watcher<ping::Ping> = event_loop.make_watcher("/test").unwrap();
+
+ // The sender is used to send messages back to the pong channel.
+ let mut pong_sender: Sender<pong::Pong> = event_loop.make_sender("/test").unwrap();
+
+ let on_run = event_loop.on_run();
+ on_run.borrow().await;
+ loop {
+ let ping = ping_watcher.next().await;
+ let ping = ping.message().unwrap();
+ log::info!("Got ping: {}", ping.value());
+
+ let mut builder = pong_sender.make_builder();
+ let mut pong = pong::PongBuilder::new(builder.fbb());
+ pong.add_value(ping.value());
+ pong.add_initial_send_time(event_loop.monotonic_now().into());
+ let pong = pong.finish();
+ builder.send(pong).expect("Can't send pong reponse");
+ }
+}
diff --git a/aos/events/shm_event_loop.rs b/aos/events/shm_event_loop.rs
index 880f72b..ee6df18 100644
--- a/aos/events/shm_event_loop.rs
+++ b/aos/events/shm_event_loop.rs
@@ -1,6 +1,7 @@
pub use aos_configuration::{Configuration, ConfigurationExt};
-pub use aos_events_event_loop_runtime::EventLoop;
-pub use aos_events_event_loop_runtime::{CppExitHandle, EventLoopRuntime, ExitHandle};
+pub use aos_events_event_loop_runtime::{
+ CppEventLoop, CppEventLoopRuntime, CppExitHandle, EventLoopRuntime, ExitHandle,
+};
use aos_configuration_fbs::aos::Configuration as RustConfiguration;
use aos_flatbuffers::{transmute_table_to, Flatbuffer};
@@ -20,7 +21,7 @@
extern_cpp_type!("aos::ExitHandle", crate::CppExitHandle)
extern_cpp_type!("aos::Configuration", crate::Configuration)
-extern_cpp_type!("aos::EventLoop", crate::EventLoop)
+extern_cpp_type!("aos::EventLoop", crate::CppEventLoop)
);
/// A Rust-owned C++ `ShmEventLoop` object.
@@ -65,6 +66,7 @@
/// # use aos_events_shm_event_loop::*;
/// use ping_rust_fbs::aos::examples as ping;
/// use pong_rust_fbs::aos::examples as pong;
+ /// use std::borrow::Borrow;
/// use std::cell::Cell;
/// use std::path::Path;
/// use aos_configuration::read_config_from;
@@ -81,7 +83,7 @@
/// let on_run = runtime.on_run();
/// // Sends a single ping message.
/// let send_task = async move {
- /// on_run.await;
+ /// on_run.borrow().await;
/// let mut builder = sender.make_builder();
/// let mut ping = ping::PingBuilder::new(builder.fbb());
/// ping.add_value(10);
@@ -163,14 +165,16 @@
/// ```
pub fn run_with<'env, F>(mut self, fun: F)
where
- F: for<'event_loop> FnOnce(&mut Scoped<'event_loop, 'env, EventLoopRuntime<'event_loop>>),
+ F: for<'event_loop> FnOnce(Scoped<'event_loop, 'env, EventLoopRuntime<'event_loop>>),
{
// SAFETY: The runtime and the event loop (i.e. self) both get destroyed at the end of this
// scope: first the runtime followed by the event loop. The runtime gets exclusive access
// during initialization in `fun` while the event loop remains unused.
- let runtime = unsafe { EventLoopRuntime::new(self.inner.as_mut().event_loop_mut()) };
- let mut runtime = Scoped::new(runtime);
- fun(&mut runtime);
+ let cpp_runtime =
+ unsafe { CppEventLoopRuntime::new(self.inner.as_mut().event_loop_mut()).within_box() };
+ let runtime = unsafe { EventLoopRuntime::new(&cpp_runtime) };
+ let runtime = Scoped::new(runtime);
+ fun(runtime);
self.run();
}
@@ -195,6 +199,7 @@
/// any `'scope`, this allows the compiler to propagate lifetime bounds which
/// outlive any of the possible `'scope`. This is the simplest way to express
/// this concept to the compiler right now.
+#[derive(Clone, Copy)]
pub struct Scoped<'scope, 'env: 'scope, T: 'scope> {
data: T,
_env: PhantomData<fn(&'env ()) -> &'env ()>,
@@ -235,6 +240,7 @@
use aos_events_event_loop_runtime::{Sender, Watcher};
use aos_test_init::test_init;
use ping_rust_fbs::aos::examples as ping;
+ use std::borrow::Borrow;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Barrier;
@@ -259,12 +265,11 @@
let mut event_loop = ShmEventLoop::new(config);
let exit_handle = event_loop.make_exit_handle();
event_loop.run_with(|runtime| {
- let mut watcher: Watcher<ping::Ping> = runtime
- .make_watcher("/test")
- .expect("Can't create `Ping` watcher");
- let on_run = runtime.on_run();
runtime.spawn(async move {
- on_run.await;
+ let mut watcher: Watcher<ping::Ping> = runtime
+ .make_watcher("/test")
+ .expect("Can't create `Ping` watcher");
+ runtime.on_run().borrow().await;
barrier.wait();
let ping = watcher.next().await;
assert_eq!(ping.message().unwrap().value(), VALUE);
@@ -277,12 +282,11 @@
let mut event_loop = ShmEventLoop::new(config);
let exit_handle = event_loop.make_exit_handle();
event_loop.run_with(|runtime| {
- let mut sender: Sender<ping::Ping> = runtime
- .make_sender("/test")
- .expect("Can't create `Ping` sender");
- let on_run = runtime.on_run();
runtime.spawn(async move {
- on_run.await;
+ let mut sender: Sender<ping::Ping> = runtime
+ .make_sender("/test")
+ .expect("Can't create `Ping` sender");
+ runtime.on_run().borrow().await;
// Give the waiting thread a chance to start.
barrier.wait();
let mut sender = sender.make_builder();
diff --git a/aos/events/simulated_event_loop.rs b/aos/events/simulated_event_loop.rs
index 90c4c86..c75f34e 100644
--- a/aos/events/simulated_event_loop.rs
+++ b/aos/events/simulated_event_loop.rs
@@ -1,11 +1,11 @@
-use std::{marker::PhantomData, pin::Pin, ptr};
+use std::{marker::PhantomData, pin::Pin, ptr, time::Duration};
use autocxx::WithinBox;
use cxx::UniquePtr;
pub use aos_configuration::{Channel, Configuration, ConfigurationExt, Node};
use aos_configuration_fbs::aos::Configuration as RustConfiguration;
-pub use aos_events_event_loop_runtime::{CppExitHandle, EventLoop, ExitHandle};
+pub use aos_events_event_loop_runtime::{CppEventLoop, CppExitHandle, ExitHandle};
use aos_events_event_loop_runtime::{EventLoopHolder, EventLoopRuntime, EventLoopRuntimeHolder};
use aos_flatbuffers::{transmute_table_to, Flatbuffer};
@@ -20,7 +20,7 @@
extern_cpp_type!("aos::ExitHandle", crate::CppExitHandle)
extern_cpp_type!("aos::Configuration", crate::Configuration)
extern_cpp_type!("aos::Node", crate::Node)
-extern_cpp_type!("aos::EventLoop", crate::EventLoop)
+extern_cpp_type!("aos::EventLoop", crate::CppEventLoop)
);
/// A Rust-owned C++ `SimulatedEventLoopFactory` object.
@@ -60,9 +60,9 @@
/// Creates a Rust-owned EventLoop.
///
/// You probably don't want to call this directly if you're creating a Rust application. This
- /// is intended for creating C++ applications. Use [`make_runtime`] instead when creating Rust
+ /// is intended for creating C++ applications. Use [`Self::make_runtime`] instead when creating Rust
/// applications.
- pub fn make_event_loop(&mut self, name: &str, node: Option<&Node>) -> UniquePtr<EventLoop> {
+ pub fn make_event_loop(&mut self, name: &str, node: Option<&Node>) -> UniquePtr<CppEventLoop> {
// SAFETY:
// * `self` has a valid C++ object.
// * C++ doesn't need the lifetimes of `name` or `node` to last any longer than this method
@@ -77,7 +77,7 @@
}
}
- /// Creates an [`EventLoopRuntime`] wrapper which also owns its underlying [`EventLoop`].
+ /// Creates an [`EventLoopRuntime`] wrapper which also owns its underlying [`CppEventLoop`].
///
/// All setup must be performed with `fun`, which is called before this function returns. `fun`
/// may create further objects to use in async functions via [`EventLoop.spawn`] etc, but it is
@@ -91,7 +91,7 @@
fun: F,
) -> EventLoopRuntimeHolder<SimulatedEventLoopHolder>
where
- F: for<'event_loop> FnOnce(&mut EventLoopRuntime<'event_loop>),
+ F: for<'event_loop> FnOnce(EventLoopRuntime<'event_loop>),
{
let event_loop = self.make_event_loop(name, node);
// SAFETY: We just created this EventLoop, so we are the exclusive owner of it.
@@ -107,16 +107,25 @@
self.as_mut().Run();
}
+ pub fn run_for(&mut self, duration: Duration) {
+ self.as_mut().RunFor(
+ duration
+ .as_nanos()
+ .try_into()
+ .expect("Out of range: Internal clock uses 64 bits"),
+ );
+ }
+
// TODO(Brian): Expose OnStartup. Just take a callback for creating things, and rely on
// dropping the created objects instead of OnShutdown.
// pub fn spawn_on_startup(&mut self, spawner: impl FnMut());
}
-pub struct SimulatedEventLoopHolder(UniquePtr<EventLoop>);
+pub struct SimulatedEventLoopHolder(UniquePtr<CppEventLoop>);
impl SimulatedEventLoopHolder {
/// SAFETY: `event_loop` must be the exclusive owner of the underlying EventLoop.
- pub unsafe fn new(event_loop: UniquePtr<EventLoop>) -> Self {
+ pub unsafe fn new(event_loop: UniquePtr<CppEventLoop>) -> Self {
Self(event_loop)
}
}
@@ -124,12 +133,11 @@
// SAFETY: The UniquePtr functions we're using here mirror most of the EventLoopHolder requirements
// exactly. Safety requirements on [`SimulatedEventLoopHolder.new`] take care of the rest.
unsafe impl EventLoopHolder for SimulatedEventLoopHolder {
- fn into_raw(self) -> *mut ffi::aos::EventLoop {
- self.0.into_raw()
- }
-
- unsafe fn from_raw(raw: *mut ffi::aos::EventLoop) -> Self {
- Self(UniquePtr::from_raw(raw))
+ fn as_raw(&self) -> *const CppEventLoop {
+ self.0
+ .as_ref()
+ .map(|event_loop| event_loop as *const CppEventLoop)
+ .unwrap_or(core::ptr::null())
}
}
diff --git a/aos/events/simulated_event_loop_for_rust.h b/aos/events/simulated_event_loop_for_rust.h
index a8d630c..663d163 100644
--- a/aos/events/simulated_event_loop_for_rust.h
+++ b/aos/events/simulated_event_loop_for_rust.h
@@ -17,6 +17,9 @@
}
void Run() { factory_.Run(); }
+ void RunFor(int64_t nanos) {
+ factory_.RunFor(std::chrono::nanoseconds(nanos));
+ }
std::unique_ptr<ExitHandle> MakeExitHandle() {
return factory_.MakeExitHandle();
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index a299fe9..607303c 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -118,6 +118,11 @@
// make attempts to use it fail more obviously.
void Wipe() { memset(span().data(), 0, span().size()); }
+ // Returns true if the flatbuffer is valid. Returns false if either:
+ // * The flatbuffer is incorrectly constructed (e.g., it points to memory
+ // locations outside of the current memory buffer).
+ // * The flatbuffer is too complex, and the flatbuffer verifier chosen to bail
+ // when attempting to traverse the tree of tables.
bool Verify() const {
if (span().size() < 4u) {
return false;
diff --git a/aos/flatbuffers/BUILD b/aos/flatbuffers/BUILD
index 08d548f..32f1d39 100644
--- a/aos/flatbuffers/BUILD
+++ b/aos/flatbuffers/BUILD
@@ -102,6 +102,7 @@
":test_schema",
"//aos:flatbuffers",
"//aos:json_to_flatbuffer",
+ "//aos/flatbuffers/test_dir:include_reflection_fbs",
"//aos/flatbuffers/test_dir:type_coverage_fbs",
"//aos/testing:googletest",
"//aos/testing:path",
diff --git a/aos/flatbuffers/builder.h b/aos/flatbuffers/builder.h
index db89d10..36225c0 100644
--- a/aos/flatbuffers/builder.h
+++ b/aos/flatbuffers/builder.h
@@ -77,6 +77,9 @@
FlatbufferSpan<typename T::Flatbuffer> AsFlatbufferSpan() {
return {buffer()};
}
+ FlatbufferSpan<const typename T::Flatbuffer> AsFlatbufferSpan() const {
+ return {buffer()};
+ }
// Returns true if the flatbuffer is validly constructed. Should always return
// true (barring some sort of memory corruption). Exposed for convenience.
diff --git a/aos/flatbuffers/reflection/BUILD.bazel b/aos/flatbuffers/reflection/BUILD.bazel
new file mode 100644
index 0000000..475f2a2
--- /dev/null
+++ b/aos/flatbuffers/reflection/BUILD.bazel
@@ -0,0 +1,18 @@
+load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file")
+load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
+
+copy_file(
+ name = "reflection_fbs_copy",
+ src = "@com_github_google_flatbuffers//reflection:reflection_fbs_schema",
+ out = "reflection.fbs",
+)
+
+# This autogenerates both a reflection_static.h and a reflection_generated.h.
+# However, in order to avoid having two conflicting headers floating around,
+# we forcibly override the #include to use flatbuffers/reflection_generated.h
+# in static_flatbuffers.cc
+static_flatbuffer(
+ name = "reflection_fbs",
+ srcs = ["reflection.fbs"],
+ visibility = ["//visibility:public"],
+)
diff --git a/aos/flatbuffers/static_flatbuffers.cc b/aos/flatbuffers/static_flatbuffers.cc
index 8d8942c..c1b617f 100644
--- a/aos/flatbuffers/static_flatbuffers.cc
+++ b/aos/flatbuffers/static_flatbuffers.cc
@@ -19,6 +19,8 @@
bool is_inline = true;
// Whether this is a struct or not.
bool is_struct = false;
+ // Whether this is a repeated type (vector or string).
+ bool is_repeated = false;
// Full C++ type of this field.
std::string full_type = "";
// Full flatbuffer type for this field.
@@ -118,6 +120,22 @@
const std::string IncludePathForFbs(
std::string_view fbs_file, std::string_view include_suffix = "static") {
+ // Special case for the reflection_generated.h, which is checked into the
+ // repo.
+ // Note that we *do* autogenerated the reflection_static.h but that because
+ // it uses a special import path, we end up overriding the include anyways
+ // (note that we could muck around with the paths on the bazel side to instead
+ // get a cc_library with the correct include paths specified, although it is
+ // not clear that that would be any simpler than the extra else-if).
+ if (fbs_file == "reflection/reflection.fbs") {
+ if (include_suffix == "generated") {
+ return "flatbuffers/reflection_generated.h";
+ } else if (include_suffix == "static") {
+ return "aos/flatbuffers/reflection/reflection_static.h";
+ } else {
+ LOG(FATAL) << "This should be unreachable.";
+ }
+ }
fbs_file.remove_suffix(4);
return absl::StrCat(fbs_file, "_", include_suffix, ".h");
}
@@ -151,12 +169,14 @@
// straightforwards.
field->is_inline = true;
field->is_struct = false;
+ field->is_repeated = false;
field->full_type =
ScalarOrEnumType(schema, type->base_type(), type->index());
return;
case reflection::BaseType::String: {
field->is_inline = false;
field->is_struct = false;
+ field->is_repeated = true;
field->full_type =
absl::StrFormat("::aos::fbs::String<%d>",
GetLengthAttributeOrZero(field_fbs, "static_length"));
@@ -166,6 +186,7 @@
// We need to extract the name of the elements of the vector.
std::string element_type;
bool elements_are_inline = true;
+ field->is_repeated = true;
if (type->base_type() == reflection::BaseType::Vector) {
switch (type->element()) {
case reflection::BaseType::Obj: {
@@ -207,6 +228,7 @@
const reflection::Object *object = GetObject(schema, type->index());
field->is_inline = object->is_struct();
field->is_struct = object->is_struct();
+ field->is_repeated = false;
const std::string flatbuffer_name =
FlatbufferNameToCppName(object->name()->string_view());
if (field->is_inline) {
@@ -437,27 +459,97 @@
absl::StrJoin(clearers, "\n"));
}
+// Creates the FromFlatbuffer() method that copies from a flatbuffer object API
+// object (i.e., the FlatbufferT types).
+std::string MakeObjectCopier(const std::vector<FieldData> &fields) {
+ std::vector<std::string> copiers;
+ for (const FieldData &field : fields) {
+ if (field.is_struct) {
+ // Structs are stored as unique_ptr<FooStruct>
+ copiers.emplace_back(absl::StrFormat(R"code(
+ if (other.%s) {
+ set_%s(*other.%s);
+ }
+ )code",
+ field.name, field.name, field.name));
+ } else if (field.is_inline) {
+ // Inline non-struct elements are stored as FooType.
+ copiers.emplace_back(absl::StrFormat(R"code(
+ set_%s(other.%s);
+ )code",
+ field.name, field.name));
+ } else if (field.is_repeated) {
+ // strings are stored as std::string's.
+ // vectors are stored as std::vector's.
+ copiers.emplace_back(absl::StrFormat(R"code(
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(other.%s)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ )code",
+ field.name, field.name));
+ } else {
+ // Tables are stored as unique_ptr<FooTable>
+ copiers.emplace_back(absl::StrFormat(R"code(
+ if (other.%s) {
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(*other.%s)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+ )code",
+ field.name, field.name, field.name));
+ }
+ }
+ return absl::StrFormat(
+ R"code(
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+ %s
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(const flatbuffers::unique_ptr<Flatbuffer::NativeTableType>& other) {
+ return FromFlatbuffer(*other);
+ }
+)code",
+ absl::StrJoin(copiers, "\n"));
+}
+
+// Creates the FromFlatbuffer() method that copies from an actual flatbuffer
+// object.
std::string MakeCopier(const std::vector<FieldData> &fields) {
std::vector<std::string> copiers;
for (const FieldData &field : fields) {
if (field.is_struct) {
copiers.emplace_back(absl::StrFormat(R"code(
- if (other->has_%s()) {
- set_%s(*other->%s());
+ if (other.has_%s()) {
+ set_%s(*other.%s());
}
)code",
field.name, field.name, field.name));
} else if (field.is_inline) {
copiers.emplace_back(absl::StrFormat(R"code(
- if (other->has_%s()) {
- set_%s(other->%s());
+ if (other.has_%s()) {
+ set_%s(other.%s());
}
)code",
field.name, field.name, field.name));
} else {
copiers.emplace_back(absl::StrFormat(R"code(
- if (other->has_%s()) {
- if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(other->%s())) {
+ if (other.has_%s()) {
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(other.%s())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
@@ -473,11 +565,16 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
%s
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
)code",
absl::StrJoin(copiers, "\n"));
}
@@ -689,6 +786,7 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef %s Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType> FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -699,6 +797,7 @@
%s
%s
%s
+%s
private:
%s
%s
@@ -708,7 +807,7 @@
)code",
type_namespace, type_name, FlatbufferNameToCppName(fbs_type_name),
constants, MakeConstructor(type_name), type_name, accessors,
- MakeFullClearer(fields), MakeCopier(fields),
+ MakeFullClearer(fields), MakeCopier(fields), MakeObjectCopier(fields),
MakeMoveConstructor(type_name), members, MakeSubObjectList(fields));
GeneratedObject result;
diff --git a/aos/flatbuffers/static_flatbuffers_test.cc b/aos/flatbuffers/static_flatbuffers_test.cc
index fd95db3..52fa01e 100644
--- a/aos/flatbuffers/static_flatbuffers_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_test.cc
@@ -10,6 +10,7 @@
#include "aos/flatbuffers.h"
#include "aos/flatbuffers/builder.h"
#include "aos/flatbuffers/interesting_schemas.h"
+#include "aos/flatbuffers/test_dir/include_reflection_static.h"
#include "aos/flatbuffers/test_dir/type_coverage_static.h"
#include "aos/flatbuffers/test_schema.h"
#include "aos/flatbuffers/test_static.h"
@@ -1042,4 +1043,55 @@
TestMemory(builder.buffer());
}
+// Uses a small example to manually verify that we can copy from the flatbuffer
+// object API.
+TEST_F(StaticFlatbuffersTest, ObjectApiCopy) {
+ aos::fbs::testing::TestTableT object_t;
+ object_t.scalar = 971;
+ object_t.vector_of_strings.push_back("971");
+ object_t.vector_of_structs.push_back({1, 2});
+ object_t.subtable = std::make_unique<SubTableT>();
+ aos::fbs::VectorAllocator allocator;
+ Builder<TestTableStatic> builder(&allocator);
+ ASSERT_TRUE(builder->FromFlatbuffer(object_t));
+ ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
+ // Note that vectors and strings get set to zero-length, but present, values.
+ EXPECT_EQ(
+ "{ \"scalar\": 971, \"vector_of_scalars\": [ ], \"string\": \"\", "
+ "\"vector_of_strings\": [ \"971\" ], \"subtable\": { \"foo\": 0, "
+ "\"baz\": 0.0 }, \"vector_aligned\": [ ], \"vector_of_structs\": [ { "
+ "\"x\": 1.0, \"y\": 2.0 } ], \"vector_of_tables\": [ ], "
+ "\"unspecified_length_vector\": [ ], \"unspecified_length_string\": "
+ "\"\", \"unspecified_length_vector_of_strings\": [ ] }",
+ aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
+}
+
+// More completely covers our object API copying by comparing the flatbuffer
+// Pack() methods to our FromFlatbuffer() methods.
+TEST_F(StaticFlatbuffersTest, FlatbufferObjectTypeCoverage) {
+ VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
+ std::string populated_config =
+ aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
+ "aos/flatbuffers/test_dir/type_coverage.json"));
+ Builder<aos::testing::ConfigurationStatic> json_builder =
+ aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
+ populated_config);
+ aos::testing::ConfigurationT object_t;
+ json_builder->AsFlatbuffer().UnPackTo(&object_t);
+
+ Builder<aos::testing::ConfigurationStatic> from_object_static;
+ ASSERT_TRUE(from_object_static->FromFlatbuffer(object_t));
+ flatbuffers::FlatBufferBuilder fbb;
+ fbb.Finish(aos::testing::Configuration::Pack(fbb, &object_t));
+ aos::FlatbufferDetachedBuffer<aos::testing::Configuration> from_object_raw =
+ fbb.Release();
+ EXPECT_EQ(aos::FlatbufferToJson(from_object_raw, {.multi_line = true}),
+ aos::FlatbufferToJson(from_object_static, {.multi_line = true}));
+}
+
+// Tests that we can build code that uses the reflection types.
+TEST_F(StaticFlatbuffersTest, IncludeReflectionTypes) {
+ VerifyJson<::aos::testing::UseSchemaStatic>("{\n\n}");
+}
+
} // namespace aos::fbs::testing
diff --git a/aos/flatbuffers/static_vector.h b/aos/flatbuffers/static_vector.h
index 7349a8d..6133075 100644
--- a/aos/flatbuffers/static_vector.h
+++ b/aos/flatbuffers/static_vector.h
@@ -203,6 +203,11 @@
typename internal::InlineWrapper<T, kInline>::FlatbufferType;
using ConstFlatbufferType =
typename internal::InlineWrapper<T, kInline>::ConstFlatbufferType;
+ // FlatbufferObjectType corresponds to the type used by the flatbuffer
+ // "object" API (i.e. the FlatbufferT types).
+ // This type will be something unintelligble for inline types.
+ using FlatbufferObjectType =
+ typename internal::InlineWrapper<T, kInline>::FlatbufferObjectType;
// flatbuffers::Vector type that corresponds to this Vector.
typedef flatbuffers::Vector<FlatbufferType> Flatbuffer;
typedef const flatbuffers::Vector<ConstFlatbufferType> ConstFlatbuffer;
@@ -329,7 +334,70 @@
// we can allocate through reserve()).
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer *vector);
+ [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer *vector) {
+ return FromFlatbuffer(*CHECK_NOTNULL(vector));
+ }
+ [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer &vector);
+ // The remaining FromFlatbuffer() overloads are for when using the flatbuffer
+ // "object" API, which uses std::vector's for representing vectors.
+ [[nodiscard]] bool FromFlatbuffer(const std::vector<InlineType> &vector) {
+ static_assert(kInline);
+ return FromData(vector.data(), vector.size());
+ }
+ // Overload for vectors of bools, since the standard library may not use a
+ // full byte per vector element.
+ [[nodiscard]] bool FromFlatbuffer(const std::vector<bool> &vector) {
+ static_assert(kInline);
+ // We won't be able to do a clean memcpy because std::vector<bool> may be
+ // implemented using bit-packing.
+ return FromIterator(vector.cbegin(), vector.cend());
+ }
+ // Overload for non-inline types. Note that to avoid having this overload get
+ // resolved with inline types, we make FlatbufferObjectType != InlineType.
+ [[nodiscard]] bool FromFlatbuffer(
+ const std::vector<FlatbufferObjectType> &vector) {
+ static_assert(!kInline);
+ return FromNotInlineIterable(vector);
+ }
+
+ // Copies values from the provided data pointer into the vector, resizing the
+ // vector as needed to match. Returns false on failure (e.g., if the
+ // underlying allocator has insufficient space to perform the copy). Only
+ // works for inline data types.
+ [[nodiscard]] bool FromData(const InlineType *input_data, size_t input_size) {
+ static_assert(kInline);
+ if (!reserve(input_size)) {
+ return false;
+ }
+
+ // We will be overwriting the whole vector very shortly; there is no need to
+ // clear the buffer to zero.
+ resize_inline(input_size, SetZero::kNo);
+
+ memcpy(inline_data(), input_data, size() * sizeof(InlineType));
+ return true;
+ }
+
+ // Copies values from the provided iterators into the vector, resizing the
+ // vector as needed to match. Returns false on failure (e.g., if the
+ // underlying allocator has insufficient space to perform the copy). Only
+ // works for inline data types.
+ // Does not attempt any optimizations if the iterators meet the
+ // std::contiguous_iterator concept; instead, it simply copies each element
+ // out one-by-one.
+ template <typename Iterator>
+ [[nodiscard]] bool FromIterator(Iterator begin, Iterator end) {
+ static_assert(kInline);
+ resize(0);
+ for (Iterator it = begin; it != end; ++it) {
+ if (!reserve(size() + 1)) {
+ return false;
+ }
+ // Should never fail, due to the reserve() above.
+ CHECK(emplace_back(*it));
+ }
+ return true;
+ }
// Returns the element at the provided index. index must be less than size().
const T &at(size_t index) const {
@@ -569,29 +637,22 @@
}
// Implementation that handles copying from a flatbuffers::Vector of an inline
// data type.
- [[nodiscard]] bool FromInlineFlatbuffer(ConstFlatbuffer *vector) {
- if (!reserve(CHECK_NOTNULL(vector)->size())) {
- return false;
- }
-
- // We will be overwriting the whole vector very shortly; there is no need to
- // clear the buffer to zero.
- resize_inline(vector->size(), SetZero::kNo);
-
- memcpy(inline_data(), vector->Data(), size() * sizeof(InlineType));
- return true;
+ [[nodiscard]] bool FromInlineFlatbuffer(ConstFlatbuffer &vector) {
+ return FromData(reinterpret_cast<const InlineType *>(vector.Data()),
+ vector.size());
}
// Implementation that handles copying from a flatbuffers::Vector of a
// not-inline data type.
- [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer *vector) {
- if (!reserve(vector->size())) {
+ template <typename Iterable>
+ [[nodiscard]] bool FromNotInlineIterable(const Iterable &vector) {
+ if (!reserve(vector.size())) {
return false;
}
// "Clear" the vector.
resize_not_inline(0);
- for (const typename T::Flatbuffer *entry : *vector) {
+ for (const auto &entry : vector) {
if (!CHECK_NOTNULL(emplace_back())->FromFlatbuffer(entry)) {
return false;
}
@@ -599,6 +660,10 @@
return true;
}
+ [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer &vector) {
+ return FromNotInlineIterable(vector);
+ }
+
// In order to allow for easy partial template specialization, we use a
// non-member class to call FromInline/FromNotInlineFlatbuffer and
// resize_inline/resize_not_inline. There are not actually any great ways to
@@ -659,6 +724,7 @@
public:
typedef Vector<char, kStaticLength, true, 0, true> VectorType;
typedef flatbuffers::String Flatbuffer;
+ typedef std::string FlatbufferObjectType;
String(std::span<uint8_t> buffer, ResizeableObject *parent)
: VectorType(buffer, parent) {}
virtual ~String() {}
@@ -667,6 +733,10 @@
VectorType::resize_inline(string.size(), SetZero::kNo);
memcpy(VectorType::data(), string.data(), string.size());
}
+ using VectorType::FromFlatbuffer;
+ [[nodiscard]] bool FromFlatbuffer(const std::string &string) {
+ return VectorType::FromData(string.data(), string.size());
+ }
std::string_view string_view() const {
return std::string_view(VectorType::data(), VectorType::size());
}
@@ -690,12 +760,13 @@
typedef T ObjectType;
typedef flatbuffers::Offset<typename T::Flatbuffer> FlatbufferType;
typedef flatbuffers::Offset<typename T::Flatbuffer> ConstFlatbufferType;
+ typedef T::FlatbufferObjectType FlatbufferObjectType;
static_assert((T::kSize % T::kAlign) == 0);
static constexpr size_t kDataAlign = T::kAlign;
static constexpr size_t kDataSize = T::kSize;
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromNotInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -712,11 +783,12 @@
typedef T ObjectType;
typedef T FlatbufferType;
typedef T ConstFlatbufferType;
+ typedef T *FlatbufferObjectType;
static constexpr size_t kDataAlign = alignof(T);
static constexpr size_t kDataSize = sizeof(T);
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -731,11 +803,12 @@
typedef uint8_t ObjectType;
typedef uint8_t FlatbufferType;
typedef uint8_t ConstFlatbufferType;
+ typedef uint8_t *FlatbufferObjectType;
static constexpr size_t kDataAlign = 1u;
static constexpr size_t kDataSize = 1u;
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -753,11 +826,12 @@
typedef T ObjectType;
typedef T *FlatbufferType;
typedef const T *ConstFlatbufferType;
+ typedef T *FlatbufferObjectType;
static constexpr size_t kDataAlign = alignof(T);
static constexpr size_t kDataSize = sizeof(T);
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -770,7 +844,7 @@
template <typename T, size_t kStaticLength, bool kInline, size_t kForceAlign,
bool kNullTerminate>
bool Vector<T, kStaticLength, kInline, kForceAlign,
- kNullTerminate>::FromFlatbuffer(ConstFlatbuffer *vector) {
+ kNullTerminate>::FromFlatbuffer(ConstFlatbuffer &vector) {
return internal::InlineWrapper<T, kInline>::FromFlatbuffer(this, vector);
}
diff --git a/aos/flatbuffers/test_dir/BUILD b/aos/flatbuffers/test_dir/BUILD
index 76f5fb4..a6275a5 100644
--- a/aos/flatbuffers/test_dir/BUILD
+++ b/aos/flatbuffers/test_dir/BUILD
@@ -1,6 +1,13 @@
load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
static_flatbuffer(
+ name = "include_reflection_fbs",
+ srcs = ["include_reflection.fbs"],
+ visibility = ["//visibility:public"],
+ deps = ["//aos/flatbuffers/reflection:reflection_fbs"],
+)
+
+static_flatbuffer(
name = "include_fbs",
srcs = ["include.fbs"],
visibility = ["//visibility:public"],
diff --git a/aos/flatbuffers/test_dir/include_reflection.fbs b/aos/flatbuffers/test_dir/include_reflection.fbs
new file mode 100644
index 0000000..7eda4f5
--- /dev/null
+++ b/aos/flatbuffers/test_dir/include_reflection.fbs
@@ -0,0 +1,9 @@
+include "reflection/reflection.fbs";
+
+namespace aos.testing;
+
+table UseSchema {
+ schema:reflection.Schema (id: 0);
+}
+
+root_type UseSchema;
diff --git a/aos/flatbuffers/test_dir/sample_test_static.h b/aos/flatbuffers/test_dir/sample_test_static.h
index a6362d7..57ac57a 100644
--- a/aos/flatbuffers/test_dir/sample_test_static.h
+++ b/aos/flatbuffers/test_dir/sample_test_static.h
@@ -14,6 +14,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::MinimallyAlignedTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -135,15 +137,39 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
- if (other->has_field()) {
- set_field(other->field());
+ if (other.has_field()) {
+ set_field(other.field());
}
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
+
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ set_field(other.field);
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
private:
// We need to provide a MoveConstructor to allow this table to be
@@ -168,6 +194,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::SubTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -314,19 +342,45 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
- if (other->has_baz()) {
- set_baz(other->baz());
+ if (other.has_baz()) {
+ set_baz(other.baz());
}
- if (other->has_foo()) {
- set_foo(other->foo());
+ if (other.has_foo()) {
+ set_foo(other.foo());
}
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
+
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ set_baz(other.baz);
+
+ set_foo(other.foo);
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
private:
// We need to provide a MoveConstructor to allow this table to be
@@ -354,6 +408,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::TestTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -1060,109 +1116,108 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
- if (other->has_included_table()) {
+ if (other.has_included_table()) {
if (!CHECK_NOTNULL(add_included_table())
- ->FromFlatbuffer(other->included_table())) {
+ ->FromFlatbuffer(other.included_table())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_scalar()) {
- set_scalar(other->scalar());
+ if (other.has_scalar()) {
+ set_scalar(other.scalar());
}
- if (other->has_string()) {
- if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other->string())) {
+ if (other.has_string()) {
+ if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_substruct()) {
- set_substruct(*other->substruct());
+ if (other.has_substruct()) {
+ set_substruct(*other.substruct());
}
- if (other->has_subtable()) {
- if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(other->subtable())) {
+ if (other.has_subtable()) {
+ if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(other.subtable())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_unspecified_length_string()) {
+ if (other.has_unspecified_length_string()) {
if (!CHECK_NOTNULL(add_unspecified_length_string())
- ->FromFlatbuffer(other->unspecified_length_string())) {
+ ->FromFlatbuffer(other.unspecified_length_string())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_unspecified_length_vector()) {
+ if (other.has_unspecified_length_vector()) {
if (!CHECK_NOTNULL(add_unspecified_length_vector())
- ->FromFlatbuffer(other->unspecified_length_vector())) {
+ ->FromFlatbuffer(other.unspecified_length_vector())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_unspecified_length_vector_of_strings()) {
+ if (other.has_unspecified_length_vector_of_strings()) {
if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
- ->FromFlatbuffer(
- other->unspecified_length_vector_of_strings())) {
+ ->FromFlatbuffer(other.unspecified_length_vector_of_strings())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_aligned()) {
+ if (other.has_vector_aligned()) {
if (!CHECK_NOTNULL(add_vector_aligned())
- ->FromFlatbuffer(other->vector_aligned())) {
+ ->FromFlatbuffer(other.vector_aligned())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_scalars()) {
+ if (other.has_vector_of_scalars()) {
if (!CHECK_NOTNULL(add_vector_of_scalars())
- ->FromFlatbuffer(other->vector_of_scalars())) {
+ ->FromFlatbuffer(other.vector_of_scalars())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_strings()) {
+ if (other.has_vector_of_strings()) {
if (!CHECK_NOTNULL(add_vector_of_strings())
- ->FromFlatbuffer(other->vector_of_strings())) {
+ ->FromFlatbuffer(other.vector_of_strings())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_structs()) {
+ if (other.has_vector_of_structs()) {
if (!CHECK_NOTNULL(add_vector_of_structs())
- ->FromFlatbuffer(other->vector_of_structs())) {
+ ->FromFlatbuffer(other.vector_of_structs())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_tables()) {
+ if (other.has_vector_of_tables()) {
if (!CHECK_NOTNULL(add_vector_of_tables())
- ->FromFlatbuffer(other->vector_of_tables())) {
+ ->FromFlatbuffer(other.vector_of_tables())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
@@ -1171,6 +1226,140 @@
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
+
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ if (other.included_table) {
+ if (!CHECK_NOTNULL(add_included_table())
+ ->FromFlatbuffer(*other.included_table)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+
+ set_scalar(other.scalar);
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ if (other.substruct) {
+ set_substruct(*other.substruct);
+ }
+
+ if (other.subtable) {
+ if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(*other.subtable)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_string())
+ ->FromFlatbuffer(other.unspecified_length_string)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_vector())
+ ->FromFlatbuffer(other.unspecified_length_vector)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
+ ->FromFlatbuffer(other.unspecified_length_vector_of_strings)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_aligned())
+ ->FromFlatbuffer(other.vector_aligned)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_scalars())
+ ->FromFlatbuffer(other.vector_of_scalars)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_strings())
+ ->FromFlatbuffer(other.vector_of_strings)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_structs())
+ ->FromFlatbuffer(other.vector_of_structs)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_tables())
+ ->FromFlatbuffer(other.vector_of_tables)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
private:
// We need to provide a MoveConstructor to allow this table to be
diff --git a/aos/init.cc b/aos/init.cc
index 3ca7314..579ad33 100644
--- a/aos/init.cc
+++ b/aos/init.cc
@@ -45,25 +45,6 @@
initialized = true;
}
-void InitFromRust(const char *argv0) {
- CHECK(!IsInitialized()) << "Only initialize once.";
-
- FLAGS_logtostderr = true;
-
- google::InitGoogleLogging(argv0);
-
- // TODO(Brian): Provide a way for Rust to configure C++ flags.
- char fake_argv0_val[] = "rust";
- char *fake_argv0 = fake_argv0_val;
- char **fake_argv = &fake_argv0;
- int fake_argc = 1;
- gflags::ParseCommandLineFlags(&fake_argc, &fake_argv, true);
-
- // TODO(Brian): Where should Rust binaries be configured to write coredumps?
-
- // TODO(Brian): Figure out what to do with allocator hooks for C++ and Rust.
-
- initialized = true;
-}
+void MarkInitialized() { initialized = true; }
} // namespace aos
diff --git a/aos/init.h b/aos/init.h
index 29480eb..b9270b3 100644
--- a/aos/init.h
+++ b/aos/init.h
@@ -10,10 +10,10 @@
// ShmEventLoop can confirm the world was initialized before running.
bool IsInitialized();
-// A special initialization function that initializes the C++ parts in a way
-// compatible with Rust. This requires careful coordination with `:init_rs`, do
-// not use it from anywhere else.
-void InitFromRust(const char *argv0);
+// Marks the system as initialized. This is only meant to be used from
+// init_for_rust. DO NOT call this from anywhere else, use InitGoogle
+// instead.
+void MarkInitialized();
} // namespace aos
diff --git a/aos/init.rs b/aos/init.rs
index 13066f1..fe0408d 100644
--- a/aos/init.rs
+++ b/aos/init.rs
@@ -1,20 +1,275 @@
-use std::{ffi::CString, sync::Once};
+//! AOS Initialization
+//!
+//! This module "links" the C++ library and the Rust application together.
+//! In particular it provides the [`Init`] trait which is implemented for
+//! any struct that implements [`clap::Parser`]. The reason for this is that
+//! an important part of initializing the C++ library involves setting the
+//! gFlags which get resolved dynamically thanks to their reflection API.
+//!
+//! # Examples
+//!
+//! ```no_run
+//! use aos_init::Init;
+//! use clap::Parser;
+//!
+//! #[derive(Parser, Debug)]
+//! struct App {
+//! /// Time to sleep between pings.
+//! #[arg(long, default_value_t = 10000, value_name = "MICROS")]
+//! sleep: u64,
+//! }
+//!
+//! fn main() {
+//! // Initializes AOS and returns the App struct with the parsed CLI flags
+//! let app: App = App::init();
+//! // At this point your flags are parsed and AOS is initialized.
+//! }
+//! ```
+//! You can also use [`DefaultApp`] to avoid having to specify your own CLI options if you don't
+//! need them. For example:
+//!
+//! ```no_run
+//! use aos_init::{DefaultApp, Init};
+//! use clap::Parser;
+//!
+//! fn main() {
+//! // Initializes AOS. DefaultApp doesn't have any flags to parse.
+//! let _ = DefaultApp::init();
+//! // At this point AOS is initialized and you can create event loop.
+//! }
+//!```
+
+use std::{
+ env,
+ ffi::{CString, OsStr, OsString},
+ os::unix::prelude::OsStrExt,
+ sync::Once,
+};
+
+use clap::{
+ error::{ContextKind, ContextValue},
+ Arg, ArgAction, Error, Parser,
+};
autocxx::include_cpp! (
-#include "aos/init.h"
+#include "aos/init_for_rust.h"
safety!(unsafe)
generate!("aos::InitFromRust")
+generate!("aos::GetCppFlags")
+generate!("aos::FlagInfo")
+generate!("aos::SetCommandLineOption")
+generate!("aos::GetCommandLineOption")
);
-/// Initializes AOS.
-pub fn init() {
- static ONCE: Once = Once::new();
- ONCE.call_once(|| {
- let argv0 = std::env::args().next().expect("must have argv[0]");
- let argv0 = CString::new(argv0).expect("argv[0] may not have NUL");
- // SAFETY: argv0 is a NUL-terminated string.
- unsafe { ffi::aos::InitFromRust(argv0.as_ptr()) };
- });
+// Intended to be used only from here and test_init. Don't use it anywhere else please.
+#[doc(hidden)]
+pub mod internal {
+ use super::*;
+ /// Generic initialization for production and tests.
+ ///
+ /// Sets up the C++ side of things. Notably, it doesn't setup the command line flags.
+ pub fn init() {
+ static ONCE: Once = Once::new();
+ ONCE.call_once(|| {
+ // We leak the `CString` with `into_raw`. It's not sound for C++ to free
+ // it but `InitGoogleLogging` requries that it is long-lived.
+ let argv0 = std::env::args()
+ .map(|arg| CString::new(arg).expect("Arg may not have NUL"))
+ .next()
+ .expect("Missing argv[0]?")
+ .into_raw();
+ // SAFETY: argv0 is a well-defined CString.
+ unsafe {
+ ffi::aos::InitFromRust(argv0);
+ }
+ });
+ }
+}
+
+/// An application that doesn't need custom command line flags.
+///
+/// If you need your own command line flags, use any struct that derives [`clap::Parser`] instead.
+#[derive(Parser, Debug)]
+pub struct DefaultApp {}
+
+/// Trait used to append C++ gFlags to a clap CLI.
+pub trait Init: Parser {
+ /// Initializes an AOS application.
+ ///
+ /// Parses the command line flags and runs the initialization logic.
+ fn init() -> Self {
+ let this = Self::parse_with_cpp_flags();
+ // Rust logs to stderr by default. Make that true for C++ as that will be easier than
+ // managing one or multiple files across FFI. We can pipe the stderr to a file to get
+ // a log file if we want.
+ CxxFlag::set_option("logtostderr", "true".as_ref())
+ .expect("Error setting C++ flag: logtostderr");
+ internal::init();
+ // Non-test initialization below
+ env_logger::init();
+ this
+ }
+
+ /// Parses the comannd line arguments while also setting the C++ gFlags.
+ fn parse_with_cpp_flags() -> Self {
+ Self::parse_with_cpp_flags_from(env::args_os())
+ }
+
+ /// Like [`Init::parse_with_cpp_flags`] but read from an iterator.
+ fn parse_with_cpp_flags_from<I, T>(itr: I) -> Self
+ where
+ I: IntoIterator<Item = T>,
+ T: Into<OsString> + Clone,
+ {
+ let cxxflags = ffi::aos::GetCppFlags();
+ let cxxflags: Vec<CxxFlag> = cxxflags
+ .iter()
+ .map(|flag| CxxFlag::from(flag))
+ .filter(|flag| flag.name != "help" && flag.name != "version")
+ .collect();
+
+ let mut command = Self::command()
+ .next_help_heading("Flags from C++")
+ .args(cxxflags.iter().cloned());
+
+ let matches = command.clone().get_matches_from(itr);
+
+ for cxxflag in cxxflags {
+ let Some(mut value) = matches.get_raw(&cxxflag.name) else {
+ continue;
+ };
+ // We grab the last match as GFlags does.
+ let value = value.next_back().unwrap();
+ cxxflag.set(value).unwrap_or_else(|_| {
+ let mut error = Error::new(clap::error::ErrorKind::InvalidValue);
+
+ // Let user know how they messed up.
+ error.insert(
+ ContextKind::InvalidArg,
+ ContextValue::String(format!("--{}", cxxflag.name)),
+ );
+ error.insert(
+ ContextKind::InvalidValue,
+ ContextValue::String(
+ value
+ .to_owned()
+ .into_string()
+ .expect("Invalid UTF-8 String"),
+ ),
+ );
+ error.format(&mut command).exit()
+ })
+ }
+
+ match Self::from_arg_matches(&matches) {
+ Ok(flags) => flags,
+ Err(e) => e.format(&mut command).exit(),
+ }
+ }
+}
+
+impl<T: Parser> Init for T {}
+
+#[derive(Clone)]
+#[allow(unused)]
+struct CxxFlag {
+ name: String,
+ ty: String,
+ description: String,
+ default_value: String,
+ filename: String,
+}
+
+#[derive(Debug)]
+struct SetFlagError;
+
+impl CxxFlag {
+ /// Sets the command gFlag to the specified value.
+ fn set(&self, value: &OsStr) -> Result<(), SetFlagError> {
+ Self::set_option(&self.name, value)
+ }
+
+ /// Sets the command gFlag to the specified value.
+ fn set_option(name: &str, value: &OsStr) -> Result<(), SetFlagError> {
+ unsafe {
+ let name = CString::new(name.clone()).expect("Flag name may not have NUL");
+ let value = CString::new(value.as_bytes()).expect("Arg may not have NUL");
+ if ffi::aos::SetCommandLineOption(name.as_ptr(), value.as_ptr()) {
+ Ok(())
+ } else {
+ Err(SetFlagError)
+ }
+ }
+ }
+
+ #[allow(dead_code)]
+ fn get_option(name: &str) -> String {
+ unsafe {
+ let name = CString::new(name).expect("Flag may not have NUL");
+ ffi::aos::GetCommandLineOption(name.as_ptr()).to_string()
+ }
+ }
+}
+
+impl From<&ffi::aos::FlagInfo> for CxxFlag {
+ fn from(value: &ffi::aos::FlagInfo) -> Self {
+ Self {
+ name: value.name().to_string(),
+ ty: value.ty().to_string(),
+ description: value.description().to_string(),
+ default_value: value.default_value().to_string(),
+ filename: value.filename().to_string(),
+ }
+ }
+}
+
+impl From<CxxFlag> for Arg {
+ fn from(value: CxxFlag) -> Self {
+ assert_ne!(&value.name, "help");
+ Arg::new(&value.name)
+ .long(&value.name)
+ .help(&value.description)
+ .default_value(&value.default_value)
+ .action(ArgAction::Set)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::sync::Mutex;
+
+ use super::*;
+
+ #[derive(Parser)]
+ #[command()]
+ struct App {
+ #[arg(long)]
+ myarg: u64,
+ }
+
+ // We are sharing global state through gFlags. Use a mutex to prevent races.
+ static MUTEX: Mutex<()> = Mutex::new(());
+
+ #[test]
+ fn simple_rust() {
+ let _guard = MUTEX.lock();
+ let app = App::parse_with_cpp_flags_from(&["mytest", "--myarg", "23"]);
+ assert_eq!(app.myarg, 23);
+ }
+
+ #[test]
+ fn set_cxx_flag() {
+ let _guard = MUTEX.lock();
+ let app = App::parse_with_cpp_flags_from(&[
+ "mytest",
+ "--alsologtostderr",
+ "true",
+ "--myarg",
+ "23",
+ ]);
+ assert_eq!(app.myarg, 23);
+ assert_eq!(CxxFlag::get_option("alsologtostderr"), "true");
+ }
}
diff --git a/aos/init_for_rust.cc b/aos/init_for_rust.cc
new file mode 100644
index 0000000..9af4d83
--- /dev/null
+++ b/aos/init_for_rust.cc
@@ -0,0 +1,49 @@
+#include "aos/init_for_rust.h"
+
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+#include "aos/init.h"
+
+namespace aos {
+
+void InitFromRust(const char *argv0) {
+ CHECK(!IsInitialized()) << "Only initialize once.";
+
+ google::InitGoogleLogging(argv0);
+
+ // TODO(Brian): Where should Rust binaries be configured to write coredumps?
+
+ // TODO(Brian): Figure out what to do with allocator hooks for C++ and Rust.
+
+ MarkInitialized();
+}
+
+std::vector<FlagInfo> GetCppFlags() {
+ std::vector<gflags::CommandLineFlagInfo> info;
+ gflags::GetAllFlags(&info);
+ std::vector<FlagInfo> out;
+ for (const auto &flag : info) {
+ FlagInfo out_flag = {
+ .name_ = flag.name,
+ .type_ = flag.type,
+ .description_ = flag.description,
+ .default_value_ = flag.default_value,
+ .filename_ = flag.filename,
+ };
+ out.push_back(out_flag);
+ }
+ return out;
+}
+
+bool SetCommandLineOption(const char *name, const char *value) {
+ return !gflags::SetCommandLineOption(name, value).empty();
+}
+
+std::string GetCommandLineOption(const char *name) {
+ std::string out;
+ CHECK(gflags::GetCommandLineOption(name, &out)) << "Unknown flag " << name;
+ return out;
+}
+
+} // namespace aos
diff --git a/aos/init_for_rust.h b/aos/init_for_rust.h
new file mode 100644
index 0000000..86bfd66
--- /dev/null
+++ b/aos/init_for_rust.h
@@ -0,0 +1,47 @@
+#ifndef AOS_INIT_FOR_RUST_H_
+#define AOS_INIT_FOR_RUST_H_
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "aos/for_rust.h"
+#include "cxx.h"
+
+namespace aos {
+
+struct FlagInfo {
+ std::string name_;
+ std::string type_;
+ std::string description_;
+ std::string default_value_;
+ std::string filename_;
+
+ rust::Str name() const {
+ return StringViewToRustStr(std::string_view(name_));
+ }
+ rust::Str ty() const { return StringViewToRustStr(std::string_view(type_)); }
+ rust::Str description() const {
+ return StringViewToRustStr(std::string_view(description_));
+ }
+ rust::Str default_value() const {
+ return StringViewToRustStr(std::string_view(default_value_));
+ }
+ rust::Str filename() const {
+ return StringViewToRustStr(std::string_view(filename_));
+ }
+};
+
+// A special initialization function that initializes the C++ parts in a way
+// compatible with Rust. This requires careful coordination with `:init_rs`, do
+// not use it from anywhere else.
+void InitFromRust(const char *argv0);
+
+std::vector<FlagInfo> GetCppFlags();
+
+bool SetCommandLineOption(const char *name, const char *value);
+std::string GetCommandLineOption(const char *name);
+
+} // namespace aos
+
+#endif // AOS_INIT_FOR_RUST_H_
diff --git a/aos/json_to_flatbuffer.h b/aos/json_to_flatbuffer.h
index 6ef3544..deafaa7 100644
--- a/aos/json_to_flatbuffer.h
+++ b/aos/json_to_flatbuffer.h
@@ -75,6 +75,12 @@
Flatbuffer<T>::MiniReflectTypeTable(), json_options);
}
+template <typename T, typename Enable = T::Flatbuffer>
+inline ::std::string FlatbufferToJson(const fbs::Builder<T> &flatbuffer,
+ JsonOptions json_options = {}) {
+ return FlatbufferToJson(flatbuffer.AsFlatbufferSpan(), json_options);
+}
+
// Converts a flatbuffer::Table to JSON.
template <typename T>
typename std::enable_if<
diff --git a/aos/logging/log_namer.cc b/aos/logging/log_namer.cc
index 16ba0f2..412c74e 100644
--- a/aos/logging/log_namer.cc
+++ b/aos/logging/log_namer.cc
@@ -76,7 +76,7 @@
PLOG(FATAL) << "couldn't create final name";
}
// Fix basename formatting.
- LOG(INFO) << "Created log file (" << filename << "). Previous file was ("
+ LOG(INFO) << "Created log file (" << *filename << "). Previous file was ("
<< directory << "/" << previous << ").";
}
diff --git a/aos/network/sctp_lib.cc b/aos/network/sctp_lib.cc
index 829ae67..cf50ad6 100644
--- a/aos/network/sctp_lib.cc
+++ b/aos/network/sctp_lib.cc
@@ -4,6 +4,7 @@
#include <linux/sctp.h>
#include <net/if.h>
#include <netdb.h>
+#include <netinet/ip.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -26,6 +27,25 @@
DEFINE_bool(disable_ipv6, false, "disable ipv6");
DEFINE_int32(rmem, 0, "If nonzero, set rmem to this size.");
+// The Type of Service.
+// https://www.tucny.com/Home/dscp-tos
+//
+// We want to set the highest precedence (i.e. critical) with minimal delay. We
+// also want to be able to stuff the packets into bucket 0 for queue
+// disciplining. Experiments show that 176 works for this. Other values (e.g.
+// DSCP class EF) cannot be stuffed into bucket 0 (for unknown reasons).
+//
+// Note that the two least significant bits are reserved and should always set
+// to zero. Those two bits are the "Explicit Congestion Notification" bits. They
+// are controlled by the IP stack itself (and used by the router). We don't
+// control that via the TOS value we set here.
+DEFINE_int32(
+ sctp_tos, 176,
+ "The Type-Of-Service value to use. Defaults to a critical priority. "
+ "Always set values here whose two least significant bits are set to zero. "
+ "When using tcpdump, the `tos` field may show the least significant two "
+ "bits set to something other than zero.");
+
namespace aos::message_bridge {
namespace {
@@ -272,6 +292,13 @@
LOG(INFO) << "socket(" << Family(sockaddr_local)
<< ", SOCK_SEQPACKET, IPPROTOSCTP) = " << fd_;
{
+ // Set up Type-Of-Service.
+ //
+ // See comments for the --sctp_tos flag for more information.
+ int tos = IPTOS_DSCP(FLAGS_sctp_tos);
+ PCHECK(setsockopt(fd_, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == 0);
+ }
+ {
// Per https://tools.ietf.org/html/rfc6458
// Setting this to !0 allows event notifications to be interleaved
// with data if enabled. This typically only matters during congestion.
diff --git a/aos/starter/starterd_lib.cc b/aos/starter/starterd_lib.cc
index 95210c0..38d519d 100644
--- a/aos/starter/starterd_lib.cc
+++ b/aos/starter/starterd_lib.cc
@@ -19,6 +19,7 @@
DEFINE_uint32(queue_initialization_threads, 0,
"Number of threads to spin up to initialize the queue. 0 means "
"use the main thread.");
+DECLARE_bool(enable_ftrace);
namespace aos::starter {
@@ -214,6 +215,11 @@
if (info.ssi_signo == SIGCHLD) {
// SIGCHLD messages can be collapsed if multiple are received, so all
// applications must check their status.
+ if (FLAGS_enable_ftrace) {
+ ftrace_.FormatMessage("SIGCHLD");
+ ftrace_.TurnOffOrDie();
+ }
+
for (auto iter = applications_.begin(); iter != applications_.end();) {
if (iter->second.MaybeHandleSignal()) {
iter = applications_.erase(iter);
diff --git a/aos/starter/starterd_lib.h b/aos/starter/starterd_lib.h
index 3779c84..1ffc782 100644
--- a/aos/starter/starterd_lib.h
+++ b/aos/starter/starterd_lib.h
@@ -77,6 +77,8 @@
aos::PhasedLoopHandler *status_timer_;
aos::TimerHandler *cleanup_timer_;
+ aos::Ftrace ftrace_;
+
int status_count_ = 0;
const int max_status_count_;
diff --git a/aos/starter/subprocess.cc b/aos/starter/subprocess.cc
index 9606adc..d677922 100644
--- a/aos/starter/subprocess.cc
+++ b/aos/starter/subprocess.cc
@@ -266,6 +266,8 @@
if (application->has_memory_limit() && application->memory_limit() > 0) {
SetMemoryLimit(application->memory_limit());
}
+
+ set_stop_grace_period(std::chrono::nanoseconds(application->stop_time()));
}
void Application::DoStart() {
@@ -496,8 +498,7 @@
// Watchdog timer to SIGKILL application if it is still running 1 second
// after SIGINT
- stop_timer_->Schedule(event_loop_->monotonic_now() +
- std::chrono::seconds(1));
+ stop_timer_->Schedule(event_loop_->monotonic_now() + stop_grace_period_);
queue_restart_ = restart;
OnChange();
break;
diff --git a/aos/starter/subprocess.h b/aos/starter/subprocess.h
index ca7ef9d..eb36874 100644
--- a/aos/starter/subprocess.h
+++ b/aos/starter/subprocess.h
@@ -139,6 +139,14 @@
void set_capture_stderr(bool capture);
void set_run_as_sudo(bool value) { run_as_sudo_ = value; }
+ // Sets the time for a process to stop gracefully. If an application is asked
+ // to stop, but doesn't stop within the specified time limit, then it is
+ // forcefully killed. Defaults to 1 second unless overridden by the
+ // aos::Application instance in the constructor.
+ void set_stop_grace_period(std::chrono::nanoseconds stop_grace_period) {
+ stop_grace_period_ = stop_grace_period;
+ }
+
bool autostart() const { return autostart_; }
bool autorestart() const { return autorestart_; }
@@ -222,6 +230,7 @@
std::optional<uid_t> user_;
std::optional<gid_t> group_;
bool run_as_sudo_ = false;
+ std::chrono::nanoseconds stop_grace_period_ = std::chrono::seconds(1);
bool capture_stdout_ = false;
PipePair stdout_pipes_;
diff --git a/aos/starter/subprocess_reliable_test.cc b/aos/starter/subprocess_reliable_test.cc
index 0460bc3..701de11 100644
--- a/aos/starter/subprocess_reliable_test.cc
+++ b/aos/starter/subprocess_reliable_test.cc
@@ -3,6 +3,7 @@
#include <filesystem>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "aos/events/shm_event_loop.h"
@@ -124,4 +125,55 @@
ASSERT_TRUE(std::filesystem::exists(shutdown_signal_file));
}
+// Validates that a process that is known to take a while to stop can shut down
+// gracefully without being killed.
+TEST(SubprocessTest, CanSlowlyStopGracefully) {
+ const std::string config_file =
+ ::aos::testing::ArtifactPath("aos/events/pingpong_config.json");
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(config_file);
+ aos::ShmEventLoop event_loop(&config.message());
+
+ // Use a file to signal that the subprocess has started up properly and that
+ // the exit handler has been installed. Otherwise we risk killing the process
+ // uncleanly before the signal handler got installed.
+ auto signal_dir = std::filesystem::path(aos::testing::TestTmpDir()) /
+ "slow_death_startup_file_signals";
+ ASSERT_TRUE(std::filesystem::create_directory(signal_dir));
+ auto startup_signal_file = signal_dir / "startup";
+
+ // Create an application that should never get killed automatically. It should
+ // have plenty of time to shut down on its own. In this case, we use 2 seconds
+ // to mean "plenty of time".
+ auto application = std::make_unique<Application>("/bin/bash", "/bin/bash",
+ &event_loop, [] {});
+ application->set_args(
+ {"-c",
+ absl::StrCat(
+ "trap 'echo got int; sleep 2; echo shutting down; exit 0' SIGINT; "
+ "while true; do sleep 0.1; touch ",
+ startup_signal_file.string(), "; done;")});
+ application->set_capture_stdout(true);
+ application->set_stop_grace_period(std::chrono::seconds(999));
+ application->AddOnChange([&] {
+ if (application->status() == aos::starter::State::STOPPED) {
+ event_loop.Exit();
+ }
+ });
+ application->Start();
+ event_loop
+ .AddTimer([&] {
+ if (std::filesystem::exists(startup_signal_file)) {
+ // Now that the subprocess has properly started up, let's kill it.
+ application->Stop();
+ }
+ })
+ ->Schedule(event_loop.monotonic_now(), std::chrono::milliseconds(100));
+ event_loop.Run();
+
+ EXPECT_EQ(application->exit_code(), 0);
+ EXPECT_THAT(application->GetStdout(), ::testing::HasSubstr("got int"));
+ EXPECT_THAT(application->GetStdout(), ::testing::HasSubstr("shutting down"));
+}
+
} // namespace aos::starter::testing
diff --git a/aos/test_init.rs b/aos/test_init.rs
index a8380e2..d17d2ef 100644
--- a/aos/test_init.rs
+++ b/aos/test_init.rs
@@ -1,4 +1,4 @@
-use aos_init::init;
+use std::sync::Once;
autocxx::include_cpp! (
#include "aos/testing/tmpdir.h"
@@ -15,7 +15,11 @@
///
/// Panics if non-test initialization has already been performed.
pub fn test_init() {
- init();
- ffi::aos::testing::SetTestShmBase();
- // TODO(Brian): Do we want any of the other stuff that `:gtest_main` has?
+ static ONCE: Once = Once::new();
+ ONCE.call_once(|| {
+ aos_init::internal::init();
+ ffi::aos::testing::SetTestShmBase();
+ env_logger::builder().is_test(true).init();
+ // TODO(Brian): Do we want any of the other stuff that `:gtest_main` has?
+ });
}
diff --git a/aos/testing/BUILD b/aos/testing/BUILD
index 02459e4..eb9e2b2 100644
--- a/aos/testing/BUILD
+++ b/aos/testing/BUILD
@@ -1,3 +1,5 @@
+load("//tools/rust:defs.bzl", "rust_library")
+
cc_library(
name = "googletest",
testonly = True,
@@ -131,3 +133,17 @@
"@com_google_absl//absl/strings",
],
)
+
+rust_library(
+ name = "aos_rs",
+ testonly = True,
+ srcs = ["aos.rs"],
+ crate_name = "aos",
+ gen_docs = False,
+ gen_doctests = False,
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos:aos_rs",
+ "//aos:test_init_rs",
+ ],
+)
diff --git a/aos/testing/aos.rs b/aos/testing/aos.rs
new file mode 100644
index 0000000..6c7795d
--- /dev/null
+++ b/aos/testing/aos.rs
@@ -0,0 +1,7 @@
+// Reexport all of the [`aos`] crate
+pub use aos::*;
+
+/// Utilities for testing an AOS application.
+pub mod testing {
+ pub use aos_test_init as init;
+}
diff --git a/aos/time/time.cc b/aos/time/time.cc
index 6a25aa9..e75fe45 100644
--- a/aos/time/time.cc
+++ b/aos/time/time.cc
@@ -6,6 +6,7 @@
#include <cstring>
#include <ctime>
#include <iomanip>
+#include <sstream>
#ifdef __linux__
@@ -79,6 +80,18 @@
return stream;
}
+std::string ToString(const aos::monotonic_clock::time_point &now) {
+ std::ostringstream stream;
+ stream << now;
+ return stream.str();
+}
+
+std::string ToString(const aos::realtime_clock::time_point &now) {
+ std::ostringstream stream;
+ stream << now;
+ return stream.str();
+}
+
#ifdef __linux__
std::optional<monotonic_clock::time_point> monotonic_clock::FromString(
const std::string_view now) {
@@ -170,6 +183,16 @@
: std::chrono::duration_cast<std::chrono::seconds>(
now.time_since_epoch());
+ // We can run into some corner cases where the seconds value is large enough
+ // to cause the conversion to nanoseconds to overflow. That is undefined
+ // behaviour so we prevent it with this check here.
+ if (int64_t result;
+ __builtin_mul_overflow(seconds.count(), 1'000'000'000, &result)) {
+ stream << "(unrepresentable realtime " << now.time_since_epoch().count()
+ << ")";
+ return stream;
+ }
+
std::time_t seconds_t = seconds.count();
stream << std::put_time(localtime_r(&seconds_t, &tm), "%Y-%m-%d_%H-%M-%S.")
<< std::setfill('0') << std::setw(9)
diff --git a/aos/time/time.h b/aos/time/time.h
index 1a5cbd1..8462625 100644
--- a/aos/time/time.h
+++ b/aos/time/time.h
@@ -81,6 +81,9 @@
std::ostream &operator<<(std::ostream &stream,
const aos::realtime_clock::time_point &now);
+std::string ToString(const aos::monotonic_clock::time_point &now);
+std::string ToString(const aos::realtime_clock::time_point &now);
+
namespace time {
#ifdef __linux__
diff --git a/aos/time/time_test.cc b/aos/time/time_test.cc
index 63a145b..97116b0 100644
--- a/aos/time/time_test.cc
+++ b/aos/time/time_test.cc
@@ -208,8 +208,9 @@
std::stringstream s;
s << t;
- EXPECT_EQ(s.str(), "1677-09-21_00-12-43.145224192");
- EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
+ // min_time happens to be unrepresentable because of rounding and signed
+ // integer overflow.
+ EXPECT_EQ(s.str(), "(unrepresentable realtime -9223372036854775808)");
}
{
@@ -224,4 +225,12 @@
}
}
+// Test that ToString works for monotonic and realtime time points.
+TEST(TimeTest, ToStringTimePoints) {
+ EXPECT_EQ(ToString(realtime_clock::epoch() + std::chrono::hours(5 * 24) +
+ std::chrono::seconds(11) + std::chrono::milliseconds(5)),
+ "1970-01-06_00-00-11.005000000");
+ EXPECT_EQ(ToString(monotonic_clock::min_time), "-9223372036.854775808sec");
+}
+
} // namespace aos::time::testing
diff --git a/aos/util/error_counter.h b/aos/util/error_counter.h
index 3e69a71..1e04571 100644
--- a/aos/util/error_counter.h
+++ b/aos/util/error_counter.h
@@ -41,6 +41,16 @@
return offset;
}
+ template <typename Static>
+ static void InitializeStaticFbs(Static *builder) {
+ CHECK(builder->reserve(kNumErrors));
+ for (size_t ii = 0; ii < kNumErrors; ++ii) {
+ auto element = CHECK_NOTNULL(builder->emplace_back());
+ element->set_error(static_cast<Error>(ii));
+ element->set_count(0);
+ }
+ }
+
void set_mutable_vector(
flatbuffers::Vector<flatbuffers::Offset<Count>> *vector) {
vector_ = vector;
@@ -91,6 +101,14 @@
return offset;
}
+ template <typename Static>
+ void PopulateCountsStaticFbs(Static *builder) const {
+ ErrorCounter<Error, Count>::InitializeStaticFbs(builder);
+ for (size_t ii = 0; ii < kNumErrors; ++ii) {
+ builder->at(ii).set_count(error_counts_.at(ii));
+ }
+ }
+
void IncrementError(Error error) {
DCHECK_LT(static_cast<size_t>(error), error_counts_.size());
error_counts_.at(static_cast<size_t>(error))++;
diff --git a/aos/util/mcap_logger_test.cc b/aos/util/mcap_logger_test.cc
index c6febf9..3684e93 100644
--- a/aos/util/mcap_logger_test.cc
+++ b/aos/util/mcap_logger_test.cc
@@ -81,6 +81,20 @@
"values": {
"items": {
"properties": {
+ "attributes": {
+ "items": {
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ },
"documentation": {
"items": {
"type": "string"
diff --git a/aos/util/trapezoid_profile.cc b/aos/util/trapezoid_profile.cc
index 938ca75..05e79ca 100644
--- a/aos/util/trapezoid_profile.cc
+++ b/aos/util/trapezoid_profile.cc
@@ -1,54 +1,70 @@
#include "aos/util/trapezoid_profile.h"
-#include "aos/logging/logging.h"
-
-using ::Eigen::Matrix;
+#include "glog/logging.h"
namespace aos::util {
-TrapezoidProfile::TrapezoidProfile(::std::chrono::nanoseconds delta_time)
- : maximum_acceleration_(0), maximum_velocity_(0), timestep_(delta_time) {
+AsymmetricTrapezoidProfile::AsymmetricTrapezoidProfile(
+ ::std::chrono::nanoseconds delta_time)
+ : timestep_(delta_time) {
output_.setZero();
}
-void TrapezoidProfile::UpdateVals(double acceleration, double delta_time) {
+void AsymmetricTrapezoidProfile::UpdateVals(double acceleration,
+ double delta_time) {
output_(0) +=
output_(1) * delta_time + 0.5 * acceleration * delta_time * delta_time;
output_(1) += acceleration * delta_time;
}
-const Matrix<double, 2, 1> &TrapezoidProfile::Update(double goal_position,
- double goal_velocity) {
- CalculateTimes(goal_position - output_(0), goal_velocity);
+const Eigen::Matrix<double, 2, 1> &AsymmetricTrapezoidProfile::Update(
+ double goal_position, double goal_velocity) {
+ CalculateTimes(goal_position - output_(0), goal_velocity, output_);
double next_timestep = ::aos::time::DurationInSeconds(timestep_);
+ if (deceleration_reversal_time_ > next_timestep) {
+ UpdateVals(deceleration_reversal_, next_timestep);
+ return output_;
+ }
+
+ UpdateVals(deceleration_reversal_, deceleration_reversal_time_);
+ next_timestep -= deceleration_reversal_time_;
+
if (acceleration_time_ > next_timestep) {
UpdateVals(acceleration_, next_timestep);
- } else {
- UpdateVals(acceleration_, acceleration_time_);
+ return output_;
+ }
- next_timestep -= acceleration_time_;
- if (constant_time_ > next_timestep) {
- UpdateVals(0, next_timestep);
- } else {
- UpdateVals(0, constant_time_);
- next_timestep -= constant_time_;
- if (deceleration_time_ > next_timestep) {
- UpdateVals(deceleration_, next_timestep);
- } else {
- UpdateVals(deceleration_, deceleration_time_);
- next_timestep -= deceleration_time_;
- UpdateVals(0, next_timestep);
- }
+ UpdateVals(acceleration_, acceleration_time_);
+ next_timestep -= acceleration_time_;
+
+ if (constant_time_ > next_timestep) {
+ UpdateVals(0, next_timestep);
+ return output_;
+ }
+
+ UpdateVals(0, constant_time_);
+ next_timestep -= constant_time_;
+ if (deceleration_time_ > next_timestep) {
+ UpdateVals(deceleration_, next_timestep);
+ } else {
+ UpdateVals(deceleration_, deceleration_time_);
+ next_timestep -= deceleration_time_;
+ UpdateVals(0, next_timestep);
+
+ if (next_timestep >= 0 && goal_velocity == 0) {
+ output_(0) = goal_position;
+ output_(1) = goal_velocity;
}
}
return output_;
}
-void TrapezoidProfile::CalculateTimes(double distance_to_target,
- double goal_velocity) {
+void AsymmetricTrapezoidProfile::CalculateTimes(
+ double distance_to_target, double goal_velocity,
+ Eigen::Matrix<double, 2, 1> current) {
if (distance_to_target == 0) {
// We're there. Stop everything.
// TODO(aschuh): Deal with velocity not right.
@@ -57,39 +73,69 @@
constant_time_ = 0;
deceleration_time_ = 0;
deceleration_ = 0;
+ deceleration_reversal_time_ = 0;
+ deceleration_reversal_ = 0;
return;
} else if (distance_to_target < 0) {
// Recurse with everything inverted.
- output_(1) *= -1;
- CalculateTimes(-distance_to_target, -goal_velocity);
- output_(1) *= -1;
+ current(1) *= -1;
+ CalculateTimes(-distance_to_target, -goal_velocity, current);
acceleration_ *= -1;
deceleration_ *= -1;
+ deceleration_reversal_ *= -1;
return;
}
constant_time_ = 0;
acceleration_ = maximum_acceleration_;
+
+ // Calculate the fastest speed we could get going to by the distance to
+ // target. We will have normalized everything out to be a positive distance
+ // by now so we never have to deal with going "backwards".
double maximum_acceleration_velocity =
distance_to_target * 2 * std::abs(acceleration_) +
- output_(1) * output_(1);
- if (maximum_acceleration_velocity > 0) {
- maximum_acceleration_velocity = sqrt(maximum_acceleration_velocity);
- } else {
- maximum_acceleration_velocity = -sqrt(-maximum_acceleration_velocity);
- }
+ current(1) * current(1);
+ CHECK_GE(maximum_acceleration_velocity, 0);
+ maximum_acceleration_velocity = sqrt(maximum_acceleration_velocity);
- // Since we know what we'd have to do if we kept after it to decelerate, we
- // know the sign of the acceleration.
+ // If we could get going faster than the target, we will need to decelerate
+ // after accelerating.
if (maximum_acceleration_velocity > goal_velocity) {
- deceleration_ = -maximum_acceleration_;
+ deceleration_ = -maximum_deceleration_;
} else {
+ // We couldn't get up to speed by the destination. Set our decel to
+ // accelerate to keep accelerating to get up to speed.
+ //
+ // Note: goal_velocity != 0 isn't well tested, use at your own risk.
+ LOG(FATAL) << "Untested";
deceleration_ = maximum_acceleration_;
}
+ // If we are going away from the goal, we will need to change directions.
+ if (current(1) < 0) {
+ deceleration_reversal_time_ = current(1) / deceleration_;
+ deceleration_reversal_ = -deceleration_;
+ } else if ((goal_velocity - current(1)) * (goal_velocity + current(1)) <
+ 2.0 * deceleration_ * distance_to_target) {
+ // Then, can we stop in time if we get after it? If so, we don't need to
+ // decel first before running the profile.
+ deceleration_reversal_time_ = -current(1) / deceleration_;
+ deceleration_reversal_ = deceleration_;
+ } else {
+ // Otherwise, we are all good and don't need to handle reversing.
+ deceleration_reversal_time_ = 0.0;
+ deceleration_reversal_ = 0.0;
+ }
+
+ current(0) += current(1) * deceleration_reversal_time_ +
+ 0.5 * deceleration_reversal_ * deceleration_reversal_time_ *
+ deceleration_reversal_time_;
+ current(1) += deceleration_reversal_ * deceleration_reversal_time_;
+ // OK, now we've compensated for slowing down.
+
// We now know the top velocity we can get to.
- double top_velocity = sqrt(
- (distance_to_target + (output_(1) * output_(1)) / (2.0 * acceleration_) +
+ const double top_velocity = sqrt(
+ (distance_to_target + (current(1) * current(1)) / (2.0 * acceleration_) +
(goal_velocity * goal_velocity) / (2.0 * deceleration_)) /
(-1.0 / (2.0 * deceleration_) + 1.0 / (2.0 * acceleration_)));
@@ -97,24 +143,32 @@
// how long to go at constant velocity.
if (top_velocity > maximum_velocity_) {
acceleration_time_ =
- (maximum_velocity_ - output_(1)) / maximum_acceleration_;
- constant_time_ =
- (distance_to_target + (goal_velocity * goal_velocity -
- maximum_velocity_ * maximum_velocity_) /
- (2.0 * maximum_acceleration_)) /
- maximum_velocity_;
+ (maximum_velocity_ - current(1)) / maximum_acceleration_;
+ constant_time_ = ((-0.5 * maximum_acceleration_ * acceleration_time_ *
+ acceleration_time_ -
+ current(1) * acceleration_time_) +
+ distance_to_target +
+ (goal_velocity * goal_velocity -
+ maximum_velocity_ * maximum_velocity_) /
+ (2.0 * maximum_deceleration_)) /
+ maximum_velocity_;
} else {
- acceleration_time_ = (top_velocity - output_(1)) / acceleration_;
+ acceleration_time_ = (top_velocity - current(1)) / acceleration_;
}
- AOS_CHECK_GT(top_velocity, -maximum_velocity_);
+ CHECK_GT(top_velocity, -maximum_velocity_);
- if (output_(1) > maximum_velocity_) {
+ if (current(1) > maximum_velocity_) {
constant_time_ = 0;
acceleration_time_ = 0;
}
- deceleration_time_ = (goal_velocity - top_velocity) / deceleration_;
+ deceleration_time_ =
+ (goal_velocity - ::std::min(top_velocity, maximum_velocity_)) /
+ deceleration_;
+ if (acceleration_time_ <= 0) acceleration_time_ = 0;
+ if (constant_time_ <= 0) constant_time_ = 0;
+ if (deceleration_time_ <= 0) deceleration_time_ = 0;
}
} // namespace aos::util
diff --git a/aos/util/trapezoid_profile.h b/aos/util/trapezoid_profile.h
index 6669777..621ff30 100644
--- a/aos/util/trapezoid_profile.h
+++ b/aos/util/trapezoid_profile.h
@@ -9,57 +9,116 @@
namespace aos::util {
// Calculates a trapezoidal motion profile (like for a control loop's goals).
-// Supports having the end speed and position changed in the middle.
+// Supports having the destination position, acceleration, and velocity caps
+// changed in the middle, and for having different accelerations and
+// decelerations.
//
// The only units assumption that this class makes is that the unit of time is
// seconds.
-class TrapezoidProfile {
+class AsymmetricTrapezoidProfile {
public:
- // delta_time is how long between each call to Update.
- TrapezoidProfile(::std::chrono::nanoseconds delta_time);
+ // Constructs a profile. delta_time is the timestep to assume when solving.
+ AsymmetricTrapezoidProfile(::std::chrono::nanoseconds delta_time);
- // Updates the state.
+ // Updates the state to provide the next position and velocity to go to to
+ // follow the profile.
const Eigen::Matrix<double, 2, 1> &Update(double goal_position,
double goal_velocity);
- // Useful for preventing windup etc.
+
+ // Updates the internal position. Useful for handling windup when a loop
+ // saturates or gets disabled.
void MoveCurrentState(const Eigen::Matrix<double, 2, 1> ¤t) {
output_ = current;
}
- // Useful for preventing windup etc.
+ // Adjusts the internal position by the provided position delta.
void MoveGoal(double dx) { output_(0, 0) += dx; }
+ // Sets the internal position to the provided position.
void SetGoal(double x) { output_(0, 0) = x; }
void set_maximum_acceleration(double maximum_acceleration) {
maximum_acceleration_ = maximum_acceleration;
}
+ void set_maximum_deceleration(double maximum_deceleration) {
+ maximum_deceleration_ = maximum_deceleration;
+ }
+
void set_maximum_velocity(double maximum_velocity) {
maximum_velocity_ = maximum_velocity;
}
private:
- // Basic kinematics to update output_, given that we are going to accelerate
- // by acceleration over delta_time.
+ // Updates output_ to match the basic kinematics, given that we are going to
+ // accelerate by acceleration over delta_time.
void UpdateVals(double acceleration, double delta_time);
// Calculates how long to go for each segment.
- void CalculateTimes(double distance_to_target, double goal_velocity);
+ void CalculateTimes(double distance_to_target, double goal_velocity,
+ Eigen::Matrix<double, 2, 1> current);
// output_ is where it should go at time_.
Eigen::Matrix<double, 2, 1> output_;
+ // Time and acceleration to slow down if we need to reverse directions.
+ double deceleration_reversal_time_;
+ double deceleration_reversal_;
+
+ // Time and acceleration to speed up with.
double acceleration_time_;
double acceleration_;
+ // Time to go at max speed at.
double constant_time_;
+ // Time and acceleration to slow down with.
double deceleration_time_;
double deceleration_;
- double maximum_acceleration_;
- double maximum_velocity_;
+ double maximum_acceleration_ = 0;
+ double maximum_deceleration_ = 0;
+ double maximum_velocity_ = 0;
// How long between calls to Update.
::std::chrono::nanoseconds timestep_;
- DISALLOW_COPY_AND_ASSIGN(TrapezoidProfile);
+ DISALLOW_COPY_AND_ASSIGN(AsymmetricTrapezoidProfile);
+};
+
+// Class to implement a AsymmetricTrapezoidProfile where both acceleration and
+// deceleration match.
+class TrapezoidProfile {
+ public:
+ TrapezoidProfile(::std::chrono::nanoseconds delta_time)
+ : asymmetric_trapezoid_profile_(delta_time) {}
+
+ // Updates the state to provide the next position and velocity to go to to
+ // follow the profile.
+ const Eigen::Matrix<double, 2, 1> &Update(double goal_position,
+ double goal_velocity) {
+ return asymmetric_trapezoid_profile_.Update(goal_position, goal_velocity);
+ }
+
+ // Updates the internal position. Useful for handling windup when a loop
+ // saturates or gets disabled.
+ void MoveCurrentState(const Eigen::Matrix<double, 2, 1> ¤t) {
+ asymmetric_trapezoid_profile_.MoveCurrentState(current);
+ }
+
+ // Adjusts the internal position by the provided position delta.
+ void MoveGoal(double dx) { asymmetric_trapezoid_profile_.MoveGoal(dx); }
+
+ // Sets the internal position to the provided position.
+ void SetGoal(double x) { asymmetric_trapezoid_profile_.SetGoal(x); }
+
+ void set_maximum_acceleration(double maximum_acceleration) {
+ asymmetric_trapezoid_profile_.set_maximum_acceleration(
+ maximum_acceleration);
+ asymmetric_trapezoid_profile_.set_maximum_deceleration(
+ maximum_acceleration);
+ }
+ void set_maximum_velocity(double maximum_velocity) {
+ asymmetric_trapezoid_profile_.set_maximum_velocity(maximum_velocity);
+ }
+
+ private:
+ AsymmetricTrapezoidProfile asymmetric_trapezoid_profile_;
};
} // namespace aos::util
diff --git a/aos/util/trapezoid_profile_test.cc b/aos/util/trapezoid_profile_test.cc
index 92b9996..54ffeb5 100644
--- a/aos/util/trapezoid_profile_test.cc
+++ b/aos/util/trapezoid_profile_test.cc
@@ -1,6 +1,7 @@
#include "aos/util/trapezoid_profile.h"
#include "Eigen/Dense"
+#include "glog/logging.h"
#include "gtest/gtest.h"
namespace aos::util::testing {
@@ -10,9 +11,10 @@
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
protected:
- TrapezoidProfileTest() : profile_(delta_time) {
+ TrapezoidProfileTest() : profile_(kDeltaTime) {
position_.setZero();
profile_.set_maximum_acceleration(0.75);
+ profile_.set_maximum_deceleration(0.75);
profile_.set_maximum_velocity(1.75);
}
@@ -21,9 +23,19 @@
position_ = profile_.Update(goal_position, goal_velocity);
}
+ void RunFor(double goal_position, double goal_velocity,
+ std::chrono::nanoseconds duration) {
+ while (duration > std::chrono::nanoseconds(0)) {
+ position_ = profile_.Update(goal_position, goal_velocity);
+ duration -= kDeltaTime;
+ }
+
+ ASSERT_EQ(duration.count(), 0);
+ }
+
const Eigen::Matrix<double, 2, 1> &position() { return position_; }
- TrapezoidProfile profile_;
+ AsymmetricTrapezoidProfile profile_;
::testing::AssertionResult At(double position, double velocity) {
static const double kDoubleNear = 0.00001;
@@ -40,33 +52,31 @@
}
private:
- static constexpr ::std::chrono::nanoseconds delta_time =
+ static constexpr ::std::chrono::nanoseconds kDeltaTime =
::std::chrono::milliseconds(10);
Eigen::Matrix<double, 2, 1> position_;
};
-constexpr ::std::chrono::nanoseconds TrapezoidProfileTest::delta_time;
+constexpr ::std::chrono::nanoseconds TrapezoidProfileTest::kDeltaTime;
TEST_F(TrapezoidProfileTest, ReachesGoal) {
- for (int i = 0; i < 450; ++i) {
- RunIteration(3, 0);
- }
+ RunFor(3, 0, std::chrono::milliseconds(4500));
EXPECT_TRUE(At(3, 0));
}
-// Tests that decresing the maximum velocity in the middle when it is already
+// Tests that decreasing the maximum velocity in the middle when it is already
// moving faster than the new max is handled correctly.
TEST_F(TrapezoidProfileTest, ContinousUnderVelChange) {
profile_.set_maximum_velocity(1.75);
- RunIteration(12.0, 0);
+ RunFor(12.0, 0, std::chrono::milliseconds(10));
double last_pos = position()(0);
double last_vel = 1.75;
for (int i = 0; i < 1600; ++i) {
if (i == 400) {
profile_.set_maximum_velocity(0.75);
}
- RunIteration(12.0, 0);
+ RunFor(12.0, 0, std::chrono::milliseconds(10));
if (i >= 400) {
EXPECT_TRUE(::std::abs(last_pos - position()(0)) <= 1.75 * 0.01);
EXPECT_NEAR(last_vel, ::std::abs(last_pos - position()(0)), 0.0001);
@@ -79,43 +89,212 @@
// There is some somewhat tricky code for dealing with going backwards.
TEST_F(TrapezoidProfileTest, Backwards) {
- for (int i = 0; i < 400; ++i) {
- RunIteration(-2, 0);
- }
+ RunFor(-2, 0, std::chrono::milliseconds(4000));
EXPECT_TRUE(At(-2, 0));
}
TEST_F(TrapezoidProfileTest, SwitchGoalInMiddle) {
- for (int i = 0; i < 200; ++i) {
- RunIteration(-2, 0);
- }
+ RunFor(-2, 0, std::chrono::milliseconds(2000));
EXPECT_FALSE(At(-2, 0));
- for (int i = 0; i < 550; ++i) {
- RunIteration(0, 0);
- }
+ RunFor(0, 0, std::chrono::milliseconds(5500));
EXPECT_TRUE(At(0, 0));
}
// Checks to make sure that it hits top speed.
TEST_F(TrapezoidProfileTest, TopSpeed) {
- for (int i = 0; i < 200; ++i) {
- RunIteration(4, 0);
- }
+ RunFor(4, 0, std::chrono::milliseconds(2000));
EXPECT_NEAR(1.5, position()(1), 10e-5);
- for (int i = 0; i < 2000; ++i) {
- RunIteration(4, 0);
- }
+ RunFor(4, 0, std::chrono::milliseconds(20000));
EXPECT_TRUE(At(4, 0));
}
// Tests that the position and velocity exactly match at the end. Some code we
// have assumes this to be true as a simplification.
TEST_F(TrapezoidProfileTest, ExactlyReachesGoal) {
- for (int i = 0; i < 450; ++i) {
- RunIteration(1, 0);
- }
+ RunFor(1, 0, std::chrono::milliseconds(4500));
EXPECT_EQ(position()(1), 0.0);
EXPECT_EQ(position()(0), 1.0);
}
+// Tests that we can move a goal without the trajectory teleporting. The goal
+// needs to move to something we haven't already passed, but will blow by.
+TEST_F(TrapezoidProfileTest, MoveGoal) {
+ profile_.set_maximum_acceleration(2.0);
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(2.0);
+
+ RunFor(5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(1.0, 2.0));
+ RunFor(5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(3.0, 2.0));
+ RunFor(3.5, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(4.0, 0.0));
+ RunFor(3.5, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(3.5, 0.0));
+}
+
+// Tests that we can move a goal back before where we currently are without
+// teleporting.
+TEST_F(TrapezoidProfileTest, MoveGoalFar) {
+ profile_.set_maximum_acceleration(2.0);
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(2.0);
+
+ RunFor(5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(1.0, 2.0));
+ RunFor(5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(3.0, 2.0));
+ RunFor(2.5, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(4.0, 0.0));
+ RunFor(2.5, 0, std::chrono::seconds(2));
+ EXPECT_TRUE(At(2.5, 0.0));
+}
+
+// Tests that we can move a goal without the trajectory teleporting. The goal
+// needs to move to something we haven't already passed, but will blow by. Do
+// this one in the negative direction.
+TEST_F(TrapezoidProfileTest, MoveGoalNegative) {
+ profile_.set_maximum_acceleration(2.0);
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(2.0);
+
+ RunFor(-5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-1.0, -2.0));
+ RunFor(-5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-3.0, -2.0));
+ RunFor(-3.5, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-4.0, 0.0));
+ RunFor(-3.5, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-3.5, 0.0));
+}
+
+// Tests that we can move a goal back before where we currently are without
+// teleporting. Do this one in the negative direction.
+TEST_F(TrapezoidProfileTest, MoveGoalNegativeFar) {
+ profile_.set_maximum_acceleration(2.0);
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(2.0);
+
+ RunFor(-5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-1.0, -2.0));
+ RunFor(-5.0, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-3.0, -2.0));
+ RunFor(-2.5, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-4.0, 0.0));
+ RunFor(-2.5, 0, std::chrono::seconds(2));
+ EXPECT_TRUE(At(-2.5, 0.0));
+}
+
+// Tests that we can execute a profile with acceleration and deceleration not
+// matching in magnitude.
+TEST_F(TrapezoidProfileTest, AsymmetricAccelDecel) {
+ // Accelerates up until t=1. Will be at x=0.5
+ profile_.set_maximum_acceleration(1.0);
+ // Decelerates in t=0.5 Will take x=0.25
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(1.0);
+
+ RunFor(1.75, 0, std::chrono::seconds(1));
+
+ EXPECT_TRUE(At(0.5, 1.0));
+
+ RunFor(1.75, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(1.5, 1.0));
+ RunFor(1.75, 0, std::chrono::milliseconds(500));
+ EXPECT_TRUE(At(1.75, 0.0));
+}
+
+// Tests that we can execute a profile with acceleration and deceleration not
+// matching in magnitude, and hitting saturation.
+TEST_F(TrapezoidProfileTest, AsymmetricAccelDecelUnconstrained) {
+ // Accelerates up until t=1. Will be at x=0.5
+ profile_.set_maximum_acceleration(1.0);
+ // Decelerates in t=0.5 Will take x=0.25
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(2.0);
+
+ RunFor(0.75, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(0.5, 1.0));
+
+ RunFor(0.75, 0, std::chrono::milliseconds(500));
+ EXPECT_TRUE(At(0.75, 0.0));
+}
+
+// Tests that we can execute a profile with acceleration and deceleration not
+// matching in magnitude, and hitting saturation.
+TEST_F(TrapezoidProfileTest, AsymmetricAccelDecelUnconstrainedNegative) {
+ // Accelerates up until t=1. Will be at x=0.5
+ profile_.set_maximum_acceleration(1.0);
+ // Decelerates in t=0.5 Will take x=0.25
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(2.0);
+
+ RunFor(-0.75, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-0.5, -1.0));
+
+ RunFor(-0.75, 0, std::chrono::milliseconds(500));
+ EXPECT_TRUE(At(-0.75, 0.0));
+}
+
+// Tests that we can execute a profile with acceleration and deceleration not
+// matching in magnitude when going in the negative direction.
+TEST_F(TrapezoidProfileTest, AsymmetricAccelDecelNegative) {
+ // Accelerates up until t=1. Will be at x=0.5
+ profile_.set_maximum_acceleration(1.0);
+ // Decelerates in t=0.5 Will take x=0.25
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(1.0);
+
+ RunFor(-1.75, 0, std::chrono::seconds(1));
+
+ EXPECT_TRUE(At(-0.5, -1.0));
+
+ RunFor(-1.75, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(-1.5, -1.0));
+ RunFor(-1.75, 0, std::chrono::milliseconds(500));
+ EXPECT_TRUE(At(-1.75, 0.0));
+}
+
+// Tests that we can move the goal when an asymmetric profile is executing.
+TEST_F(TrapezoidProfileTest, AsymmetricAccelDecelMoveGoal) {
+ // Accelerates up until t=1. Will be at x=0.5
+ profile_.set_maximum_acceleration(1.0);
+ // Decelerates in t=0.5 Will take x=0.25
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(1.0);
+
+ RunFor(1.75, 0, std::chrono::seconds(1));
+
+ EXPECT_TRUE(At(0.5, 1.0));
+
+ RunFor(1.75, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(1.5, 1.0));
+ RunFor(1.6, 0, std::chrono::milliseconds(500));
+ EXPECT_TRUE(At(1.75, 0.0));
+ RunFor(1.6, 0, std::chrono::milliseconds(520));
+ RunFor(1.6, 0, std::chrono::milliseconds(2500));
+ EXPECT_TRUE(At(1.6, 0.0));
+}
+
+// Tests that we can move the goal when an asymmetric profile is executing in
+// the negative direction.
+TEST_F(TrapezoidProfileTest, AsymmetricAccelDecelMoveGoalFar) {
+ // Accelerates up until t=1. Will be at x=0.5
+ profile_.set_maximum_acceleration(1.0);
+ // Decelerates in t=0.5 Will take x=0.25
+ profile_.set_maximum_deceleration(2.0);
+ profile_.set_maximum_velocity(1.0);
+
+ RunFor(1.75, 0, std::chrono::seconds(1));
+
+ EXPECT_TRUE(At(0.5, 1.0));
+
+ RunFor(1.75, 0, std::chrono::seconds(1));
+ EXPECT_TRUE(At(1.5, 1.0));
+ RunFor(1.0, 0, std::chrono::milliseconds(500));
+ EXPECT_TRUE(At(1.75, 0.0));
+ RunFor(1.0, 0, std::chrono::milliseconds(2500));
+ EXPECT_TRUE(At(1.0, 0.0));
+}
+
} // namespace aos::util::testing
diff --git a/aos/uuid.h b/aos/uuid.h
index 8dd93d4..a2cf8ae 100644
--- a/aos/uuid.h
+++ b/aos/uuid.h
@@ -8,6 +8,7 @@
#include "absl/types/span.h"
#include "flatbuffers/flatbuffers.h"
+#include "glog/logging.h"
namespace aos {
@@ -66,6 +67,11 @@
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> PackVector(
flatbuffers::FlatBufferBuilder *fbb) const;
+ template <typename T>
+ void PackStaticVector(T *static_vector) const {
+ CHECK(static_vector->FromData(data_.data(), data_.size()));
+ }
+
// Returns a human-readable string representing this UUID.
//
// This is done without any memory allocation, which means it's returned in a
diff --git a/build_tests/BUILD b/build_tests/BUILD
index f0ff94d..282365e 100644
--- a/build_tests/BUILD
+++ b/build_tests/BUILD
@@ -4,7 +4,7 @@
load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_go_library", "flatbuffer_py_library")
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("//tools/build_rules:apache.bzl", "apache_wrapper")
-load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
+load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library")
load("//tools/build_rules:autocxx.bzl", "autocxx_library")
cc_test(
@@ -153,12 +153,6 @@
target_compatible_with = ["//tools/platforms/rust:has_support"],
)
-rust_test(
- name = "hello_lib_test",
- crate = ":hello_lib",
- target_compatible_with = ["//tools/platforms/rust:has_support"],
-)
-
rust_binary(
name = "rust_hello",
srcs = ["rust_hello.rs"],
@@ -199,15 +193,6 @@
target_compatible_with = ["//tools/platforms/rust:has_support"],
)
-rust_test(
- name = "hello_autocxx_test",
- crate = ":hello_autocxx",
- # TODO: Make Rust play happy with pic vs nopic. Details at:
- # https://github.com/bazelbuild/rules_rust/issues/118
- rustc_flags = ["-Crelocation-model=static"],
- target_compatible_with = ["//tools/platforms/rust:has_support"],
-)
-
py_test(
name = "upstream_python_test",
srcs = [
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/3D model/70555-0036_stp/705550036.stp b/circuit_boards/CAN_boards/CAN_Terminator/3D model/70555-0036_stp/705550036.stp
new file mode 100644
index 0000000..4c49dbe
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/3D model/70555-0036_stp/705550036.stp
@@ -0,0 +1,4390 @@
+ISO-10303-21;
+HEADER;
+FILE_DESCRIPTION((''),'2;1');
+FILE_NAME('705550036','2016-12-06T',('ghp'),(''),
+'PRO/ENGINEER BY PARAMETRIC TECHNOLOGY CORPORATION, 2012310',
+'PRO/ENGINEER BY PARAMETRIC TECHNOLOGY CORPORATION, 2012310','');
+FILE_SCHEMA(('CONFIG_CONTROL_DESIGN'));
+ENDSEC;
+DATA;
+#1=DIRECTION('',(1.E0,0.E0,0.E0));
+#2=VECTOR('',#1,7.E-2);
+#3=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,-2.25E-1));
+#4=LINE('',#3,#2);
+#5=DIRECTION('',(7.071067811866E-1,-7.071067811865E-1,0.E0));
+#6=VECTOR('',#5,2.121320343560E-2);
+#7=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,-2.25E-1));
+#8=LINE('',#7,#6);
+#9=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#10=VECTOR('',#9,2.121320343560E-2);
+#11=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,-2.25E-1));
+#12=LINE('',#11,#10);
+#13=DIRECTION('',(1.E0,0.E0,0.E0));
+#14=VECTOR('',#13,1.3E-1);
+#15=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,-2.25E-1));
+#16=LINE('',#15,#14);
+#17=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#18=VECTOR('',#17,2.121320343560E-2);
+#19=CARTESIAN_POINT('',(6.5E-2,-1.E-1,-2.25E-1));
+#20=LINE('',#19,#18);
+#21=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#22=VECTOR('',#21,2.121320343560E-2);
+#23=CARTESIAN_POINT('',(8.E-2,-1.15E-1,-2.25E-1));
+#24=LINE('',#23,#22);
+#25=DIRECTION('',(1.E0,0.E0,0.E0));
+#26=VECTOR('',#25,7.E-2);
+#27=CARTESIAN_POINT('',(9.5E-2,-1.E-1,-2.25E-1));
+#28=LINE('',#27,#26);
+#29=DIRECTION('',(0.E0,1.E0,0.E0));
+#30=VECTOR('',#29,5.E-2);
+#31=CARTESIAN_POINT('',(1.65E-1,-1.E-1,-2.25E-1));
+#32=LINE('',#31,#30);
+#33=DIRECTION('',(-1.E0,0.E0,0.E0));
+#34=VECTOR('',#33,2.5E-2);
+#35=CARTESIAN_POINT('',(1.65E-1,-5.E-2,-2.25E-1));
+#36=LINE('',#35,#34);
+#37=DIRECTION('',(0.E0,1.E0,0.E0));
+#38=VECTOR('',#37,1.5E-1);
+#39=CARTESIAN_POINT('',(1.4E-1,-5.E-2,-2.25E-1));
+#40=LINE('',#39,#38);
+#41=DIRECTION('',(-1.E0,0.E0,0.E0));
+#42=VECTOR('',#41,2.8E-1);
+#43=CARTESIAN_POINT('',(1.4E-1,1.E-1,-2.25E-1));
+#44=LINE('',#43,#42);
+#45=DIRECTION('',(0.E0,-1.E0,0.E0));
+#46=VECTOR('',#45,1.5E-1);
+#47=CARTESIAN_POINT('',(-1.4E-1,1.E-1,-2.25E-1));
+#48=LINE('',#47,#46);
+#49=DIRECTION('',(-1.E0,0.E0,0.E0));
+#50=VECTOR('',#49,2.5E-2);
+#51=CARTESIAN_POINT('',(-1.4E-1,-5.E-2,-2.25E-1));
+#52=LINE('',#51,#50);
+#53=DIRECTION('',(0.E0,-1.E0,0.E0));
+#54=VECTOR('',#53,5.E-2);
+#55=CARTESIAN_POINT('',(-1.65E-1,-5.E-2,-2.25E-1));
+#56=LINE('',#55,#54);
+#57=DIRECTION('',(0.E0,1.E0,0.E0));
+#58=VECTOR('',#57,1.2E-1);
+#59=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.25E-1));
+#60=LINE('',#59,#58);
+#61=DIRECTION('',(1.E0,0.E0,0.E0));
+#62=VECTOR('',#61,2.E-1);
+#63=CARTESIAN_POINT('',(-1.E-1,6.E-2,-2.25E-1));
+#64=LINE('',#63,#62);
+#65=DIRECTION('',(0.E0,1.E0,0.E0));
+#66=VECTOR('',#65,1.2E-1);
+#67=CARTESIAN_POINT('',(1.E-1,-6.E-2,-2.25E-1));
+#68=LINE('',#67,#66);
+#69=DIRECTION('',(1.E0,0.E0,0.E0));
+#70=VECTOR('',#69,2.E-1);
+#71=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.25E-1));
+#72=LINE('',#71,#70);
+#73=DIRECTION('',(5.E-1,0.E0,8.660254037845E-1));
+#74=VECTOR('',#73,1.028468088848E-1);
+#75=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-5.5E-3));
+#76=LINE('',#75,#74);
+#77=DIRECTION('',(8.660254037844E-1,0.E0,5.E-1));
+#78=VECTOR('',#77,2.8E-3);
+#79=CARTESIAN_POINT('',(1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#80=LINE('',#79,#78);
+#81=DIRECTION('',(-5.E-1,0.E0,8.660254037844E-1));
+#82=VECTOR('',#81,2.E-3);
+#83=CARTESIAN_POINT('',(1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#84=LINE('',#83,#82);
+#85=DIRECTION('',(-8.660254037844E-1,0.E0,-5.E-1));
+#86=VECTOR('',#85,2.8E-3);
+#87=CARTESIAN_POINT('',(1.588482755730E-1,-1.E-1,8.67E-2));
+#88=LINE('',#87,#86);
+#89=DIRECTION('',(-1.E0,0.E0,0.E0));
+#90=VECTOR('',#89,6.142340444241E-2);
+#91=CARTESIAN_POINT('',(1.564234044424E-1,-1.E-1,8.53E-2));
+#92=LINE('',#91,#90);
+#93=DIRECTION('',(0.E0,0.E0,1.E0));
+#94=VECTOR('',#93,1.397E-1);
+#95=CARTESIAN_POINT('',(9.5E-2,-1.E-1,8.53E-2));
+#96=LINE('',#95,#94);
+#97=DIRECTION('',(0.E0,0.E0,1.E0));
+#98=VECTOR('',#97,2.350884572681E-1);
+#99=CARTESIAN_POINT('',(9.5E-2,-1.E-1,-2.25E-1));
+#100=LINE('',#99,#98);
+#101=DIRECTION('',(5.E-1,0.E0,-8.660254037845E-1));
+#102=VECTOR('',#101,1.8E-2);
+#103=CARTESIAN_POINT('',(9.5E-2,-1.E-1,1.008845726812E-2));
+#104=LINE('',#103,#102);
+#105=DIRECTION('',(0.E0,0.E0,-1.E0));
+#106=VECTOR('',#105,2.8E-3);
+#107=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-5.5E-3));
+#108=LINE('',#107,#106);
+#109=DIRECTION('',(1.E0,0.E0,0.E0));
+#110=VECTOR('',#109,2.E-3);
+#111=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-8.3E-3));
+#112=LINE('',#111,#110);
+#113=DIRECTION('',(0.E0,0.E0,1.E0));
+#114=VECTOR('',#113,2.8E-3);
+#115=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-8.3E-3));
+#116=LINE('',#115,#114);
+#117=DIRECTION('',(-5.E-1,0.E0,8.660254037845E-1));
+#118=VECTOR('',#117,1.028468088848E-1);
+#119=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-5.5E-3));
+#120=LINE('',#119,#118);
+#121=DIRECTION('',(0.E0,0.E0,1.E0));
+#122=VECTOR('',#121,2.8E-3);
+#123=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-8.3E-3));
+#124=LINE('',#123,#122);
+#125=DIRECTION('',(-1.E0,0.E0,0.E0));
+#126=VECTOR('',#125,2.E-3);
+#127=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-8.3E-3));
+#128=LINE('',#127,#126);
+#129=DIRECTION('',(0.E0,0.E0,-1.E0));
+#130=VECTOR('',#129,2.8E-3);
+#131=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-5.5E-3));
+#132=LINE('',#131,#130);
+#133=DIRECTION('',(-5.E-1,0.E0,-8.660254037845E-1));
+#134=VECTOR('',#133,1.8E-2);
+#135=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,1.008845726812E-2));
+#136=LINE('',#135,#134);
+#137=DIRECTION('',(0.E0,0.E0,1.E0));
+#138=VECTOR('',#137,2.350884572681E-1);
+#139=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,-2.25E-1));
+#140=LINE('',#139,#138);
+#141=DIRECTION('',(0.E0,0.E0,1.E0));
+#142=VECTOR('',#141,4.5E-1);
+#143=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,-2.25E-1));
+#144=LINE('',#143,#142);
+#145=DIRECTION('',(0.E0,0.E0,1.E0));
+#146=VECTOR('',#145,1.397E-1);
+#147=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,8.53E-2));
+#148=LINE('',#147,#146);
+#149=DIRECTION('',(1.E0,0.E0,0.E0));
+#150=VECTOR('',#149,6.142340444241E-2);
+#151=CARTESIAN_POINT('',(-1.564234044424E-1,-1.E-1,8.53E-2));
+#152=LINE('',#151,#150);
+#153=DIRECTION('',(8.660254037844E-1,0.E0,-5.E-1));
+#154=VECTOR('',#153,2.8E-3);
+#155=CARTESIAN_POINT('',(-1.588482755730E-1,-1.E-1,8.67E-2));
+#156=LINE('',#155,#154);
+#157=DIRECTION('',(5.E-1,0.E0,8.660254037844E-1));
+#158=VECTOR('',#157,2.E-3);
+#159=CARTESIAN_POINT('',(-1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#160=LINE('',#159,#158);
+#161=DIRECTION('',(-8.660254037844E-1,0.E0,5.E-1));
+#162=VECTOR('',#161,2.8E-3);
+#163=CARTESIAN_POINT('',(-1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#164=LINE('',#163,#162);
+#165=DIRECTION('',(0.E0,0.E0,-1.E0));
+#166=VECTOR('',#165,3.5E-1);
+#167=CARTESIAN_POINT('',(-2.5E-2,-1.E-1,2.25E-1));
+#168=LINE('',#167,#166);
+#169=DIRECTION('',(1.E0,0.E0,0.E0));
+#170=VECTOR('',#169,5.E-2);
+#171=CARTESIAN_POINT('',(-2.5E-2,-1.E-1,-1.25E-1));
+#172=LINE('',#171,#170);
+#173=DIRECTION('',(0.E0,0.E0,-1.E0));
+#174=VECTOR('',#173,3.5E-1);
+#175=CARTESIAN_POINT('',(2.5E-2,-1.E-1,2.25E-1));
+#176=LINE('',#175,#174);
+#177=DIRECTION('',(0.E0,0.E0,1.E0));
+#178=VECTOR('',#177,1.397E-1);
+#179=CARTESIAN_POINT('',(6.5E-2,-1.E-1,8.53E-2));
+#180=LINE('',#179,#178);
+#181=DIRECTION('',(-1.E0,0.E0,0.E0));
+#182=VECTOR('',#181,1.142340444241E-2);
+#183=CARTESIAN_POINT('',(6.5E-2,-1.E-1,8.53E-2));
+#184=LINE('',#183,#182);
+#185=DIRECTION('',(-8.660254037844E-1,0.E0,5.E-1));
+#186=VECTOR('',#185,2.8E-3);
+#187=CARTESIAN_POINT('',(5.357659555759E-2,-1.E-1,8.53E-2));
+#188=LINE('',#187,#186);
+#189=DIRECTION('',(-5.E-1,0.E0,-8.660254037845E-1));
+#190=VECTOR('',#189,2.E-3);
+#191=CARTESIAN_POINT('',(5.115172442699E-2,-1.E-1,8.67E-2));
+#192=LINE('',#191,#190);
+#193=DIRECTION('',(8.660254037845E-1,0.E0,-5.E-1));
+#194=VECTOR('',#193,2.8E-3);
+#195=CARTESIAN_POINT('',(5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#196=LINE('',#195,#194);
+#197=DIRECTION('',(5.E-1,0.E0,-8.660254037845E-1));
+#198=VECTOR('',#197,2.484680888483E-2);
+#199=CARTESIAN_POINT('',(5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#200=LINE('',#199,#198);
+#201=DIRECTION('',(0.E0,0.E0,1.E0));
+#202=VECTOR('',#201,2.870499814952E-1);
+#203=CARTESIAN_POINT('',(6.5E-2,-1.E-1,-2.25E-1));
+#204=LINE('',#203,#202);
+#205=DIRECTION('',(0.E0,0.E0,1.E0));
+#206=VECTOR('',#205,2.870499814952E-1);
+#207=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,-2.25E-1));
+#208=LINE('',#207,#206);
+#209=DIRECTION('',(-5.E-1,0.E0,-8.660254037845E-1));
+#210=VECTOR('',#209,2.484680888483E-2);
+#211=CARTESIAN_POINT('',(-5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#212=LINE('',#211,#210);
+#213=DIRECTION('',(-8.660254037845E-1,0.E0,-5.E-1));
+#214=VECTOR('',#213,2.8E-3);
+#215=CARTESIAN_POINT('',(-5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#216=LINE('',#215,#214);
+#217=DIRECTION('',(5.E-1,0.E0,-8.660254037845E-1));
+#218=VECTOR('',#217,2.E-3);
+#219=CARTESIAN_POINT('',(-5.115172442699E-2,-1.E-1,8.67E-2));
+#220=LINE('',#219,#218);
+#221=DIRECTION('',(8.660254037844E-1,0.E0,5.E-1));
+#222=VECTOR('',#221,2.8E-3);
+#223=CARTESIAN_POINT('',(-5.357659555759E-2,-1.E-1,8.53E-2));
+#224=LINE('',#223,#222);
+#225=DIRECTION('',(1.E0,0.E0,0.E0));
+#226=VECTOR('',#225,1.142340444241E-2);
+#227=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,8.53E-2));
+#228=LINE('',#227,#226);
+#229=DIRECTION('',(0.E0,0.E0,1.E0));
+#230=VECTOR('',#229,1.397E-1);
+#231=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,8.53E-2));
+#232=LINE('',#231,#230);
+#233=CARTESIAN_POINT('',(1.359067439440E-1,-1.707146793688E-1,4.63E-2));
+#234=CARTESIAN_POINT('',(1.347818470453E-1,-1.716427314769E-1,
+4.435162141820E-2));
+#235=CARTESIAN_POINT('',(1.325826344524E-1,-1.732269599096E-1,
+4.054247347054E-2));
+#236=CARTESIAN_POINT('',(1.294826259671E-1,-1.747302252402E-1,
+3.517310127021E-2));
+#237=CARTESIAN_POINT('',(1.264158758803E-1,-1.754149394403E-1,
+2.986133430575E-2));
+#238=CARTESIAN_POINT('',(1.233600650301E-1,-1.752360062162E-1,
+2.456851465476E-2));
+#239=CARTESIAN_POINT('',(1.202923466739E-1,-1.742123050750E-1,
+1.925507059854E-2));
+#240=CARTESIAN_POINT('',(1.171345716667E-1,-1.723650607587E-1,
+1.378564384723E-2));
+#241=CARTESIAN_POINT('',(1.137627364566E-1,-1.696464238197E-1,
+7.945453948624E-3));
+#242=CARTESIAN_POINT('',(1.100782418638E-1,-1.659770814270E-1,
+1.563722113738E-3));
+#243=CARTESIAN_POINT('',(1.073972705934E-1,-1.628925036232E-1,
+-3.079856340275E-3));
+#244=CARTESIAN_POINT('',(1.06E-1,-1.611975091251E-1,-5.5E-3));
+#246=CARTESIAN_POINT('',(1.574234044424E-1,-1.445586341714E-1,
+8.356794919243E-2));
+#247=CARTESIAN_POINT('',(1.556510049068E-1,-1.471059134075E-1,
+8.049806314542E-2));
+#248=CARTESIAN_POINT('',(1.522538698027E-1,-1.518856038577E-1,
+7.461405254494E-2));
+#249=CARTESIAN_POINT('',(1.476039632571E-1,-1.580684021107E-1,
+6.656017815753E-2));
+#250=CARTESIAN_POINT('',(1.433530093473E-1,-1.632839965971E-1,
+5.919731000509E-2));
+#251=CARTESIAN_POINT('',(1.394853686290E-1,-1.675180097546E-1,
+5.249835977563E-2));
+#252=CARTESIAN_POINT('',(1.370686310072E-1,-1.697561099750E-1,
+4.831244742601E-2));
+#253=CARTESIAN_POINT('',(1.359067439440E-1,-1.707146793688E-1,4.63E-2));
+#255=DIRECTION('',(0.E0,-1.E0,0.E0));
+#256=VECTOR('',#255,6.119750912510E-2);
+#257=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-5.5E-3));
+#258=LINE('',#257,#256);
+#259=CARTESIAN_POINT('',(1.06E-1,-1.611975091251E-1,-5.5E-3));
+#260=CARTESIAN_POINT('',(1.06E-1,-1.608256025983E-1,-5.962053370720E-3));
+#261=CARTESIAN_POINT('',(1.06E-1,-1.600776027218E-1,-6.890766483237E-3));
+#262=CARTESIAN_POINT('',(1.06E-1,-1.593212552633E-1,-7.828713292664E-3));
+#263=CARTESIAN_POINT('',(1.06E-1,-1.589410006771E-1,-8.3E-3));
+#265=DIRECTION('',(0.E0,-1.E0,0.E0));
+#266=VECTOR('',#265,5.894100067710E-2);
+#267=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-8.3E-3));
+#268=LINE('',#267,#266);
+#269=CARTESIAN_POINT('',(1.06E-1,-1.589410006771E-1,-8.3E-3));
+#270=CARTESIAN_POINT('',(1.056659451396E-1,-1.589985187898E-1,-8.3E-3));
+#271=CARTESIAN_POINT('',(1.049985592412E-1,-1.591086984013E-1,-8.3E-3));
+#272=CARTESIAN_POINT('',(1.043326136922E-1,-1.592091252124E-1,-8.3E-3));
+#273=CARTESIAN_POINT('',(1.04E-1,-1.592568924846E-1,-8.3E-3));
+#275=DIRECTION('',(0.E0,-1.E0,0.E0));
+#276=VECTOR('',#275,5.925689248462E-2);
+#277=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-8.3E-3));
+#278=LINE('',#277,#276);
+#279=CARTESIAN_POINT('',(1.04E-1,-1.592568924846E-1,-8.3E-3));
+#280=CARTESIAN_POINT('',(1.04E-1,-1.596394088286E-1,-7.829161520218E-3));
+#281=CARTESIAN_POINT('',(1.04E-1,-1.604007484507E-1,-6.891661373974E-3));
+#282=CARTESIAN_POINT('',(1.04E-1,-1.611546836768E-1,-5.962500002914E-3));
+#283=CARTESIAN_POINT('',(1.04E-1,-1.615297952414E-1,-5.5E-3));
+#285=DIRECTION('',(0.E0,-1.E0,0.E0));
+#286=VECTOR('',#285,6.152979524142E-2);
+#287=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-5.5E-3));
+#288=LINE('',#287,#286);
+#289=CARTESIAN_POINT('',(1.04E-1,-1.615297952414E-1,-5.5E-3));
+#290=CARTESIAN_POINT('',(1.026677871810E-1,-1.636025904772E-1,
+-3.192539710925E-3));
+#291=CARTESIAN_POINT('',(1.001103913130E-1,-1.675448457360E-1,
+1.236999867532E-3));
+#292=CARTESIAN_POINT('',(9.659307349503E-2,-1.728280968712E-1,
+7.329173034530E-3));
+#293=CARTESIAN_POINT('',(9.337629943372E-2,-1.774769255690E-1,
+1.290078914519E-2));
+#294=CARTESIAN_POINT('',(9.043799421210E-2,-1.814798916580E-1,
+1.799008307719E-2));
+#295=CARTESIAN_POINT('',(8.774875873874E-2,-1.848176417052E-1,
+2.264797555057E-2));
+#296=CARTESIAN_POINT('',(8.529914553242E-2,-1.874264761523E-1,
+2.689083008281E-2));
+#297=CARTESIAN_POINT('',(8.306147490191E-2,-1.892749685909E-1,
+3.076658930545E-2));
+#298=CARTESIAN_POINT('',(8.084973385689E-2,-1.904151960862E-1,
+3.459743716861E-2));
+#299=CARTESIAN_POINT('',(7.856031871802E-2,-1.906603971436E-1,
+3.856282050876E-2));
+#300=CARTESIAN_POINT('',(7.631311750265E-2,-1.899215158794E-1,
+4.245508718861E-2));
+#301=CARTESIAN_POINT('',(7.483504001672E-2,-1.889022348772E-1,
+4.501519249176E-2));
+#302=CARTESIAN_POINT('',(7.409325605598E-2,-1.882902550866E-1,4.63E-2));
+#304=DIRECTION('',(0.E0,-1.E0,0.E0));
+#305=VECTOR('',#304,5.881666482353E-2);
+#306=CARTESIAN_POINT('',(5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#307=LINE('',#306,#305);
+#308=CARTESIAN_POINT('',(7.409325605598E-2,-1.882902550866E-1,4.63E-2));
+#309=CARTESIAN_POINT('',(7.331203565857E-2,-1.876457398093E-1,
+4.765311342023E-2));
+#310=CARTESIAN_POINT('',(7.171639888257E-2,-1.861029442667E-1,
+5.041683738668E-2));
+#311=CARTESIAN_POINT('',(6.926336705934E-2,-1.832450047646E-1,
+5.466561313710E-2));
+#312=CARTESIAN_POINT('',(6.652871483494E-2,-1.796792193425E-1,
+5.940216973080E-2));
+#313=CARTESIAN_POINT('',(6.352762475034E-2,-1.754649235327E-1,
+6.460021023541E-2));
+#314=CARTESIAN_POINT('',(6.022345713244E-2,-1.705986395660E-1,
+7.032319642634E-2));
+#315=CARTESIAN_POINT('',(5.659934298402E-2,-1.650870607740E-1,
+7.660034626383E-2));
+#316=CARTESIAN_POINT('',(5.395542322199E-2,-1.609771813768E-1,
+8.117974962281E-2));
+#317=CARTESIAN_POINT('',(5.257659555759E-2,-1.588166648235E-1,
+8.356794919243E-2));
+#319=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#320=VECTOR('',#319,2.121320343560E-2);
+#321=CARTESIAN_POINT('',(8.E-2,-1.15E-1,8.53E-2));
+#322=LINE('',#321,#320);
+#323=DIRECTION('',(0.E0,0.E0,1.E0));
+#324=VECTOR('',#323,1.397E-1);
+#325=CARTESIAN_POINT('',(8.E-2,-1.15E-1,8.53E-2));
+#326=LINE('',#325,#324);
+#327=DIRECTION('',(4.472135954999E-1,4.472135954999E-1,-7.745966692415E-1));
+#328=VECTOR('',#327,3.354101966250E-2);
+#329=CARTESIAN_POINT('',(8.E-2,-1.15E-1,3.606921938166E-2));
+#330=LINE('',#329,#328);
+#331=DIRECTION('',(0.E0,0.E0,1.E0));
+#332=VECTOR('',#331,2.610692193817E-1);
+#333=CARTESIAN_POINT('',(8.E-2,-1.15E-1,-2.25E-1));
+#334=LINE('',#333,#332);
+#335=CARTESIAN_POINT('',(5.357659555759E-2,-1.584529252708E-1,8.53E-2));
+#336=CARTESIAN_POINT('',(5.663244194723E-2,-1.602747598675E-1,8.53E-2));
+#337=CARTESIAN_POINT('',(6.248790584942E-2,-1.635693235480E-1,8.53E-2));
+#338=CARTESIAN_POINT('',(7.050492033487E-2,-1.674170456356E-1,8.53E-2));
+#339=CARTESIAN_POINT('',(7.781939434949E-2,-1.701918575467E-1,8.53E-2));
+#340=CARTESIAN_POINT('',(8.468278420756E-2,-1.720001485712E-1,8.53E-2));
+#341=CARTESIAN_POINT('',(9.141161088772E-2,-1.728930552188E-1,8.53E-2));
+#342=CARTESIAN_POINT('',(9.812246616123E-2,-1.728326515409E-1,8.53E-2));
+#343=CARTESIAN_POINT('',(1.048466874162E-1,-1.718260528183E-1,8.53E-2));
+#344=CARTESIAN_POINT('',(1.117314134277E-1,-1.699090466749E-1,8.53E-2));
+#345=CARTESIAN_POINT('',(1.190967014330E-1,-1.670202176413E-1,8.53E-2));
+#346=CARTESIAN_POINT('',(1.271778953659E-1,-1.630564628607E-1,8.53E-2));
+#347=CARTESIAN_POINT('',(1.360335903179E-1,-1.580014158263E-1,8.53E-2));
+#348=CARTESIAN_POINT('',(1.457051050551E-1,-1.518781217069E-1,8.53E-2));
+#349=CARTESIAN_POINT('',(1.527502643574E-1,-1.470854943992E-1,8.53E-2));
+#350=CARTESIAN_POINT('',(1.564234044424E-1,-1.445195163487E-1,8.53E-2));
+#352=DIRECTION('',(0.E0,-1.E0,0.E0));
+#353=VECTOR('',#352,4.451951634874E-2);
+#354=CARTESIAN_POINT('',(1.564234044424E-1,-1.E-1,8.53E-2));
+#355=LINE('',#354,#353);
+#356=DIRECTION('',(-7.713691830612E-1,-6.363879189799E-1,0.E0));
+#357=VECTOR('',#356,3.737601997139E-2);
+#358=CARTESIAN_POINT('',(1.359067439440E-1,-1.707146793688E-1,4.63E-2));
+#359=LINE('',#358,#357);
+#360=DIRECTION('',(7.713691830612E-1,-6.363879189799E-1,0.E0));
+#361=VECTOR('',#360,9.758311976582E-3);
+#362=CARTESIAN_POINT('',(7.409325605598E-2,-1.882902550866E-1,4.63E-2));
+#363=LINE('',#362,#361);
+#364=CARTESIAN_POINT('',(9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#365=DIRECTION('',(0.E0,0.E0,-1.E0));
+#366=DIRECTION('',(6.363879189799E-1,-7.713691830612E-1,0.E0));
+#367=AXIS2_PLACEMENT_3D('',#364,#365,#366);
+#369=CARTESIAN_POINT('',(9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#370=DIRECTION('',(0.E0,0.E0,1.E0));
+#371=DIRECTION('',(-6.363879189799E-1,-7.713691830612E-1,0.E0));
+#372=AXIS2_PLACEMENT_3D('',#369,#370,#371);
+#374=CARTESIAN_POINT('',(9.434827557301E-2,-1.945003269382E-1,4.63E-2));
+#375=DIRECTION('',(0.E0,-1.E0,0.E0));
+#376=DIRECTION('',(1.E0,0.E0,0.E0));
+#377=AXIS2_PLACEMENT_3D('',#374,#375,#376);
+#379=CARTESIAN_POINT('',(9.434827557301E-2,-1.945003269382E-1,4.63E-2));
+#380=DIRECTION('',(0.E0,1.E0,0.E0));
+#381=DIRECTION('',(1.E0,0.E0,0.E0));
+#382=AXIS2_PLACEMENT_3D('',#379,#380,#381);
+#384=CARTESIAN_POINT('',(5.257659555759E-2,-1.588166648235E-1,
+8.356794919243E-2));
+#385=CARTESIAN_POINT('',(5.217581296567E-2,-1.584428483702E-1,
+8.379934112976E-2));
+#386=CARTESIAN_POINT('',(5.137089021924E-2,-1.576916919872E-1,
+8.426406349409E-2));
+#387=CARTESIAN_POINT('',(5.055923711301E-2,-1.569335104496E-1,
+8.473267163346E-2));
+#388=CARTESIAN_POINT('',(5.015172442699E-2,-1.565526684416E-1,
+8.496794919243E-2));
+#390=DIRECTION('',(0.E0,-1.E0,0.E0));
+#391=VECTOR('',#390,5.655266844162E-2);
+#392=CARTESIAN_POINT('',(5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#393=LINE('',#392,#391);
+#394=CARTESIAN_POINT('',(5.015172442699E-2,-1.565526684416E-1,
+8.496794919243E-2));
+#395=CARTESIAN_POINT('',(5.031806493439E-2,-1.564994378828E-1,
+8.525605940260E-2));
+#396=CARTESIAN_POINT('',(5.065107110442E-2,-1.563883832289E-1,
+8.583284300832E-2));
+#397=CARTESIAN_POINT('',(5.098473039170E-2,-1.562681801997E-1,
+8.641075784631E-2));
+#398=CARTESIAN_POINT('',(5.115172442699E-2,-1.562058027820E-1,8.67E-2));
+#400=DIRECTION('',(0.E0,-1.E0,0.E0));
+#401=VECTOR('',#400,5.620580278197E-2);
+#402=CARTESIAN_POINT('',(5.115172442699E-2,-1.E-1,8.67E-2));
+#403=LINE('',#402,#401);
+#404=CARTESIAN_POINT('',(5.115172442699E-2,-1.562058027820E-1,8.67E-2));
+#405=CARTESIAN_POINT('',(5.155953767326E-2,-1.565842164206E-1,
+8.646454891248E-2));
+#406=CARTESIAN_POINT('',(5.237149011554E-2,-1.573371590786E-1,
+8.599576795137E-2));
+#407=CARTESIAN_POINT('',(5.317611227978E-2,-1.580823069249E-1,
+8.553121912824E-2));
+#408=CARTESIAN_POINT('',(5.357659555759E-2,-1.584529252708E-1,8.53E-2));
+#410=DIRECTION('',(0.E0,-1.E0,0.E0));
+#411=VECTOR('',#410,5.845292527080E-2);
+#412=CARTESIAN_POINT('',(5.357659555759E-2,-1.E-1,8.53E-2));
+#413=LINE('',#412,#411);
+#414=CARTESIAN_POINT('',(1.564234044424E-1,-1.445195163487E-1,8.53E-2));
+#415=CARTESIAN_POINT('',(1.568263784217E-1,-1.441358947675E-1,
+8.553265713540E-2));
+#416=CARTESIAN_POINT('',(1.576334974556E-1,-1.433675265797E-1,
+8.599864752688E-2));
+#417=CARTESIAN_POINT('',(1.584429590450E-1,-1.425969089083E-1,
+8.646599039342E-2));
+#418=CARTESIAN_POINT('',(1.588482755730E-1,-1.422110378340E-1,8.67E-2));
+#420=DIRECTION('',(0.E0,-1.E0,0.E0));
+#421=VECTOR('',#420,4.221103783395E-2);
+#422=CARTESIAN_POINT('',(1.588482755730E-1,-1.E-1,8.67E-2));
+#423=LINE('',#422,#421);
+#424=CARTESIAN_POINT('',(1.588482755730E-1,-1.422110378340E-1,8.67E-2));
+#425=CARTESIAN_POINT('',(1.590149631034E-1,-1.422209303221E-1,
+8.641128872844E-2));
+#426=CARTESIAN_POINT('',(1.593483172721E-1,-1.422371043354E-1,
+8.583390237121E-2));
+#427=CARTESIAN_POINT('',(1.596816297564E-1,-1.422460507655E-1,
+8.525658821359E-2));
+#428=CARTESIAN_POINT('',(1.598482755730E-1,-1.422487166033E-1,
+8.496794919243E-2));
+#430=DIRECTION('',(0.E0,-1.E0,0.E0));
+#431=VECTOR('',#430,4.224871660332E-2);
+#432=CARTESIAN_POINT('',(1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#433=LINE('',#432,#431);
+#434=CARTESIAN_POINT('',(1.598482755730E-1,-1.422487166033E-1,
+8.496794919243E-2));
+#435=CARTESIAN_POINT('',(1.594435182579E-1,-1.426342866654E-1,
+8.473426244761E-2));
+#436=CARTESIAN_POINT('',(1.586346157645E-1,-1.434048430029E-1,
+8.426724237526E-2));
+#437=CARTESIAN_POINT('',(1.578269375145E-1,-1.441742317253E-1,
+8.380092912024E-2));
+#438=CARTESIAN_POINT('',(1.574234044424E-1,-1.445586341714E-1,
+8.356794919243E-2));
+#440=DIRECTION('',(0.E0,-1.E0,0.E0));
+#441=VECTOR('',#440,4.455863417144E-2);
+#442=CARTESIAN_POINT('',(1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#443=LINE('',#442,#441);
+#444=DIRECTION('',(7.071067811866E-1,-7.071067811865E-1,0.E0));
+#445=VECTOR('',#444,2.121320343560E-2);
+#446=CARTESIAN_POINT('',(6.5E-2,-1.E-1,8.53E-2));
+#447=LINE('',#446,#445);
+#448=DIRECTION('',(4.472135954999E-1,-4.472135954999E-1,-7.745966692415E-1));
+#449=VECTOR('',#448,3.354101966250E-2);
+#450=CARTESIAN_POINT('',(6.5E-2,-1.E-1,6.204998149519E-2));
+#451=LINE('',#450,#449);
+#452=DIRECTION('',(1.E0,0.E0,0.E0));
+#453=VECTOR('',#452,2.5E-1);
+#454=CARTESIAN_POINT('',(-1.25E-1,-5.5E-2,2.25E-1));
+#455=LINE('',#454,#453);
+#456=DIRECTION('',(1.E0,0.E0,0.E0));
+#457=VECTOR('',#456,4.E-2);
+#458=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,2.25E-1));
+#459=LINE('',#458,#457);
+#460=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#461=VECTOR('',#460,2.121320343560E-2);
+#462=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,2.25E-1));
+#463=LINE('',#462,#461);
+#464=DIRECTION('',(7.071067811866E-1,-7.071067811865E-1,0.E0));
+#465=VECTOR('',#464,2.121320343560E-2);
+#466=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,2.25E-1));
+#467=LINE('',#466,#465);
+#468=DIRECTION('',(1.E0,0.E0,0.E0));
+#469=VECTOR('',#468,7.E-2);
+#470=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,2.25E-1));
+#471=LINE('',#470,#469);
+#472=DIRECTION('',(0.E0,-1.E0,0.E0));
+#473=VECTOR('',#472,5.E-2);
+#474=CARTESIAN_POINT('',(-1.65E-1,-5.E-2,2.25E-1));
+#475=LINE('',#474,#473);
+#476=DIRECTION('',(-1.E0,0.E0,0.E0));
+#477=VECTOR('',#476,2.5E-2);
+#478=CARTESIAN_POINT('',(-1.4E-1,-5.E-2,2.25E-1));
+#479=LINE('',#478,#477);
+#480=DIRECTION('',(0.E0,-1.E0,0.E0));
+#481=VECTOR('',#480,2.1E-1);
+#482=CARTESIAN_POINT('',(-1.4E-1,1.6E-1,2.25E-1));
+#483=LINE('',#482,#481);
+#484=DIRECTION('',(-1.E0,0.E0,0.E0));
+#485=VECTOR('',#484,2.8E-1);
+#486=CARTESIAN_POINT('',(1.4E-1,1.6E-1,2.25E-1));
+#487=LINE('',#486,#485);
+#488=DIRECTION('',(0.E0,1.E0,0.E0));
+#489=VECTOR('',#488,2.1E-1);
+#490=CARTESIAN_POINT('',(1.4E-1,-5.E-2,2.25E-1));
+#491=LINE('',#490,#489);
+#492=DIRECTION('',(-1.E0,0.E0,0.E0));
+#493=VECTOR('',#492,2.5E-2);
+#494=CARTESIAN_POINT('',(1.65E-1,-5.E-2,2.25E-1));
+#495=LINE('',#494,#493);
+#496=DIRECTION('',(0.E0,1.E0,0.E0));
+#497=VECTOR('',#496,5.E-2);
+#498=CARTESIAN_POINT('',(1.65E-1,-1.E-1,2.25E-1));
+#499=LINE('',#498,#497);
+#500=DIRECTION('',(1.E0,0.E0,0.E0));
+#501=VECTOR('',#500,7.E-2);
+#502=CARTESIAN_POINT('',(9.5E-2,-1.E-1,2.25E-1));
+#503=LINE('',#502,#501);
+#504=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#505=VECTOR('',#504,2.121320343560E-2);
+#506=CARTESIAN_POINT('',(8.E-2,-1.15E-1,2.25E-1));
+#507=LINE('',#506,#505);
+#508=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#509=VECTOR('',#508,2.121320343560E-2);
+#510=CARTESIAN_POINT('',(6.5E-2,-1.E-1,2.25E-1));
+#511=LINE('',#510,#509);
+#512=DIRECTION('',(1.E0,0.E0,0.E0));
+#513=VECTOR('',#512,4.E-2);
+#514=CARTESIAN_POINT('',(2.5E-2,-1.E-1,2.25E-1));
+#515=LINE('',#514,#513);
+#516=DIRECTION('',(0.E0,1.E0,0.E0));
+#517=VECTOR('',#516,4.E-2);
+#518=CARTESIAN_POINT('',(2.5E-2,-1.E-1,2.25E-1));
+#519=LINE('',#518,#517);
+#520=DIRECTION('',(-1.E0,0.E0,0.E0));
+#521=VECTOR('',#520,5.E-2);
+#522=CARTESIAN_POINT('',(2.5E-2,-6.E-2,2.25E-1));
+#523=LINE('',#522,#521);
+#524=DIRECTION('',(0.E0,-1.E0,0.E0));
+#525=VECTOR('',#524,4.E-2);
+#526=CARTESIAN_POINT('',(-2.5E-2,-6.E-2,2.25E-1));
+#527=LINE('',#526,#525);
+#528=DIRECTION('',(0.E0,1.E0,0.E0));
+#529=VECTOR('',#528,1.9E-1);
+#530=CARTESIAN_POINT('',(1.25E-1,-5.5E-2,2.25E-1));
+#531=LINE('',#530,#529);
+#532=DIRECTION('',(-7.071067811865E-1,0.E0,-7.071067811865E-1));
+#533=VECTOR('',#532,2.828427124746E-2);
+#534=CARTESIAN_POINT('',(1.25E-1,-5.5E-2,2.25E-1));
+#535=LINE('',#534,#533);
+#536=DIRECTION('',(0.E0,1.E0,0.E0));
+#537=VECTOR('',#536,1.7E-1);
+#538=CARTESIAN_POINT('',(1.05E-1,-5.5E-2,2.05E-1));
+#539=LINE('',#538,#537);
+#540=DIRECTION('',(-5.773502691896E-1,-5.773502691896E-1,-5.773502691896E-1));
+#541=VECTOR('',#540,3.464101615138E-2);
+#542=CARTESIAN_POINT('',(1.25E-1,1.35E-1,2.25E-1));
+#543=LINE('',#542,#541);
+#544=DIRECTION('',(0.E0,0.E0,-1.E0));
+#545=VECTOR('',#544,1.8E-1);
+#546=CARTESIAN_POINT('',(-1.05E-1,6.5E-2,5.5E-2));
+#547=LINE('',#546,#545);
+#548=DIRECTION('',(0.E0,0.E0,1.E0));
+#549=VECTOR('',#548,3.3E-1);
+#550=CARTESIAN_POINT('',(-1.05E-1,-5.5E-2,-1.25E-1));
+#551=LINE('',#550,#549);
+#552=DIRECTION('',(0.E0,0.E0,-1.E0));
+#553=VECTOR('',#552,1.3E-1);
+#554=CARTESIAN_POINT('',(-1.05E-1,1.15E-1,2.05E-1));
+#555=LINE('',#554,#553);
+#556=DIRECTION('',(0.E0,-1.E0,0.E0));
+#557=VECTOR('',#556,1.5E-2);
+#558=CARTESIAN_POINT('',(-6.E-2,1.E-1,7.5E-2));
+#559=LINE('',#558,#557);
+#560=DIRECTION('',(0.E0,-1.E0,0.E0));
+#561=VECTOR('',#560,1.5E-2);
+#562=CARTESIAN_POINT('',(-1.05E-1,1.E-1,7.5E-2));
+#563=LINE('',#562,#561);
+#564=DIRECTION('',(0.E0,-1.E0,0.E0));
+#565=VECTOR('',#564,1.5E-2);
+#566=CARTESIAN_POINT('',(6.E-2,1.E-1,7.5E-2));
+#567=LINE('',#566,#565);
+#568=DIRECTION('',(0.E0,1.E0,0.E0));
+#569=VECTOR('',#568,1.5E-2);
+#570=CARTESIAN_POINT('',(1.05E-1,8.5E-2,7.5E-2));
+#571=LINE('',#570,#569);
+#572=DIRECTION('',(1.E0,0.E0,0.E0));
+#573=VECTOR('',#572,1.2E-1);
+#574=CARTESIAN_POINT('',(-6.E-2,1.E-1,-1.25E-1));
+#575=LINE('',#574,#573);
+#576=DIRECTION('',(0.E0,0.E0,-1.E0));
+#577=VECTOR('',#576,2.E-1);
+#578=CARTESIAN_POINT('',(-6.E-2,1.E-1,7.5E-2));
+#579=LINE('',#578,#577);
+#580=DIRECTION('',(1.E0,0.E0,0.E0));
+#581=VECTOR('',#580,4.5E-2);
+#582=CARTESIAN_POINT('',(-1.05E-1,1.E-1,7.5E-2));
+#583=LINE('',#582,#581);
+#584=DIRECTION('',(-1.E0,0.E0,0.E0));
+#585=VECTOR('',#584,3.5E-2);
+#586=CARTESIAN_POINT('',(-1.05E-1,1.E-1,7.5E-2));
+#587=LINE('',#586,#585);
+#588=DIRECTION('',(-1.E0,0.E0,0.E0));
+#589=VECTOR('',#588,3.5E-2);
+#590=CARTESIAN_POINT('',(1.4E-1,1.E-1,7.5E-2));
+#591=LINE('',#590,#589);
+#592=DIRECTION('',(1.E0,0.E0,0.E0));
+#593=VECTOR('',#592,4.5E-2);
+#594=CARTESIAN_POINT('',(6.E-2,1.E-1,7.5E-2));
+#595=LINE('',#594,#593);
+#596=DIRECTION('',(0.E0,0.E0,-1.E0));
+#597=VECTOR('',#596,2.E-1);
+#598=CARTESIAN_POINT('',(6.E-2,1.E-1,7.5E-2));
+#599=LINE('',#598,#597);
+#600=DIRECTION('',(0.E0,1.E0,0.E0));
+#601=VECTOR('',#600,3.5E-2);
+#602=CARTESIAN_POINT('',(6.E-2,6.5E-2,-1.25E-1));
+#603=LINE('',#602,#601);
+#604=DIRECTION('',(-1.E0,0.E0,0.E0));
+#605=VECTOR('',#604,4.5E-2);
+#606=CARTESIAN_POINT('',(1.05E-1,6.5E-2,-1.25E-1));
+#607=LINE('',#606,#605);
+#608=DIRECTION('',(0.E0,1.E0,0.E0));
+#609=VECTOR('',#608,1.2E-1);
+#610=CARTESIAN_POINT('',(1.05E-1,-5.5E-2,-1.25E-1));
+#611=LINE('',#610,#609);
+#612=DIRECTION('',(1.E0,0.E0,0.E0));
+#613=VECTOR('',#612,2.1E-1);
+#614=CARTESIAN_POINT('',(-1.05E-1,-5.5E-2,-1.25E-1));
+#615=LINE('',#614,#613);
+#616=DIRECTION('',(0.E0,-1.E0,0.E0));
+#617=VECTOR('',#616,1.2E-1);
+#618=CARTESIAN_POINT('',(-1.05E-1,6.5E-2,-1.25E-1));
+#619=LINE('',#618,#617);
+#620=DIRECTION('',(-1.E0,0.E0,0.E0));
+#621=VECTOR('',#620,4.5E-2);
+#622=CARTESIAN_POINT('',(-6.E-2,6.5E-2,-1.25E-1));
+#623=LINE('',#622,#621);
+#624=DIRECTION('',(0.E0,-1.E0,0.E0));
+#625=VECTOR('',#624,3.5E-2);
+#626=CARTESIAN_POINT('',(-6.E-2,1.E-1,-1.25E-1));
+#627=LINE('',#626,#625);
+#628=DIRECTION('',(0.E0,1.E0,0.E0));
+#629=VECTOR('',#628,2.5E-2);
+#630=CARTESIAN_POINT('',(3.75E-2,-1.25E-2,-1.25E-1));
+#631=LINE('',#630,#629);
+#632=DIRECTION('',(1.E0,0.E0,0.E0));
+#633=VECTOR('',#632,2.5E-2);
+#634=CARTESIAN_POINT('',(3.75E-2,-1.25E-2,-1.25E-1));
+#635=LINE('',#634,#633);
+#636=DIRECTION('',(0.E0,1.E0,0.E0));
+#637=VECTOR('',#636,2.5E-2);
+#638=CARTESIAN_POINT('',(6.25E-2,-1.25E-2,-1.25E-1));
+#639=LINE('',#638,#637);
+#640=DIRECTION('',(1.E0,0.E0,0.E0));
+#641=VECTOR('',#640,2.5E-2);
+#642=CARTESIAN_POINT('',(3.75E-2,1.25E-2,-1.25E-1));
+#643=LINE('',#642,#641);
+#644=DIRECTION('',(0.E0,1.E0,0.E0));
+#645=VECTOR('',#644,2.5E-2);
+#646=CARTESIAN_POINT('',(-6.25E-2,-1.25E-2,-1.25E-1));
+#647=LINE('',#646,#645);
+#648=DIRECTION('',(1.E0,0.E0,0.E0));
+#649=VECTOR('',#648,2.5E-2);
+#650=CARTESIAN_POINT('',(-6.25E-2,-1.25E-2,-1.25E-1));
+#651=LINE('',#650,#649);
+#652=DIRECTION('',(0.E0,1.E0,0.E0));
+#653=VECTOR('',#652,2.5E-2);
+#654=CARTESIAN_POINT('',(-3.75E-2,-1.25E-2,-1.25E-1));
+#655=LINE('',#654,#653);
+#656=DIRECTION('',(1.E0,0.E0,0.E0));
+#657=VECTOR('',#656,2.5E-2);
+#658=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,-1.25E-1));
+#659=LINE('',#658,#657);
+#660=DIRECTION('',(0.E0,0.E0,-1.E0));
+#661=VECTOR('',#660,1.8E-1);
+#662=CARTESIAN_POINT('',(6.E-2,6.5E-2,5.5E-2));
+#663=LINE('',#662,#661);
+#664=DIRECTION('',(1.E0,0.E0,0.E0));
+#665=VECTOR('',#664,4.5E-2);
+#666=CARTESIAN_POINT('',(6.E-2,8.5E-2,7.5E-2));
+#667=LINE('',#666,#665);
+#668=DIRECTION('',(0.E0,-7.071067811865E-1,-7.071067811865E-1));
+#669=VECTOR('',#668,2.828427124746E-2);
+#670=CARTESIAN_POINT('',(1.05E-1,8.5E-2,7.5E-2));
+#671=LINE('',#670,#669);
+#672=DIRECTION('',(1.E0,0.E0,0.E0));
+#673=VECTOR('',#672,4.5E-2);
+#674=CARTESIAN_POINT('',(6.E-2,6.5E-2,5.5E-2));
+#675=LINE('',#674,#673);
+#676=DIRECTION('',(0.E0,-7.071067811865E-1,-7.071067811865E-1));
+#677=VECTOR('',#676,2.828427124746E-2);
+#678=CARTESIAN_POINT('',(6.E-2,8.5E-2,7.5E-2));
+#679=LINE('',#678,#677);
+#680=DIRECTION('',(0.E0,0.E0,-1.E0));
+#681=VECTOR('',#680,3.3E-1);
+#682=CARTESIAN_POINT('',(1.05E-1,-5.5E-2,2.05E-1));
+#683=LINE('',#682,#681);
+#684=DIRECTION('',(0.E0,0.E0,-1.E0));
+#685=VECTOR('',#684,1.8E-1);
+#686=CARTESIAN_POINT('',(1.05E-1,6.5E-2,5.5E-2));
+#687=LINE('',#686,#685);
+#688=DIRECTION('',(0.E0,1.E0,0.E0));
+#689=VECTOR('',#688,4.5E-2);
+#690=CARTESIAN_POINT('',(1.E-1,1.15E-1,7.5E-2));
+#691=LINE('',#690,#689);
+#692=DIRECTION('',(-1.E0,0.E0,0.E0));
+#693=VECTOR('',#692,5.E-3);
+#694=CARTESIAN_POINT('',(1.05E-1,1.15E-1,7.5E-2));
+#695=LINE('',#694,#693);
+#696=DIRECTION('',(0.E0,1.E0,0.E0));
+#697=VECTOR('',#696,1.5E-2);
+#698=CARTESIAN_POINT('',(1.05E-1,1.E-1,7.5E-2));
+#699=LINE('',#698,#697);
+#700=DIRECTION('',(0.E0,1.E0,0.E0));
+#701=VECTOR('',#700,4.5E-2);
+#702=CARTESIAN_POINT('',(-1.E-1,1.15E-1,7.5E-2));
+#703=LINE('',#702,#701);
+#704=DIRECTION('',(0.E0,-1.E0,0.E0));
+#705=VECTOR('',#704,1.5E-2);
+#706=CARTESIAN_POINT('',(-1.05E-1,1.15E-1,7.5E-2));
+#707=LINE('',#706,#705);
+#708=DIRECTION('',(-1.E0,0.E0,0.E0));
+#709=VECTOR('',#708,5.E-3);
+#710=CARTESIAN_POINT('',(-1.E-1,1.15E-1,7.5E-2));
+#711=LINE('',#710,#709);
+#712=DIRECTION('',(0.E0,0.E0,1.E0));
+#713=VECTOR('',#712,3.7E-2);
+#714=CARTESIAN_POINT('',(1.E-1,1.6E-1,7.5E-2));
+#715=LINE('',#714,#713);
+#716=DIRECTION('',(-1.E0,0.E0,0.E0));
+#717=VECTOR('',#716,4.E-2);
+#718=CARTESIAN_POINT('',(1.4E-1,1.6E-1,7.5E-2));
+#719=LINE('',#718,#717);
+#720=DIRECTION('',(0.E0,0.E0,1.E0));
+#721=VECTOR('',#720,1.5E-1);
+#722=CARTESIAN_POINT('',(1.4E-1,1.6E-1,7.5E-2));
+#723=LINE('',#722,#721);
+#724=DIRECTION('',(-1.E0,0.E0,0.E0));
+#725=VECTOR('',#724,4.E-2);
+#726=CARTESIAN_POINT('',(-1.E-1,1.6E-1,7.5E-2));
+#727=LINE('',#726,#725);
+#728=DIRECTION('',(0.E0,0.E0,1.E0));
+#729=VECTOR('',#728,3.7E-2);
+#730=CARTESIAN_POINT('',(-1.E-1,1.6E-1,7.5E-2));
+#731=LINE('',#730,#729);
+#732=CARTESIAN_POINT('',(-4.7E-2,1.6E-1,1.12E-1));
+#733=DIRECTION('',(0.E0,-1.E0,0.E0));
+#734=DIRECTION('',(0.E0,0.E0,1.E0));
+#735=AXIS2_PLACEMENT_3D('',#732,#733,#734);
+#737=DIRECTION('',(-1.E0,0.E0,0.E0));
+#738=VECTOR('',#737,9.4E-2);
+#739=CARTESIAN_POINT('',(4.7E-2,1.6E-1,1.65E-1));
+#740=LINE('',#739,#738);
+#741=CARTESIAN_POINT('',(4.7E-2,1.6E-1,1.12E-1));
+#742=DIRECTION('',(0.E0,-1.E0,0.E0));
+#743=DIRECTION('',(1.E0,0.E0,0.E0));
+#744=AXIS2_PLACEMENT_3D('',#741,#742,#743);
+#746=DIRECTION('',(0.E0,1.E0,0.E0));
+#747=VECTOR('',#746,6.E-2);
+#748=CARTESIAN_POINT('',(1.4E-1,1.E-1,7.5E-2));
+#749=LINE('',#748,#747);
+#750=DIRECTION('',(0.E0,0.E0,-1.E0));
+#751=VECTOR('',#750,3.E-1);
+#752=CARTESIAN_POINT('',(1.4E-1,1.E-1,7.5E-2));
+#753=LINE('',#752,#751);
+#754=DIRECTION('',(0.E0,0.E0,1.E0));
+#755=VECTOR('',#754,4.5E-1);
+#756=CARTESIAN_POINT('',(1.4E-1,-5.E-2,-2.25E-1));
+#757=LINE('',#756,#755);
+#758=DIRECTION('',(0.E0,0.E0,1.E0));
+#759=VECTOR('',#758,4.5E-1);
+#760=CARTESIAN_POINT('',(1.65E-1,-5.E-2,-2.25E-1));
+#761=LINE('',#760,#759);
+#762=DIRECTION('',(0.E0,0.E0,1.E0));
+#763=VECTOR('',#762,4.5E-1);
+#764=CARTESIAN_POINT('',(1.65E-1,-1.E-1,-2.25E-1));
+#765=LINE('',#764,#763);
+#766=DIRECTION('',(0.E0,0.E0,1.E0));
+#767=VECTOR('',#766,1.5E-1);
+#768=CARTESIAN_POINT('',(-1.4E-1,1.6E-1,7.5E-2));
+#769=LINE('',#768,#767);
+#770=DIRECTION('',(0.E0,0.E0,-1.E0));
+#771=VECTOR('',#770,3.E-1);
+#772=CARTESIAN_POINT('',(-1.4E-1,1.E-1,7.5E-2));
+#773=LINE('',#772,#771);
+#774=DIRECTION('',(0.E0,1.E0,0.E0));
+#775=VECTOR('',#774,6.E-2);
+#776=CARTESIAN_POINT('',(-1.4E-1,1.E-1,7.5E-2));
+#777=LINE('',#776,#775);
+#778=DIRECTION('',(0.E0,0.E0,1.E0));
+#779=VECTOR('',#778,4.5E-1);
+#780=CARTESIAN_POINT('',(-1.4E-1,-5.E-2,-2.25E-1));
+#781=LINE('',#780,#779);
+#782=DIRECTION('',(0.E0,0.E0,1.E0));
+#783=VECTOR('',#782,4.5E-1);
+#784=CARTESIAN_POINT('',(-1.65E-1,-5.E-2,-2.25E-1));
+#785=LINE('',#784,#783);
+#786=DIRECTION('',(0.E0,1.E0,0.E0));
+#787=VECTOR('',#786,4.5E-2);
+#788=CARTESIAN_POINT('',(-1.E-1,1.15E-1,1.12E-1));
+#789=LINE('',#788,#787);
+#790=DIRECTION('',(0.E0,0.E0,1.E0));
+#791=VECTOR('',#790,3.7E-2);
+#792=CARTESIAN_POINT('',(1.E-1,1.15E-1,7.5E-2));
+#793=LINE('',#792,#791);
+#794=CARTESIAN_POINT('',(4.7E-2,1.15E-1,1.12E-1));
+#795=DIRECTION('',(0.E0,-1.E0,0.E0));
+#796=DIRECTION('',(1.E0,0.E0,0.E0));
+#797=AXIS2_PLACEMENT_3D('',#794,#795,#796);
+#799=DIRECTION('',(-1.E0,0.E0,0.E0));
+#800=VECTOR('',#799,9.4E-2);
+#801=CARTESIAN_POINT('',(4.7E-2,1.15E-1,1.65E-1));
+#802=LINE('',#801,#800);
+#803=CARTESIAN_POINT('',(-4.7E-2,1.15E-1,1.12E-1));
+#804=DIRECTION('',(0.E0,-1.E0,0.E0));
+#805=DIRECTION('',(0.E0,0.E0,1.E0));
+#806=AXIS2_PLACEMENT_3D('',#803,#804,#805);
+#808=DIRECTION('',(0.E0,0.E0,1.E0));
+#809=VECTOR('',#808,3.7E-2);
+#810=CARTESIAN_POINT('',(-1.E-1,1.15E-1,7.5E-2));
+#811=LINE('',#810,#809);
+#812=DIRECTION('',(0.E0,0.E0,-1.E0));
+#813=VECTOR('',#812,1.3E-1);
+#814=CARTESIAN_POINT('',(1.05E-1,1.15E-1,2.05E-1));
+#815=LINE('',#814,#813);
+#816=DIRECTION('',(0.E0,1.E0,0.E0));
+#817=VECTOR('',#816,4.5E-2);
+#818=CARTESIAN_POINT('',(1.E-1,1.15E-1,1.12E-1));
+#819=LINE('',#818,#817);
+#820=DIRECTION('',(0.E0,1.E0,0.E0));
+#821=VECTOR('',#820,4.5E-2);
+#822=CARTESIAN_POINT('',(4.7E-2,1.15E-1,1.65E-1));
+#823=LINE('',#822,#821);
+#824=DIRECTION('',(0.E0,1.E0,0.E0));
+#825=VECTOR('',#824,4.5E-2);
+#826=CARTESIAN_POINT('',(-4.7E-2,1.15E-1,1.65E-1));
+#827=LINE('',#826,#825);
+#828=DIRECTION('',(-1.E0,0.E0,0.E0));
+#829=VECTOR('',#828,2.5E-1);
+#830=CARTESIAN_POINT('',(1.25E-1,1.35E-1,2.25E-1));
+#831=LINE('',#830,#829);
+#832=DIRECTION('',(-1.E0,0.E0,0.E0));
+#833=VECTOR('',#832,2.1E-1);
+#834=CARTESIAN_POINT('',(1.05E-1,1.15E-1,2.05E-1));
+#835=LINE('',#834,#833);
+#836=DIRECTION('',(5.773502691896E-1,-5.773502691896E-1,-5.773502691896E-1));
+#837=VECTOR('',#836,3.464101615138E-2);
+#838=CARTESIAN_POINT('',(-1.25E-1,1.35E-1,2.25E-1));
+#839=LINE('',#838,#837);
+#840=DIRECTION('',(0.E0,-1.E0,0.E0));
+#841=VECTOR('',#840,1.9E-1);
+#842=CARTESIAN_POINT('',(-1.25E-1,1.35E-1,2.25E-1));
+#843=LINE('',#842,#841);
+#844=DIRECTION('',(0.E0,-1.E0,0.E0));
+#845=VECTOR('',#844,1.7E-1);
+#846=CARTESIAN_POINT('',(-1.05E-1,1.15E-1,2.05E-1));
+#847=LINE('',#846,#845);
+#848=DIRECTION('',(7.071067811865E-1,0.E0,-7.071067811865E-1));
+#849=VECTOR('',#848,2.828427124746E-2);
+#850=CARTESIAN_POINT('',(-1.25E-1,-5.5E-2,2.25E-1));
+#851=LINE('',#850,#849);
+#852=DIRECTION('',(0.E0,0.E0,-1.E0));
+#853=VECTOR('',#852,1.8E-1);
+#854=CARTESIAN_POINT('',(-6.E-2,6.5E-2,5.5E-2));
+#855=LINE('',#854,#853);
+#856=DIRECTION('',(-1.E0,0.E0,0.E0));
+#857=VECTOR('',#856,4.5E-2);
+#858=CARTESIAN_POINT('',(-6.E-2,8.5E-2,7.5E-2));
+#859=LINE('',#858,#857);
+#860=DIRECTION('',(0.E0,-7.071067811865E-1,-7.071067811865E-1));
+#861=VECTOR('',#860,2.828427124746E-2);
+#862=CARTESIAN_POINT('',(-6.E-2,8.5E-2,7.5E-2));
+#863=LINE('',#862,#861);
+#864=DIRECTION('',(-1.E0,0.E0,0.E0));
+#865=VECTOR('',#864,4.5E-2);
+#866=CARTESIAN_POINT('',(-6.E-2,6.5E-2,5.5E-2));
+#867=LINE('',#866,#865);
+#868=DIRECTION('',(0.E0,-7.071067811865E-1,-7.071067811865E-1));
+#869=VECTOR('',#868,2.828427124746E-2);
+#870=CARTESIAN_POINT('',(-1.05E-1,8.5E-2,7.5E-2));
+#871=LINE('',#870,#869);
+#872=DIRECTION('',(0.E0,0.E0,-1.E0));
+#873=VECTOR('',#872,2.4E-1);
+#874=CARTESIAN_POINT('',(3.75E-2,-1.25E-2,1.15E-1));
+#875=LINE('',#874,#873);
+#876=DIRECTION('',(0.E0,0.E0,1.E0));
+#877=VECTOR('',#876,2.4E-1);
+#878=CARTESIAN_POINT('',(3.75E-2,1.25E-2,-1.25E-1));
+#879=LINE('',#878,#877);
+#880=DIRECTION('',(0.E0,-1.E0,0.E0));
+#881=VECTOR('',#880,2.5E-2);
+#882=CARTESIAN_POINT('',(3.75E-2,1.25E-2,1.15E-1));
+#883=LINE('',#882,#881);
+#884=DIRECTION('',(0.E0,-1.E0,0.E0));
+#885=VECTOR('',#884,1.85E-1);
+#886=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.7E-1));
+#887=LINE('',#886,#885);
+#888=DIRECTION('',(0.E0,0.E0,-1.E0));
+#889=VECTOR('',#888,2.5E-2);
+#890=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.7E-1));
+#891=LINE('',#890,#889);
+#892=DIRECTION('',(0.E0,1.E0,0.E0));
+#893=VECTOR('',#892,1.85E-1);
+#894=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.95E-1));
+#895=LINE('',#894,#893);
+#896=DIRECTION('',(1.E0,0.E0,0.E0));
+#897=VECTOR('',#896,2.5E-2);
+#898=CARTESIAN_POINT('',(3.75E-2,-1.25E-2,1.15E-1));
+#899=LINE('',#898,#897);
+#900=DIRECTION('',(1.E0,0.E0,0.E0));
+#901=VECTOR('',#900,2.5E-2);
+#902=CARTESIAN_POINT('',(3.75E-2,1.25E-2,1.15E-1));
+#903=LINE('',#902,#901);
+#904=DIRECTION('',(0.E0,0.E0,-1.E0));
+#905=VECTOR('',#904,2.4E-1);
+#906=CARTESIAN_POINT('',(6.25E-2,-1.25E-2,1.15E-1));
+#907=LINE('',#906,#905);
+#908=DIRECTION('',(0.E0,-1.E0,0.E0));
+#909=VECTOR('',#908,2.5E-2);
+#910=CARTESIAN_POINT('',(6.25E-2,1.25E-2,1.15E-1));
+#911=LINE('',#910,#909);
+#912=DIRECTION('',(0.E0,0.E0,1.E0));
+#913=VECTOR('',#912,2.4E-1);
+#914=CARTESIAN_POINT('',(6.25E-2,1.25E-2,-1.25E-1));
+#915=LINE('',#914,#913);
+#916=DIRECTION('',(0.E0,-1.E0,0.E0));
+#917=VECTOR('',#916,1.85E-1);
+#918=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.7E-1));
+#919=LINE('',#918,#917);
+#920=DIRECTION('',(0.E0,1.E0,0.E0));
+#921=VECTOR('',#920,1.85E-1);
+#922=CARTESIAN_POINT('',(6.25E-2,-2.45E-1,-2.95E-1));
+#923=LINE('',#922,#921);
+#924=DIRECTION('',(0.E0,0.E0,-1.E0));
+#925=VECTOR('',#924,2.5E-2);
+#926=CARTESIAN_POINT('',(6.25E-2,-2.45E-1,-2.7E-1));
+#927=LINE('',#926,#925);
+#928=DIRECTION('',(1.E0,0.E0,0.E0));
+#929=VECTOR('',#928,2.5E-2);
+#930=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.7E-1));
+#931=LINE('',#930,#929);
+#932=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.45E-1));
+#933=DIRECTION('',(1.E0,0.E0,0.E0));
+#934=DIRECTION('',(0.E0,0.E0,-1.E0));
+#935=AXIS2_PLACEMENT_3D('',#932,#933,#934);
+#937=DIRECTION('',(0.E0,1.E0,0.E0));
+#938=VECTOR('',#937,2.25E-2);
+#939=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.7E-1));
+#940=LINE('',#939,#938);
+#941=DIRECTION('',(0.E0,1.E0,0.E0));
+#942=VECTOR('',#941,2.25E-2);
+#943=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.95E-1));
+#944=LINE('',#943,#942);
+#945=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.45E-1));
+#946=DIRECTION('',(1.E0,0.E0,0.E0));
+#947=DIRECTION('',(0.E0,0.E0,-1.E0));
+#948=AXIS2_PLACEMENT_3D('',#945,#946,#947);
+#950=DIRECTION('',(0.E0,0.E0,-1.E0));
+#951=VECTOR('',#950,4.1E-2);
+#952=CARTESIAN_POINT('',(3.75E-2,3.E-2,-2.69E-1));
+#953=LINE('',#952,#951);
+#954=DIRECTION('',(1.E0,0.E0,0.E0));
+#955=VECTOR('',#954,2.5E-2);
+#956=CARTESIAN_POINT('',(3.75E-2,-3.05E-2,-2.69E-1));
+#957=LINE('',#956,#955);
+#958=DIRECTION('',(0.E0,1.E0,0.E0));
+#959=VECTOR('',#958,2.95E-2);
+#960=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.69E-1));
+#961=LINE('',#960,#959);
+#962=DIRECTION('',(1.E0,0.E0,0.E0));
+#963=VECTOR('',#962,2.5E-2);
+#964=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.69E-1));
+#965=LINE('',#964,#963);
+#966=DIRECTION('',(0.E0,1.E0,0.E0));
+#967=VECTOR('',#966,2.95E-2);
+#968=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.69E-1));
+#969=LINE('',#968,#967);
+#970=DIRECTION('',(1.E0,0.E0,0.E0));
+#971=VECTOR('',#970,2.5E-2);
+#972=CARTESIAN_POINT('',(-6.25E-2,-3.05E-2,-2.69E-1));
+#973=LINE('',#972,#971);
+#974=DIRECTION('',(0.E0,1.E0,0.E0));
+#975=VECTOR('',#974,2.95E-2);
+#976=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.69E-1));
+#977=LINE('',#976,#975);
+#978=DIRECTION('',(1.E0,0.E0,0.E0));
+#979=VECTOR('',#978,2.5E-2);
+#980=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.69E-1));
+#981=LINE('',#980,#979);
+#982=DIRECTION('',(0.E0,1.E0,0.E0));
+#983=VECTOR('',#982,2.95E-2);
+#984=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.69E-1));
+#985=LINE('',#984,#983);
+#986=DIRECTION('',(1.E0,0.E0,0.E0));
+#987=VECTOR('',#986,2.5E-2);
+#988=CARTESIAN_POINT('',(-6.25E-2,6.363424398923E-3,-2.69E-1));
+#989=LINE('',#988,#987);
+#990=DIRECTION('',(0.E0,1.E0,0.E0));
+#991=VECTOR('',#990,2.363657560108E-2);
+#992=CARTESIAN_POINT('',(-3.75E-2,6.363424398923E-3,-2.69E-1));
+#993=LINE('',#992,#991);
+#994=DIRECTION('',(1.E0,0.E0,0.E0));
+#995=VECTOR('',#994,7.5E-2);
+#996=CARTESIAN_POINT('',(-3.75E-2,3.E-2,-2.69E-1));
+#997=LINE('',#996,#995);
+#998=DIRECTION('',(0.E0,1.E0,0.E0));
+#999=VECTOR('',#998,2.363657560108E-2);
+#1000=CARTESIAN_POINT('',(3.75E-2,6.363424398923E-3,-2.69E-1));
+#1001=LINE('',#1000,#999);
+#1002=DIRECTION('',(1.E0,0.E0,0.E0));
+#1003=VECTOR('',#1002,2.5E-2);
+#1004=CARTESIAN_POINT('',(3.75E-2,6.363424398923E-3,-2.69E-1));
+#1005=LINE('',#1004,#1003);
+#1006=DIRECTION('',(0.E0,1.E0,0.E0));
+#1007=VECTOR('',#1006,2.363657560108E-2);
+#1008=CARTESIAN_POINT('',(6.25E-2,6.363424398923E-3,-2.69E-1));
+#1009=LINE('',#1008,#1007);
+#1010=DIRECTION('',(-1.E0,0.E0,0.E0));
+#1011=VECTOR('',#1010,3.75E-2);
+#1012=CARTESIAN_POINT('',(1.E-1,3.E-2,-2.69E-1));
+#1013=LINE('',#1012,#1011);
+#1014=DIRECTION('',(1.E0,0.E0,0.E0));
+#1015=VECTOR('',#1014,3.75E-2);
+#1016=CARTESIAN_POINT('',(-1.E-1,3.E-2,-2.69E-1));
+#1017=LINE('',#1016,#1015);
+#1018=DIRECTION('',(0.E0,1.E0,0.E0));
+#1019=VECTOR('',#1018,2.363657560108E-2);
+#1020=CARTESIAN_POINT('',(-6.25E-2,6.363424398923E-3,-2.69E-1));
+#1021=LINE('',#1020,#1019);
+#1022=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1023=VECTOR('',#1022,2.5E-2);
+#1024=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.7E-1));
+#1025=LINE('',#1024,#1023);
+#1026=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1027=VECTOR('',#1026,1.5E-2);
+#1028=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.95E-1));
+#1029=LINE('',#1028,#1027);
+#1030=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1031=VECTOR('',#1030,8.5E-2);
+#1032=CARTESIAN_POINT('',(1.E-1,-6.E-2,-2.25E-1));
+#1033=LINE('',#1032,#1031);
+#1034=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1035=VECTOR('',#1034,2.5E-2);
+#1036=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.7E-1));
+#1037=LINE('',#1036,#1035);
+#1038=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1039=VECTOR('',#1038,9.999999999999E-4);
+#1040=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.69E-1));
+#1041=LINE('',#1040,#1039);
+#1042=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1043=VECTOR('',#1042,2.5E-2);
+#1044=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.7E-1));
+#1045=LINE('',#1044,#1043);
+#1046=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1047=VECTOR('',#1046,1.5E-2);
+#1048=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.95E-1));
+#1049=LINE('',#1048,#1047);
+#1050=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1051=VECTOR('',#1050,2.5E-2);
+#1052=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.7E-1));
+#1053=LINE('',#1052,#1051);
+#1054=DIRECTION('',(-1.387778780782E-14,0.E0,-1.E0));
+#1055=VECTOR('',#1054,9.999999999999E-4);
+#1056=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.69E-1));
+#1057=LINE('',#1056,#1055);
+#1058=DIRECTION('',(0.E0,0.E0,1.E0));
+#1059=VECTOR('',#1058,2.4E-1);
+#1060=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,-1.25E-1));
+#1061=LINE('',#1060,#1059);
+#1062=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1063=VECTOR('',#1062,2.5E-2);
+#1064=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,1.15E-1));
+#1065=LINE('',#1064,#1063);
+#1066=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1067=VECTOR('',#1066,2.4E-1);
+#1068=CARTESIAN_POINT('',(-6.25E-2,-1.25E-2,1.15E-1));
+#1069=LINE('',#1068,#1067);
+#1070=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1071=VECTOR('',#1070,1.85E-1);
+#1072=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.7E-1));
+#1073=LINE('',#1072,#1071);
+#1074=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1075=VECTOR('',#1074,2.5E-2);
+#1076=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.7E-1));
+#1077=LINE('',#1076,#1075);
+#1078=DIRECTION('',(0.E0,1.E0,0.E0));
+#1079=VECTOR('',#1078,1.85E-1);
+#1080=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.95E-1));
+#1081=LINE('',#1080,#1079);
+#1082=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1083=VECTOR('',#1082,2.4E-1);
+#1084=CARTESIAN_POINT('',(-3.75E-2,-1.25E-2,1.15E-1));
+#1085=LINE('',#1084,#1083);
+#1086=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1087=VECTOR('',#1086,2.5E-2);
+#1088=CARTESIAN_POINT('',(-3.75E-2,1.25E-2,1.15E-1));
+#1089=LINE('',#1088,#1087);
+#1090=DIRECTION('',(0.E0,0.E0,1.E0));
+#1091=VECTOR('',#1090,2.4E-1);
+#1092=CARTESIAN_POINT('',(-3.75E-2,1.25E-2,-1.25E-1));
+#1093=LINE('',#1092,#1091);
+#1094=DIRECTION('',(0.E0,1.E0,0.E0));
+#1095=VECTOR('',#1094,1.85E-1);
+#1096=CARTESIAN_POINT('',(-3.75E-2,-2.45E-1,-2.95E-1));
+#1097=LINE('',#1096,#1095);
+#1098=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1099=VECTOR('',#1098,2.5E-2);
+#1100=CARTESIAN_POINT('',(-3.75E-2,-2.45E-1,-2.7E-1));
+#1101=LINE('',#1100,#1099);
+#1102=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1103=VECTOR('',#1102,1.85E-1);
+#1104=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.7E-1));
+#1105=LINE('',#1104,#1103);
+#1106=DIRECTION('',(1.E0,0.E0,0.E0));
+#1107=VECTOR('',#1106,2.5E-2);
+#1108=CARTESIAN_POINT('',(-6.25E-2,-1.25E-2,1.15E-1));
+#1109=LINE('',#1108,#1107);
+#1110=DIRECTION('',(1.E0,0.E0,0.E0));
+#1111=VECTOR('',#1110,2.5E-2);
+#1112=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,1.15E-1));
+#1113=LINE('',#1112,#1111);
+#1114=DIRECTION('',(1.E0,0.E0,0.E0));
+#1115=VECTOR('',#1114,2.5E-2);
+#1116=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.95E-1));
+#1117=LINE('',#1116,#1115);
+#1118=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.45E-1));
+#1119=DIRECTION('',(1.E0,0.E0,0.E0));
+#1120=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1121=AXIS2_PLACEMENT_3D('',#1118,#1119,#1120);
+#1123=DIRECTION('',(0.E0,1.E0,0.E0));
+#1124=VECTOR('',#1123,2.25E-2);
+#1125=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.7E-1));
+#1126=LINE('',#1125,#1124);
+#1127=DIRECTION('',(0.E0,1.E0,0.E0));
+#1128=VECTOR('',#1127,2.25E-2);
+#1129=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.95E-1));
+#1130=LINE('',#1129,#1128);
+#1131=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.45E-1));
+#1132=DIRECTION('',(1.E0,0.E0,0.E0));
+#1133=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1134=AXIS2_PLACEMENT_3D('',#1131,#1132,#1133);
+#1136=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1137=VECTOR('',#1136,4.1E-2);
+#1138=CARTESIAN_POINT('',(-6.25E-2,3.E-2,-2.69E-1));
+#1139=LINE('',#1138,#1137);
+#1140=CARTESIAN_POINT('',(-3.75E-2,-3.75E-2,-2.45E-1));
+#1141=DIRECTION('',(1.E0,0.E0,0.E0));
+#1142=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1143=AXIS2_PLACEMENT_3D('',#1140,#1141,#1142);
+#1145=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1146=VECTOR('',#1145,9.999999999999E-4);
+#1147=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.69E-1));
+#1148=LINE('',#1147,#1146);
+#1149=DIRECTION('',(0.E0,1.E0,0.E0));
+#1150=VECTOR('',#1149,2.25E-2);
+#1151=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.7E-1));
+#1152=LINE('',#1151,#1150);
+#1153=DIRECTION('',(0.E0,1.E0,0.E0));
+#1154=VECTOR('',#1153,2.25E-2);
+#1155=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.95E-1));
+#1156=LINE('',#1155,#1154);
+#1157=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1158=VECTOR('',#1157,1.5E-2);
+#1159=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.95E-1));
+#1160=LINE('',#1159,#1158);
+#1161=CARTESIAN_POINT('',(-3.75E-2,-3.75E-2,-2.45E-1));
+#1162=DIRECTION('',(1.E0,0.E0,0.E0));
+#1163=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1164=AXIS2_PLACEMENT_3D('',#1161,#1162,#1163);
+#1166=DIRECTION('',(1.E0,0.E0,0.E0));
+#1167=VECTOR('',#1166,2.5E-2);
+#1168=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.7E-1));
+#1169=LINE('',#1168,#1167);
+#1170=DIRECTION('',(1.E0,0.E0,0.E0));
+#1171=VECTOR('',#1170,2.5E-2);
+#1172=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.7E-1));
+#1173=LINE('',#1172,#1171);
+#1174=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1175=VECTOR('',#1174,9.E-2);
+#1176=CARTESIAN_POINT('',(1.E-1,3.E-2,-3.1E-1));
+#1177=LINE('',#1176,#1175);
+#1178=DIRECTION('',(1.E0,0.E0,0.E0));
+#1179=VECTOR('',#1178,3.75E-2);
+#1180=CARTESIAN_POINT('',(6.25E-2,3.E-2,-3.1E-1));
+#1181=LINE('',#1180,#1179);
+#1182=DIRECTION('',(0.E0,1.E0,0.E0));
+#1183=VECTOR('',#1182,9.E-2);
+#1184=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-3.1E-1));
+#1185=LINE('',#1184,#1183);
+#1186=DIRECTION('',(-1.E0,0.E0,0.E0));
+#1187=VECTOR('',#1186,3.75E-2);
+#1188=CARTESIAN_POINT('',(1.E-1,-6.E-2,-3.1E-1));
+#1189=LINE('',#1188,#1187);
+#1190=DIRECTION('',(1.E0,0.E0,0.E0));
+#1191=VECTOR('',#1190,3.75E-2);
+#1192=CARTESIAN_POINT('',(-1.E-1,3.E-2,-3.1E-1));
+#1193=LINE('',#1192,#1191);
+#1194=DIRECTION('',(0.E0,1.E0,0.E0));
+#1195=VECTOR('',#1194,9.E-2);
+#1196=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-3.1E-1));
+#1197=LINE('',#1196,#1195);
+#1198=DIRECTION('',(-1.E0,0.E0,0.E0));
+#1199=VECTOR('',#1198,3.75E-2);
+#1200=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-3.1E-1));
+#1201=LINE('',#1200,#1199);
+#1202=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1203=VECTOR('',#1202,9.E-2);
+#1204=CARTESIAN_POINT('',(-6.25E-2,3.E-2,-3.1E-1));
+#1205=LINE('',#1204,#1203);
+#1206=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1207=VECTOR('',#1206,9.E-2);
+#1208=CARTESIAN_POINT('',(3.75E-2,3.E-2,-3.1E-1));
+#1209=LINE('',#1208,#1207);
+#1210=DIRECTION('',(1.E0,0.E0,0.E0));
+#1211=VECTOR('',#1210,7.5E-2);
+#1212=CARTESIAN_POINT('',(-3.75E-2,3.E-2,-3.1E-1));
+#1213=LINE('',#1212,#1211);
+#1214=DIRECTION('',(0.E0,1.E0,0.E0));
+#1215=VECTOR('',#1214,9.E-2);
+#1216=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-3.1E-1));
+#1217=LINE('',#1216,#1215);
+#1218=DIRECTION('',(-1.E0,0.E0,0.E0));
+#1219=VECTOR('',#1218,7.5E-2);
+#1220=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-3.1E-1));
+#1221=LINE('',#1220,#1219);
+#1222=DIRECTION('',(0.E0,1.E0,0.E0));
+#1223=VECTOR('',#1222,3.E-2);
+#1224=CARTESIAN_POINT('',(1.E-1,3.E-2,-2.69E-1));
+#1225=LINE('',#1224,#1223);
+#1226=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1227=VECTOR('',#1226,4.1E-2);
+#1228=CARTESIAN_POINT('',(1.E-1,3.E-2,-2.69E-1));
+#1229=LINE('',#1228,#1227);
+#1230=DIRECTION('',(0.E0,0.E0,1.E0));
+#1231=VECTOR('',#1230,4.4E-2);
+#1232=CARTESIAN_POINT('',(1.E-1,6.E-2,-2.69E-1));
+#1233=LINE('',#1232,#1231);
+#1234=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1235=VECTOR('',#1234,4.1E-2);
+#1236=CARTESIAN_POINT('',(6.25E-2,3.E-2,-2.69E-1));
+#1237=LINE('',#1236,#1235);
+#1238=CARTESIAN_POINT('',(6.25E-2,-3.75E-2,-2.45E-1));
+#1239=DIRECTION('',(1.E0,0.E0,0.E0));
+#1240=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1241=AXIS2_PLACEMENT_3D('',#1238,#1239,#1240);
+#1243=DIRECTION('',(1.387778780782E-14,0.E0,-1.E0));
+#1244=VECTOR('',#1243,9.999999999999E-4);
+#1245=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.69E-1));
+#1246=LINE('',#1245,#1244);
+#1247=DIRECTION('',(0.E0,1.E0,0.E0));
+#1248=VECTOR('',#1247,2.25E-2);
+#1249=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.7E-1));
+#1250=LINE('',#1249,#1248);
+#1251=DIRECTION('',(0.E0,1.E0,0.E0));
+#1252=VECTOR('',#1251,2.25E-2);
+#1253=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.95E-1));
+#1254=LINE('',#1253,#1252);
+#1255=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1256=VECTOR('',#1255,1.5E-2);
+#1257=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.95E-1));
+#1258=LINE('',#1257,#1256);
+#1259=CARTESIAN_POINT('',(6.25E-2,-3.75E-2,-2.45E-1));
+#1260=DIRECTION('',(1.E0,0.E0,0.E0));
+#1261=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1262=AXIS2_PLACEMENT_3D('',#1259,#1260,#1261);
+#1264=DIRECTION('',(1.E0,0.E0,0.E0));
+#1265=VECTOR('',#1264,2.5E-2);
+#1266=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.95E-1));
+#1267=LINE('',#1266,#1265);
+#1268=DIRECTION('',(1.E0,0.E0,0.E0));
+#1269=VECTOR('',#1268,2.5E-2);
+#1270=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.95E-1));
+#1271=LINE('',#1270,#1269);
+#1272=DIRECTION('',(1.E0,0.E0,0.E0));
+#1273=VECTOR('',#1272,2.5E-2);
+#1274=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.7E-1));
+#1275=LINE('',#1274,#1273);
+#1276=DIRECTION('',(1.E0,0.E0,0.E0));
+#1277=VECTOR('',#1276,2.E-1);
+#1278=CARTESIAN_POINT('',(-1.E-1,6.E-2,-2.69E-1));
+#1279=LINE('',#1278,#1277);
+#1280=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1281=VECTOR('',#1280,8.5E-2);
+#1282=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.25E-1));
+#1283=LINE('',#1282,#1281);
+#1284=DIRECTION('',(0.E0,1.E0,0.E0));
+#1285=VECTOR('',#1284,3.E-2);
+#1286=CARTESIAN_POINT('',(-1.E-1,3.E-2,-2.69E-1));
+#1287=LINE('',#1286,#1285);
+#1288=DIRECTION('',(0.E0,0.E0,1.E0));
+#1289=VECTOR('',#1288,4.4E-2);
+#1290=CARTESIAN_POINT('',(-1.E-1,6.E-2,-2.69E-1));
+#1291=LINE('',#1290,#1289);
+#1292=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1293=VECTOR('',#1292,4.1E-2);
+#1294=CARTESIAN_POINT('',(-1.E-1,3.E-2,-2.69E-1));
+#1295=LINE('',#1294,#1293);
+#1296=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1297=VECTOR('',#1296,4.1E-2);
+#1298=CARTESIAN_POINT('',(-3.75E-2,3.E-2,-2.69E-1));
+#1299=LINE('',#1298,#1297);
+#1300=DIRECTION('',(1.E0,0.E0,0.E0));
+#1301=VECTOR('',#1300,2.5E-2);
+#1302=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.95E-1));
+#1303=LINE('',#1302,#1301);
+#1304=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#1305=VECTOR('',#1304,2.121320343560E-2);
+#1306=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,8.53E-2));
+#1307=LINE('',#1306,#1305);
+#1308=DIRECTION('',(0.E0,0.E0,1.E0));
+#1309=VECTOR('',#1308,1.397E-1);
+#1310=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,8.53E-2));
+#1311=LINE('',#1310,#1309);
+#1312=DIRECTION('',(4.472135954999E-1,4.472135954999E-1,7.745966692415E-1));
+#1313=VECTOR('',#1312,3.354101966250E-2);
+#1314=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,3.606921938166E-2));
+#1315=LINE('',#1314,#1313);
+#1316=DIRECTION('',(0.E0,0.E0,1.E0));
+#1317=VECTOR('',#1316,2.610692193817E-1);
+#1318=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,-2.25E-1));
+#1319=LINE('',#1318,#1317);
+#1320=CARTESIAN_POINT('',(-5.357659555759E-2,-1.584529252708E-1,8.53E-2));
+#1321=CARTESIAN_POINT('',(-5.663251262146E-2,-1.602748020020E-1,8.53E-2));
+#1322=CARTESIAN_POINT('',(-6.248823265869E-2,-1.635695268730E-1,8.53E-2));
+#1323=CARTESIAN_POINT('',(-7.050677154246E-2,-1.674178932629E-1,8.53E-2));
+#1324=CARTESIAN_POINT('',(-7.782186226657E-2,-1.701926684698E-1,8.53E-2));
+#1325=CARTESIAN_POINT('',(-8.468531890115E-2,-1.720006646524E-1,8.53E-2));
+#1326=CARTESIAN_POINT('',(-9.141439480696E-2,-1.728932407002E-1,8.53E-2));
+#1327=CARTESIAN_POINT('',(-9.812537784739E-2,-1.728324213069E-1,8.53E-2));
+#1328=CARTESIAN_POINT('',(-1.048497820165E-1,-1.718253942191E-1,8.53E-2));
+#1329=CARTESIAN_POINT('',(-1.117350648462E-1,-1.699078390694E-1,8.53E-2));
+#1330=CARTESIAN_POINT('',(-1.191013480360E-1,-1.670181714848E-1,8.53E-2));
+#1331=CARTESIAN_POINT('',(-1.271830023765E-1,-1.630537253032E-1,8.53E-2));
+#1332=CARTESIAN_POINT('',(-1.360381346460E-1,-1.579986388164E-1,8.53E-2));
+#1333=CARTESIAN_POINT('',(-1.457081013489E-1,-1.518761100516E-1,8.53E-2));
+#1334=CARTESIAN_POINT('',(-1.527513889228E-1,-1.470847088016E-1,8.53E-2));
+#1335=CARTESIAN_POINT('',(-1.564234044424E-1,-1.445195163487E-1,8.53E-2));
+#1337=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1338=VECTOR('',#1337,4.451951634874E-2);
+#1339=CARTESIAN_POINT('',(-1.564234044424E-1,-1.E-1,8.53E-2));
+#1340=LINE('',#1339,#1338);
+#1341=DIRECTION('',(-7.713691830612E-1,-6.363879189799E-1,0.E0));
+#1342=VECTOR('',#1341,9.758311976582E-3);
+#1343=CARTESIAN_POINT('',(-7.409325605598E-2,-1.882902550866E-1,4.63E-2));
+#1344=LINE('',#1343,#1342);
+#1345=DIRECTION('',(7.713691830612E-1,-6.363879189799E-1,0.E0));
+#1346=VECTOR('',#1345,3.737601997139E-2);
+#1347=CARTESIAN_POINT('',(-1.359067439440E-1,-1.707146793688E-1,4.63E-2));
+#1348=LINE('',#1347,#1346);
+#1349=CARTESIAN_POINT('',(-1.359067439440E-1,-1.707146793688E-1,4.63E-2));
+#1350=CARTESIAN_POINT('',(-1.347818304692E-1,-1.716427451524E-1,
+4.435159270743E-2));
+#1351=CARTESIAN_POINT('',(-1.325825676173E-1,-1.732270113341E-1,
+4.054235770879E-2));
+#1352=CARTESIAN_POINT('',(-1.294823806596E-1,-1.747303299587E-1,
+3.517267638502E-2));
+#1353=CARTESIAN_POINT('',(-1.264154611381E-1,-1.754149892381E-1,
+2.986061595117E-2));
+#1354=CARTESIAN_POINT('',(-1.233594852548E-1,-1.752359071234E-1,
+2.456751045462E-2));
+#1355=CARTESIAN_POINT('',(-1.202915661583E-1,-1.742119607851E-1,
+1.925371870584E-2));
+#1356=CARTESIAN_POINT('',(-1.171335690736E-1,-1.723643667652E-1,
+1.378390730511E-2));
+#1357=CARTESIAN_POINT('',(-1.137616720891E-1,-1.696454501204E-1,
+7.943610410063E-3));
+#1358=CARTESIAN_POINT('',(-1.100774138201E-1,-1.659761594804E-1,
+1.562287899877E-3));
+#1359=CARTESIAN_POINT('',(-1.073969492721E-1,-1.628921138363E-1,
+-3.080412885144E-3));
+#1360=CARTESIAN_POINT('',(-1.06E-1,-1.611975091251E-1,-5.5E-3));
+#1362=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1363=VECTOR('',#1362,6.119750912510E-2);
+#1364=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-5.5E-3));
+#1365=LINE('',#1364,#1363);
+#1366=CARTESIAN_POINT('',(-1.574234044424E-1,-1.445586341714E-1,
+8.356794919243E-2));
+#1367=CARTESIAN_POINT('',(-1.556513464535E-1,-1.471054225392E-1,
+8.049865472165E-2));
+#1368=CARTESIAN_POINT('',(-1.522547386123E-1,-1.518843967519E-1,
+7.461555736733E-2));
+#1369=CARTESIAN_POINT('',(-1.476050986136E-1,-1.580669398403E-1,
+6.656214465263E-2));
+#1370=CARTESIAN_POINT('',(-1.433539961350E-1,-1.632828598805E-1,
+5.919901917161E-2));
+#1371=CARTESIAN_POINT('',(-1.394858289585E-1,-1.675175689925E-1,
+5.249915708967E-2));
+#1372=CARTESIAN_POINT('',(-1.370687942147E-1,-1.697559753270E-1,
+4.831273010978E-2));
+#1373=CARTESIAN_POINT('',(-1.359067439440E-1,-1.707146793688E-1,4.63E-2));
+#1375=CARTESIAN_POINT('',(-9.434827557301E-2,-1.945003269382E-1,4.63E-2));
+#1376=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1377=DIRECTION('',(-1.E0,0.E0,0.E0));
+#1378=AXIS2_PLACEMENT_3D('',#1375,#1376,#1377);
+#1380=CARTESIAN_POINT('',(-9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#1381=DIRECTION('',(0.E0,0.E0,1.E0));
+#1382=DIRECTION('',(-6.363879189799E-1,-7.713691830612E-1,0.E0));
+#1383=AXIS2_PLACEMENT_3D('',#1380,#1381,#1382);
+#1385=CARTESIAN_POINT('',(-9.434827557301E-2,-1.945003269382E-1,4.63E-2));
+#1386=DIRECTION('',(0.E0,1.E0,0.E0));
+#1387=DIRECTION('',(-1.E0,0.E0,0.E0));
+#1388=AXIS2_PLACEMENT_3D('',#1385,#1386,#1387);
+#1390=CARTESIAN_POINT('',(-9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#1391=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1392=DIRECTION('',(6.363879189799E-1,-7.713691830612E-1,0.E0));
+#1393=AXIS2_PLACEMENT_3D('',#1390,#1391,#1392);
+#1395=CARTESIAN_POINT('',(-1.04E-1,-1.615297952414E-1,-5.5E-3));
+#1396=CARTESIAN_POINT('',(-1.026682576530E-1,-1.636018584679E-1,
+-3.193354592429E-3));
+#1397=CARTESIAN_POINT('',(-1.001116839393E-1,-1.675428607596E-1,
+1.234760972986E-3));
+#1398=CARTESIAN_POINT('',(-9.659523240660E-2,-1.728248917968E-1,
+7.325433690009E-3));
+#1399=CARTESIAN_POINT('',(-9.337899903542E-2,-1.774731058704E-1,
+1.289611329789E-2));
+#1400=CARTESIAN_POINT('',(-9.044094627590E-2,-1.814760124802E-1,
+1.798496995270E-2));
+#1401=CARTESIAN_POINT('',(-8.775172573942E-2,-1.848141821279E-1,
+2.264283655464E-2));
+#1402=CARTESIAN_POINT('',(-8.530186951023E-2,-1.874238866681E-1,
+2.688611201484E-2));
+#1403=CARTESIAN_POINT('',(-8.306372284748E-2,-1.892734762783E-1,
+3.076269574951E-2));
+#1404=CARTESIAN_POINT('',(-8.085149192496E-2,-1.904147026451E-1,
+3.459439210539E-2));
+#1405=CARTESIAN_POINT('',(-7.856145149216E-2,-1.906606296865E-1,
+3.856085848638E-2));
+#1406=CARTESIAN_POINT('',(-7.631357120374E-2,-1.899218247534E-1,
+4.245430135527E-2));
+#1407=CARTESIAN_POINT('',(-7.483518463656E-2,-1.889023541901E-1,
+4.501494200285E-2));
+#1408=CARTESIAN_POINT('',(-7.409325605598E-2,-1.882902550866E-1,4.63E-2));
+#1410=CARTESIAN_POINT('',(-7.409325605598E-2,-1.882902550866E-1,4.63E-2));
+#1411=CARTESIAN_POINT('',(-7.331187686197E-2,-1.876456088004E-1,
+4.765338846400E-2));
+#1412=CARTESIAN_POINT('',(-7.171593453487E-2,-1.861024731040E-1,
+5.041764166049E-2));
+#1413=CARTESIAN_POINT('',(-6.926242411520E-2,-1.832438523859E-1,
+5.466724636425E-2));
+#1414=CARTESIAN_POINT('',(-6.652736541744E-2,-1.796773890294E-1,
+5.940450699046E-2));
+#1415=CARTESIAN_POINT('',(-6.352615439082E-2,-1.754627992800E-1,
+6.460275697280E-2));
+#1416=CARTESIAN_POINT('',(-6.022212646643E-2,-1.705966377575E-1,
+7.032550120747E-2));
+#1417=CARTESIAN_POINT('',(-5.659847526199E-2,-1.650857169535E-1,
+7.660184920247E-2));
+#1418=CARTESIAN_POINT('',(-5.395509643280E-2,-1.609766693234E-1,
+8.118031563828E-2));
+#1419=CARTESIAN_POINT('',(-5.257659555759E-2,-1.588166648235E-1,
+8.356794919243E-2));
+#1421=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1422=VECTOR('',#1421,5.881666482353E-2);
+#1423=CARTESIAN_POINT('',(-5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#1424=LINE('',#1423,#1422);
+#1425=CARTESIAN_POINT('',(-5.257659555759E-2,-1.588166648235E-1,
+8.356794919243E-2));
+#1426=CARTESIAN_POINT('',(-5.217581120736E-2,-1.584428467302E-1,
+8.379934214492E-2));
+#1427=CARTESIAN_POINT('',(-5.137088670405E-2,-1.576916887051E-1,
+8.426406552358E-2));
+#1428=CARTESIAN_POINT('',(-5.055923535607E-2,-1.569335088076E-1,
+8.473267264783E-2));
+#1429=CARTESIAN_POINT('',(-5.015172442699E-2,-1.565526684416E-1,
+8.496794919243E-2));
+#1431=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1432=VECTOR('',#1431,5.655266844162E-2);
+#1433=CARTESIAN_POINT('',(-5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#1434=LINE('',#1433,#1432);
+#1435=CARTESIAN_POINT('',(-5.015172442699E-2,-1.565526684416E-1,
+8.496794919243E-2));
+#1436=CARTESIAN_POINT('',(-5.031806470115E-2,-1.564994379575E-1,
+8.525605899863E-2));
+#1437=CARTESIAN_POINT('',(-5.065107063889E-2,-1.563883833904E-1,
+8.583284220201E-2));
+#1438=CARTESIAN_POINT('',(-5.098473015951E-2,-1.562681802864E-1,
+8.641075744415E-2));
+#1439=CARTESIAN_POINT('',(-5.115172442699E-2,-1.562058027820E-1,8.67E-2));
+#1441=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1442=VECTOR('',#1441,5.620580278197E-2);
+#1443=CARTESIAN_POINT('',(-5.115172442699E-2,-1.E-1,8.67E-2));
+#1444=LINE('',#1443,#1442);
+#1445=CARTESIAN_POINT('',(-5.115172442699E-2,-1.562058027820E-1,8.67E-2));
+#1446=CARTESIAN_POINT('',(-5.155953580995E-2,-1.565842146916E-1,
+8.646454998827E-2));
+#1447=CARTESIAN_POINT('',(-5.237148638706E-2,-1.573371556234E-1,
+8.599577010401E-2));
+#1448=CARTESIAN_POINT('',(-5.317611041452E-2,-1.580823051987E-1,
+8.553122020516E-2));
+#1449=CARTESIAN_POINT('',(-5.357659555759E-2,-1.584529252708E-1,8.53E-2));
+#1451=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1452=VECTOR('',#1451,5.845292527080E-2);
+#1453=CARTESIAN_POINT('',(-5.357659555759E-2,-1.E-1,8.53E-2));
+#1454=LINE('',#1453,#1452);
+#1455=DIRECTION('',(7.071067811866E-1,-7.071067811865E-1,0.E0));
+#1456=VECTOR('',#1455,2.121320343560E-2);
+#1457=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,8.53E-2));
+#1458=LINE('',#1457,#1456);
+#1459=DIRECTION('',(-4.472135954999E-1,4.472135954999E-1,-7.745966692415E-1));
+#1460=VECTOR('',#1459,3.354101966250E-2);
+#1461=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,3.606921938166E-2));
+#1462=LINE('',#1461,#1460);
+#1463=CARTESIAN_POINT('',(-1.04E-1,-1.592568924846E-1,-8.3E-3));
+#1464=CARTESIAN_POINT('',(-1.04E-1,-1.596394080684E-1,-7.829162455956E-3));
+#1465=CARTESIAN_POINT('',(-1.04E-1,-1.604007469310E-1,-6.891663246043E-3));
+#1466=CARTESIAN_POINT('',(-1.04E-1,-1.611546829174E-1,-5.962500939277E-3));
+#1467=CARTESIAN_POINT('',(-1.04E-1,-1.615297952414E-1,-5.5E-3));
+#1469=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1470=VECTOR('',#1469,6.152979524142E-2);
+#1471=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-5.5E-3));
+#1472=LINE('',#1471,#1470);
+#1473=CARTESIAN_POINT('',(-1.06E-1,-1.589410006771E-1,-8.3E-3));
+#1474=CARTESIAN_POINT('',(-1.056659442691E-1,-1.589985189396E-1,-8.3E-3));
+#1475=CARTESIAN_POINT('',(-1.049985574959E-1,-1.591086986771E-1,-8.3E-3));
+#1476=CARTESIAN_POINT('',(-1.043326128179E-1,-1.592091253379E-1,-8.3E-3));
+#1477=CARTESIAN_POINT('',(-1.04E-1,-1.592568924846E-1,-8.3E-3));
+#1479=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1480=VECTOR('',#1479,5.925689248462E-2);
+#1481=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-8.3E-3));
+#1482=LINE('',#1481,#1480);
+#1483=CARTESIAN_POINT('',(-1.06E-1,-1.611975091251E-1,-5.5E-3));
+#1484=CARTESIAN_POINT('',(-1.06E-1,-1.608256017647E-1,-5.962054406320E-3));
+#1485=CARTESIAN_POINT('',(-1.06E-1,-1.600776010535E-1,-6.890768553405E-3));
+#1486=CARTESIAN_POINT('',(-1.06E-1,-1.593212544286E-1,-7.828714327281E-3));
+#1487=CARTESIAN_POINT('',(-1.06E-1,-1.589410006771E-1,-8.3E-3));
+#1489=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1490=VECTOR('',#1489,5.894100067710E-2);
+#1491=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-8.3E-3));
+#1492=LINE('',#1491,#1490);
+#1493=CARTESIAN_POINT('',(-1.598482755730E-1,-1.422487166033E-1,
+8.496794919243E-2));
+#1494=CARTESIAN_POINT('',(-1.594435190594E-1,-1.426342859019E-1,
+8.473426291033E-2));
+#1495=CARTESIAN_POINT('',(-1.586346173674E-1,-1.434048414760E-1,
+8.426724330069E-2));
+#1496=CARTESIAN_POINT('',(-1.578269383160E-1,-1.441742309618E-1,
+8.380092958296E-2));
+#1497=CARTESIAN_POINT('',(-1.574234044424E-1,-1.445586341714E-1,
+8.356794919243E-2));
+#1499=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1500=VECTOR('',#1499,4.455863417144E-2);
+#1501=CARTESIAN_POINT('',(-1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#1502=LINE('',#1501,#1500);
+#1503=CARTESIAN_POINT('',(-1.588482755730E-1,-1.422110378340E-1,8.67E-2));
+#1504=CARTESIAN_POINT('',(-1.590149631225E-1,-1.422209303233E-1,
+8.641128869537E-2));
+#1505=CARTESIAN_POINT('',(-1.593483173103E-1,-1.422371043369E-1,
+8.583390230505E-2));
+#1506=CARTESIAN_POINT('',(-1.596816297755E-1,-1.422460507658E-1,
+8.525658818051E-2));
+#1507=CARTESIAN_POINT('',(-1.598482755730E-1,-1.422487166033E-1,
+8.496794919243E-2));
+#1509=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1510=VECTOR('',#1509,4.224871660332E-2);
+#1511=CARTESIAN_POINT('',(-1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#1512=LINE('',#1511,#1510);
+#1513=CARTESIAN_POINT('',(-1.564234044424E-1,-1.445195163487E-1,8.53E-2));
+#1514=CARTESIAN_POINT('',(-1.568263799912E-1,-1.441358932733E-1,
+8.553265804156E-2));
+#1515=CARTESIAN_POINT('',(-1.576335005946E-1,-1.433675235913E-1,
+8.599864933919E-2));
+#1516=CARTESIAN_POINT('',(-1.584429606144E-1,-1.425969074141E-1,
+8.646599129957E-2));
+#1517=CARTESIAN_POINT('',(-1.588482755730E-1,-1.422110378340E-1,8.67E-2));
+#1519=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1520=VECTOR('',#1519,4.221103783395E-2);
+#1521=CARTESIAN_POINT('',(-1.588482755730E-1,-1.E-1,8.67E-2));
+#1522=LINE('',#1521,#1520);
+#1523=DIRECTION('',(0.E0,-1.E0,0.E0));
+#1524=VECTOR('',#1523,4.E-2);
+#1525=CARTESIAN_POINT('',(-2.5E-2,-6.E-2,-1.25E-1));
+#1526=LINE('',#1525,#1524);
+#1527=DIRECTION('',(-1.E0,0.E0,0.E0));
+#1528=VECTOR('',#1527,5.E-2);
+#1529=CARTESIAN_POINT('',(2.5E-2,-6.E-2,-1.25E-1));
+#1530=LINE('',#1529,#1528);
+#1531=DIRECTION('',(0.E0,1.E0,0.E0));
+#1532=VECTOR('',#1531,4.E-2);
+#1533=CARTESIAN_POINT('',(2.5E-2,-1.E-1,-1.25E-1));
+#1534=LINE('',#1533,#1532);
+#1535=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1536=VECTOR('',#1535,3.5E-1);
+#1537=CARTESIAN_POINT('',(-2.5E-2,-6.E-2,2.25E-1));
+#1538=LINE('',#1537,#1536);
+#1539=DIRECTION('',(0.E0,0.E0,-1.E0));
+#1540=VECTOR('',#1539,3.5E-1);
+#1541=CARTESIAN_POINT('',(2.5E-2,-6.E-2,2.25E-1));
+#1542=LINE('',#1541,#1540);
+#1543=CARTESIAN_POINT('',(3.75E-2,1.25E-2,1.15E-1));
+#1544=CARTESIAN_POINT('',(3.75E-2,-1.25E-2,1.15E-1));
+#1545=VERTEX_POINT('',#1543);
+#1546=VERTEX_POINT('',#1544);
+#1547=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.7E-1));
+#1548=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.95E-1));
+#1549=VERTEX_POINT('',#1547);
+#1550=VERTEX_POINT('',#1548);
+#1551=CARTESIAN_POINT('',(6.25E-2,1.25E-2,1.15E-1));
+#1552=CARTESIAN_POINT('',(6.25E-2,-1.25E-2,1.15E-1));
+#1553=VERTEX_POINT('',#1551);
+#1554=VERTEX_POINT('',#1552);
+#1555=CARTESIAN_POINT('',(6.25E-2,-2.45E-1,-2.7E-1));
+#1556=CARTESIAN_POINT('',(6.25E-2,-2.45E-1,-2.95E-1));
+#1557=VERTEX_POINT('',#1555);
+#1558=VERTEX_POINT('',#1556);
+#1559=CARTESIAN_POINT('',(3.75E-2,-1.25E-2,-1.25E-1));
+#1560=VERTEX_POINT('',#1559);
+#1561=CARTESIAN_POINT('',(3.75E-2,1.25E-2,-1.25E-1));
+#1562=VERTEX_POINT('',#1561);
+#1563=CARTESIAN_POINT('',(6.25E-2,-1.25E-2,-1.25E-1));
+#1564=VERTEX_POINT('',#1563);
+#1565=CARTESIAN_POINT('',(6.25E-2,1.25E-2,-1.25E-1));
+#1566=VERTEX_POINT('',#1565);
+#1567=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.7E-1));
+#1568=VERTEX_POINT('',#1567);
+#1569=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.7E-1));
+#1570=VERTEX_POINT('',#1569);
+#1571=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.95E-1));
+#1572=VERTEX_POINT('',#1571);
+#1573=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.95E-1));
+#1574=CARTESIAN_POINT('',(6.25E-2,-3.75E-2,-2.95E-1));
+#1575=VERTEX_POINT('',#1573);
+#1576=VERTEX_POINT('',#1574);
+#1577=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.95E-1));
+#1578=VERTEX_POINT('',#1577);
+#1579=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.7E-1));
+#1580=CARTESIAN_POINT('',(6.25E-2,-3.75E-2,-2.7E-1));
+#1581=VERTEX_POINT('',#1579);
+#1582=VERTEX_POINT('',#1580);
+#1583=CARTESIAN_POINT('',(-1.4E-1,1.6E-1,2.25E-1));
+#1584=CARTESIAN_POINT('',(-1.4E-1,-5.E-2,2.25E-1));
+#1585=VERTEX_POINT('',#1583);
+#1586=VERTEX_POINT('',#1584);
+#1587=CARTESIAN_POINT('',(1.4E-1,-5.E-2,2.25E-1));
+#1588=CARTESIAN_POINT('',(1.4E-1,1.6E-1,2.25E-1));
+#1589=VERTEX_POINT('',#1587);
+#1590=VERTEX_POINT('',#1588);
+#1591=CARTESIAN_POINT('',(-1.4E-1,1.6E-1,7.5E-2));
+#1592=VERTEX_POINT('',#1591);
+#1593=CARTESIAN_POINT('',(1.4E-1,1.6E-1,7.5E-2));
+#1594=VERTEX_POINT('',#1593);
+#1595=CARTESIAN_POINT('',(1.4E-1,1.E-1,-2.25E-1));
+#1596=CARTESIAN_POINT('',(-1.4E-1,1.E-1,-2.25E-1));
+#1597=VERTEX_POINT('',#1595);
+#1598=VERTEX_POINT('',#1596);
+#1599=CARTESIAN_POINT('',(-1.4E-1,1.E-1,7.5E-2));
+#1600=VERTEX_POINT('',#1599);
+#1601=CARTESIAN_POINT('',(1.4E-1,1.E-1,7.5E-2));
+#1602=VERTEX_POINT('',#1601);
+#1603=CARTESIAN_POINT('',(-1.4E-1,-5.E-2,-2.25E-1));
+#1604=VERTEX_POINT('',#1603);
+#1605=CARTESIAN_POINT('',(1.4E-1,-5.E-2,-2.25E-1));
+#1606=VERTEX_POINT('',#1605);
+#1607=CARTESIAN_POINT('',(1.05E-1,1.E-1,7.5E-2));
+#1608=CARTESIAN_POINT('',(1.05E-1,1.15E-1,7.5E-2));
+#1609=VERTEX_POINT('',#1607);
+#1610=VERTEX_POINT('',#1608);
+#1611=CARTESIAN_POINT('',(-1.05E-1,1.E-1,7.5E-2));
+#1612=VERTEX_POINT('',#1611);
+#1613=CARTESIAN_POINT('',(1.05E-1,6.5E-2,-1.25E-1));
+#1614=CARTESIAN_POINT('',(6.E-2,6.5E-2,-1.25E-1));
+#1615=VERTEX_POINT('',#1613);
+#1616=VERTEX_POINT('',#1614);
+#1617=CARTESIAN_POINT('',(-1.05E-1,-5.5E-2,-1.25E-1));
+#1618=CARTESIAN_POINT('',(1.05E-1,-5.5E-2,-1.25E-1));
+#1619=VERTEX_POINT('',#1617);
+#1620=VERTEX_POINT('',#1618);
+#1621=CARTESIAN_POINT('',(-6.E-2,6.5E-2,-1.25E-1));
+#1622=CARTESIAN_POINT('',(-1.05E-1,6.5E-2,-1.25E-1));
+#1623=VERTEX_POINT('',#1621);
+#1624=VERTEX_POINT('',#1622);
+#1625=CARTESIAN_POINT('',(-6.E-2,1.E-1,-1.25E-1));
+#1626=CARTESIAN_POINT('',(6.E-2,1.E-1,-1.25E-1));
+#1627=VERTEX_POINT('',#1625);
+#1628=VERTEX_POINT('',#1626);
+#1629=CARTESIAN_POINT('',(-6.E-2,1.E-1,7.5E-2));
+#1630=VERTEX_POINT('',#1629);
+#1631=CARTESIAN_POINT('',(6.E-2,1.E-1,7.5E-2));
+#1632=VERTEX_POINT('',#1631);
+#1633=CARTESIAN_POINT('',(1.E-1,1.15E-1,7.5E-2));
+#1634=CARTESIAN_POINT('',(1.E-1,1.6E-1,7.5E-2));
+#1635=VERTEX_POINT('',#1633);
+#1636=VERTEX_POINT('',#1634);
+#1637=CARTESIAN_POINT('',(-1.E-1,1.15E-1,7.5E-2));
+#1638=CARTESIAN_POINT('',(-1.E-1,1.6E-1,7.5E-2));
+#1639=VERTEX_POINT('',#1637);
+#1640=VERTEX_POINT('',#1638);
+#1641=CARTESIAN_POINT('',(1.E-1,1.15E-1,1.12E-1));
+#1642=CARTESIAN_POINT('',(1.E-1,1.6E-1,1.12E-1));
+#1643=VERTEX_POINT('',#1641);
+#1644=VERTEX_POINT('',#1642);
+#1645=CARTESIAN_POINT('',(4.7E-2,1.15E-1,1.65E-1));
+#1646=CARTESIAN_POINT('',(4.7E-2,1.6E-1,1.65E-1));
+#1647=VERTEX_POINT('',#1645);
+#1648=VERTEX_POINT('',#1646);
+#1649=CARTESIAN_POINT('',(-4.7E-2,1.15E-1,1.65E-1));
+#1650=CARTESIAN_POINT('',(-4.7E-2,1.6E-1,1.65E-1));
+#1651=VERTEX_POINT('',#1649);
+#1652=VERTEX_POINT('',#1650);
+#1653=CARTESIAN_POINT('',(-1.E-1,1.15E-1,1.12E-1));
+#1654=CARTESIAN_POINT('',(-1.E-1,1.6E-1,1.12E-1));
+#1655=VERTEX_POINT('',#1653);
+#1656=VERTEX_POINT('',#1654);
+#1657=CARTESIAN_POINT('',(-1.25E-1,1.35E-1,2.25E-1));
+#1658=CARTESIAN_POINT('',(-1.25E-1,-5.5E-2,2.25E-1));
+#1659=VERTEX_POINT('',#1657);
+#1660=VERTEX_POINT('',#1658);
+#1661=CARTESIAN_POINT('',(-1.05E-1,1.15E-1,2.05E-1));
+#1662=CARTESIAN_POINT('',(-1.05E-1,-5.5E-2,2.05E-1));
+#1663=VERTEX_POINT('',#1661);
+#1664=VERTEX_POINT('',#1662);
+#1665=CARTESIAN_POINT('',(1.25E-1,-5.5E-2,2.25E-1));
+#1666=CARTESIAN_POINT('',(1.25E-1,1.35E-1,2.25E-1));
+#1667=VERTEX_POINT('',#1665);
+#1668=VERTEX_POINT('',#1666);
+#1669=CARTESIAN_POINT('',(1.05E-1,-5.5E-2,2.05E-1));
+#1670=CARTESIAN_POINT('',(1.05E-1,1.15E-1,2.05E-1));
+#1671=VERTEX_POINT('',#1669);
+#1672=VERTEX_POINT('',#1670);
+#1673=CARTESIAN_POINT('',(6.E-2,8.5E-2,7.5E-2));
+#1674=CARTESIAN_POINT('',(1.05E-1,8.5E-2,7.5E-2));
+#1675=VERTEX_POINT('',#1673);
+#1676=VERTEX_POINT('',#1674);
+#1677=CARTESIAN_POINT('',(6.E-2,6.5E-2,5.5E-2));
+#1678=CARTESIAN_POINT('',(1.05E-1,6.5E-2,5.5E-2));
+#1679=VERTEX_POINT('',#1677);
+#1680=VERTEX_POINT('',#1678);
+#1681=CARTESIAN_POINT('',(-6.E-2,8.5E-2,7.5E-2));
+#1682=CARTESIAN_POINT('',(-1.05E-1,8.5E-2,7.5E-2));
+#1683=VERTEX_POINT('',#1681);
+#1684=VERTEX_POINT('',#1682);
+#1685=CARTESIAN_POINT('',(-6.E-2,6.5E-2,5.5E-2));
+#1686=CARTESIAN_POINT('',(-1.05E-1,6.5E-2,5.5E-2));
+#1687=VERTEX_POINT('',#1685);
+#1688=VERTEX_POINT('',#1686);
+#1689=CARTESIAN_POINT('',(-1.65E-1,-5.E-2,-2.25E-1));
+#1690=VERTEX_POINT('',#1689);
+#1691=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,-2.25E-1));
+#1692=VERTEX_POINT('',#1691);
+#1693=CARTESIAN_POINT('',(1.65E-1,-1.E-1,-2.25E-1));
+#1694=CARTESIAN_POINT('',(1.65E-1,-5.E-2,-2.25E-1));
+#1695=VERTEX_POINT('',#1693);
+#1696=VERTEX_POINT('',#1694);
+#1697=CARTESIAN_POINT('',(-1.65E-1,-5.E-2,2.25E-1));
+#1698=VERTEX_POINT('',#1697);
+#1699=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,2.25E-1));
+#1700=VERTEX_POINT('',#1699);
+#1701=CARTESIAN_POINT('',(1.65E-1,-1.E-1,2.25E-1));
+#1702=CARTESIAN_POINT('',(1.65E-1,-5.E-2,2.25E-1));
+#1703=VERTEX_POINT('',#1701);
+#1704=VERTEX_POINT('',#1702);
+#1705=CARTESIAN_POINT('',(-1.E-1,6.E-2,-2.69E-1));
+#1706=CARTESIAN_POINT('',(1.E-1,6.E-2,-2.69E-1));
+#1707=VERTEX_POINT('',#1705);
+#1708=VERTEX_POINT('',#1706);
+#1709=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.25E-1));
+#1710=CARTESIAN_POINT('',(-1.E-1,6.E-2,-2.25E-1));
+#1711=VERTEX_POINT('',#1709);
+#1712=VERTEX_POINT('',#1710);
+#1713=CARTESIAN_POINT('',(1.E-1,-6.E-2,-2.25E-1));
+#1714=CARTESIAN_POINT('',(1.E-1,6.E-2,-2.25E-1));
+#1715=VERTEX_POINT('',#1713);
+#1716=VERTEX_POINT('',#1714);
+#1717=CARTESIAN_POINT('',(3.75E-2,-3.05E-2,-2.69E-1));
+#1718=CARTESIAN_POINT('',(6.25E-2,-3.05E-2,-2.69E-1));
+#1719=VERTEX_POINT('',#1717);
+#1720=VERTEX_POINT('',#1718);
+#1721=CARTESIAN_POINT('',(3.75E-2,6.363424398923E-3,-2.69E-1));
+#1722=CARTESIAN_POINT('',(6.25E-2,6.363424398923E-3,-2.69E-1));
+#1723=VERTEX_POINT('',#1721);
+#1724=VERTEX_POINT('',#1722);
+#1725=CARTESIAN_POINT('',(-1.05E-1,1.15E-1,7.5E-2));
+#1726=VERTEX_POINT('',#1725);
+#1727=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,-2.25E-1));
+#1728=VERTEX_POINT('',#1727);
+#1729=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,-2.25E-1));
+#1730=CARTESIAN_POINT('',(6.5E-2,-1.E-1,-2.25E-1));
+#1731=VERTEX_POINT('',#1729);
+#1732=VERTEX_POINT('',#1730);
+#1733=CARTESIAN_POINT('',(9.5E-2,-1.E-1,-2.25E-1));
+#1734=VERTEX_POINT('',#1733);
+#1735=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,2.25E-1));
+#1736=VERTEX_POINT('',#1735);
+#1737=CARTESIAN_POINT('',(9.5E-2,-1.E-1,2.25E-1));
+#1738=VERTEX_POINT('',#1737);
+#1739=CARTESIAN_POINT('',(8.E-2,-1.15E-1,-2.25E-1));
+#1740=VERTEX_POINT('',#1739);
+#1741=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,-2.25E-1));
+#1742=VERTEX_POINT('',#1741);
+#1743=CARTESIAN_POINT('',(6.5E-2,-1.E-1,2.25E-1));
+#1744=CARTESIAN_POINT('',(8.E-2,-1.15E-1,2.25E-1));
+#1745=VERTEX_POINT('',#1743);
+#1746=VERTEX_POINT('',#1744);
+#1747=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,2.25E-1));
+#1748=VERTEX_POINT('',#1747);
+#1749=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,2.25E-1));
+#1750=VERTEX_POINT('',#1749);
+#1751=CARTESIAN_POINT('',(2.5E-2,-6.E-2,-1.25E-1));
+#1752=CARTESIAN_POINT('',(-2.5E-2,-6.E-2,-1.25E-1));
+#1753=VERTEX_POINT('',#1751);
+#1754=VERTEX_POINT('',#1752);
+#1755=CARTESIAN_POINT('',(-2.5E-2,-1.E-1,2.25E-1));
+#1756=VERTEX_POINT('',#1755);
+#1757=CARTESIAN_POINT('',(-2.5E-2,-1.E-1,-1.25E-1));
+#1758=VERTEX_POINT('',#1757);
+#1759=CARTESIAN_POINT('',(-2.5E-2,-6.E-2,2.25E-1));
+#1760=VERTEX_POINT('',#1759);
+#1761=CARTESIAN_POINT('',(2.5E-2,-1.E-1,-1.25E-1));
+#1762=VERTEX_POINT('',#1761);
+#1763=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-5.5E-3));
+#1764=CARTESIAN_POINT('',(1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#1765=VERTEX_POINT('',#1763);
+#1766=VERTEX_POINT('',#1764);
+#1767=CARTESIAN_POINT('',(1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#1768=VERTEX_POINT('',#1767);
+#1769=CARTESIAN_POINT('',(1.588482755730E-1,-1.E-1,8.67E-2));
+#1770=VERTEX_POINT('',#1769);
+#1771=CARTESIAN_POINT('',(1.564234044424E-1,-1.E-1,8.53E-2));
+#1772=VERTEX_POINT('',#1771);
+#1773=CARTESIAN_POINT('',(9.5E-2,-1.E-1,8.53E-2));
+#1774=VERTEX_POINT('',#1773);
+#1775=CARTESIAN_POINT('',(6.5E-2,-1.E-1,8.53E-2));
+#1776=CARTESIAN_POINT('',(5.357659555759E-2,-1.E-1,8.53E-2));
+#1777=VERTEX_POINT('',#1775);
+#1778=VERTEX_POINT('',#1776);
+#1779=CARTESIAN_POINT('',(5.115172442699E-2,-1.E-1,8.67E-2));
+#1780=VERTEX_POINT('',#1779);
+#1781=CARTESIAN_POINT('',(5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#1782=VERTEX_POINT('',#1781);
+#1783=CARTESIAN_POINT('',(5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#1784=VERTEX_POINT('',#1783);
+#1785=CARTESIAN_POINT('',(6.5E-2,-1.E-1,6.204998149519E-2));
+#1786=VERTEX_POINT('',#1785);
+#1787=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-8.3E-3));
+#1788=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-8.3E-3));
+#1789=VERTEX_POINT('',#1787);
+#1790=VERTEX_POINT('',#1788);
+#1791=CARTESIAN_POINT('',(8.E-2,-1.15E-1,8.53E-2));
+#1792=VERTEX_POINT('',#1791);
+#1793=CARTESIAN_POINT('',(8.E-2,-1.15E-1,3.606921938166E-2));
+#1794=VERTEX_POINT('',#1793);
+#1795=CARTESIAN_POINT('',(9.5E-2,-1.E-1,1.008845726812E-2));
+#1796=VERTEX_POINT('',#1795);
+#1797=CARTESIAN_POINT('',(1.070760339526E-1,-1.945003269382E-1,4.63E-2));
+#1798=CARTESIAN_POINT('',(9.434827557301E-2,-1.990729432769E-1,4.63E-2));
+#1799=VERTEX_POINT('',#1797);
+#1800=VERTEX_POINT('',#1798);
+#1801=CARTESIAN_POINT('',(8.162051719341E-2,-1.945003269382E-1,4.63E-2));
+#1802=VERTEX_POINT('',#1801);
+#1803=VERTEX_POINT('',#233);
+#1804=VERTEX_POINT('',#244);
+#1805=VERTEX_POINT('',#246);
+#1806=VERTEX_POINT('',#434);
+#1807=VERTEX_POINT('',#424);
+#1808=VERTEX_POINT('',#414);
+#1809=VERTEX_POINT('',#335);
+#1810=VERTEX_POINT('',#404);
+#1811=VERTEX_POINT('',#394);
+#1812=VERTEX_POINT('',#384);
+#1813=VERTEX_POINT('',#289);
+#1814=VERTEX_POINT('',#302);
+#1815=VERTEX_POINT('',#279);
+#1816=VERTEX_POINT('',#269);
+#1817=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-5.5E-3));
+#1818=VERTEX_POINT('',#1817);
+#1819=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-5.5E-3));
+#1820=CARTESIAN_POINT('',(-1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#1821=VERTEX_POINT('',#1819);
+#1822=VERTEX_POINT('',#1820);
+#1823=CARTESIAN_POINT('',(-1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#1824=VERTEX_POINT('',#1823);
+#1825=CARTESIAN_POINT('',(-1.588482755730E-1,-1.E-1,8.67E-2));
+#1826=VERTEX_POINT('',#1825);
+#1827=CARTESIAN_POINT('',(-1.564234044424E-1,-1.E-1,8.53E-2));
+#1828=VERTEX_POINT('',#1827);
+#1829=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,8.53E-2));
+#1830=VERTEX_POINT('',#1829);
+#1831=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,8.53E-2));
+#1832=CARTESIAN_POINT('',(-5.357659555759E-2,-1.E-1,8.53E-2));
+#1833=VERTEX_POINT('',#1831);
+#1834=VERTEX_POINT('',#1832);
+#1835=CARTESIAN_POINT('',(-5.115172442699E-2,-1.E-1,8.67E-2));
+#1836=VERTEX_POINT('',#1835);
+#1837=CARTESIAN_POINT('',(-5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#1838=VERTEX_POINT('',#1837);
+#1839=CARTESIAN_POINT('',(-5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#1840=VERTEX_POINT('',#1839);
+#1841=CARTESIAN_POINT('',(-6.5E-2,-1.E-1,6.204998149519E-2));
+#1842=VERTEX_POINT('',#1841);
+#1843=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-8.3E-3));
+#1844=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-8.3E-3));
+#1845=VERTEX_POINT('',#1843);
+#1846=VERTEX_POINT('',#1844);
+#1847=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,8.53E-2));
+#1848=VERTEX_POINT('',#1847);
+#1849=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,3.606921938166E-2));
+#1850=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,1.008845726812E-2));
+#1851=VERTEX_POINT('',#1849);
+#1852=VERTEX_POINT('',#1850);
+#1853=CARTESIAN_POINT('',(-1.070760339526E-1,-1.945003269382E-1,4.63E-2));
+#1854=CARTESIAN_POINT('',(-9.434827557301E-2,-1.990729432769E-1,4.63E-2));
+#1855=VERTEX_POINT('',#1853);
+#1856=VERTEX_POINT('',#1854);
+#1857=CARTESIAN_POINT('',(-8.162051719341E-2,-1.945003269382E-1,4.63E-2));
+#1858=VERTEX_POINT('',#1857);
+#1859=VERTEX_POINT('',#1349);
+#1860=VERTEX_POINT('',#1360);
+#1861=VERTEX_POINT('',#1366);
+#1862=VERTEX_POINT('',#1493);
+#1863=VERTEX_POINT('',#1503);
+#1864=VERTEX_POINT('',#1513);
+#1865=VERTEX_POINT('',#1320);
+#1866=VERTEX_POINT('',#1445);
+#1867=VERTEX_POINT('',#1435);
+#1868=VERTEX_POINT('',#1425);
+#1869=VERTEX_POINT('',#1395);
+#1870=VERTEX_POINT('',#1408);
+#1871=VERTEX_POINT('',#1463);
+#1872=VERTEX_POINT('',#1473);
+#1873=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-5.5E-3));
+#1874=VERTEX_POINT('',#1873);
+#1875=CARTESIAN_POINT('',(1.E-1,3.E-2,-3.1E-1));
+#1876=CARTESIAN_POINT('',(1.E-1,-6.E-2,-3.1E-1));
+#1877=VERTEX_POINT('',#1875);
+#1878=VERTEX_POINT('',#1876);
+#1879=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-3.1E-1));
+#1880=VERTEX_POINT('',#1879);
+#1881=CARTESIAN_POINT('',(6.25E-2,3.E-2,-3.1E-1));
+#1882=VERTEX_POINT('',#1881);
+#1883=CARTESIAN_POINT('',(-1.E-1,3.E-2,-3.1E-1));
+#1884=CARTESIAN_POINT('',(-6.25E-2,3.E-2,-3.1E-1));
+#1885=VERTEX_POINT('',#1883);
+#1886=VERTEX_POINT('',#1884);
+#1887=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-3.1E-1));
+#1888=VERTEX_POINT('',#1887);
+#1889=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-3.1E-1));
+#1890=VERTEX_POINT('',#1889);
+#1891=CARTESIAN_POINT('',(3.75E-2,3.E-2,-3.1E-1));
+#1892=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-3.1E-1));
+#1893=VERTEX_POINT('',#1891);
+#1894=VERTEX_POINT('',#1892);
+#1895=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-3.1E-1));
+#1896=VERTEX_POINT('',#1895);
+#1897=CARTESIAN_POINT('',(-3.75E-2,3.E-2,-3.1E-1));
+#1898=VERTEX_POINT('',#1897);
+#1899=CARTESIAN_POINT('',(1.E-1,3.E-2,-2.69E-1));
+#1900=CARTESIAN_POINT('',(6.25E-2,3.E-2,-2.69E-1));
+#1901=VERTEX_POINT('',#1899);
+#1902=VERTEX_POINT('',#1900);
+#1903=CARTESIAN_POINT('',(-1.E-1,3.E-2,-2.69E-1));
+#1904=CARTESIAN_POINT('',(-6.25E-2,3.E-2,-2.69E-1));
+#1905=VERTEX_POINT('',#1903);
+#1906=VERTEX_POINT('',#1904);
+#1907=CARTESIAN_POINT('',(-3.75E-2,3.E-2,-2.69E-1));
+#1908=CARTESIAN_POINT('',(3.75E-2,3.E-2,-2.69E-1));
+#1909=VERTEX_POINT('',#1907);
+#1910=VERTEX_POINT('',#1908);
+#1911=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.69E-1));
+#1912=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.69E-1));
+#1913=VERTEX_POINT('',#1911);
+#1914=VERTEX_POINT('',#1912);
+#1915=CARTESIAN_POINT('',(3.75E-2,-6.E-2,-2.69E-1));
+#1916=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.69E-1));
+#1917=VERTEX_POINT('',#1915);
+#1918=VERTEX_POINT('',#1916);
+#1919=CARTESIAN_POINT('',(2.5E-2,-1.E-1,2.25E-1));
+#1920=CARTESIAN_POINT('',(2.5E-2,-6.E-2,2.25E-1));
+#1921=VERTEX_POINT('',#1919);
+#1922=VERTEX_POINT('',#1920);
+#1923=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,1.15E-1));
+#1924=CARTESIAN_POINT('',(-6.25E-2,-1.25E-2,1.15E-1));
+#1925=VERTEX_POINT('',#1923);
+#1926=VERTEX_POINT('',#1924);
+#1927=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.7E-1));
+#1928=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.95E-1));
+#1929=VERTEX_POINT('',#1927);
+#1930=VERTEX_POINT('',#1928);
+#1931=CARTESIAN_POINT('',(-3.75E-2,1.25E-2,1.15E-1));
+#1932=CARTESIAN_POINT('',(-3.75E-2,-1.25E-2,1.15E-1));
+#1933=VERTEX_POINT('',#1931);
+#1934=VERTEX_POINT('',#1932);
+#1935=CARTESIAN_POINT('',(-3.75E-2,-2.45E-1,-2.7E-1));
+#1936=CARTESIAN_POINT('',(-3.75E-2,-2.45E-1,-2.95E-1));
+#1937=VERTEX_POINT('',#1935);
+#1938=VERTEX_POINT('',#1936);
+#1939=CARTESIAN_POINT('',(-6.25E-2,-1.25E-2,-1.25E-1));
+#1940=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,-1.25E-1));
+#1941=VERTEX_POINT('',#1939);
+#1942=VERTEX_POINT('',#1940);
+#1943=CARTESIAN_POINT('',(-3.75E-2,-1.25E-2,-1.25E-1));
+#1944=CARTESIAN_POINT('',(-3.75E-2,1.25E-2,-1.25E-1));
+#1945=VERTEX_POINT('',#1943);
+#1946=VERTEX_POINT('',#1944);
+#1947=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.7E-1));
+#1948=CARTESIAN_POINT('',(-6.25E-2,-6.E-2,-2.95E-1));
+#1949=VERTEX_POINT('',#1947);
+#1950=VERTEX_POINT('',#1948);
+#1951=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.7E-1));
+#1952=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.95E-1));
+#1953=VERTEX_POINT('',#1951);
+#1954=VERTEX_POINT('',#1952);
+#1955=CARTESIAN_POINT('',(-6.25E-2,-3.05E-2,-2.69E-1));
+#1956=CARTESIAN_POINT('',(-3.75E-2,-3.05E-2,-2.69E-1));
+#1957=VERTEX_POINT('',#1955);
+#1958=VERTEX_POINT('',#1956);
+#1959=CARTESIAN_POINT('',(-6.25E-2,6.363424398923E-3,-2.69E-1));
+#1960=CARTESIAN_POINT('',(-3.75E-2,6.363424398923E-3,-2.69E-1));
+#1961=VERTEX_POINT('',#1959);
+#1962=VERTEX_POINT('',#1960);
+#1963=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.7E-1));
+#1964=VERTEX_POINT('',#1963);
+#1965=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.95E-1));
+#1966=VERTEX_POINT('',#1965);
+#1967=CARTESIAN_POINT('',(-3.75E-2,-3.75E-2,-2.7E-1));
+#1968=VERTEX_POINT('',#1967);
+#1969=CARTESIAN_POINT('',(-3.75E-2,-3.75E-2,-2.95E-1));
+#1970=VERTEX_POINT('',#1969);
+#1971=CARTESIAN_POINT('',(0.E0,0.E0,-2.25E-1));
+#1972=DIRECTION('',(0.E0,0.E0,1.E0));
+#1973=DIRECTION('',(1.E0,0.E0,0.E0));
+#1974=AXIS2_PLACEMENT_3D('',#1971,#1972,#1973);
+#1975=PLANE('',#1974);
+#1977=ORIENTED_EDGE('',*,*,#1976,.T.);
+#1979=ORIENTED_EDGE('',*,*,#1978,.T.);
+#1981=ORIENTED_EDGE('',*,*,#1980,.T.);
+#1983=ORIENTED_EDGE('',*,*,#1982,.T.);
+#1985=ORIENTED_EDGE('',*,*,#1984,.T.);
+#1987=ORIENTED_EDGE('',*,*,#1986,.T.);
+#1989=ORIENTED_EDGE('',*,*,#1988,.T.);
+#1991=ORIENTED_EDGE('',*,*,#1990,.T.);
+#1993=ORIENTED_EDGE('',*,*,#1992,.T.);
+#1995=ORIENTED_EDGE('',*,*,#1994,.T.);
+#1997=ORIENTED_EDGE('',*,*,#1996,.T.);
+#1999=ORIENTED_EDGE('',*,*,#1998,.T.);
+#2001=ORIENTED_EDGE('',*,*,#2000,.T.);
+#2003=ORIENTED_EDGE('',*,*,#2002,.T.);
+#2004=EDGE_LOOP('',(#1977,#1979,#1981,#1983,#1985,#1987,#1989,#1991,#1993,#1995,
+#1997,#1999,#2001,#2003));
+#2005=FACE_OUTER_BOUND('',#2004,.F.);
+#2007=ORIENTED_EDGE('',*,*,#2006,.T.);
+#2009=ORIENTED_EDGE('',*,*,#2008,.T.);
+#2011=ORIENTED_EDGE('',*,*,#2010,.F.);
+#2013=ORIENTED_EDGE('',*,*,#2012,.F.);
+#2014=EDGE_LOOP('',(#2007,#2009,#2011,#2013));
+#2015=FACE_BOUND('',#2014,.F.);
+#2016=ADVANCED_FACE('',(#2005,#2015),#1975,.F.);
+#2017=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,-2.25E-1));
+#2018=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2019=DIRECTION('',(1.E0,0.E0,0.E0));
+#2020=AXIS2_PLACEMENT_3D('',#2017,#2018,#2019);
+#2021=PLANE('',#2020);
+#2023=ORIENTED_EDGE('',*,*,#2022,.T.);
+#2025=ORIENTED_EDGE('',*,*,#2024,.T.);
+#2027=ORIENTED_EDGE('',*,*,#2026,.T.);
+#2029=ORIENTED_EDGE('',*,*,#2028,.T.);
+#2031=ORIENTED_EDGE('',*,*,#2030,.T.);
+#2033=ORIENTED_EDGE('',*,*,#2032,.T.);
+#2035=ORIENTED_EDGE('',*,*,#2034,.T.);
+#2037=ORIENTED_EDGE('',*,*,#2036,.F.);
+#2038=ORIENTED_EDGE('',*,*,#1988,.F.);
+#2040=ORIENTED_EDGE('',*,*,#2039,.T.);
+#2042=ORIENTED_EDGE('',*,*,#2041,.T.);
+#2044=ORIENTED_EDGE('',*,*,#2043,.T.);
+#2046=ORIENTED_EDGE('',*,*,#2045,.T.);
+#2048=ORIENTED_EDGE('',*,*,#2047,.T.);
+#2049=EDGE_LOOP('',(#2023,#2025,#2027,#2029,#2031,#2033,#2035,#2037,#2038,#2040,
+#2042,#2044,#2046,#2048));
+#2050=FACE_OUTER_BOUND('',#2049,.F.);
+#2051=ADVANCED_FACE('',(#2050),#2021,.T.);
+#2052=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,-2.25E-1));
+#2053=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2054=DIRECTION('',(1.E0,0.E0,0.E0));
+#2055=AXIS2_PLACEMENT_3D('',#2052,#2053,#2054);
+#2056=PLANE('',#2055);
+#2058=ORIENTED_EDGE('',*,*,#2057,.F.);
+#2060=ORIENTED_EDGE('',*,*,#2059,.F.);
+#2062=ORIENTED_EDGE('',*,*,#2061,.F.);
+#2064=ORIENTED_EDGE('',*,*,#2063,.F.);
+#2066=ORIENTED_EDGE('',*,*,#2065,.F.);
+#2068=ORIENTED_EDGE('',*,*,#2067,.F.);
+#2069=ORIENTED_EDGE('',*,*,#1976,.F.);
+#2071=ORIENTED_EDGE('',*,*,#2070,.T.);
+#2073=ORIENTED_EDGE('',*,*,#2072,.T.);
+#2075=ORIENTED_EDGE('',*,*,#2074,.F.);
+#2077=ORIENTED_EDGE('',*,*,#2076,.F.);
+#2079=ORIENTED_EDGE('',*,*,#2078,.F.);
+#2081=ORIENTED_EDGE('',*,*,#2080,.F.);
+#2083=ORIENTED_EDGE('',*,*,#2082,.F.);
+#2084=EDGE_LOOP('',(#2058,#2060,#2062,#2064,#2066,#2068,#2069,#2071,#2073,#2075,
+#2077,#2079,#2081,#2083));
+#2085=FACE_OUTER_BOUND('',#2084,.F.);
+#2086=ADVANCED_FACE('',(#2085),#2056,.T.);
+#2087=CARTESIAN_POINT('',(-1.65E-1,-1.E-1,-2.25E-1));
+#2088=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2089=DIRECTION('',(1.E0,0.E0,0.E0));
+#2090=AXIS2_PLACEMENT_3D('',#2087,#2088,#2089);
+#2091=PLANE('',#2090);
+#2093=ORIENTED_EDGE('',*,*,#2092,.T.);
+#2095=ORIENTED_EDGE('',*,*,#2094,.T.);
+#2097=ORIENTED_EDGE('',*,*,#2096,.T.);
+#2099=ORIENTED_EDGE('',*,*,#2098,.F.);
+#2101=ORIENTED_EDGE('',*,*,#2100,.T.);
+#2103=ORIENTED_EDGE('',*,*,#2102,.F.);
+#2105=ORIENTED_EDGE('',*,*,#2104,.T.);
+#2107=ORIENTED_EDGE('',*,*,#2106,.T.);
+#2109=ORIENTED_EDGE('',*,*,#2108,.T.);
+#2111=ORIENTED_EDGE('',*,*,#2110,.T.);
+#2113=ORIENTED_EDGE('',*,*,#2112,.T.);
+#2115=ORIENTED_EDGE('',*,*,#2114,.F.);
+#2116=ORIENTED_EDGE('',*,*,#1982,.F.);
+#2118=ORIENTED_EDGE('',*,*,#2117,.T.);
+#2120=ORIENTED_EDGE('',*,*,#2119,.F.);
+#2122=ORIENTED_EDGE('',*,*,#2121,.F.);
+#2124=ORIENTED_EDGE('',*,*,#2123,.F.);
+#2126=ORIENTED_EDGE('',*,*,#2125,.F.);
+#2128=ORIENTED_EDGE('',*,*,#2127,.F.);
+#2130=ORIENTED_EDGE('',*,*,#2129,.T.);
+#2131=EDGE_LOOP('',(#2093,#2095,#2097,#2099,#2101,#2103,#2105,#2107,#2109,#2111,
+#2113,#2115,#2116,#2118,#2120,#2122,#2124,#2126,#2128,#2130));
+#2132=FACE_OUTER_BOUND('',#2131,.F.);
+#2133=ADVANCED_FACE('',(#2132),#2091,.T.);
+#2134=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-5.5E-3));
+#2135=DIRECTION('',(8.660254037845E-1,0.E0,-5.E-1));
+#2136=DIRECTION('',(5.E-1,0.E0,8.660254037845E-1));
+#2137=AXIS2_PLACEMENT_3D('',#2134,#2135,#2136);
+#2138=PLANE('',#2137);
+#2140=ORIENTED_EDGE('',*,*,#2139,.F.);
+#2142=ORIENTED_EDGE('',*,*,#2141,.F.);
+#2144=ORIENTED_EDGE('',*,*,#2143,.F.);
+#2145=ORIENTED_EDGE('',*,*,#2022,.F.);
+#2147=ORIENTED_EDGE('',*,*,#2146,.T.);
+#2148=EDGE_LOOP('',(#2140,#2142,#2144,#2145,#2147));
+#2149=FACE_OUTER_BOUND('',#2148,.F.);
+#2150=ADVANCED_FACE('',(#2149),#2138,.T.);
+#2151=CARTESIAN_POINT('',(9.434827557301E-2,-1.767206638076E-1,4.63E-2));
+#2152=DIRECTION('',(0.E0,1.E0,0.E0));
+#2153=DIRECTION('',(1.E0,0.E0,0.E0));
+#2154=AXIS2_PLACEMENT_3D('',#2151,#2152,#2153);
+#2155=CONICAL_SURFACE('',#2154,3.427858267256E-2,5.0477E1);
+#2156=ORIENTED_EDGE('',*,*,#2139,.T.);
+#2158=ORIENTED_EDGE('',*,*,#2157,.T.);
+#2160=ORIENTED_EDGE('',*,*,#2159,.T.);
+#2162=ORIENTED_EDGE('',*,*,#2161,.T.);
+#2164=ORIENTED_EDGE('',*,*,#2163,.T.);
+#2166=ORIENTED_EDGE('',*,*,#2165,.T.);
+#2168=ORIENTED_EDGE('',*,*,#2167,.F.);
+#2170=ORIENTED_EDGE('',*,*,#2169,.F.);
+#2171=EDGE_LOOP('',(#2156,#2158,#2160,#2162,#2164,#2166,#2168,#2170));
+#2172=FACE_OUTER_BOUND('',#2171,.F.);
+#2173=ADVANCED_FACE('',(#2172),#2155,.T.);
+#2174=CARTESIAN_POINT('',(1.06E-1,-1.E-1,-8.3E-3));
+#2175=DIRECTION('',(1.E0,0.E0,0.E0));
+#2176=DIRECTION('',(0.E0,0.E0,1.E0));
+#2177=AXIS2_PLACEMENT_3D('',#2174,#2175,#2176);
+#2178=PLANE('',#2177);
+#2179=ORIENTED_EDGE('',*,*,#2157,.F.);
+#2180=ORIENTED_EDGE('',*,*,#2146,.F.);
+#2181=ORIENTED_EDGE('',*,*,#2047,.F.);
+#2183=ORIENTED_EDGE('',*,*,#2182,.T.);
+#2184=EDGE_LOOP('',(#2179,#2180,#2181,#2183));
+#2185=FACE_OUTER_BOUND('',#2184,.F.);
+#2186=ADVANCED_FACE('',(#2185),#2178,.T.);
+#2187=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-8.3E-3));
+#2188=DIRECTION('',(0.E0,0.E0,-1.E0));
+#2189=DIRECTION('',(1.E0,0.E0,0.E0));
+#2190=AXIS2_PLACEMENT_3D('',#2187,#2188,#2189);
+#2191=PLANE('',#2190);
+#2192=ORIENTED_EDGE('',*,*,#2159,.F.);
+#2193=ORIENTED_EDGE('',*,*,#2182,.F.);
+#2194=ORIENTED_EDGE('',*,*,#2045,.F.);
+#2196=ORIENTED_EDGE('',*,*,#2195,.T.);
+#2197=EDGE_LOOP('',(#2192,#2193,#2194,#2196));
+#2198=FACE_OUTER_BOUND('',#2197,.F.);
+#2199=ADVANCED_FACE('',(#2198),#2191,.T.);
+#2200=CARTESIAN_POINT('',(1.04E-1,-1.E-1,-5.5E-3));
+#2201=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2202=DIRECTION('',(0.E0,0.E0,-1.E0));
+#2203=AXIS2_PLACEMENT_3D('',#2200,#2201,#2202);
+#2204=PLANE('',#2203);
+#2205=ORIENTED_EDGE('',*,*,#2161,.F.);
+#2206=ORIENTED_EDGE('',*,*,#2195,.F.);
+#2207=ORIENTED_EDGE('',*,*,#2043,.F.);
+#2209=ORIENTED_EDGE('',*,*,#2208,.T.);
+#2210=EDGE_LOOP('',(#2205,#2206,#2207,#2209));
+#2211=FACE_OUTER_BOUND('',#2210,.F.);
+#2212=ADVANCED_FACE('',(#2211),#2204,.T.);
+#2213=CARTESIAN_POINT('',(5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#2214=DIRECTION('',(-8.660254037845E-1,0.E0,-5.E-1));
+#2215=DIRECTION('',(5.E-1,0.E0,-8.660254037845E-1));
+#2216=AXIS2_PLACEMENT_3D('',#2213,#2214,#2215);
+#2217=PLANE('',#2216);
+#2218=ORIENTED_EDGE('',*,*,#2163,.F.);
+#2219=ORIENTED_EDGE('',*,*,#2208,.F.);
+#2220=ORIENTED_EDGE('',*,*,#2041,.F.);
+#2222=ORIENTED_EDGE('',*,*,#2221,.F.);
+#2224=ORIENTED_EDGE('',*,*,#2223,.F.);
+#2225=ORIENTED_EDGE('',*,*,#2112,.F.);
+#2227=ORIENTED_EDGE('',*,*,#2226,.T.);
+#2229=ORIENTED_EDGE('',*,*,#2228,.F.);
+#2230=EDGE_LOOP('',(#2218,#2219,#2220,#2222,#2224,#2225,#2227,#2229));
+#2231=FACE_OUTER_BOUND('',#2230,.F.);
+#2232=ADVANCED_FACE('',(#2231),#2217,.T.);
+#2233=CARTESIAN_POINT('',(8.E-2,-1.15E-1,-2.25E-1));
+#2234=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#2235=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#2236=AXIS2_PLACEMENT_3D('',#2233,#2234,#2235);
+#2237=PLANE('',#2236);
+#2239=ORIENTED_EDGE('',*,*,#2238,.F.);
+#2241=ORIENTED_EDGE('',*,*,#2240,.T.);
+#2243=ORIENTED_EDGE('',*,*,#2242,.T.);
+#2244=ORIENTED_EDGE('',*,*,#2032,.F.);
+#2245=EDGE_LOOP('',(#2239,#2241,#2243,#2244));
+#2246=FACE_OUTER_BOUND('',#2245,.F.);
+#2247=ADVANCED_FACE('',(#2246),#2237,.T.);
+#2248=CARTESIAN_POINT('',(8.E-2,-1.15E-1,-2.25E-1));
+#2249=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#2250=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#2251=AXIS2_PLACEMENT_3D('',#2248,#2249,#2250);
+#2252=PLANE('',#2251);
+#2253=ORIENTED_EDGE('',*,*,#2221,.T.);
+#2254=ORIENTED_EDGE('',*,*,#2039,.F.);
+#2255=ORIENTED_EDGE('',*,*,#1986,.F.);
+#2257=ORIENTED_EDGE('',*,*,#2256,.T.);
+#2258=EDGE_LOOP('',(#2253,#2254,#2255,#2257));
+#2259=FACE_OUTER_BOUND('',#2258,.F.);
+#2260=ADVANCED_FACE('',(#2259),#2252,.T.);
+#2261=CARTESIAN_POINT('',(1.564234044424E-1,-1.E-1,8.53E-2));
+#2262=DIRECTION('',(0.E0,0.E0,1.E0));
+#2263=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2264=AXIS2_PLACEMENT_3D('',#2261,#2262,#2263);
+#2265=PLANE('',#2264);
+#2267=ORIENTED_EDGE('',*,*,#2266,.F.);
+#2269=ORIENTED_EDGE('',*,*,#2268,.F.);
+#2270=ORIENTED_EDGE('',*,*,#2104,.F.);
+#2272=ORIENTED_EDGE('',*,*,#2271,.T.);
+#2273=ORIENTED_EDGE('',*,*,#2238,.T.);
+#2274=ORIENTED_EDGE('',*,*,#2030,.F.);
+#2276=ORIENTED_EDGE('',*,*,#2275,.T.);
+#2277=EDGE_LOOP('',(#2267,#2269,#2270,#2272,#2273,#2274,#2276));
+#2278=FACE_OUTER_BOUND('',#2277,.F.);
+#2279=ADVANCED_FACE('',(#2278),#2265,.T.);
+#2280=CARTESIAN_POINT('',(9.434827557301E-2,-1.683556823861E-1,4.63E-2));
+#2281=DIRECTION('',(0.E0,1.E0,0.E0));
+#2282=DIRECTION('',(1.E0,0.E0,0.E0));
+#2283=AXIS2_PLACEMENT_3D('',#2280,#2281,#2282);
+#2284=CONICAL_SURFACE('',#2283,4.441782116695E-2,5.0477E1);
+#2285=ORIENTED_EDGE('',*,*,#2141,.T.);
+#2286=ORIENTED_EDGE('',*,*,#2169,.T.);
+#2288=ORIENTED_EDGE('',*,*,#2287,.T.);
+#2289=ORIENTED_EDGE('',*,*,#2165,.F.);
+#2290=ORIENTED_EDGE('',*,*,#2228,.T.);
+#2292=ORIENTED_EDGE('',*,*,#2291,.T.);
+#2294=ORIENTED_EDGE('',*,*,#2293,.T.);
+#2296=ORIENTED_EDGE('',*,*,#2295,.T.);
+#2297=ORIENTED_EDGE('',*,*,#2266,.T.);
+#2299=ORIENTED_EDGE('',*,*,#2298,.T.);
+#2301=ORIENTED_EDGE('',*,*,#2300,.T.);
+#2303=ORIENTED_EDGE('',*,*,#2302,.T.);
+#2304=EDGE_LOOP('',(#2285,#2286,#2288,#2289,#2290,#2292,#2294,#2296,#2297,#2299,
+#2301,#2303));
+#2305=FACE_OUTER_BOUND('',#2304,.F.);
+#2306=ADVANCED_FACE('',(#2305),#2284,.T.);
+#2307=CARTESIAN_POINT('',(9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#2308=DIRECTION('',(0.E0,1.E0,0.E0));
+#2309=DIRECTION('',(1.E0,0.E0,0.E0));
+#2310=AXIS2_PLACEMENT_3D('',#2307,#2308,#2309);
+#2311=SPHERICAL_SURFACE('',#2310,2.E-2);
+#2313=ORIENTED_EDGE('',*,*,#2312,.T.);
+#2315=ORIENTED_EDGE('',*,*,#2314,.F.);
+#2316=ORIENTED_EDGE('',*,*,#2287,.F.);
+#2317=EDGE_LOOP('',(#2313,#2315,#2316));
+#2318=FACE_OUTER_BOUND('',#2317,.F.);
+#2319=ADVANCED_FACE('',(#2318),#2311,.T.);
+#2320=CARTESIAN_POINT('',(9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#2321=DIRECTION('',(0.E0,1.E0,0.E0));
+#2322=DIRECTION('',(1.E0,0.E0,0.E0));
+#2323=AXIS2_PLACEMENT_3D('',#2320,#2321,#2322);
+#2324=SPHERICAL_SURFACE('',#2323,2.E-2);
+#2325=ORIENTED_EDGE('',*,*,#2312,.F.);
+#2326=ORIENTED_EDGE('',*,*,#2167,.T.);
+#2327=ORIENTED_EDGE('',*,*,#2314,.T.);
+#2328=EDGE_LOOP('',(#2325,#2326,#2327));
+#2329=FACE_OUTER_BOUND('',#2328,.F.);
+#2330=ADVANCED_FACE('',(#2329),#2324,.T.);
+#2331=CARTESIAN_POINT('',(5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#2332=DIRECTION('',(-5.E-1,0.E0,-8.660254037845E-1));
+#2333=DIRECTION('',(8.660254037845E-1,0.E0,-5.E-1));
+#2334=AXIS2_PLACEMENT_3D('',#2331,#2332,#2333);
+#2335=PLANE('',#2334);
+#2336=ORIENTED_EDGE('',*,*,#2291,.F.);
+#2337=ORIENTED_EDGE('',*,*,#2226,.F.);
+#2338=ORIENTED_EDGE('',*,*,#2110,.F.);
+#2340=ORIENTED_EDGE('',*,*,#2339,.T.);
+#2341=EDGE_LOOP('',(#2336,#2337,#2338,#2340));
+#2342=FACE_OUTER_BOUND('',#2341,.F.);
+#2343=ADVANCED_FACE('',(#2342),#2335,.T.);
+#2344=CARTESIAN_POINT('',(5.115172442699E-2,-1.E-1,8.67E-2));
+#2345=DIRECTION('',(-8.660254037845E-1,0.E0,5.E-1));
+#2346=DIRECTION('',(-5.E-1,0.E0,-8.660254037845E-1));
+#2347=AXIS2_PLACEMENT_3D('',#2344,#2345,#2346);
+#2348=PLANE('',#2347);
+#2349=ORIENTED_EDGE('',*,*,#2293,.F.);
+#2350=ORIENTED_EDGE('',*,*,#2339,.F.);
+#2351=ORIENTED_EDGE('',*,*,#2108,.F.);
+#2353=ORIENTED_EDGE('',*,*,#2352,.T.);
+#2354=EDGE_LOOP('',(#2349,#2350,#2351,#2353));
+#2355=FACE_OUTER_BOUND('',#2354,.F.);
+#2356=ADVANCED_FACE('',(#2355),#2348,.T.);
+#2357=CARTESIAN_POINT('',(5.357659555759E-2,-1.E-1,8.53E-2));
+#2358=DIRECTION('',(5.E-1,0.E0,8.660254037844E-1));
+#2359=DIRECTION('',(-8.660254037844E-1,0.E0,5.E-1));
+#2360=AXIS2_PLACEMENT_3D('',#2357,#2358,#2359);
+#2361=PLANE('',#2360);
+#2362=ORIENTED_EDGE('',*,*,#2295,.F.);
+#2363=ORIENTED_EDGE('',*,*,#2352,.F.);
+#2364=ORIENTED_EDGE('',*,*,#2106,.F.);
+#2365=ORIENTED_EDGE('',*,*,#2268,.T.);
+#2366=EDGE_LOOP('',(#2362,#2363,#2364,#2365));
+#2367=FACE_OUTER_BOUND('',#2366,.F.);
+#2368=ADVANCED_FACE('',(#2367),#2361,.T.);
+#2369=CARTESIAN_POINT('',(1.588482755730E-1,-1.E-1,8.67E-2));
+#2370=DIRECTION('',(-5.E-1,0.E0,8.660254037844E-1));
+#2371=DIRECTION('',(-8.660254037844E-1,0.E0,-5.E-1));
+#2372=AXIS2_PLACEMENT_3D('',#2369,#2370,#2371);
+#2373=PLANE('',#2372);
+#2374=ORIENTED_EDGE('',*,*,#2298,.F.);
+#2375=ORIENTED_EDGE('',*,*,#2275,.F.);
+#2376=ORIENTED_EDGE('',*,*,#2028,.F.);
+#2378=ORIENTED_EDGE('',*,*,#2377,.T.);
+#2379=EDGE_LOOP('',(#2374,#2375,#2376,#2378));
+#2380=FACE_OUTER_BOUND('',#2379,.F.);
+#2381=ADVANCED_FACE('',(#2380),#2373,.T.);
+#2382=CARTESIAN_POINT('',(1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#2383=DIRECTION('',(8.660254037844E-1,0.E0,5.E-1));
+#2384=DIRECTION('',(-5.E-1,0.E0,8.660254037844E-1));
+#2385=AXIS2_PLACEMENT_3D('',#2382,#2383,#2384);
+#2386=PLANE('',#2385);
+#2387=ORIENTED_EDGE('',*,*,#2300,.F.);
+#2388=ORIENTED_EDGE('',*,*,#2377,.F.);
+#2389=ORIENTED_EDGE('',*,*,#2026,.F.);
+#2391=ORIENTED_EDGE('',*,*,#2390,.T.);
+#2392=EDGE_LOOP('',(#2387,#2388,#2389,#2391));
+#2393=FACE_OUTER_BOUND('',#2392,.F.);
+#2394=ADVANCED_FACE('',(#2393),#2386,.T.);
+#2395=CARTESIAN_POINT('',(1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#2396=DIRECTION('',(5.E-1,0.E0,-8.660254037844E-1));
+#2397=DIRECTION('',(8.660254037844E-1,0.E0,5.E-1));
+#2398=AXIS2_PLACEMENT_3D('',#2395,#2396,#2397);
+#2399=PLANE('',#2398);
+#2400=ORIENTED_EDGE('',*,*,#2302,.F.);
+#2401=ORIENTED_EDGE('',*,*,#2390,.F.);
+#2402=ORIENTED_EDGE('',*,*,#2024,.F.);
+#2403=ORIENTED_EDGE('',*,*,#2143,.T.);
+#2404=EDGE_LOOP('',(#2400,#2401,#2402,#2403));
+#2405=FACE_OUTER_BOUND('',#2404,.F.);
+#2406=ADVANCED_FACE('',(#2405),#2399,.T.);
+#2407=CARTESIAN_POINT('',(6.5E-2,-1.E-1,-2.25E-1));
+#2408=DIRECTION('',(-7.071067811865E-1,-7.071067811865E-1,0.E0));
+#2409=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#2410=AXIS2_PLACEMENT_3D('',#2407,#2408,#2409);
+#2411=PLANE('',#2410);
+#2412=ORIENTED_EDGE('',*,*,#2271,.F.);
+#2413=ORIENTED_EDGE('',*,*,#2102,.T.);
+#2415=ORIENTED_EDGE('',*,*,#2414,.T.);
+#2416=ORIENTED_EDGE('',*,*,#2240,.F.);
+#2417=EDGE_LOOP('',(#2412,#2413,#2415,#2416));
+#2418=FACE_OUTER_BOUND('',#2417,.F.);
+#2419=ADVANCED_FACE('',(#2418),#2411,.T.);
+#2420=CARTESIAN_POINT('',(6.5E-2,-1.E-1,-2.25E-1));
+#2421=DIRECTION('',(-7.071067811865E-1,-7.071067811865E-1,0.E0));
+#2422=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#2423=AXIS2_PLACEMENT_3D('',#2420,#2421,#2422);
+#2424=PLANE('',#2423);
+#2425=ORIENTED_EDGE('',*,*,#2223,.T.);
+#2426=ORIENTED_EDGE('',*,*,#2256,.F.);
+#2427=ORIENTED_EDGE('',*,*,#1984,.F.);
+#2428=ORIENTED_EDGE('',*,*,#2114,.T.);
+#2429=EDGE_LOOP('',(#2425,#2426,#2427,#2428));
+#2430=FACE_OUTER_BOUND('',#2429,.F.);
+#2431=ADVANCED_FACE('',(#2430),#2424,.T.);
+#2432=CARTESIAN_POINT('',(0.E0,0.E0,2.25E-1));
+#2433=DIRECTION('',(0.E0,0.E0,1.E0));
+#2434=DIRECTION('',(1.E0,0.E0,0.E0));
+#2435=AXIS2_PLACEMENT_3D('',#2432,#2433,#2434);
+#2436=PLANE('',#2435);
+#2437=ORIENTED_EDGE('',*,*,#2092,.F.);
+#2439=ORIENTED_EDGE('',*,*,#2438,.F.);
+#2441=ORIENTED_EDGE('',*,*,#2440,.F.);
+#2442=ORIENTED_EDGE('',*,*,#2072,.F.);
+#2444=ORIENTED_EDGE('',*,*,#2443,.F.);
+#2446=ORIENTED_EDGE('',*,*,#2445,.F.);
+#2448=ORIENTED_EDGE('',*,*,#2447,.F.);
+#2450=ORIENTED_EDGE('',*,*,#2449,.F.);
+#2452=ORIENTED_EDGE('',*,*,#2451,.F.);
+#2454=ORIENTED_EDGE('',*,*,#2453,.F.);
+#2456=ORIENTED_EDGE('',*,*,#2455,.F.);
+#2457=ORIENTED_EDGE('',*,*,#2034,.F.);
+#2458=ORIENTED_EDGE('',*,*,#2242,.F.);
+#2459=ORIENTED_EDGE('',*,*,#2414,.F.);
+#2460=ORIENTED_EDGE('',*,*,#2100,.F.);
+#2462=ORIENTED_EDGE('',*,*,#2461,.T.);
+#2464=ORIENTED_EDGE('',*,*,#2463,.T.);
+#2466=ORIENTED_EDGE('',*,*,#2465,.T.);
+#2467=EDGE_LOOP('',(#2437,#2439,#2441,#2442,#2444,#2446,#2448,#2450,#2452,#2454,
+#2456,#2457,#2458,#2459,#2460,#2462,#2464,#2466));
+#2468=FACE_OUTER_BOUND('',#2467,.F.);
+#2470=ORIENTED_EDGE('',*,*,#2469,.T.);
+#2472=ORIENTED_EDGE('',*,*,#2471,.T.);
+#2474=ORIENTED_EDGE('',*,*,#2473,.T.);
+#2476=ORIENTED_EDGE('',*,*,#2475,.T.);
+#2477=EDGE_LOOP('',(#2470,#2472,#2474,#2476));
+#2478=FACE_BOUND('',#2477,.F.);
+#2479=ADVANCED_FACE('',(#2468,#2478),#2436,.T.);
+#2480=CARTESIAN_POINT('',(1.25E-1,-5.5E-2,2.25E-1));
+#2481=DIRECTION('',(-7.071067811865E-1,0.E0,7.071067811865E-1));
+#2482=DIRECTION('',(0.E0,1.E0,0.E0));
+#2483=AXIS2_PLACEMENT_3D('',#2480,#2481,#2482);
+#2484=PLANE('',#2483);
+#2485=ORIENTED_EDGE('',*,*,#2469,.F.);
+#2487=ORIENTED_EDGE('',*,*,#2486,.T.);
+#2489=ORIENTED_EDGE('',*,*,#2488,.T.);
+#2491=ORIENTED_EDGE('',*,*,#2490,.F.);
+#2492=EDGE_LOOP('',(#2485,#2487,#2489,#2491));
+#2493=FACE_OUTER_BOUND('',#2492,.F.);
+#2494=ADVANCED_FACE('',(#2493),#2484,.T.);
+#2495=CARTESIAN_POINT('',(-1.05E-1,-5.5E-2,2.25E-1));
+#2496=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2497=DIRECTION('',(1.E0,0.E0,0.E0));
+#2498=AXIS2_PLACEMENT_3D('',#2495,#2496,#2497);
+#2499=PLANE('',#2498);
+#2501=ORIENTED_EDGE('',*,*,#2500,.F.);
+#2503=ORIENTED_EDGE('',*,*,#2502,.T.);
+#2505=ORIENTED_EDGE('',*,*,#2504,.F.);
+#2506=ORIENTED_EDGE('',*,*,#2486,.F.);
+#2507=ORIENTED_EDGE('',*,*,#2475,.F.);
+#2509=ORIENTED_EDGE('',*,*,#2508,.T.);
+#2510=EDGE_LOOP('',(#2501,#2503,#2505,#2506,#2507,#2509));
+#2511=FACE_OUTER_BOUND('',#2510,.F.);
+#2512=ADVANCED_FACE('',(#2511),#2499,.F.);
+#2513=CARTESIAN_POINT('',(-1.05E-1,1.15E-1,2.25E-1));
+#2514=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2515=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2516=AXIS2_PLACEMENT_3D('',#2513,#2514,#2515);
+#2517=PLANE('',#2516);
+#2519=ORIENTED_EDGE('',*,*,#2518,.T.);
+#2521=ORIENTED_EDGE('',*,*,#2520,.T.);
+#2523=ORIENTED_EDGE('',*,*,#2522,.T.);
+#2525=ORIENTED_EDGE('',*,*,#2524,.T.);
+#2526=ORIENTED_EDGE('',*,*,#2500,.T.);
+#2528=ORIENTED_EDGE('',*,*,#2527,.F.);
+#2530=ORIENTED_EDGE('',*,*,#2529,.T.);
+#2532=ORIENTED_EDGE('',*,*,#2531,.T.);
+#2533=EDGE_LOOP('',(#2519,#2521,#2523,#2525,#2526,#2528,#2530,#2532));
+#2534=FACE_OUTER_BOUND('',#2533,.F.);
+#2535=ADVANCED_FACE('',(#2534),#2517,.F.);
+#2536=CARTESIAN_POINT('',(1.98E-1,1.6E-1,7.5E-2));
+#2537=DIRECTION('',(0.E0,0.E0,1.E0));
+#2538=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2539=AXIS2_PLACEMENT_3D('',#2536,#2537,#2538);
+#2540=PLANE('',#2539);
+#2542=ORIENTED_EDGE('',*,*,#2541,.T.);
+#2544=ORIENTED_EDGE('',*,*,#2543,.T.);
+#2545=ORIENTED_EDGE('',*,*,#2518,.F.);
+#2547=ORIENTED_EDGE('',*,*,#2546,.T.);
+#2548=EDGE_LOOP('',(#2542,#2544,#2545,#2547));
+#2549=FACE_OUTER_BOUND('',#2548,.F.);
+#2550=ADVANCED_FACE('',(#2549),#2540,.T.);
+#2551=CARTESIAN_POINT('',(1.98E-1,1.6E-1,7.5E-2));
+#2552=DIRECTION('',(0.E0,0.E0,1.E0));
+#2553=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2554=AXIS2_PLACEMENT_3D('',#2551,#2552,#2553);
+#2555=PLANE('',#2554);
+#2557=ORIENTED_EDGE('',*,*,#2556,.F.);
+#2559=ORIENTED_EDGE('',*,*,#2558,.F.);
+#2561=ORIENTED_EDGE('',*,*,#2560,.T.);
+#2563=ORIENTED_EDGE('',*,*,#2562,.F.);
+#2564=EDGE_LOOP('',(#2557,#2559,#2561,#2563));
+#2565=FACE_OUTER_BOUND('',#2564,.F.);
+#2566=ADVANCED_FACE('',(#2565),#2555,.T.);
+#2567=CARTESIAN_POINT('',(-6.E-2,1.15E-1,2.25E-1));
+#2568=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2569=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2570=AXIS2_PLACEMENT_3D('',#2567,#2568,#2569);
+#2571=PLANE('',#2570);
+#2573=ORIENTED_EDGE('',*,*,#2572,.T.);
+#2575=ORIENTED_EDGE('',*,*,#2574,.T.);
+#2577=ORIENTED_EDGE('',*,*,#2576,.F.);
+#2579=ORIENTED_EDGE('',*,*,#2578,.F.);
+#2580=ORIENTED_EDGE('',*,*,#2541,.F.);
+#2581=EDGE_LOOP('',(#2573,#2575,#2577,#2579,#2580));
+#2582=FACE_OUTER_BOUND('',#2581,.F.);
+#2583=ADVANCED_FACE('',(#2582),#2571,.F.);
+#2584=CARTESIAN_POINT('',(1.98E-1,1.E-1,7.5E-2));
+#2585=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2586=DIRECTION('',(0.E0,0.E0,-1.E0));
+#2587=AXIS2_PLACEMENT_3D('',#2584,#2585,#2586);
+#2588=PLANE('',#2587);
+#2590=ORIENTED_EDGE('',*,*,#2589,.F.);
+#2591=ORIENTED_EDGE('',*,*,#2572,.F.);
+#2592=ORIENTED_EDGE('',*,*,#2546,.F.);
+#2594=ORIENTED_EDGE('',*,*,#2593,.T.);
+#2596=ORIENTED_EDGE('',*,*,#2595,.T.);
+#2597=ORIENTED_EDGE('',*,*,#1996,.F.);
+#2599=ORIENTED_EDGE('',*,*,#2598,.F.);
+#2601=ORIENTED_EDGE('',*,*,#2600,.T.);
+#2602=ORIENTED_EDGE('',*,*,#2560,.F.);
+#2604=ORIENTED_EDGE('',*,*,#2603,.T.);
+#2605=EDGE_LOOP('',(#2590,#2591,#2592,#2594,#2596,#2597,#2599,#2601,#2602,
+#2604));
+#2606=FACE_OUTER_BOUND('',#2605,.F.);
+#2607=ADVANCED_FACE('',(#2606),#2588,.F.);
+#2608=CARTESIAN_POINT('',(0.E0,0.E0,-1.25E-1));
+#2609=DIRECTION('',(0.E0,0.E0,1.E0));
+#2610=DIRECTION('',(1.E0,0.E0,0.E0));
+#2611=AXIS2_PLACEMENT_3D('',#2608,#2609,#2610);
+#2612=PLANE('',#2611);
+#2613=ORIENTED_EDGE('',*,*,#2589,.T.);
+#2615=ORIENTED_EDGE('',*,*,#2614,.F.);
+#2617=ORIENTED_EDGE('',*,*,#2616,.F.);
+#2619=ORIENTED_EDGE('',*,*,#2618,.F.);
+#2620=ORIENTED_EDGE('',*,*,#2502,.F.);
+#2621=ORIENTED_EDGE('',*,*,#2524,.F.);
+#2623=ORIENTED_EDGE('',*,*,#2622,.F.);
+#2624=ORIENTED_EDGE('',*,*,#2574,.F.);
+#2625=EDGE_LOOP('',(#2613,#2615,#2617,#2619,#2620,#2621,#2623,#2624));
+#2626=FACE_OUTER_BOUND('',#2625,.F.);
+#2628=ORIENTED_EDGE('',*,*,#2627,.F.);
+#2630=ORIENTED_EDGE('',*,*,#2629,.T.);
+#2632=ORIENTED_EDGE('',*,*,#2631,.T.);
+#2634=ORIENTED_EDGE('',*,*,#2633,.F.);
+#2635=EDGE_LOOP('',(#2628,#2630,#2632,#2634));
+#2636=FACE_BOUND('',#2635,.F.);
+#2638=ORIENTED_EDGE('',*,*,#2637,.F.);
+#2640=ORIENTED_EDGE('',*,*,#2639,.T.);
+#2642=ORIENTED_EDGE('',*,*,#2641,.T.);
+#2644=ORIENTED_EDGE('',*,*,#2643,.F.);
+#2645=EDGE_LOOP('',(#2638,#2640,#2642,#2644));
+#2646=FACE_BOUND('',#2645,.F.);
+#2647=ADVANCED_FACE('',(#2626,#2636,#2646),#2612,.T.);
+#2648=CARTESIAN_POINT('',(6.E-2,6.5E-2,2.25E-1));
+#2649=DIRECTION('',(1.E0,0.E0,0.E0));
+#2650=DIRECTION('',(0.E0,1.E0,0.E0));
+#2651=AXIS2_PLACEMENT_3D('',#2648,#2649,#2650);
+#2652=PLANE('',#2651);
+#2653=ORIENTED_EDGE('',*,*,#2603,.F.);
+#2654=ORIENTED_EDGE('',*,*,#2558,.T.);
+#2656=ORIENTED_EDGE('',*,*,#2655,.T.);
+#2658=ORIENTED_EDGE('',*,*,#2657,.T.);
+#2659=ORIENTED_EDGE('',*,*,#2614,.T.);
+#2660=EDGE_LOOP('',(#2653,#2654,#2656,#2658,#2659));
+#2661=FACE_OUTER_BOUND('',#2660,.F.);
+#2662=ADVANCED_FACE('',(#2661),#2652,.F.);
+#2663=CARTESIAN_POINT('',(6.E-2,8.5E-2,7.5E-2));
+#2664=DIRECTION('',(0.E0,7.071067811865E-1,-7.071067811865E-1));
+#2665=DIRECTION('',(1.E0,0.E0,0.E0));
+#2666=AXIS2_PLACEMENT_3D('',#2663,#2664,#2665);
+#2667=PLANE('',#2666);
+#2668=ORIENTED_EDGE('',*,*,#2556,.T.);
+#2670=ORIENTED_EDGE('',*,*,#2669,.T.);
+#2672=ORIENTED_EDGE('',*,*,#2671,.F.);
+#2673=ORIENTED_EDGE('',*,*,#2655,.F.);
+#2674=EDGE_LOOP('',(#2668,#2670,#2672,#2673));
+#2675=FACE_OUTER_BOUND('',#2674,.F.);
+#2676=ADVANCED_FACE('',(#2675),#2667,.F.);
+#2677=CARTESIAN_POINT('',(1.05E-1,-5.5E-2,2.25E-1));
+#2678=DIRECTION('',(1.E0,0.E0,0.E0));
+#2679=DIRECTION('',(0.E0,1.E0,0.E0));
+#2680=AXIS2_PLACEMENT_3D('',#2677,#2678,#2679);
+#2681=PLANE('',#2680);
+#2682=ORIENTED_EDGE('',*,*,#2504,.T.);
+#2683=ORIENTED_EDGE('',*,*,#2618,.T.);
+#2685=ORIENTED_EDGE('',*,*,#2684,.F.);
+#2686=ORIENTED_EDGE('',*,*,#2669,.F.);
+#2687=ORIENTED_EDGE('',*,*,#2562,.T.);
+#2689=ORIENTED_EDGE('',*,*,#2688,.T.);
+#2691=ORIENTED_EDGE('',*,*,#2690,.F.);
+#2692=ORIENTED_EDGE('',*,*,#2488,.F.);
+#2693=EDGE_LOOP('',(#2682,#2683,#2685,#2686,#2687,#2689,#2691,#2692));
+#2694=FACE_OUTER_BOUND('',#2693,.F.);
+#2695=ADVANCED_FACE('',(#2694),#2681,.F.);
+#2696=CARTESIAN_POINT('',(1.05E-1,6.5E-2,2.25E-1));
+#2697=DIRECTION('',(0.E0,1.E0,0.E0));
+#2698=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2699=AXIS2_PLACEMENT_3D('',#2696,#2697,#2698);
+#2700=PLANE('',#2699);
+#2701=ORIENTED_EDGE('',*,*,#2671,.T.);
+#2702=ORIENTED_EDGE('',*,*,#2684,.T.);
+#2703=ORIENTED_EDGE('',*,*,#2616,.T.);
+#2704=ORIENTED_EDGE('',*,*,#2657,.F.);
+#2705=EDGE_LOOP('',(#2701,#2702,#2703,#2704));
+#2706=FACE_OUTER_BOUND('',#2705,.F.);
+#2707=ADVANCED_FACE('',(#2706),#2700,.F.);
+#2708=CARTESIAN_POINT('',(1.98E-1,1.6E-1,7.5E-2));
+#2709=DIRECTION('',(0.E0,0.E0,1.E0));
+#2710=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2711=AXIS2_PLACEMENT_3D('',#2708,#2709,#2710);
+#2712=PLANE('',#2711);
+#2714=ORIENTED_EDGE('',*,*,#2713,.F.);
+#2716=ORIENTED_EDGE('',*,*,#2715,.F.);
+#2717=ORIENTED_EDGE('',*,*,#2688,.F.);
+#2718=ORIENTED_EDGE('',*,*,#2600,.F.);
+#2720=ORIENTED_EDGE('',*,*,#2719,.T.);
+#2722=ORIENTED_EDGE('',*,*,#2721,.T.);
+#2723=EDGE_LOOP('',(#2714,#2716,#2717,#2718,#2720,#2722));
+#2724=FACE_OUTER_BOUND('',#2723,.F.);
+#2725=ADVANCED_FACE('',(#2724),#2712,.F.);
+#2726=CARTESIAN_POINT('',(1.98E-1,1.6E-1,7.5E-2));
+#2727=DIRECTION('',(0.E0,0.E0,1.E0));
+#2728=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2729=AXIS2_PLACEMENT_3D('',#2726,#2727,#2728);
+#2730=PLANE('',#2729);
+#2732=ORIENTED_EDGE('',*,*,#2731,.T.);
+#2734=ORIENTED_EDGE('',*,*,#2733,.T.);
+#2736=ORIENTED_EDGE('',*,*,#2735,.F.);
+#2737=ORIENTED_EDGE('',*,*,#2593,.F.);
+#2738=ORIENTED_EDGE('',*,*,#2531,.F.);
+#2740=ORIENTED_EDGE('',*,*,#2739,.F.);
+#2741=EDGE_LOOP('',(#2732,#2734,#2736,#2737,#2738,#2740));
+#2742=FACE_OUTER_BOUND('',#2741,.F.);
+#2743=ADVANCED_FACE('',(#2742),#2730,.F.);
+#2744=CARTESIAN_POINT('',(1.E-1,1.6E-1,7.5E-2));
+#2745=DIRECTION('',(1.E0,0.E0,0.E0));
+#2746=DIRECTION('',(0.E0,0.E0,1.E0));
+#2747=AXIS2_PLACEMENT_3D('',#2744,#2745,#2746);
+#2748=PLANE('',#2747);
+#2749=ORIENTED_EDGE('',*,*,#2713,.T.);
+#2751=ORIENTED_EDGE('',*,*,#2750,.T.);
+#2753=ORIENTED_EDGE('',*,*,#2752,.F.);
+#2755=ORIENTED_EDGE('',*,*,#2754,.F.);
+#2756=EDGE_LOOP('',(#2749,#2751,#2753,#2755));
+#2757=FACE_OUTER_BOUND('',#2756,.F.);
+#2758=ADVANCED_FACE('',(#2757),#2748,.F.);
+#2759=CARTESIAN_POINT('',(1.4E-1,1.6E-1,-2.25E-1));
+#2760=DIRECTION('',(0.E0,1.E0,0.E0));
+#2761=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2762=AXIS2_PLACEMENT_3D('',#2759,#2760,#2761);
+#2763=PLANE('',#2762);
+#2764=ORIENTED_EDGE('',*,*,#2750,.F.);
+#2765=ORIENTED_EDGE('',*,*,#2721,.F.);
+#2767=ORIENTED_EDGE('',*,*,#2766,.T.);
+#2768=ORIENTED_EDGE('',*,*,#2449,.T.);
+#2770=ORIENTED_EDGE('',*,*,#2769,.F.);
+#2771=ORIENTED_EDGE('',*,*,#2733,.F.);
+#2773=ORIENTED_EDGE('',*,*,#2772,.T.);
+#2775=ORIENTED_EDGE('',*,*,#2774,.F.);
+#2777=ORIENTED_EDGE('',*,*,#2776,.F.);
+#2779=ORIENTED_EDGE('',*,*,#2778,.F.);
+#2780=EDGE_LOOP('',(#2764,#2765,#2767,#2768,#2770,#2771,#2773,#2775,#2777,
+#2779));
+#2781=FACE_OUTER_BOUND('',#2780,.F.);
+#2782=ADVANCED_FACE('',(#2781),#2763,.T.);
+#2783=CARTESIAN_POINT('',(1.4E-1,-5.E-2,-2.25E-1));
+#2784=DIRECTION('',(1.E0,0.E0,0.E0));
+#2785=DIRECTION('',(0.E0,1.E0,0.E0));
+#2786=AXIS2_PLACEMENT_3D('',#2783,#2784,#2785);
+#2787=PLANE('',#2786);
+#2788=ORIENTED_EDGE('',*,*,#2766,.F.);
+#2789=ORIENTED_EDGE('',*,*,#2719,.F.);
+#2790=ORIENTED_EDGE('',*,*,#2598,.T.);
+#2791=ORIENTED_EDGE('',*,*,#1994,.F.);
+#2793=ORIENTED_EDGE('',*,*,#2792,.T.);
+#2794=ORIENTED_EDGE('',*,*,#2451,.T.);
+#2795=EDGE_LOOP('',(#2788,#2789,#2790,#2791,#2793,#2794));
+#2796=FACE_OUTER_BOUND('',#2795,.F.);
+#2797=ADVANCED_FACE('',(#2796),#2787,.T.);
+#2798=CARTESIAN_POINT('',(1.65E-1,-5.E-2,-2.25E-1));
+#2799=DIRECTION('',(0.E0,1.E0,0.E0));
+#2800=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2801=AXIS2_PLACEMENT_3D('',#2798,#2799,#2800);
+#2802=PLANE('',#2801);
+#2803=ORIENTED_EDGE('',*,*,#1992,.F.);
+#2805=ORIENTED_EDGE('',*,*,#2804,.T.);
+#2806=ORIENTED_EDGE('',*,*,#2453,.T.);
+#2807=ORIENTED_EDGE('',*,*,#2792,.F.);
+#2808=EDGE_LOOP('',(#2803,#2805,#2806,#2807));
+#2809=FACE_OUTER_BOUND('',#2808,.F.);
+#2810=ADVANCED_FACE('',(#2809),#2802,.T.);
+#2811=CARTESIAN_POINT('',(1.65E-1,-1.E-1,-2.25E-1));
+#2812=DIRECTION('',(1.E0,0.E0,0.E0));
+#2813=DIRECTION('',(0.E0,1.E0,0.E0));
+#2814=AXIS2_PLACEMENT_3D('',#2811,#2812,#2813);
+#2815=PLANE('',#2814);
+#2816=ORIENTED_EDGE('',*,*,#1990,.F.);
+#2817=ORIENTED_EDGE('',*,*,#2036,.T.);
+#2818=ORIENTED_EDGE('',*,*,#2455,.T.);
+#2819=ORIENTED_EDGE('',*,*,#2804,.F.);
+#2820=EDGE_LOOP('',(#2816,#2817,#2818,#2819));
+#2821=FACE_OUTER_BOUND('',#2820,.F.);
+#2822=ADVANCED_FACE('',(#2821),#2815,.T.);
+#2823=CARTESIAN_POINT('',(-1.4E-1,1.6E-1,-2.25E-1));
+#2824=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2825=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2826=AXIS2_PLACEMENT_3D('',#2823,#2824,#2825);
+#2827=PLANE('',#2826);
+#2828=ORIENTED_EDGE('',*,*,#2769,.T.);
+#2829=ORIENTED_EDGE('',*,*,#2447,.T.);
+#2831=ORIENTED_EDGE('',*,*,#2830,.F.);
+#2832=ORIENTED_EDGE('',*,*,#1998,.F.);
+#2833=ORIENTED_EDGE('',*,*,#2595,.F.);
+#2834=ORIENTED_EDGE('',*,*,#2735,.T.);
+#2835=EDGE_LOOP('',(#2828,#2829,#2831,#2832,#2833,#2834));
+#2836=FACE_OUTER_BOUND('',#2835,.F.);
+#2837=ADVANCED_FACE('',(#2836),#2827,.T.);
+#2838=CARTESIAN_POINT('',(-1.4E-1,-5.E-2,-2.25E-1));
+#2839=DIRECTION('',(0.E0,1.E0,0.E0));
+#2840=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2841=AXIS2_PLACEMENT_3D('',#2838,#2839,#2840);
+#2842=PLANE('',#2841);
+#2843=ORIENTED_EDGE('',*,*,#2000,.F.);
+#2844=ORIENTED_EDGE('',*,*,#2830,.T.);
+#2845=ORIENTED_EDGE('',*,*,#2445,.T.);
+#2847=ORIENTED_EDGE('',*,*,#2846,.F.);
+#2848=EDGE_LOOP('',(#2843,#2844,#2845,#2847));
+#2849=FACE_OUTER_BOUND('',#2848,.F.);
+#2850=ADVANCED_FACE('',(#2849),#2842,.T.);
+#2851=CARTESIAN_POINT('',(-1.65E-1,-5.E-2,-2.25E-1));
+#2852=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2853=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2854=AXIS2_PLACEMENT_3D('',#2851,#2852,#2853);
+#2855=PLANE('',#2854);
+#2856=ORIENTED_EDGE('',*,*,#2002,.F.);
+#2857=ORIENTED_EDGE('',*,*,#2846,.T.);
+#2858=ORIENTED_EDGE('',*,*,#2443,.T.);
+#2859=ORIENTED_EDGE('',*,*,#2070,.F.);
+#2860=EDGE_LOOP('',(#2856,#2857,#2858,#2859));
+#2861=FACE_OUTER_BOUND('',#2860,.F.);
+#2862=ADVANCED_FACE('',(#2861),#2855,.T.);
+#2863=CARTESIAN_POINT('',(-1.E-1,1.6E-1,1.12E-1));
+#2864=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2865=DIRECTION('',(0.E0,0.E0,-1.E0));
+#2866=AXIS2_PLACEMENT_3D('',#2863,#2864,#2865);
+#2867=PLANE('',#2866);
+#2868=ORIENTED_EDGE('',*,*,#2731,.F.);
+#2870=ORIENTED_EDGE('',*,*,#2869,.T.);
+#2872=ORIENTED_EDGE('',*,*,#2871,.T.);
+#2873=ORIENTED_EDGE('',*,*,#2772,.F.);
+#2874=EDGE_LOOP('',(#2868,#2870,#2872,#2873));
+#2875=FACE_OUTER_BOUND('',#2874,.F.);
+#2876=ADVANCED_FACE('',(#2875),#2867,.F.);
+#2877=CARTESIAN_POINT('',(1.05E-1,1.15E-1,2.25E-1));
+#2878=DIRECTION('',(0.E0,1.E0,0.E0));
+#2879=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2880=AXIS2_PLACEMENT_3D('',#2877,#2878,#2879);
+#2881=PLANE('',#2880);
+#2882=ORIENTED_EDGE('',*,*,#2754,.T.);
+#2884=ORIENTED_EDGE('',*,*,#2883,.T.);
+#2886=ORIENTED_EDGE('',*,*,#2885,.T.);
+#2888=ORIENTED_EDGE('',*,*,#2887,.T.);
+#2889=ORIENTED_EDGE('',*,*,#2869,.F.);
+#2890=ORIENTED_EDGE('',*,*,#2739,.T.);
+#2891=ORIENTED_EDGE('',*,*,#2529,.F.);
+#2893=ORIENTED_EDGE('',*,*,#2892,.F.);
+#2894=ORIENTED_EDGE('',*,*,#2690,.T.);
+#2895=ORIENTED_EDGE('',*,*,#2715,.T.);
+#2896=EDGE_LOOP('',(#2882,#2884,#2886,#2888,#2889,#2890,#2891,#2893,#2894,
+#2895));
+#2897=FACE_OUTER_BOUND('',#2896,.F.);
+#2898=ADVANCED_FACE('',(#2897),#2881,.F.);
+#2899=CARTESIAN_POINT('',(4.7E-2,1.6E-1,1.12E-1));
+#2900=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2901=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2902=AXIS2_PLACEMENT_3D('',#2899,#2900,#2901);
+#2903=CYLINDRICAL_SURFACE('',#2902,5.3E-2);
+#2904=ORIENTED_EDGE('',*,*,#2752,.T.);
+#2905=ORIENTED_EDGE('',*,*,#2778,.T.);
+#2907=ORIENTED_EDGE('',*,*,#2906,.F.);
+#2908=ORIENTED_EDGE('',*,*,#2883,.F.);
+#2909=EDGE_LOOP('',(#2904,#2905,#2907,#2908));
+#2910=FACE_OUTER_BOUND('',#2909,.F.);
+#2911=ADVANCED_FACE('',(#2910),#2903,.F.);
+#2912=CARTESIAN_POINT('',(4.7E-2,1.6E-1,1.65E-1));
+#2913=DIRECTION('',(0.E0,0.E0,1.E0));
+#2914=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2915=AXIS2_PLACEMENT_3D('',#2912,#2913,#2914);
+#2916=PLANE('',#2915);
+#2917=ORIENTED_EDGE('',*,*,#2906,.T.);
+#2918=ORIENTED_EDGE('',*,*,#2776,.T.);
+#2920=ORIENTED_EDGE('',*,*,#2919,.F.);
+#2921=ORIENTED_EDGE('',*,*,#2885,.F.);
+#2922=EDGE_LOOP('',(#2917,#2918,#2920,#2921));
+#2923=FACE_OUTER_BOUND('',#2922,.F.);
+#2924=ADVANCED_FACE('',(#2923),#2916,.F.);
+#2925=CARTESIAN_POINT('',(-4.7E-2,1.6E-1,1.12E-1));
+#2926=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2927=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2928=AXIS2_PLACEMENT_3D('',#2925,#2926,#2927);
+#2929=CYLINDRICAL_SURFACE('',#2928,5.3E-2);
+#2930=ORIENTED_EDGE('',*,*,#2919,.T.);
+#2931=ORIENTED_EDGE('',*,*,#2774,.T.);
+#2932=ORIENTED_EDGE('',*,*,#2871,.F.);
+#2933=ORIENTED_EDGE('',*,*,#2887,.F.);
+#2934=EDGE_LOOP('',(#2930,#2931,#2932,#2933));
+#2935=FACE_OUTER_BOUND('',#2934,.F.);
+#2936=ADVANCED_FACE('',(#2935),#2929,.F.);
+#2937=CARTESIAN_POINT('',(1.05E-1,1.35E-1,2.25E-1));
+#2938=DIRECTION('',(0.E0,-7.071067811865E-1,7.071067811865E-1));
+#2939=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2940=AXIS2_PLACEMENT_3D('',#2937,#2938,#2939);
+#2941=PLANE('',#2940);
+#2942=ORIENTED_EDGE('',*,*,#2471,.F.);
+#2943=ORIENTED_EDGE('',*,*,#2490,.T.);
+#2944=ORIENTED_EDGE('',*,*,#2892,.T.);
+#2946=ORIENTED_EDGE('',*,*,#2945,.F.);
+#2947=EDGE_LOOP('',(#2942,#2943,#2944,#2946));
+#2948=FACE_OUTER_BOUND('',#2947,.F.);
+#2949=ADVANCED_FACE('',(#2948),#2941,.T.);
+#2950=CARTESIAN_POINT('',(-1.25E-1,1.15E-1,2.25E-1));
+#2951=DIRECTION('',(7.071067811865E-1,0.E0,7.071067811865E-1));
+#2952=DIRECTION('',(0.E0,-1.E0,0.E0));
+#2953=AXIS2_PLACEMENT_3D('',#2950,#2951,#2952);
+#2954=PLANE('',#2953);
+#2955=ORIENTED_EDGE('',*,*,#2473,.F.);
+#2956=ORIENTED_EDGE('',*,*,#2945,.T.);
+#2957=ORIENTED_EDGE('',*,*,#2527,.T.);
+#2958=ORIENTED_EDGE('',*,*,#2508,.F.);
+#2959=EDGE_LOOP('',(#2955,#2956,#2957,#2958));
+#2960=FACE_OUTER_BOUND('',#2959,.F.);
+#2961=ADVANCED_FACE('',(#2960),#2954,.T.);
+#2962=CARTESIAN_POINT('',(-6.E-2,6.5E-2,2.25E-1));
+#2963=DIRECTION('',(0.E0,1.E0,0.E0));
+#2964=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2965=AXIS2_PLACEMENT_3D('',#2962,#2963,#2964);
+#2966=PLANE('',#2965);
+#2968=ORIENTED_EDGE('',*,*,#2967,.F.);
+#2969=ORIENTED_EDGE('',*,*,#2576,.T.);
+#2970=ORIENTED_EDGE('',*,*,#2622,.T.);
+#2971=ORIENTED_EDGE('',*,*,#2522,.F.);
+#2972=EDGE_LOOP('',(#2968,#2969,#2970,#2971));
+#2973=FACE_OUTER_BOUND('',#2972,.F.);
+#2974=ADVANCED_FACE('',(#2973),#2966,.F.);
+#2975=CARTESIAN_POINT('',(-6.E-2,8.5E-2,7.5E-2));
+#2976=DIRECTION('',(0.E0,-7.071067811865E-1,7.071067811865E-1));
+#2977=DIRECTION('',(-1.E0,0.E0,0.E0));
+#2978=AXIS2_PLACEMENT_3D('',#2975,#2976,#2977);
+#2979=PLANE('',#2978);
+#2980=ORIENTED_EDGE('',*,*,#2543,.F.);
+#2981=ORIENTED_EDGE('',*,*,#2578,.T.);
+#2982=ORIENTED_EDGE('',*,*,#2967,.T.);
+#2983=ORIENTED_EDGE('',*,*,#2520,.F.);
+#2984=EDGE_LOOP('',(#2980,#2981,#2982,#2983));
+#2985=FACE_OUTER_BOUND('',#2984,.F.);
+#2986=ADVANCED_FACE('',(#2985),#2979,.T.);
+#2987=CARTESIAN_POINT('',(3.75E-2,0.E0,0.E0));
+#2988=DIRECTION('',(1.E0,0.E0,0.E0));
+#2989=DIRECTION('',(0.E0,0.E0,-1.E0));
+#2990=AXIS2_PLACEMENT_3D('',#2987,#2988,#2989);
+#2991=PLANE('',#2990);
+#2993=ORIENTED_EDGE('',*,*,#2992,.T.);
+#2994=ORIENTED_EDGE('',*,*,#2627,.T.);
+#2996=ORIENTED_EDGE('',*,*,#2995,.T.);
+#2998=ORIENTED_EDGE('',*,*,#2997,.T.);
+#2999=EDGE_LOOP('',(#2993,#2994,#2996,#2998));
+#3000=FACE_OUTER_BOUND('',#2999,.F.);
+#3001=ADVANCED_FACE('',(#3000),#2991,.F.);
+#3002=CARTESIAN_POINT('',(3.75E-2,0.E0,0.E0));
+#3003=DIRECTION('',(1.E0,0.E0,0.E0));
+#3004=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3005=AXIS2_PLACEMENT_3D('',#3002,#3003,#3004);
+#3006=PLANE('',#3005);
+#3008=ORIENTED_EDGE('',*,*,#3007,.T.);
+#3010=ORIENTED_EDGE('',*,*,#3009,.T.);
+#3012=ORIENTED_EDGE('',*,*,#3011,.T.);
+#3014=ORIENTED_EDGE('',*,*,#3013,.F.);
+#3015=EDGE_LOOP('',(#3008,#3010,#3012,#3014));
+#3016=FACE_OUTER_BOUND('',#3015,.F.);
+#3017=ADVANCED_FACE('',(#3016),#3006,.F.);
+#3018=CARTESIAN_POINT('',(3.75E-2,-1.25E-2,1.15E-1));
+#3019=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3020=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3021=AXIS2_PLACEMENT_3D('',#3018,#3019,#3020);
+#3022=PLANE('',#3021);
+#3023=ORIENTED_EDGE('',*,*,#2992,.F.);
+#3025=ORIENTED_EDGE('',*,*,#3024,.T.);
+#3027=ORIENTED_EDGE('',*,*,#3026,.T.);
+#3028=ORIENTED_EDGE('',*,*,#2629,.F.);
+#3029=EDGE_LOOP('',(#3023,#3025,#3027,#3028));
+#3030=FACE_OUTER_BOUND('',#3029,.F.);
+#3031=ADVANCED_FACE('',(#3030),#3022,.T.);
+#3032=CARTESIAN_POINT('',(3.75E-2,1.25E-2,1.15E-1));
+#3033=DIRECTION('',(0.E0,0.E0,1.E0));
+#3034=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3035=AXIS2_PLACEMENT_3D('',#3032,#3033,#3034);
+#3036=PLANE('',#3035);
+#3037=ORIENTED_EDGE('',*,*,#2997,.F.);
+#3039=ORIENTED_EDGE('',*,*,#3038,.T.);
+#3041=ORIENTED_EDGE('',*,*,#3040,.T.);
+#3042=ORIENTED_EDGE('',*,*,#3024,.F.);
+#3043=EDGE_LOOP('',(#3037,#3039,#3041,#3042));
+#3044=FACE_OUTER_BOUND('',#3043,.F.);
+#3045=ADVANCED_FACE('',(#3044),#3036,.T.);
+#3046=CARTESIAN_POINT('',(3.75E-2,1.25E-2,-2.45E-1));
+#3047=DIRECTION('',(0.E0,1.E0,0.E0));
+#3048=DIRECTION('',(0.E0,0.E0,1.E0));
+#3049=AXIS2_PLACEMENT_3D('',#3046,#3047,#3048);
+#3050=PLANE('',#3049);
+#3051=ORIENTED_EDGE('',*,*,#2995,.F.);
+#3052=ORIENTED_EDGE('',*,*,#2633,.T.);
+#3054=ORIENTED_EDGE('',*,*,#3053,.T.);
+#3055=ORIENTED_EDGE('',*,*,#3038,.F.);
+#3056=EDGE_LOOP('',(#3051,#3052,#3054,#3055));
+#3057=FACE_OUTER_BOUND('',#3056,.F.);
+#3058=ADVANCED_FACE('',(#3057),#3050,.T.);
+#3059=CARTESIAN_POINT('',(6.25E-2,0.E0,0.E0));
+#3060=DIRECTION('',(1.E0,0.E0,0.E0));
+#3061=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3062=AXIS2_PLACEMENT_3D('',#3059,#3060,#3061);
+#3063=PLANE('',#3062);
+#3064=ORIENTED_EDGE('',*,*,#3026,.F.);
+#3065=ORIENTED_EDGE('',*,*,#3040,.F.);
+#3066=ORIENTED_EDGE('',*,*,#3053,.F.);
+#3067=ORIENTED_EDGE('',*,*,#2631,.F.);
+#3068=EDGE_LOOP('',(#3064,#3065,#3066,#3067));
+#3069=FACE_OUTER_BOUND('',#3068,.F.);
+#3070=ADVANCED_FACE('',(#3069),#3063,.T.);
+#3071=CARTESIAN_POINT('',(6.25E-2,0.E0,0.E0));
+#3072=DIRECTION('',(1.E0,0.E0,0.E0));
+#3073=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3074=AXIS2_PLACEMENT_3D('',#3071,#3072,#3073);
+#3075=PLANE('',#3074);
+#3077=ORIENTED_EDGE('',*,*,#3076,.F.);
+#3079=ORIENTED_EDGE('',*,*,#3078,.T.);
+#3081=ORIENTED_EDGE('',*,*,#3080,.F.);
+#3083=ORIENTED_EDGE('',*,*,#3082,.F.);
+#3084=EDGE_LOOP('',(#3077,#3079,#3081,#3083));
+#3085=FACE_OUTER_BOUND('',#3084,.F.);
+#3086=ADVANCED_FACE('',(#3085),#3075,.T.);
+#3087=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.7E-1));
+#3088=DIRECTION('',(0.E0,0.E0,1.E0));
+#3089=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3090=AXIS2_PLACEMENT_3D('',#3087,#3088,#3089);
+#3091=PLANE('',#3090);
+#3092=ORIENTED_EDGE('',*,*,#3007,.F.);
+#3094=ORIENTED_EDGE('',*,*,#3093,.T.);
+#3096=ORIENTED_EDGE('',*,*,#3095,.T.);
+#3098=ORIENTED_EDGE('',*,*,#3097,.F.);
+#3099=ORIENTED_EDGE('',*,*,#3076,.T.);
+#3101=ORIENTED_EDGE('',*,*,#3100,.F.);
+#3102=EDGE_LOOP('',(#3092,#3094,#3096,#3098,#3099,#3101));
+#3103=FACE_OUTER_BOUND('',#3102,.F.);
+#3104=ADVANCED_FACE('',(#3103),#3091,.T.);
+#3105=CARTESIAN_POINT('',(3.75E-2,3.E-2,-2.69E-1));
+#3106=DIRECTION('',(1.E0,0.E0,0.E0));
+#3107=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3108=AXIS2_PLACEMENT_3D('',#3105,#3106,#3107);
+#3109=PLANE('',#3108);
+#3111=ORIENTED_EDGE('',*,*,#3110,.F.);
+#3112=ORIENTED_EDGE('',*,*,#3093,.F.);
+#3114=ORIENTED_EDGE('',*,*,#3113,.F.);
+#3116=ORIENTED_EDGE('',*,*,#3115,.T.);
+#3117=EDGE_LOOP('',(#3111,#3112,#3114,#3116));
+#3118=FACE_OUTER_BOUND('',#3117,.F.);
+#3119=ADVANCED_FACE('',(#3118),#3109,.T.);
+#3120=CARTESIAN_POINT('',(3.75E-2,3.E-2,-2.69E-1));
+#3121=DIRECTION('',(1.E0,0.E0,0.E0));
+#3122=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3123=AXIS2_PLACEMENT_3D('',#3120,#3121,#3122);
+#3124=PLANE('',#3123);
+#3126=ORIENTED_EDGE('',*,*,#3125,.T.);
+#3128=ORIENTED_EDGE('',*,*,#3127,.T.);
+#3130=ORIENTED_EDGE('',*,*,#3129,.T.);
+#3132=ORIENTED_EDGE('',*,*,#3131,.T.);
+#3134=ORIENTED_EDGE('',*,*,#3133,.T.);
+#3136=ORIENTED_EDGE('',*,*,#3135,.F.);
+#3137=EDGE_LOOP('',(#3126,#3128,#3130,#3132,#3134,#3136));
+#3138=FACE_OUTER_BOUND('',#3137,.F.);
+#3139=ADVANCED_FACE('',(#3138),#3124,.T.);
+#3140=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.45E-1));
+#3141=DIRECTION('',(1.E0,0.E0,0.E0));
+#3142=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3143=AXIS2_PLACEMENT_3D('',#3140,#3141,#3142);
+#3144=CYLINDRICAL_SURFACE('',#3143,2.5E-2);
+#3145=ORIENTED_EDGE('',*,*,#3095,.F.);
+#3146=ORIENTED_EDGE('',*,*,#3110,.T.);
+#3148=ORIENTED_EDGE('',*,*,#3147,.T.);
+#3150=ORIENTED_EDGE('',*,*,#3149,.F.);
+#3151=EDGE_LOOP('',(#3145,#3146,#3148,#3150));
+#3152=FACE_OUTER_BOUND('',#3151,.F.);
+#3153=ADVANCED_FACE('',(#3152),#3144,.F.);
+#3154=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.69E-1));
+#3155=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3156=DIRECTION('',(0.E0,1.E0,0.E0));
+#3157=AXIS2_PLACEMENT_3D('',#3154,#3155,#3156);
+#3158=PLANE('',#3157);
+#3159=ORIENTED_EDGE('',*,*,#3147,.F.);
+#3160=ORIENTED_EDGE('',*,*,#3115,.F.);
+#3162=ORIENTED_EDGE('',*,*,#3161,.T.);
+#3164=ORIENTED_EDGE('',*,*,#3163,.T.);
+#3165=EDGE_LOOP('',(#3159,#3160,#3162,#3164));
+#3166=FACE_OUTER_BOUND('',#3165,.F.);
+#3167=ADVANCED_FACE('',(#3166),#3158,.T.);
+#3168=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.69E-1));
+#3169=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3170=DIRECTION('',(0.E0,1.E0,0.E0));
+#3171=AXIS2_PLACEMENT_3D('',#3168,#3169,#3170);
+#3172=PLANE('',#3171);
+#3174=ORIENTED_EDGE('',*,*,#3173,.F.);
+#3176=ORIENTED_EDGE('',*,*,#3175,.F.);
+#3178=ORIENTED_EDGE('',*,*,#3177,.T.);
+#3180=ORIENTED_EDGE('',*,*,#3179,.T.);
+#3181=EDGE_LOOP('',(#3174,#3176,#3178,#3180));
+#3182=FACE_OUTER_BOUND('',#3181,.F.);
+#3183=ADVANCED_FACE('',(#3182),#3172,.T.);
+#3184=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.69E-1));
+#3185=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3186=DIRECTION('',(0.E0,1.E0,0.E0));
+#3187=AXIS2_PLACEMENT_3D('',#3184,#3185,#3186);
+#3188=PLANE('',#3187);
+#3190=ORIENTED_EDGE('',*,*,#3189,.T.);
+#3192=ORIENTED_EDGE('',*,*,#3191,.T.);
+#3194=ORIENTED_EDGE('',*,*,#3193,.T.);
+#3195=ORIENTED_EDGE('',*,*,#3129,.F.);
+#3197=ORIENTED_EDGE('',*,*,#3196,.T.);
+#3199=ORIENTED_EDGE('',*,*,#3198,.T.);
+#3201=ORIENTED_EDGE('',*,*,#3200,.F.);
+#3203=ORIENTED_EDGE('',*,*,#3202,.T.);
+#3205=ORIENTED_EDGE('',*,*,#3204,.F.);
+#3207=ORIENTED_EDGE('',*,*,#3206,.F.);
+#3209=ORIENTED_EDGE('',*,*,#3208,.T.);
+#3211=ORIENTED_EDGE('',*,*,#3210,.F.);
+#3212=EDGE_LOOP('',(#3190,#3192,#3194,#3195,#3197,#3199,#3201,#3203,#3205,#3207,
+#3209,#3211));
+#3213=FACE_OUTER_BOUND('',#3212,.F.);
+#3214=ADVANCED_FACE('',(#3213),#3188,.T.);
+#3215=CARTESIAN_POINT('',(-1.E-1,-6.E-2,-2.25E-1));
+#3216=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3217=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3218=AXIS2_PLACEMENT_3D('',#3215,#3216,#3217);
+#3219=PLANE('',#3218);
+#3221=ORIENTED_EDGE('',*,*,#3220,.T.);
+#3223=ORIENTED_EDGE('',*,*,#3222,.T.);
+#3225=ORIENTED_EDGE('',*,*,#3224,.T.);
+#3227=ORIENTED_EDGE('',*,*,#3226,.F.);
+#3228=ORIENTED_EDGE('',*,*,#2012,.T.);
+#3230=ORIENTED_EDGE('',*,*,#3229,.T.);
+#3232=ORIENTED_EDGE('',*,*,#3231,.T.);
+#3234=ORIENTED_EDGE('',*,*,#3233,.F.);
+#3235=ORIENTED_EDGE('',*,*,#3078,.F.);
+#3237=ORIENTED_EDGE('',*,*,#3236,.F.);
+#3238=ORIENTED_EDGE('',*,*,#3161,.F.);
+#3239=ORIENTED_EDGE('',*,*,#3113,.T.);
+#3240=ORIENTED_EDGE('',*,*,#3013,.T.);
+#3241=ORIENTED_EDGE('',*,*,#3135,.T.);
+#3243=ORIENTED_EDGE('',*,*,#3242,.T.);
+#3245=ORIENTED_EDGE('',*,*,#3244,.F.);
+#3247=ORIENTED_EDGE('',*,*,#3246,.F.);
+#3249=ORIENTED_EDGE('',*,*,#3248,.F.);
+#3250=ORIENTED_EDGE('',*,*,#3177,.F.);
+#3252=ORIENTED_EDGE('',*,*,#3251,.T.);
+#3253=EDGE_LOOP('',(#3221,#3223,#3225,#3227,#3228,#3230,#3232,#3234,#3235,#3237,
+#3238,#3239,#3240,#3241,#3243,#3245,#3247,#3249,#3250,#3252));
+#3254=FACE_OUTER_BOUND('',#3253,.F.);
+#3255=ADVANCED_FACE('',(#3254),#3219,.T.);
+#3256=CARTESIAN_POINT('',(-6.25E-2,0.E0,0.E0));
+#3257=DIRECTION('',(1.E0,0.E0,0.E0));
+#3258=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3259=AXIS2_PLACEMENT_3D('',#3256,#3257,#3258);
+#3260=PLANE('',#3259);
+#3261=ORIENTED_EDGE('',*,*,#2637,.T.);
+#3263=ORIENTED_EDGE('',*,*,#3262,.T.);
+#3265=ORIENTED_EDGE('',*,*,#3264,.T.);
+#3267=ORIENTED_EDGE('',*,*,#3266,.T.);
+#3268=EDGE_LOOP('',(#3261,#3263,#3265,#3267));
+#3269=FACE_OUTER_BOUND('',#3268,.F.);
+#3270=ADVANCED_FACE('',(#3269),#3260,.F.);
+#3271=CARTESIAN_POINT('',(-6.25E-2,0.E0,0.E0));
+#3272=DIRECTION('',(1.E0,0.E0,0.E0));
+#3273=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3274=AXIS2_PLACEMENT_3D('',#3271,#3272,#3273);
+#3275=PLANE('',#3274);
+#3276=ORIENTED_EDGE('',*,*,#3220,.F.);
+#3278=ORIENTED_EDGE('',*,*,#3277,.T.);
+#3280=ORIENTED_EDGE('',*,*,#3279,.T.);
+#3282=ORIENTED_EDGE('',*,*,#3281,.T.);
+#3283=EDGE_LOOP('',(#3276,#3278,#3280,#3282));
+#3284=FACE_OUTER_BOUND('',#3283,.F.);
+#3285=ADVANCED_FACE('',(#3284),#3275,.F.);
+#3286=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,-2.45E-1));
+#3287=DIRECTION('',(0.E0,1.E0,0.E0));
+#3288=DIRECTION('',(0.E0,0.E0,1.E0));
+#3289=AXIS2_PLACEMENT_3D('',#3286,#3287,#3288);
+#3290=PLANE('',#3289);
+#3291=ORIENTED_EDGE('',*,*,#2643,.T.);
+#3293=ORIENTED_EDGE('',*,*,#3292,.T.);
+#3295=ORIENTED_EDGE('',*,*,#3294,.F.);
+#3296=ORIENTED_EDGE('',*,*,#3262,.F.);
+#3297=EDGE_LOOP('',(#3291,#3293,#3295,#3296));
+#3298=FACE_OUTER_BOUND('',#3297,.F.);
+#3299=ADVANCED_FACE('',(#3298),#3290,.T.);
+#3300=CARTESIAN_POINT('',(-3.75E-2,0.E0,0.E0));
+#3301=DIRECTION('',(1.E0,0.E0,0.E0));
+#3302=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3303=AXIS2_PLACEMENT_3D('',#3300,#3301,#3302);
+#3304=PLANE('',#3303);
+#3305=ORIENTED_EDGE('',*,*,#2641,.F.);
+#3307=ORIENTED_EDGE('',*,*,#3306,.F.);
+#3309=ORIENTED_EDGE('',*,*,#3308,.F.);
+#3310=ORIENTED_EDGE('',*,*,#3292,.F.);
+#3311=EDGE_LOOP('',(#3305,#3307,#3309,#3310));
+#3312=FACE_OUTER_BOUND('',#3311,.F.);
+#3313=ADVANCED_FACE('',(#3312),#3304,.T.);
+#3314=CARTESIAN_POINT('',(-3.75E-2,0.E0,0.E0));
+#3315=DIRECTION('',(1.E0,0.E0,0.E0));
+#3316=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3317=AXIS2_PLACEMENT_3D('',#3314,#3315,#3316);
+#3318=PLANE('',#3317);
+#3319=ORIENTED_EDGE('',*,*,#3246,.T.);
+#3321=ORIENTED_EDGE('',*,*,#3320,.F.);
+#3323=ORIENTED_EDGE('',*,*,#3322,.F.);
+#3325=ORIENTED_EDGE('',*,*,#3324,.F.);
+#3326=EDGE_LOOP('',(#3319,#3321,#3323,#3325));
+#3327=FACE_OUTER_BOUND('',#3326,.F.);
+#3328=ADVANCED_FACE('',(#3327),#3318,.T.);
+#3329=CARTESIAN_POINT('',(-6.25E-2,-1.25E-2,1.15E-1));
+#3330=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3331=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3332=AXIS2_PLACEMENT_3D('',#3329,#3330,#3331);
+#3333=PLANE('',#3332);
+#3334=ORIENTED_EDGE('',*,*,#2639,.F.);
+#3335=ORIENTED_EDGE('',*,*,#3266,.F.);
+#3337=ORIENTED_EDGE('',*,*,#3336,.T.);
+#3338=ORIENTED_EDGE('',*,*,#3306,.T.);
+#3339=EDGE_LOOP('',(#3334,#3335,#3337,#3338));
+#3340=FACE_OUTER_BOUND('',#3339,.F.);
+#3341=ADVANCED_FACE('',(#3340),#3333,.T.);
+#3342=CARTESIAN_POINT('',(-6.25E-2,1.25E-2,1.15E-1));
+#3343=DIRECTION('',(0.E0,0.E0,1.E0));
+#3344=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3345=AXIS2_PLACEMENT_3D('',#3342,#3343,#3344);
+#3346=PLANE('',#3345);
+#3347=ORIENTED_EDGE('',*,*,#3264,.F.);
+#3348=ORIENTED_EDGE('',*,*,#3294,.T.);
+#3349=ORIENTED_EDGE('',*,*,#3308,.T.);
+#3350=ORIENTED_EDGE('',*,*,#3336,.F.);
+#3351=EDGE_LOOP('',(#3347,#3348,#3349,#3350));
+#3352=FACE_OUTER_BOUND('',#3351,.F.);
+#3353=ADVANCED_FACE('',(#3352),#3346,.T.);
+#3354=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.95E-1));
+#3355=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3356=DIRECTION('',(0.E0,1.E0,0.E0));
+#3357=AXIS2_PLACEMENT_3D('',#3354,#3355,#3356);
+#3358=PLANE('',#3357);
+#3360=ORIENTED_EDGE('',*,*,#3359,.F.);
+#3361=ORIENTED_EDGE('',*,*,#3281,.F.);
+#3363=ORIENTED_EDGE('',*,*,#3362,.T.);
+#3364=ORIENTED_EDGE('',*,*,#3320,.T.);
+#3366=ORIENTED_EDGE('',*,*,#3365,.T.);
+#3368=ORIENTED_EDGE('',*,*,#3367,.F.);
+#3369=EDGE_LOOP('',(#3360,#3361,#3363,#3364,#3366,#3368));
+#3370=FACE_OUTER_BOUND('',#3369,.F.);
+#3371=ADVANCED_FACE('',(#3370),#3358,.T.);
+#3372=CARTESIAN_POINT('',(-6.25E-2,3.E-2,-2.69E-1));
+#3373=DIRECTION('',(1.E0,0.E0,0.E0));
+#3374=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3375=AXIS2_PLACEMENT_3D('',#3372,#3373,#3374);
+#3376=PLANE('',#3375);
+#3378=ORIENTED_EDGE('',*,*,#3377,.F.);
+#3380=ORIENTED_EDGE('',*,*,#3379,.F.);
+#3381=ORIENTED_EDGE('',*,*,#3251,.F.);
+#3382=ORIENTED_EDGE('',*,*,#3175,.T.);
+#3383=EDGE_LOOP('',(#3378,#3380,#3381,#3382));
+#3384=FACE_OUTER_BOUND('',#3383,.F.);
+#3385=ADVANCED_FACE('',(#3384),#3376,.T.);
+#3386=CARTESIAN_POINT('',(-6.25E-2,3.E-2,-2.69E-1));
+#3387=DIRECTION('',(1.E0,0.E0,0.E0));
+#3388=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3389=AXIS2_PLACEMENT_3D('',#3386,#3387,#3388);
+#3390=PLANE('',#3389);
+#3391=ORIENTED_EDGE('',*,*,#3359,.T.);
+#3393=ORIENTED_EDGE('',*,*,#3392,.T.);
+#3394=ORIENTED_EDGE('',*,*,#3210,.T.);
+#3396=ORIENTED_EDGE('',*,*,#3395,.T.);
+#3398=ORIENTED_EDGE('',*,*,#3397,.T.);
+#3399=ORIENTED_EDGE('',*,*,#3222,.F.);
+#3400=EDGE_LOOP('',(#3391,#3393,#3394,#3396,#3398,#3399));
+#3401=FACE_OUTER_BOUND('',#3400,.F.);
+#3402=ADVANCED_FACE('',(#3401),#3390,.T.);
+#3403=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.45E-1));
+#3404=DIRECTION('',(1.E0,0.E0,0.E0));
+#3405=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3406=AXIS2_PLACEMENT_3D('',#3403,#3404,#3405);
+#3407=CYLINDRICAL_SURFACE('',#3406,2.5E-2);
+#3408=ORIENTED_EDGE('',*,*,#3173,.T.);
+#3410=ORIENTED_EDGE('',*,*,#3409,.F.);
+#3412=ORIENTED_EDGE('',*,*,#3411,.F.);
+#3413=ORIENTED_EDGE('',*,*,#3377,.T.);
+#3414=EDGE_LOOP('',(#3408,#3410,#3412,#3413));
+#3415=FACE_OUTER_BOUND('',#3414,.F.);
+#3416=ADVANCED_FACE('',(#3415),#3407,.F.);
+#3417=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.69E-1));
+#3418=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3419=DIRECTION('',(0.E0,1.E0,0.E0));
+#3420=AXIS2_PLACEMENT_3D('',#3417,#3418,#3419);
+#3421=PLANE('',#3420);
+#3422=ORIENTED_EDGE('',*,*,#3409,.T.);
+#3423=ORIENTED_EDGE('',*,*,#3179,.F.);
+#3424=ORIENTED_EDGE('',*,*,#3248,.T.);
+#3426=ORIENTED_EDGE('',*,*,#3425,.T.);
+#3427=EDGE_LOOP('',(#3422,#3423,#3424,#3426));
+#3428=FACE_OUTER_BOUND('',#3427,.F.);
+#3429=ADVANCED_FACE('',(#3428),#3421,.T.);
+#3430=CARTESIAN_POINT('',(-3.75E-2,-6.E-2,-2.69E-1));
+#3431=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3432=DIRECTION('',(0.E0,1.E0,0.E0));
+#3433=AXIS2_PLACEMENT_3D('',#3430,#3431,#3432);
+#3434=PLANE('',#3433);
+#3435=ORIENTED_EDGE('',*,*,#3365,.F.);
+#3436=ORIENTED_EDGE('',*,*,#3244,.T.);
+#3438=ORIENTED_EDGE('',*,*,#3437,.T.);
+#3440=ORIENTED_EDGE('',*,*,#3439,.F.);
+#3441=ORIENTED_EDGE('',*,*,#3191,.F.);
+#3443=ORIENTED_EDGE('',*,*,#3442,.F.);
+#3444=EDGE_LOOP('',(#3435,#3436,#3438,#3440,#3441,#3443));
+#3445=FACE_OUTER_BOUND('',#3444,.F.);
+#3446=ADVANCED_FACE('',(#3445),#3434,.T.);
+#3447=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.7E-1));
+#3448=DIRECTION('',(0.E0,0.E0,1.E0));
+#3449=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3450=AXIS2_PLACEMENT_3D('',#3447,#3448,#3449);
+#3451=PLANE('',#3450);
+#3452=ORIENTED_EDGE('',*,*,#3379,.T.);
+#3453=ORIENTED_EDGE('',*,*,#3411,.T.);
+#3454=ORIENTED_EDGE('',*,*,#3425,.F.);
+#3455=ORIENTED_EDGE('',*,*,#3324,.T.);
+#3457=ORIENTED_EDGE('',*,*,#3456,.F.);
+#3458=ORIENTED_EDGE('',*,*,#3277,.F.);
+#3459=EDGE_LOOP('',(#3452,#3453,#3454,#3455,#3457,#3458));
+#3460=FACE_OUTER_BOUND('',#3459,.F.);
+#3461=ADVANCED_FACE('',(#3460),#3451,.T.);
+#3462=CARTESIAN_POINT('',(-6.25E-2,-2.45E-1,-2.7E-1));
+#3463=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3464=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3465=AXIS2_PLACEMENT_3D('',#3462,#3463,#3464);
+#3466=PLANE('',#3465);
+#3467=ORIENTED_EDGE('',*,*,#3279,.F.);
+#3468=ORIENTED_EDGE('',*,*,#3456,.T.);
+#3469=ORIENTED_EDGE('',*,*,#3322,.T.);
+#3470=ORIENTED_EDGE('',*,*,#3362,.F.);
+#3471=EDGE_LOOP('',(#3467,#3468,#3469,#3470));
+#3472=FACE_OUTER_BOUND('',#3471,.F.);
+#3473=ADVANCED_FACE('',(#3472),#3466,.T.);
+#3474=CARTESIAN_POINT('',(0.E0,0.E0,-3.1E-1));
+#3475=DIRECTION('',(0.E0,0.E0,1.E0));
+#3476=DIRECTION('',(1.E0,0.E0,0.E0));
+#3477=AXIS2_PLACEMENT_3D('',#3474,#3475,#3476);
+#3478=PLANE('',#3477);
+#3480=ORIENTED_EDGE('',*,*,#3479,.F.);
+#3482=ORIENTED_EDGE('',*,*,#3481,.F.);
+#3484=ORIENTED_EDGE('',*,*,#3483,.F.);
+#3485=ORIENTED_EDGE('',*,*,#3231,.F.);
+#3486=EDGE_LOOP('',(#3480,#3482,#3484,#3485));
+#3487=FACE_OUTER_BOUND('',#3486,.F.);
+#3488=ADVANCED_FACE('',(#3487),#3478,.F.);
+#3489=CARTESIAN_POINT('',(0.E0,0.E0,-3.1E-1));
+#3490=DIRECTION('',(0.E0,0.E0,1.E0));
+#3491=DIRECTION('',(1.E0,0.E0,0.E0));
+#3492=AXIS2_PLACEMENT_3D('',#3489,#3490,#3491);
+#3493=PLANE('',#3492);
+#3495=ORIENTED_EDGE('',*,*,#3494,.F.);
+#3497=ORIENTED_EDGE('',*,*,#3496,.F.);
+#3498=ORIENTED_EDGE('',*,*,#3224,.F.);
+#3499=ORIENTED_EDGE('',*,*,#3397,.F.);
+#3500=EDGE_LOOP('',(#3495,#3497,#3498,#3499));
+#3501=FACE_OUTER_BOUND('',#3500,.F.);
+#3502=ADVANCED_FACE('',(#3501),#3493,.F.);
+#3503=CARTESIAN_POINT('',(0.E0,0.E0,-3.1E-1));
+#3504=DIRECTION('',(0.E0,0.E0,1.E0));
+#3505=DIRECTION('',(1.E0,0.E0,0.E0));
+#3506=AXIS2_PLACEMENT_3D('',#3503,#3504,#3505);
+#3507=PLANE('',#3506);
+#3508=ORIENTED_EDGE('',*,*,#3133,.F.);
+#3510=ORIENTED_EDGE('',*,*,#3509,.F.);
+#3511=ORIENTED_EDGE('',*,*,#3437,.F.);
+#3512=ORIENTED_EDGE('',*,*,#3242,.F.);
+#3513=EDGE_LOOP('',(#3508,#3510,#3511,#3512));
+#3514=FACE_OUTER_BOUND('',#3513,.F.);
+#3515=ADVANCED_FACE('',(#3514),#3507,.F.);
+#3516=CARTESIAN_POINT('',(1.E-1,0.E0,0.E0));
+#3517=DIRECTION('',(1.E0,0.E0,0.E0));
+#3518=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3519=AXIS2_PLACEMENT_3D('',#3516,#3517,#3518);
+#3520=PLANE('',#3519);
+#3521=ORIENTED_EDGE('',*,*,#3202,.F.);
+#3523=ORIENTED_EDGE('',*,*,#3522,.T.);
+#3524=ORIENTED_EDGE('',*,*,#3479,.T.);
+#3525=ORIENTED_EDGE('',*,*,#3229,.F.);
+#3526=ORIENTED_EDGE('',*,*,#2010,.T.);
+#3528=ORIENTED_EDGE('',*,*,#3527,.F.);
+#3529=EDGE_LOOP('',(#3521,#3523,#3524,#3525,#3526,#3528));
+#3530=FACE_OUTER_BOUND('',#3529,.F.);
+#3531=ADVANCED_FACE('',(#3530),#3520,.T.);
+#3532=CARTESIAN_POINT('',(6.25E-2,3.E-2,-2.69E-1));
+#3533=DIRECTION('',(0.E0,1.E0,0.E0));
+#3534=DIRECTION('',(1.E0,0.E0,0.E0));
+#3535=AXIS2_PLACEMENT_3D('',#3532,#3533,#3534);
+#3536=PLANE('',#3535);
+#3537=ORIENTED_EDGE('',*,*,#3200,.T.);
+#3539=ORIENTED_EDGE('',*,*,#3538,.T.);
+#3540=ORIENTED_EDGE('',*,*,#3481,.T.);
+#3541=ORIENTED_EDGE('',*,*,#3522,.F.);
+#3542=EDGE_LOOP('',(#3537,#3539,#3540,#3541));
+#3543=FACE_OUTER_BOUND('',#3542,.F.);
+#3544=ADVANCED_FACE('',(#3543),#3536,.T.);
+#3545=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.69E-1));
+#3546=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3547=DIRECTION('',(0.E0,1.E0,0.E0));
+#3548=AXIS2_PLACEMENT_3D('',#3545,#3546,#3547);
+#3549=PLANE('',#3548);
+#3550=ORIENTED_EDGE('',*,*,#3149,.T.);
+#3551=ORIENTED_EDGE('',*,*,#3163,.F.);
+#3552=ORIENTED_EDGE('',*,*,#3236,.T.);
+#3553=ORIENTED_EDGE('',*,*,#3097,.T.);
+#3554=EDGE_LOOP('',(#3550,#3551,#3552,#3553));
+#3555=FACE_OUTER_BOUND('',#3554,.F.);
+#3556=ADVANCED_FACE('',(#3555),#3549,.T.);
+#3557=CARTESIAN_POINT('',(6.25E-2,-6.E-2,-2.69E-1));
+#3558=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3559=DIRECTION('',(0.E0,1.E0,0.E0));
+#3560=AXIS2_PLACEMENT_3D('',#3557,#3558,#3559);
+#3561=PLANE('',#3560);
+#3563=ORIENTED_EDGE('',*,*,#3562,.F.);
+#3564=ORIENTED_EDGE('',*,*,#3233,.T.);
+#3565=ORIENTED_EDGE('',*,*,#3483,.T.);
+#3566=ORIENTED_EDGE('',*,*,#3538,.F.);
+#3567=ORIENTED_EDGE('',*,*,#3198,.F.);
+#3569=ORIENTED_EDGE('',*,*,#3568,.F.);
+#3570=EDGE_LOOP('',(#3563,#3564,#3565,#3566,#3567,#3569));
+#3571=FACE_OUTER_BOUND('',#3570,.F.);
+#3572=ADVANCED_FACE('',(#3571),#3561,.T.);
+#3573=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.95E-1));
+#3574=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3575=DIRECTION('',(0.E0,1.E0,0.E0));
+#3576=AXIS2_PLACEMENT_3D('',#3573,#3574,#3575);
+#3577=PLANE('',#3576);
+#3578=ORIENTED_EDGE('',*,*,#3080,.T.);
+#3579=ORIENTED_EDGE('',*,*,#3562,.T.);
+#3581=ORIENTED_EDGE('',*,*,#3580,.F.);
+#3582=ORIENTED_EDGE('',*,*,#3125,.F.);
+#3583=ORIENTED_EDGE('',*,*,#3011,.F.);
+#3585=ORIENTED_EDGE('',*,*,#3584,.T.);
+#3586=EDGE_LOOP('',(#3578,#3579,#3581,#3582,#3583,#3585));
+#3587=FACE_OUTER_BOUND('',#3586,.F.);
+#3588=ADVANCED_FACE('',(#3587),#3577,.T.);
+#3589=CARTESIAN_POINT('',(3.75E-2,-3.75E-2,-2.45E-1));
+#3590=DIRECTION('',(1.E0,0.E0,0.E0));
+#3591=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3592=AXIS2_PLACEMENT_3D('',#3589,#3590,#3591);
+#3593=CYLINDRICAL_SURFACE('',#3592,5.E-2);
+#3594=ORIENTED_EDGE('',*,*,#3580,.T.);
+#3595=ORIENTED_EDGE('',*,*,#3568,.T.);
+#3596=ORIENTED_EDGE('',*,*,#3196,.F.);
+#3597=ORIENTED_EDGE('',*,*,#3127,.F.);
+#3598=EDGE_LOOP('',(#3594,#3595,#3596,#3597));
+#3599=FACE_OUTER_BOUND('',#3598,.F.);
+#3600=ADVANCED_FACE('',(#3599),#3593,.T.);
+#3601=CARTESIAN_POINT('',(3.75E-2,-2.45E-1,-2.7E-1));
+#3602=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3603=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3604=AXIS2_PLACEMENT_3D('',#3601,#3602,#3603);
+#3605=PLANE('',#3604);
+#3606=ORIENTED_EDGE('',*,*,#3009,.F.);
+#3607=ORIENTED_EDGE('',*,*,#3100,.T.);
+#3608=ORIENTED_EDGE('',*,*,#3082,.T.);
+#3609=ORIENTED_EDGE('',*,*,#3584,.F.);
+#3610=EDGE_LOOP('',(#3606,#3607,#3608,#3609));
+#3611=FACE_OUTER_BOUND('',#3610,.F.);
+#3612=ADVANCED_FACE('',(#3611),#3605,.T.);
+#3613=CARTESIAN_POINT('',(-1.E-1,6.E-2,-2.69E-1));
+#3614=DIRECTION('',(0.E0,1.E0,0.E0));
+#3615=DIRECTION('',(0.E0,0.E0,1.E0));
+#3616=AXIS2_PLACEMENT_3D('',#3613,#3614,#3615);
+#3617=PLANE('',#3616);
+#3618=ORIENTED_EDGE('',*,*,#2008,.F.);
+#3620=ORIENTED_EDGE('',*,*,#3619,.F.);
+#3621=ORIENTED_EDGE('',*,*,#3204,.T.);
+#3622=ORIENTED_EDGE('',*,*,#3527,.T.);
+#3623=EDGE_LOOP('',(#3618,#3620,#3621,#3622));
+#3624=FACE_OUTER_BOUND('',#3623,.F.);
+#3625=ADVANCED_FACE('',(#3624),#3617,.T.);
+#3626=CARTESIAN_POINT('',(-1.E-1,0.E0,0.E0));
+#3627=DIRECTION('',(1.E0,0.E0,0.E0));
+#3628=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3629=AXIS2_PLACEMENT_3D('',#3626,#3627,#3628);
+#3630=PLANE('',#3629);
+#3631=ORIENTED_EDGE('',*,*,#3226,.T.);
+#3632=ORIENTED_EDGE('',*,*,#3496,.T.);
+#3634=ORIENTED_EDGE('',*,*,#3633,.F.);
+#3635=ORIENTED_EDGE('',*,*,#3206,.T.);
+#3636=ORIENTED_EDGE('',*,*,#3619,.T.);
+#3637=ORIENTED_EDGE('',*,*,#2006,.F.);
+#3638=EDGE_LOOP('',(#3631,#3632,#3634,#3635,#3636,#3637));
+#3639=FACE_OUTER_BOUND('',#3638,.F.);
+#3640=ADVANCED_FACE('',(#3639),#3630,.F.);
+#3641=CARTESIAN_POINT('',(-1.E-1,3.E-2,-2.69E-1));
+#3642=DIRECTION('',(0.E0,1.E0,0.E0));
+#3643=DIRECTION('',(1.E0,0.E0,0.E0));
+#3644=AXIS2_PLACEMENT_3D('',#3641,#3642,#3643);
+#3645=PLANE('',#3644);
+#3646=ORIENTED_EDGE('',*,*,#3208,.F.);
+#3647=ORIENTED_EDGE('',*,*,#3633,.T.);
+#3648=ORIENTED_EDGE('',*,*,#3494,.T.);
+#3649=ORIENTED_EDGE('',*,*,#3395,.F.);
+#3650=EDGE_LOOP('',(#3646,#3647,#3648,#3649));
+#3651=FACE_OUTER_BOUND('',#3650,.F.);
+#3652=ADVANCED_FACE('',(#3651),#3645,.T.);
+#3653=CARTESIAN_POINT('',(-3.75E-2,3.E-2,-2.69E-1));
+#3654=DIRECTION('',(0.E0,1.E0,0.E0));
+#3655=DIRECTION('',(1.E0,0.E0,0.E0));
+#3656=AXIS2_PLACEMENT_3D('',#3653,#3654,#3655);
+#3657=PLANE('',#3656);
+#3658=ORIENTED_EDGE('',*,*,#3193,.F.);
+#3659=ORIENTED_EDGE('',*,*,#3439,.T.);
+#3660=ORIENTED_EDGE('',*,*,#3509,.T.);
+#3661=ORIENTED_EDGE('',*,*,#3131,.F.);
+#3662=EDGE_LOOP('',(#3658,#3659,#3660,#3661));
+#3663=FACE_OUTER_BOUND('',#3662,.F.);
+#3664=ADVANCED_FACE('',(#3663),#3657,.T.);
+#3665=CARTESIAN_POINT('',(-6.25E-2,-3.75E-2,-2.45E-1));
+#3666=DIRECTION('',(1.E0,0.E0,0.E0));
+#3667=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3668=AXIS2_PLACEMENT_3D('',#3665,#3666,#3667);
+#3669=CYLINDRICAL_SURFACE('',#3668,5.E-2);
+#3670=ORIENTED_EDGE('',*,*,#3189,.F.);
+#3671=ORIENTED_EDGE('',*,*,#3392,.F.);
+#3672=ORIENTED_EDGE('',*,*,#3367,.T.);
+#3673=ORIENTED_EDGE('',*,*,#3442,.T.);
+#3674=EDGE_LOOP('',(#3670,#3671,#3672,#3673));
+#3675=FACE_OUTER_BOUND('',#3674,.F.);
+#3676=ADVANCED_FACE('',(#3675),#3669,.T.);
+#3677=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,-2.25E-1));
+#3678=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#3679=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#3680=AXIS2_PLACEMENT_3D('',#3677,#3678,#3679);
+#3681=PLANE('',#3680);
+#3683=ORIENTED_EDGE('',*,*,#3682,.F.);
+#3685=ORIENTED_EDGE('',*,*,#3684,.T.);
+#3686=ORIENTED_EDGE('',*,*,#2438,.T.);
+#3687=ORIENTED_EDGE('',*,*,#2129,.F.);
+#3688=EDGE_LOOP('',(#3683,#3685,#3686,#3687));
+#3689=FACE_OUTER_BOUND('',#3688,.F.);
+#3690=ADVANCED_FACE('',(#3689),#3681,.T.);
+#3691=CARTESIAN_POINT('',(-8.E-2,-1.15E-1,-2.25E-1));
+#3692=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#3693=DIRECTION('',(7.071067811865E-1,7.071067811865E-1,0.E0));
+#3694=AXIS2_PLACEMENT_3D('',#3691,#3692,#3693);
+#3695=PLANE('',#3694);
+#3697=ORIENTED_EDGE('',*,*,#3696,.T.);
+#3698=ORIENTED_EDGE('',*,*,#2117,.F.);
+#3699=ORIENTED_EDGE('',*,*,#1980,.F.);
+#3701=ORIENTED_EDGE('',*,*,#3700,.T.);
+#3702=EDGE_LOOP('',(#3697,#3698,#3699,#3701));
+#3703=FACE_OUTER_BOUND('',#3702,.F.);
+#3704=ADVANCED_FACE('',(#3703),#3695,.T.);
+#3705=CARTESIAN_POINT('',(-1.564234044424E-1,-1.E-1,8.53E-2));
+#3706=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3707=DIRECTION('',(1.E0,0.E0,0.E0));
+#3708=AXIS2_PLACEMENT_3D('',#3705,#3706,#3707);
+#3709=PLANE('',#3708);
+#3711=ORIENTED_EDGE('',*,*,#3710,.T.);
+#3713=ORIENTED_EDGE('',*,*,#3712,.F.);
+#3714=ORIENTED_EDGE('',*,*,#2076,.T.);
+#3716=ORIENTED_EDGE('',*,*,#3715,.T.);
+#3717=ORIENTED_EDGE('',*,*,#3682,.T.);
+#3718=ORIENTED_EDGE('',*,*,#2127,.T.);
+#3720=ORIENTED_EDGE('',*,*,#3719,.T.);
+#3721=EDGE_LOOP('',(#3711,#3713,#3714,#3716,#3717,#3718,#3720));
+#3722=FACE_OUTER_BOUND('',#3721,.F.);
+#3723=ADVANCED_FACE('',(#3722),#3709,.F.);
+#3724=CARTESIAN_POINT('',(-9.434827557301E-2,-1.683556823861E-1,4.63E-2));
+#3725=DIRECTION('',(0.E0,1.E0,0.E0));
+#3726=DIRECTION('',(1.E0,0.E0,0.E0));
+#3727=AXIS2_PLACEMENT_3D('',#3724,#3725,#3726);
+#3728=CONICAL_SURFACE('',#3727,4.441782116695E-2,5.0477E1);
+#3730=ORIENTED_EDGE('',*,*,#3729,.F.);
+#3732=ORIENTED_EDGE('',*,*,#3731,.F.);
+#3734=ORIENTED_EDGE('',*,*,#3733,.F.);
+#3736=ORIENTED_EDGE('',*,*,#3735,.F.);
+#3737=ORIENTED_EDGE('',*,*,#3710,.F.);
+#3739=ORIENTED_EDGE('',*,*,#3738,.F.);
+#3741=ORIENTED_EDGE('',*,*,#3740,.F.);
+#3743=ORIENTED_EDGE('',*,*,#3742,.F.);
+#3745=ORIENTED_EDGE('',*,*,#3744,.F.);
+#3747=ORIENTED_EDGE('',*,*,#3746,.T.);
+#3749=ORIENTED_EDGE('',*,*,#3748,.F.);
+#3751=ORIENTED_EDGE('',*,*,#3750,.F.);
+#3752=EDGE_LOOP('',(#3730,#3732,#3734,#3736,#3737,#3739,#3741,#3743,#3745,#3747,
+#3749,#3751));
+#3753=FACE_OUTER_BOUND('',#3752,.F.);
+#3754=ADVANCED_FACE('',(#3753),#3728,.T.);
+#3755=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-5.5E-3));
+#3756=DIRECTION('',(8.660254037845E-1,0.E0,5.E-1));
+#3757=DIRECTION('',(-5.E-1,0.E0,8.660254037845E-1));
+#3758=AXIS2_PLACEMENT_3D('',#3755,#3756,#3757);
+#3759=PLANE('',#3758);
+#3761=ORIENTED_EDGE('',*,*,#3760,.T.);
+#3763=ORIENTED_EDGE('',*,*,#3762,.F.);
+#3764=ORIENTED_EDGE('',*,*,#2057,.T.);
+#3766=ORIENTED_EDGE('',*,*,#3765,.T.);
+#3767=ORIENTED_EDGE('',*,*,#3729,.T.);
+#3768=EDGE_LOOP('',(#3761,#3763,#3764,#3766,#3767));
+#3769=FACE_OUTER_BOUND('',#3768,.F.);
+#3770=ADVANCED_FACE('',(#3769),#3759,.F.);
+#3771=CARTESIAN_POINT('',(-9.434827557301E-2,-1.767206638076E-1,4.63E-2));
+#3772=DIRECTION('',(0.E0,1.E0,0.E0));
+#3773=DIRECTION('',(1.E0,0.E0,0.E0));
+#3774=AXIS2_PLACEMENT_3D('',#3771,#3772,#3773);
+#3775=CONICAL_SURFACE('',#3774,3.427858267256E-2,5.0477E1);
+#3776=ORIENTED_EDGE('',*,*,#3760,.F.);
+#3777=ORIENTED_EDGE('',*,*,#3750,.T.);
+#3779=ORIENTED_EDGE('',*,*,#3778,.T.);
+#3780=ORIENTED_EDGE('',*,*,#3746,.F.);
+#3782=ORIENTED_EDGE('',*,*,#3781,.F.);
+#3784=ORIENTED_EDGE('',*,*,#3783,.F.);
+#3786=ORIENTED_EDGE('',*,*,#3785,.F.);
+#3788=ORIENTED_EDGE('',*,*,#3787,.F.);
+#3789=EDGE_LOOP('',(#3776,#3777,#3779,#3780,#3782,#3784,#3786,#3788));
+#3790=FACE_OUTER_BOUND('',#3789,.F.);
+#3791=ADVANCED_FACE('',(#3790),#3775,.T.);
+#3792=CARTESIAN_POINT('',(-9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#3793=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3794=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3795=AXIS2_PLACEMENT_3D('',#3792,#3793,#3794);
+#3796=SPHERICAL_SURFACE('',#3795,2.E-2);
+#3798=ORIENTED_EDGE('',*,*,#3797,.T.);
+#3800=ORIENTED_EDGE('',*,*,#3799,.F.);
+#3801=ORIENTED_EDGE('',*,*,#3778,.F.);
+#3802=EDGE_LOOP('',(#3798,#3800,#3801));
+#3803=FACE_OUTER_BOUND('',#3802,.F.);
+#3804=ADVANCED_FACE('',(#3803),#3796,.T.);
+#3805=CARTESIAN_POINT('',(-9.434827557301E-2,-1.790729432769E-1,4.63E-2));
+#3806=DIRECTION('',(0.E0,-1.E0,0.E0));
+#3807=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3808=AXIS2_PLACEMENT_3D('',#3805,#3806,#3807);
+#3809=SPHERICAL_SURFACE('',#3808,2.E-2);
+#3810=ORIENTED_EDGE('',*,*,#3797,.F.);
+#3811=ORIENTED_EDGE('',*,*,#3748,.T.);
+#3812=ORIENTED_EDGE('',*,*,#3799,.T.);
+#3813=EDGE_LOOP('',(#3810,#3811,#3812));
+#3814=FACE_OUTER_BOUND('',#3813,.F.);
+#3815=ADVANCED_FACE('',(#3814),#3809,.T.);
+#3816=CARTESIAN_POINT('',(-5.257659555759E-2,-1.E-1,8.356794919243E-2));
+#3817=DIRECTION('',(-8.660254037845E-1,0.E0,5.E-1));
+#3818=DIRECTION('',(-5.E-1,0.E0,-8.660254037845E-1));
+#3819=AXIS2_PLACEMENT_3D('',#3816,#3817,#3818);
+#3820=PLANE('',#3819);
+#3821=ORIENTED_EDGE('',*,*,#3781,.T.);
+#3822=ORIENTED_EDGE('',*,*,#3744,.T.);
+#3824=ORIENTED_EDGE('',*,*,#3823,.F.);
+#3825=ORIENTED_EDGE('',*,*,#2119,.T.);
+#3826=ORIENTED_EDGE('',*,*,#3696,.F.);
+#3828=ORIENTED_EDGE('',*,*,#3827,.T.);
+#3829=ORIENTED_EDGE('',*,*,#2065,.T.);
+#3831=ORIENTED_EDGE('',*,*,#3830,.T.);
+#3832=EDGE_LOOP('',(#3821,#3822,#3824,#3825,#3826,#3828,#3829,#3831));
+#3833=FACE_OUTER_BOUND('',#3832,.F.);
+#3834=ADVANCED_FACE('',(#3833),#3820,.F.);
+#3835=CARTESIAN_POINT('',(-5.015172442699E-2,-1.E-1,8.496794919243E-2));
+#3836=DIRECTION('',(-5.E-1,0.E0,8.660254037845E-1));
+#3837=DIRECTION('',(-8.660254037845E-1,0.E0,-5.E-1));
+#3838=AXIS2_PLACEMENT_3D('',#3835,#3836,#3837);
+#3839=PLANE('',#3838);
+#3840=ORIENTED_EDGE('',*,*,#3742,.T.);
+#3842=ORIENTED_EDGE('',*,*,#3841,.F.);
+#3843=ORIENTED_EDGE('',*,*,#2121,.T.);
+#3844=ORIENTED_EDGE('',*,*,#3823,.T.);
+#3845=EDGE_LOOP('',(#3840,#3842,#3843,#3844));
+#3846=FACE_OUTER_BOUND('',#3845,.F.);
+#3847=ADVANCED_FACE('',(#3846),#3839,.F.);
+#3848=CARTESIAN_POINT('',(-5.115172442699E-2,-1.E-1,8.67E-2));
+#3849=DIRECTION('',(-8.660254037845E-1,0.E0,-5.E-1));
+#3850=DIRECTION('',(5.E-1,0.E0,-8.660254037845E-1));
+#3851=AXIS2_PLACEMENT_3D('',#3848,#3849,#3850);
+#3852=PLANE('',#3851);
+#3853=ORIENTED_EDGE('',*,*,#3740,.T.);
+#3855=ORIENTED_EDGE('',*,*,#3854,.F.);
+#3856=ORIENTED_EDGE('',*,*,#2123,.T.);
+#3857=ORIENTED_EDGE('',*,*,#3841,.T.);
+#3858=EDGE_LOOP('',(#3853,#3855,#3856,#3857));
+#3859=FACE_OUTER_BOUND('',#3858,.F.);
+#3860=ADVANCED_FACE('',(#3859),#3852,.F.);
+#3861=CARTESIAN_POINT('',(-5.357659555759E-2,-1.E-1,8.53E-2));
+#3862=DIRECTION('',(5.E-1,0.E0,-8.660254037844E-1));
+#3863=DIRECTION('',(8.660254037844E-1,0.E0,5.E-1));
+#3864=AXIS2_PLACEMENT_3D('',#3861,#3862,#3863);
+#3865=PLANE('',#3864);
+#3866=ORIENTED_EDGE('',*,*,#3738,.T.);
+#3867=ORIENTED_EDGE('',*,*,#3719,.F.);
+#3868=ORIENTED_EDGE('',*,*,#2125,.T.);
+#3869=ORIENTED_EDGE('',*,*,#3854,.T.);
+#3870=EDGE_LOOP('',(#3866,#3867,#3868,#3869));
+#3871=FACE_OUTER_BOUND('',#3870,.F.);
+#3872=ADVANCED_FACE('',(#3871),#3865,.F.);
+#3873=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,-2.25E-1));
+#3874=DIRECTION('',(-7.071067811865E-1,-7.071067811865E-1,0.E0));
+#3875=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#3876=AXIS2_PLACEMENT_3D('',#3873,#3874,#3875);
+#3877=PLANE('',#3876);
+#3878=ORIENTED_EDGE('',*,*,#3715,.F.);
+#3879=ORIENTED_EDGE('',*,*,#2074,.T.);
+#3880=ORIENTED_EDGE('',*,*,#2440,.T.);
+#3881=ORIENTED_EDGE('',*,*,#3684,.F.);
+#3882=EDGE_LOOP('',(#3878,#3879,#3880,#3881));
+#3883=FACE_OUTER_BOUND('',#3882,.F.);
+#3884=ADVANCED_FACE('',(#3883),#3877,.T.);
+#3885=CARTESIAN_POINT('',(-9.5E-2,-1.E-1,-2.25E-1));
+#3886=DIRECTION('',(-7.071067811865E-1,-7.071067811865E-1,0.E0));
+#3887=DIRECTION('',(7.071067811865E-1,-7.071067811865E-1,0.E0));
+#3888=AXIS2_PLACEMENT_3D('',#3885,#3886,#3887);
+#3889=PLANE('',#3888);
+#3890=ORIENTED_EDGE('',*,*,#3827,.F.);
+#3891=ORIENTED_EDGE('',*,*,#3700,.F.);
+#3892=ORIENTED_EDGE('',*,*,#1978,.F.);
+#3893=ORIENTED_EDGE('',*,*,#2067,.T.);
+#3894=EDGE_LOOP('',(#3890,#3891,#3892,#3893));
+#3895=FACE_OUTER_BOUND('',#3894,.F.);
+#3896=ADVANCED_FACE('',(#3895),#3889,.T.);
+#3897=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-5.5E-3));
+#3898=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3899=DIRECTION('',(0.E0,0.E0,-1.E0));
+#3900=AXIS2_PLACEMENT_3D('',#3897,#3898,#3899);
+#3901=PLANE('',#3900);
+#3902=ORIENTED_EDGE('',*,*,#3783,.T.);
+#3903=ORIENTED_EDGE('',*,*,#3830,.F.);
+#3904=ORIENTED_EDGE('',*,*,#2063,.T.);
+#3906=ORIENTED_EDGE('',*,*,#3905,.T.);
+#3907=EDGE_LOOP('',(#3902,#3903,#3904,#3906));
+#3908=FACE_OUTER_BOUND('',#3907,.F.);
+#3909=ADVANCED_FACE('',(#3908),#3901,.F.);
+#3910=CARTESIAN_POINT('',(-1.04E-1,-1.E-1,-8.3E-3));
+#3911=DIRECTION('',(0.E0,0.E0,1.E0));
+#3912=DIRECTION('',(-1.E0,0.E0,0.E0));
+#3913=AXIS2_PLACEMENT_3D('',#3910,#3911,#3912);
+#3914=PLANE('',#3913);
+#3915=ORIENTED_EDGE('',*,*,#3785,.T.);
+#3916=ORIENTED_EDGE('',*,*,#3905,.F.);
+#3917=ORIENTED_EDGE('',*,*,#2061,.T.);
+#3919=ORIENTED_EDGE('',*,*,#3918,.T.);
+#3920=EDGE_LOOP('',(#3915,#3916,#3917,#3919));
+#3921=FACE_OUTER_BOUND('',#3920,.F.);
+#3922=ADVANCED_FACE('',(#3921),#3914,.F.);
+#3923=CARTESIAN_POINT('',(-1.06E-1,-1.E-1,-8.3E-3));
+#3924=DIRECTION('',(1.E0,0.E0,0.E0));
+#3925=DIRECTION('',(0.E0,0.E0,1.E0));
+#3926=AXIS2_PLACEMENT_3D('',#3923,#3924,#3925);
+#3927=PLANE('',#3926);
+#3928=ORIENTED_EDGE('',*,*,#3787,.T.);
+#3929=ORIENTED_EDGE('',*,*,#3918,.F.);
+#3930=ORIENTED_EDGE('',*,*,#2059,.T.);
+#3931=ORIENTED_EDGE('',*,*,#3762,.T.);
+#3932=EDGE_LOOP('',(#3928,#3929,#3930,#3931));
+#3933=FACE_OUTER_BOUND('',#3932,.F.);
+#3934=ADVANCED_FACE('',(#3933),#3927,.F.);
+#3935=CARTESIAN_POINT('',(-1.574234044424E-1,-1.E-1,8.356794919243E-2));
+#3936=DIRECTION('',(5.E-1,0.E0,8.660254037844E-1));
+#3937=DIRECTION('',(-8.660254037844E-1,0.E0,5.E-1));
+#3938=AXIS2_PLACEMENT_3D('',#3935,#3936,#3937);
+#3939=PLANE('',#3938);
+#3940=ORIENTED_EDGE('',*,*,#3731,.T.);
+#3941=ORIENTED_EDGE('',*,*,#3765,.F.);
+#3942=ORIENTED_EDGE('',*,*,#2082,.T.);
+#3944=ORIENTED_EDGE('',*,*,#3943,.T.);
+#3945=EDGE_LOOP('',(#3940,#3941,#3942,#3944));
+#3946=FACE_OUTER_BOUND('',#3945,.F.);
+#3947=ADVANCED_FACE('',(#3946),#3939,.F.);
+#3948=CARTESIAN_POINT('',(-1.598482755730E-1,-1.E-1,8.496794919243E-2));
+#3949=DIRECTION('',(8.660254037844E-1,0.E0,-5.E-1));
+#3950=DIRECTION('',(5.E-1,0.E0,8.660254037844E-1));
+#3951=AXIS2_PLACEMENT_3D('',#3948,#3949,#3950);
+#3952=PLANE('',#3951);
+#3953=ORIENTED_EDGE('',*,*,#3733,.T.);
+#3954=ORIENTED_EDGE('',*,*,#3943,.F.);
+#3955=ORIENTED_EDGE('',*,*,#2080,.T.);
+#3957=ORIENTED_EDGE('',*,*,#3956,.T.);
+#3958=EDGE_LOOP('',(#3953,#3954,#3955,#3957));
+#3959=FACE_OUTER_BOUND('',#3958,.F.);
+#3960=ADVANCED_FACE('',(#3959),#3952,.F.);
+#3961=CARTESIAN_POINT('',(-1.588482755730E-1,-1.E-1,8.67E-2));
+#3962=DIRECTION('',(-5.E-1,0.E0,-8.660254037844E-1));
+#3963=DIRECTION('',(8.660254037844E-1,0.E0,-5.E-1));
+#3964=AXIS2_PLACEMENT_3D('',#3961,#3962,#3963);
+#3965=PLANE('',#3964);
+#3966=ORIENTED_EDGE('',*,*,#3735,.T.);
+#3967=ORIENTED_EDGE('',*,*,#3956,.F.);
+#3968=ORIENTED_EDGE('',*,*,#2078,.T.);
+#3969=ORIENTED_EDGE('',*,*,#3712,.T.);
+#3970=EDGE_LOOP('',(#3966,#3967,#3968,#3969));
+#3971=FACE_OUTER_BOUND('',#3970,.F.);
+#3972=ADVANCED_FACE('',(#3971),#3965,.F.);
+#3973=CARTESIAN_POINT('',(2.5E-2,-1.E-1,2.25E-1));
+#3974=DIRECTION('',(1.E0,0.E0,0.E0));
+#3975=DIRECTION('',(0.E0,1.E0,0.E0));
+#3976=AXIS2_PLACEMENT_3D('',#3973,#3974,#3975);
+#3977=PLANE('',#3976);
+#3979=ORIENTED_EDGE('',*,*,#3978,.T.);
+#3981=ORIENTED_EDGE('',*,*,#3980,.F.);
+#3982=ORIENTED_EDGE('',*,*,#2461,.F.);
+#3983=ORIENTED_EDGE('',*,*,#2098,.T.);
+#3984=EDGE_LOOP('',(#3979,#3981,#3982,#3983));
+#3985=FACE_OUTER_BOUND('',#3984,.F.);
+#3986=ADVANCED_FACE('',(#3985),#3977,.F.);
+#3987=CARTESIAN_POINT('',(0.E0,0.E0,-1.25E-1));
+#3988=DIRECTION('',(0.E0,0.E0,1.E0));
+#3989=DIRECTION('',(1.E0,0.E0,0.E0));
+#3990=AXIS2_PLACEMENT_3D('',#3987,#3988,#3989);
+#3991=PLANE('',#3990);
+#3993=ORIENTED_EDGE('',*,*,#3992,.F.);
+#3995=ORIENTED_EDGE('',*,*,#3994,.F.);
+#3996=ORIENTED_EDGE('',*,*,#3978,.F.);
+#3997=ORIENTED_EDGE('',*,*,#2096,.F.);
+#3998=EDGE_LOOP('',(#3993,#3995,#3996,#3997));
+#3999=FACE_OUTER_BOUND('',#3998,.F.);
+#4000=ADVANCED_FACE('',(#3999),#3991,.T.);
+#4001=CARTESIAN_POINT('',(-2.5E-2,-6.E-2,2.25E-1));
+#4002=DIRECTION('',(-1.E0,0.E0,0.E0));
+#4003=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4004=AXIS2_PLACEMENT_3D('',#4001,#4002,#4003);
+#4005=PLANE('',#4004);
+#4006=ORIENTED_EDGE('',*,*,#3992,.T.);
+#4007=ORIENTED_EDGE('',*,*,#2094,.F.);
+#4008=ORIENTED_EDGE('',*,*,#2465,.F.);
+#4010=ORIENTED_EDGE('',*,*,#4009,.T.);
+#4011=EDGE_LOOP('',(#4006,#4007,#4008,#4010));
+#4012=FACE_OUTER_BOUND('',#4011,.F.);
+#4013=ADVANCED_FACE('',(#4012),#4005,.F.);
+#4014=CARTESIAN_POINT('',(2.5E-2,-6.E-2,2.25E-1));
+#4015=DIRECTION('',(0.E0,1.E0,0.E0));
+#4016=DIRECTION('',(-1.E0,0.E0,0.E0));
+#4017=AXIS2_PLACEMENT_3D('',#4014,#4015,#4016);
+#4018=PLANE('',#4017);
+#4019=ORIENTED_EDGE('',*,*,#2463,.F.);
+#4020=ORIENTED_EDGE('',*,*,#3980,.T.);
+#4021=ORIENTED_EDGE('',*,*,#3994,.T.);
+#4022=ORIENTED_EDGE('',*,*,#4009,.F.);
+#4023=EDGE_LOOP('',(#4019,#4020,#4021,#4022));
+#4024=FACE_OUTER_BOUND('',#4023,.F.);
+#4025=ADVANCED_FACE('',(#4024),#4018,.F.);
+#4026=CLOSED_SHELL('',(#2016,#2051,#2086,#2133,#2150,#2173,#2186,#2199,#2212,
+#2232,#2247,#2260,#2279,#2306,#2319,#2330,#2343,#2356,#2368,#2381,#2394,#2406,
+#2419,#2431,#2479,#2494,#2512,#2535,#2550,#2566,#2583,#2607,#2647,#2662,#2676,
+#2695,#2707,#2725,#2743,#2758,#2782,#2797,#2810,#2822,#2837,#2850,#2862,#2876,
+#2898,#2911,#2924,#2936,#2949,#2961,#2974,#2986,#3001,#3017,#3031,#3045,#3058,
+#3070,#3086,#3104,#3119,#3139,#3153,#3167,#3183,#3214,#3255,#3270,#3285,#3299,
+#3313,#3328,#3341,#3353,#3371,#3385,#3402,#3416,#3429,#3446,#3461,#3473,#3488,
+#3502,#3515,#3531,#3544,#3556,#3572,#3588,#3600,#3612,#3625,#3640,#3652,#3664,
+#3676,#3690,#3704,#3723,#3754,#3770,#3791,#3804,#3815,#3834,#3847,#3860,#3872,
+#3884,#3896,#3909,#3922,#3934,#3947,#3960,#3972,#3986,#4000,#4013,#4025));
+#4027=MANIFOLD_SOLID_BREP('',#4026);
+#4028=CARTESIAN_POINT('',(0.E0,0.E0,0.E0));
+#4029=DIRECTION('',(0.E0,0.E0,1.E0));
+#4030=DIRECTION('',(1.E0,0.E0,0.E0));
+#4031=AXIS2_PLACEMENT_3D('CSYS',#4028,#4029,#4030);
+#4032=DIRECTION('',(0.E0,1.E0,0.E0));
+#4033=VECTOR('',#4032,6.6E-2);
+#4034=CARTESIAN_POINT('',(9.434827557301E-2,-2.05E-1,4.63E-2));
+#4035=LINE('',#4034,#4033);
+#4038=DIRECTION('',(0.E0,1.E0,0.E0));
+#4039=VECTOR('',#4038,6.6E-2);
+#4040=CARTESIAN_POINT('',(-9.434827557301E-2,-2.05E-1,4.63E-2));
+#4041=LINE('',#4040,#4039);
+#4043=CARTESIAN_POINT('',(5.E-2,-1.E-1,-2.825E-1));
+#4044=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4045=DIRECTION('',(-1.E0,0.E0,0.E0));
+#4046=AXIS2_PLACEMENT_3D('',#4043,#4044,#4045);
+#4050=CARTESIAN_POINT('',(5.E-2,-1.E-1,-2.825E-1));
+#4051=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4052=DIRECTION('',(1.E0,0.E0,0.E0));
+#4053=AXIS2_PLACEMENT_3D('',#4050,#4051,#4052);
+#4058=CARTESIAN_POINT('',(-5.E-2,-1.E-1,-2.825E-1));
+#4059=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4060=DIRECTION('',(-1.E0,0.E0,0.E0));
+#4061=AXIS2_PLACEMENT_3D('',#4058,#4059,#4060);
+#4065=CARTESIAN_POINT('',(-5.E-2,-1.E-1,-2.825E-1));
+#4066=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4067=DIRECTION('',(1.E0,0.E0,0.E0));
+#4068=AXIS2_PLACEMENT_3D('',#4065,#4066,#4067);
+#4073=CARTESIAN_POINT('',(5.E-2,-2.45E-1,-2.825E-1));
+#4074=DIRECTION('',(0.E0,0.E0,1.E0));
+#4075=DIRECTION('',(1.E0,0.E0,0.E0));
+#4076=AXIS2_PLACEMENT_3D('CS0',#4073,#4074,#4075);
+#4077=CARTESIAN_POINT('',(1.05E-1,-1.E-1,5.75E-2));
+#4078=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4079=DIRECTION('',(-1.E0,0.E0,0.E0));
+#4080=AXIS2_PLACEMENT_3D('',#4077,#4078,#4079);
+#4084=CARTESIAN_POINT('',(1.05E-1,-1.E-1,5.75E-2));
+#4085=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4086=DIRECTION('',(1.E0,0.E0,0.E0));
+#4087=AXIS2_PLACEMENT_3D('',#4084,#4085,#4086);
+#4092=CARTESIAN_POINT('',(-1.05E-1,-1.E-1,5.75E-2));
+#4093=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4094=DIRECTION('',(-1.E0,0.E0,0.E0));
+#4095=AXIS2_PLACEMENT_3D('',#4092,#4093,#4094);
+#4099=CARTESIAN_POINT('',(-1.05E-1,-1.E-1,5.75E-2));
+#4100=DIRECTION('',(0.E0,-1.E0,0.E0));
+#4101=DIRECTION('',(1.E0,0.E0,0.E0));
+#4102=AXIS2_PLACEMENT_3D('',#4099,#4100,#4101);
+#4107=DIMENSIONAL_EXPONENTS(1.E0,0.E0,0.E0,0.E0,0.E0,0.E0,0.E0);
+#4109=LENGTH_MEASURE_WITH_UNIT(LENGTH_MEASURE(2.54E1),#4108);
+#4110=(CONVERSION_BASED_UNIT('INCH',#4109)LENGTH_UNIT()NAMED_UNIT(#4107));
+#4111=DIMENSIONAL_EXPONENTS(0.E0,0.E0,0.E0,0.E0,0.E0,0.E0,0.E0);
+#4113=PLANE_ANGLE_MEASURE_WITH_UNIT(PLANE_ANGLE_MEASURE(1.745329251994E-2),
+#4112);
+#4114=(CONVERSION_BASED_UNIT('DEGREE',#4113)NAMED_UNIT(*)PLANE_ANGLE_UNIT());
+#4116=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(7.540162558127E-5),#4110,
+'closure',
+'Maximum model space distance between geometric entities at asserted connectivities');
+#4117=(GEOMETRIC_REPRESENTATION_CONTEXT(3)GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((
+#4116))GLOBAL_UNIT_ASSIGNED_CONTEXT((#4110,#4114,#4115))REPRESENTATION_CONTEXT(
+'ID1','3'));
+#4037=GEOMETRIC_SET('',(#4036,#4042,#4057,#4072,#4091,#4106));
+#4121=SHAPE_REPRESENTATION_RELATIONSHIP('','',#4120,#4118);
+#4122=SHAPE_REPRESENTATION_RELATIONSHIP('','',#4120,#4119);
+#4123=APPLICATION_CONTEXT(
+'CONFIGURATION CONTROLLED 3D DESIGNS OF MECHANICAL PARTS AND ASSEMBLIES');
+#4124=APPLICATION_PROTOCOL_DEFINITION('international standard',
+'config_control_design',1994,#4123);
+#4125=DESIGN_CONTEXT('',#4123,'design');
+#4126=MECHANICAL_CONTEXT('',#4123,'mechanical');
+#4127=PRODUCT('705550036','705550036','NOT SPECIFIED',(#4126));
+#4128=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','LAST_VERSION',
+#4127,.MADE.);
+#4132=PRODUCT_CATEGORY('part','');
+#4133=PRODUCT_RELATED_PRODUCT_CATEGORY('detail','',(#4127));
+#4134=PRODUCT_CATEGORY_RELATIONSHIP('','',#4132,#4133);
+#4135=SECURITY_CLASSIFICATION_LEVEL('unclassified');
+#4136=SECURITY_CLASSIFICATION('','',#4135);
+#4137=CC_DESIGN_SECURITY_CLASSIFICATION(#4136,(#4128));
+#4138=APPROVAL_STATUS('approved');
+#4139=APPROVAL(#4138,'');
+#4140=CC_DESIGN_APPROVAL(#4139,(#4136,#4128,#4129));
+#4141=CALENDAR_DATE(116,6,12);
+#4142=COORDINATED_UNIVERSAL_TIME_OFFSET(5,30,.AHEAD.);
+#4143=LOCAL_TIME(12,37,5.2E1,#4142);
+#4144=DATE_AND_TIME(#4141,#4143);
+#4145=APPROVAL_DATE_TIME(#4144,#4139);
+#4146=DATE_TIME_ROLE('creation_date');
+#4147=CC_DESIGN_DATE_AND_TIME_ASSIGNMENT(#4144,#4146,(#4129));
+#4148=DATE_TIME_ROLE('classification_date');
+#4149=CC_DESIGN_DATE_AND_TIME_ASSIGNMENT(#4144,#4148,(#4136));
+#4150=PERSON('UNSPECIFIED','UNSPECIFIED',$,$,$,$);
+#4151=ORGANIZATION('UNSPECIFIED','UNSPECIFIED','UNSPECIFIED');
+#4152=PERSON_AND_ORGANIZATION(#4150,#4151);
+#4153=APPROVAL_ROLE('approver');
+#4154=APPROVAL_PERSON_ORGANIZATION(#4152,#4139,#4153);
+#4155=PERSON_AND_ORGANIZATION_ROLE('creator');
+#4156=CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#4152,#4155,(#4128,#4129));
+#4157=PERSON_AND_ORGANIZATION_ROLE('design_supplier');
+#4158=CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#4152,#4157,(#4128));
+#4159=PERSON_AND_ORGANIZATION_ROLE('classification_officer');
+#4160=CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#4152,#4159,(#4136));
+#4161=PERSON_AND_ORGANIZATION_ROLE('design_owner');
+#4162=CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#4152,#4161,(#4127));
+#245=B_SPLINE_CURVE_WITH_KNOTS('',3,(#233,#234,#235,#236,#237,#238,#239,#240,
+#241,#242,#243,#244),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,1,1,1,1,4),(0.E0,
+1.111111111111E-1,2.222222222222E-1,3.333333333333E-1,4.444444444444E-1,
+5.555555555556E-1,6.666666666667E-1,7.777777777778E-1,8.888888888889E-1,1.E0),
+.UNSPECIFIED.);
+#254=B_SPLINE_CURVE_WITH_KNOTS('',3,(#246,#247,#248,#249,#250,#251,#252,#253),
+.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,4),(0.E0,2.E-1,4.E-1,6.E-1,8.E-1,1.E0),
+.UNSPECIFIED.);
+#264=B_SPLINE_CURVE_WITH_KNOTS('',3,(#259,#260,#261,#262,#263),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#274=B_SPLINE_CURVE_WITH_KNOTS('',3,(#269,#270,#271,#272,#273),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#284=B_SPLINE_CURVE_WITH_KNOTS('',3,(#279,#280,#281,#282,#283),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#303=B_SPLINE_CURVE_WITH_KNOTS('',3,(#289,#290,#291,#292,#293,#294,#295,#296,
+#297,#298,#299,#300,#301,#302),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,1,1,1,1,1,1,4),(
+0.E0,9.090909090909E-2,1.818181818182E-1,2.727272727273E-1,3.636363636364E-1,
+4.545454545455E-1,5.454545454545E-1,6.363636363636E-1,7.272727272727E-1,
+8.181818181818E-1,9.090909090909E-1,1.E0),.UNSPECIFIED.);
+#318=B_SPLINE_CURVE_WITH_KNOTS('',3,(#308,#309,#310,#311,#312,#313,#314,#315,
+#316,#317),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,1,1,4),(0.E0,1.428571428571E-1,
+2.857142857143E-1,4.285714285714E-1,5.714285714286E-1,7.142857142857E-1,
+8.571428571429E-1,1.E0),.UNSPECIFIED.);
+#351=B_SPLINE_CURVE_WITH_KNOTS('',3,(#335,#336,#337,#338,#339,#340,#341,#342,
+#343,#344,#345,#346,#347,#348,#349,#350),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,1,1,1,
+1,1,1,1,1,4),(0.E0,7.692307692308E-2,1.538461538462E-1,2.307692307692E-1,
+3.076923076923E-1,3.846153846154E-1,4.615384615385E-1,5.384615384615E-1,
+6.153846153846E-1,6.923076923077E-1,7.692307692308E-1,8.461538461538E-1,
+9.230769230769E-1,1.E0),.UNSPECIFIED.);
+#368=CIRCLE('',#367,2.E-2);
+#373=CIRCLE('',#372,2.E-2);
+#378=CIRCLE('',#377,1.272775837960E-2);
+#383=CIRCLE('',#382,1.272775837960E-2);
+#389=B_SPLINE_CURVE_WITH_KNOTS('',3,(#384,#385,#386,#387,#388),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#399=B_SPLINE_CURVE_WITH_KNOTS('',3,(#394,#395,#396,#397,#398),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#409=B_SPLINE_CURVE_WITH_KNOTS('',3,(#404,#405,#406,#407,#408),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#419=B_SPLINE_CURVE_WITH_KNOTS('',3,(#414,#415,#416,#417,#418),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#429=B_SPLINE_CURVE_WITH_KNOTS('',3,(#424,#425,#426,#427,#428),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#439=B_SPLINE_CURVE_WITH_KNOTS('',3,(#434,#435,#436,#437,#438),.UNSPECIFIED.,
+.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#736=CIRCLE('',#735,5.3E-2);
+#745=CIRCLE('',#744,5.3E-2);
+#798=CIRCLE('',#797,5.3E-2);
+#807=CIRCLE('',#806,5.3E-2);
+#936=CIRCLE('',#935,2.5E-2);
+#949=CIRCLE('',#948,5.E-2);
+#1122=CIRCLE('',#1121,2.5E-2);
+#1135=CIRCLE('',#1134,5.E-2);
+#1144=CIRCLE('',#1143,2.5E-2);
+#1165=CIRCLE('',#1164,5.E-2);
+#1242=CIRCLE('',#1241,2.5E-2);
+#1263=CIRCLE('',#1262,5.E-2);
+#1336=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1320,#1321,#1322,#1323,#1324,#1325,#1326,
+#1327,#1328,#1329,#1330,#1331,#1332,#1333,#1334,#1335),.UNSPECIFIED.,.F.,.F.,(4,
+1,1,1,1,1,1,1,1,1,1,1,1,4),(0.E0,7.692307692308E-2,1.538461538462E-1,
+2.307692307692E-1,3.076923076923E-1,3.846153846154E-1,4.615384615385E-1,
+5.384615384615E-1,6.153846153846E-1,6.923076923077E-1,7.692307692308E-1,
+8.461538461538E-1,9.230769230769E-1,1.E0),.UNSPECIFIED.);
+#1361=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1349,#1350,#1351,#1352,#1353,#1354,#1355,
+#1356,#1357,#1358,#1359,#1360),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,1,1,1,1,4),
+(0.E0,1.111111111111E-1,2.222222222222E-1,3.333333333333E-1,4.444444444444E-1,
+5.555555555556E-1,6.666666666667E-1,7.777777777778E-1,8.888888888889E-1,1.E0),
+.UNSPECIFIED.);
+#1374=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1366,#1367,#1368,#1369,#1370,#1371,#1372,
+#1373),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,4),(0.E0,2.E-1,4.E-1,6.E-1,8.E-1,1.E0),
+.UNSPECIFIED.);
+#1379=CIRCLE('',#1378,1.272775837960E-2);
+#1384=CIRCLE('',#1383,2.E-2);
+#1389=CIRCLE('',#1388,1.272775837960E-2);
+#1394=CIRCLE('',#1393,2.E-2);
+#1409=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1395,#1396,#1397,#1398,#1399,#1400,#1401,
+#1402,#1403,#1404,#1405,#1406,#1407,#1408),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,1,1,
+1,1,1,1,4),(0.E0,9.090909090909E-2,1.818181818182E-1,2.727272727273E-1,
+3.636363636364E-1,4.545454545455E-1,5.454545454545E-1,6.363636363636E-1,
+7.272727272727E-1,8.181818181818E-1,9.090909090909E-1,1.E0),.UNSPECIFIED.);
+#1420=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1410,#1411,#1412,#1413,#1414,#1415,#1416,
+#1417,#1418,#1419),.UNSPECIFIED.,.F.,.F.,(4,1,1,1,1,1,1,4),(0.E0,
+1.428571428571E-1,2.857142857143E-1,4.285714285714E-1,5.714285714286E-1,
+7.142857142857E-1,8.571428571429E-1,1.E0),.UNSPECIFIED.);
+#1430=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1425,#1426,#1427,#1428,#1429),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1440=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1435,#1436,#1437,#1438,#1439),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1450=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1445,#1446,#1447,#1448,#1449),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1468=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1463,#1464,#1465,#1466,#1467),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1478=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1473,#1474,#1475,#1476,#1477),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1488=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1483,#1484,#1485,#1486,#1487),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1498=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1493,#1494,#1495,#1496,#1497),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1508=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1503,#1504,#1505,#1506,#1507),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1518=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1513,#1514,#1515,#1516,#1517),
+.UNSPECIFIED.,.F.,.F.,(4,1,4),(0.E0,5.E-1,1.E0),.UNSPECIFIED.);
+#1976=EDGE_CURVE('',#1692,#1728,#4,.T.);
+#1978=EDGE_CURVE('',#1728,#1742,#8,.T.);
+#1980=EDGE_CURVE('',#1742,#1731,#12,.T.);
+#1982=EDGE_CURVE('',#1731,#1732,#16,.T.);
+#1984=EDGE_CURVE('',#1732,#1740,#20,.T.);
+#1986=EDGE_CURVE('',#1740,#1734,#24,.T.);
+#1988=EDGE_CURVE('',#1734,#1695,#28,.T.);
+#1990=EDGE_CURVE('',#1695,#1696,#32,.T.);
+#1992=EDGE_CURVE('',#1696,#1606,#36,.T.);
+#1994=EDGE_CURVE('',#1606,#1597,#40,.T.);
+#1996=EDGE_CURVE('',#1597,#1598,#44,.T.);
+#1998=EDGE_CURVE('',#1598,#1604,#48,.T.);
+#2000=EDGE_CURVE('',#1604,#1690,#52,.T.);
+#2002=EDGE_CURVE('',#1690,#1692,#56,.T.);
+#2006=EDGE_CURVE('',#1711,#1712,#60,.T.);
+#2008=EDGE_CURVE('',#1712,#1716,#64,.T.);
+#2010=EDGE_CURVE('',#1715,#1716,#68,.T.);
+#2012=EDGE_CURVE('',#1711,#1715,#72,.T.);
+#2022=EDGE_CURVE('',#1765,#1766,#76,.T.);
+#2024=EDGE_CURVE('',#1766,#1768,#80,.T.);
+#2026=EDGE_CURVE('',#1768,#1770,#84,.T.);
+#2028=EDGE_CURVE('',#1770,#1772,#88,.T.);
+#2030=EDGE_CURVE('',#1772,#1774,#92,.T.);
+#2032=EDGE_CURVE('',#1774,#1738,#96,.T.);
+#2034=EDGE_CURVE('',#1738,#1703,#503,.T.);
+#2036=EDGE_CURVE('',#1695,#1703,#765,.T.);
+#2039=EDGE_CURVE('',#1734,#1796,#100,.T.);
+#2041=EDGE_CURVE('',#1796,#1818,#104,.T.);
+#2043=EDGE_CURVE('',#1818,#1789,#108,.T.);
+#2045=EDGE_CURVE('',#1789,#1790,#112,.T.);
+#2047=EDGE_CURVE('',#1790,#1765,#116,.T.);
+#2057=EDGE_CURVE('',#1821,#1822,#120,.T.);
+#2059=EDGE_CURVE('',#1846,#1821,#124,.T.);
+#2061=EDGE_CURVE('',#1845,#1846,#128,.T.);
+#2063=EDGE_CURVE('',#1874,#1845,#132,.T.);
+#2065=EDGE_CURVE('',#1852,#1874,#136,.T.);
+#2067=EDGE_CURVE('',#1728,#1852,#140,.T.);
+#2070=EDGE_CURVE('',#1692,#1700,#144,.T.);
+#2072=EDGE_CURVE('',#1700,#1736,#471,.T.);
+#2074=EDGE_CURVE('',#1830,#1736,#148,.T.);
+#2076=EDGE_CURVE('',#1828,#1830,#152,.T.);
+#2078=EDGE_CURVE('',#1826,#1828,#156,.T.);
+#2080=EDGE_CURVE('',#1824,#1826,#160,.T.);
+#2082=EDGE_CURVE('',#1822,#1824,#164,.T.);
+#2092=EDGE_CURVE('',#1750,#1756,#459,.T.);
+#2094=EDGE_CURVE('',#1756,#1758,#168,.T.);
+#2096=EDGE_CURVE('',#1758,#1762,#172,.T.);
+#2098=EDGE_CURVE('',#1921,#1762,#176,.T.);
+#2100=EDGE_CURVE('',#1921,#1745,#515,.T.);
+#2102=EDGE_CURVE('',#1777,#1745,#180,.T.);
+#2104=EDGE_CURVE('',#1777,#1778,#184,.T.);
+#2106=EDGE_CURVE('',#1778,#1780,#188,.T.);
+#2108=EDGE_CURVE('',#1780,#1782,#192,.T.);
+#2110=EDGE_CURVE('',#1782,#1784,#196,.T.);
+#2112=EDGE_CURVE('',#1784,#1786,#200,.T.);
+#2114=EDGE_CURVE('',#1732,#1786,#204,.T.);
+#2117=EDGE_CURVE('',#1731,#1842,#208,.T.);
+#2119=EDGE_CURVE('',#1840,#1842,#212,.T.);
+#2121=EDGE_CURVE('',#1838,#1840,#216,.T.);
+#2123=EDGE_CURVE('',#1836,#1838,#220,.T.);
+#2125=EDGE_CURVE('',#1834,#1836,#224,.T.);
+#2127=EDGE_CURVE('',#1833,#1834,#228,.T.);
+#2129=EDGE_CURVE('',#1833,#1750,#232,.T.);
+#2139=EDGE_CURVE('',#1803,#1804,#245,.T.);
+#2141=EDGE_CURVE('',#1805,#1803,#254,.T.);
+#2143=EDGE_CURVE('',#1766,#1805,#443,.T.);
+#2146=EDGE_CURVE('',#1765,#1804,#258,.T.);
+#2157=EDGE_CURVE('',#1804,#1816,#264,.T.);
+#2159=EDGE_CURVE('',#1816,#1815,#274,.T.);
+#2161=EDGE_CURVE('',#1815,#1813,#284,.T.);
+#2163=EDGE_CURVE('',#1813,#1814,#303,.T.);
+#2165=EDGE_CURVE('',#1814,#1802,#363,.T.);
+#2167=EDGE_CURVE('',#1799,#1802,#383,.T.);
+#2169=EDGE_CURVE('',#1803,#1799,#359,.T.);
+#2182=EDGE_CURVE('',#1790,#1816,#268,.T.);
+#2195=EDGE_CURVE('',#1789,#1815,#278,.T.);
+#2208=EDGE_CURVE('',#1818,#1813,#288,.T.);
+#2221=EDGE_CURVE('',#1794,#1796,#330,.T.);
+#2223=EDGE_CURVE('',#1786,#1794,#451,.T.);
+#2226=EDGE_CURVE('',#1784,#1812,#307,.T.);
+#2228=EDGE_CURVE('',#1814,#1812,#318,.T.);
+#2238=EDGE_CURVE('',#1792,#1774,#322,.T.);
+#2240=EDGE_CURVE('',#1792,#1746,#326,.T.);
+#2242=EDGE_CURVE('',#1746,#1738,#507,.T.);
+#2256=EDGE_CURVE('',#1740,#1794,#334,.T.);
+#2266=EDGE_CURVE('',#1809,#1808,#351,.T.);
+#2268=EDGE_CURVE('',#1778,#1809,#413,.T.);
+#2271=EDGE_CURVE('',#1777,#1792,#447,.T.);
+#2275=EDGE_CURVE('',#1772,#1808,#355,.T.);
+#2287=EDGE_CURVE('',#1799,#1802,#378,.T.);
+#2291=EDGE_CURVE('',#1812,#1811,#389,.T.);
+#2293=EDGE_CURVE('',#1811,#1810,#399,.T.);
+#2295=EDGE_CURVE('',#1810,#1809,#409,.T.);
+#2298=EDGE_CURVE('',#1808,#1807,#419,.T.);
+#2300=EDGE_CURVE('',#1807,#1806,#429,.T.);
+#2302=EDGE_CURVE('',#1806,#1805,#439,.T.);
+#2312=EDGE_CURVE('',#1799,#1800,#368,.T.);
+#2314=EDGE_CURVE('',#1802,#1800,#373,.T.);
+#2339=EDGE_CURVE('',#1782,#1811,#393,.T.);
+#2352=EDGE_CURVE('',#1780,#1810,#403,.T.);
+#2377=EDGE_CURVE('',#1770,#1807,#423,.T.);
+#2390=EDGE_CURVE('',#1768,#1806,#433,.T.);
+#2414=EDGE_CURVE('',#1745,#1746,#511,.T.);
+#2438=EDGE_CURVE('',#1748,#1750,#463,.T.);
+#2440=EDGE_CURVE('',#1736,#1748,#467,.T.);
+#2443=EDGE_CURVE('',#1698,#1700,#475,.T.);
+#2445=EDGE_CURVE('',#1586,#1698,#479,.T.);
+#2447=EDGE_CURVE('',#1585,#1586,#483,.T.);
+#2449=EDGE_CURVE('',#1590,#1585,#487,.T.);
+#2451=EDGE_CURVE('',#1589,#1590,#491,.T.);
+#2453=EDGE_CURVE('',#1704,#1589,#495,.T.);
+#2455=EDGE_CURVE('',#1703,#1704,#499,.T.);
+#2461=EDGE_CURVE('',#1921,#1922,#519,.T.);
+#2463=EDGE_CURVE('',#1922,#1760,#523,.T.);
+#2465=EDGE_CURVE('',#1760,#1756,#527,.T.);
+#2469=EDGE_CURVE('',#1667,#1668,#531,.T.);
+#2471=EDGE_CURVE('',#1668,#1659,#831,.T.);
+#2473=EDGE_CURVE('',#1659,#1660,#843,.T.);
+#2475=EDGE_CURVE('',#1660,#1667,#455,.T.);
+#2486=EDGE_CURVE('',#1667,#1671,#535,.T.);
+#2488=EDGE_CURVE('',#1671,#1672,#539,.T.);
+#2490=EDGE_CURVE('',#1668,#1672,#543,.T.);
+#2500=EDGE_CURVE('',#1619,#1664,#551,.T.);
+#2502=EDGE_CURVE('',#1619,#1620,#615,.T.);
+#2504=EDGE_CURVE('',#1671,#1620,#683,.T.);
+#2508=EDGE_CURVE('',#1660,#1664,#851,.T.);
+#2518=EDGE_CURVE('',#1612,#1684,#563,.T.);
+#2520=EDGE_CURVE('',#1684,#1688,#871,.T.);
+#2522=EDGE_CURVE('',#1688,#1624,#547,.T.);
+#2524=EDGE_CURVE('',#1624,#1619,#619,.T.);
+#2527=EDGE_CURVE('',#1663,#1664,#847,.T.);
+#2529=EDGE_CURVE('',#1663,#1726,#555,.T.);
+#2531=EDGE_CURVE('',#1726,#1612,#707,.T.);
+#2541=EDGE_CURVE('',#1630,#1683,#559,.T.);
+#2543=EDGE_CURVE('',#1683,#1684,#859,.T.);
+#2546=EDGE_CURVE('',#1612,#1630,#583,.T.);
+#2556=EDGE_CURVE('',#1675,#1676,#667,.T.);
+#2558=EDGE_CURVE('',#1632,#1675,#567,.T.);
+#2560=EDGE_CURVE('',#1632,#1609,#595,.T.);
+#2562=EDGE_CURVE('',#1676,#1609,#571,.T.);
+#2572=EDGE_CURVE('',#1630,#1627,#579,.T.);
+#2574=EDGE_CURVE('',#1627,#1623,#627,.T.);
+#2576=EDGE_CURVE('',#1687,#1623,#855,.T.);
+#2578=EDGE_CURVE('',#1683,#1687,#863,.T.);
+#2589=EDGE_CURVE('',#1627,#1628,#575,.T.);
+#2593=EDGE_CURVE('',#1612,#1600,#587,.T.);
+#2595=EDGE_CURVE('',#1600,#1598,#773,.T.);
+#2598=EDGE_CURVE('',#1602,#1597,#753,.T.);
+#2600=EDGE_CURVE('',#1602,#1609,#591,.T.);
+#2603=EDGE_CURVE('',#1632,#1628,#599,.T.);
+#2614=EDGE_CURVE('',#1616,#1628,#603,.T.);
+#2616=EDGE_CURVE('',#1615,#1616,#607,.T.);
+#2618=EDGE_CURVE('',#1620,#1615,#611,.T.);
+#2622=EDGE_CURVE('',#1623,#1624,#623,.T.);
+#2627=EDGE_CURVE('',#1560,#1562,#631,.T.);
+#2629=EDGE_CURVE('',#1560,#1564,#635,.T.);
+#2631=EDGE_CURVE('',#1564,#1566,#639,.T.);
+#2633=EDGE_CURVE('',#1562,#1566,#643,.T.);
+#2637=EDGE_CURVE('',#1941,#1942,#647,.T.);
+#2639=EDGE_CURVE('',#1941,#1945,#651,.T.);
+#2641=EDGE_CURVE('',#1945,#1946,#655,.T.);
+#2643=EDGE_CURVE('',#1942,#1946,#659,.T.);
+#2655=EDGE_CURVE('',#1675,#1679,#679,.T.);
+#2657=EDGE_CURVE('',#1679,#1616,#663,.T.);
+#2669=EDGE_CURVE('',#1676,#1680,#671,.T.);
+#2671=EDGE_CURVE('',#1679,#1680,#675,.T.);
+#2684=EDGE_CURVE('',#1680,#1615,#687,.T.);
+#2688=EDGE_CURVE('',#1609,#1610,#699,.T.);
+#2690=EDGE_CURVE('',#1672,#1610,#815,.T.);
+#2713=EDGE_CURVE('',#1635,#1636,#691,.T.);
+#2715=EDGE_CURVE('',#1610,#1635,#695,.T.);
+#2719=EDGE_CURVE('',#1602,#1594,#749,.T.);
+#2721=EDGE_CURVE('',#1594,#1636,#719,.T.);
+#2731=EDGE_CURVE('',#1639,#1640,#703,.T.);
+#2733=EDGE_CURVE('',#1640,#1592,#727,.T.);
+#2735=EDGE_CURVE('',#1600,#1592,#777,.T.);
+#2739=EDGE_CURVE('',#1639,#1726,#711,.T.);
+#2750=EDGE_CURVE('',#1636,#1644,#715,.T.);
+#2752=EDGE_CURVE('',#1643,#1644,#819,.T.);
+#2754=EDGE_CURVE('',#1635,#1643,#793,.T.);
+#2766=EDGE_CURVE('',#1594,#1590,#723,.T.);
+#2769=EDGE_CURVE('',#1592,#1585,#769,.T.);
+#2772=EDGE_CURVE('',#1640,#1656,#731,.T.);
+#2774=EDGE_CURVE('',#1652,#1656,#736,.T.);
+#2776=EDGE_CURVE('',#1648,#1652,#740,.T.);
+#2778=EDGE_CURVE('',#1644,#1648,#745,.T.);
+#2792=EDGE_CURVE('',#1606,#1589,#757,.T.);
+#2804=EDGE_CURVE('',#1696,#1704,#761,.T.);
+#2830=EDGE_CURVE('',#1604,#1586,#781,.T.);
+#2846=EDGE_CURVE('',#1690,#1698,#785,.T.);
+#2869=EDGE_CURVE('',#1639,#1655,#811,.T.);
+#2871=EDGE_CURVE('',#1655,#1656,#789,.T.);
+#2883=EDGE_CURVE('',#1643,#1647,#798,.T.);
+#2885=EDGE_CURVE('',#1647,#1651,#802,.T.);
+#2887=EDGE_CURVE('',#1651,#1655,#807,.T.);
+#2892=EDGE_CURVE('',#1672,#1663,#835,.T.);
+#2906=EDGE_CURVE('',#1647,#1648,#823,.T.);
+#2919=EDGE_CURVE('',#1651,#1652,#827,.T.);
+#2945=EDGE_CURVE('',#1659,#1663,#839,.T.);
+#2967=EDGE_CURVE('',#1687,#1688,#867,.T.);
+#2992=EDGE_CURVE('',#1546,#1560,#875,.T.);
+#2995=EDGE_CURVE('',#1562,#1545,#879,.T.);
+#2997=EDGE_CURVE('',#1545,#1546,#883,.T.);
+#3007=EDGE_CURVE('',#1568,#1549,#887,.T.);
+#3009=EDGE_CURVE('',#1549,#1550,#891,.T.);
+#3011=EDGE_CURVE('',#1550,#1578,#895,.T.);
+#3013=EDGE_CURVE('',#1568,#1578,#1045,.T.);
+#3024=EDGE_CURVE('',#1546,#1554,#899,.T.);
+#3026=EDGE_CURVE('',#1554,#1564,#907,.T.);
+#3038=EDGE_CURVE('',#1545,#1553,#903,.T.);
+#3040=EDGE_CURVE('',#1553,#1554,#911,.T.);
+#3053=EDGE_CURVE('',#1566,#1553,#915,.T.);
+#3076=EDGE_CURVE('',#1570,#1557,#919,.T.);
+#3078=EDGE_CURVE('',#1570,#1572,#1037,.T.);
+#3080=EDGE_CURVE('',#1558,#1572,#923,.T.);
+#3082=EDGE_CURVE('',#1557,#1558,#927,.T.);
+#3093=EDGE_CURVE('',#1568,#1581,#940,.T.);
+#3095=EDGE_CURVE('',#1581,#1582,#931,.T.);
+#3097=EDGE_CURVE('',#1570,#1582,#1250,.T.);
+#3100=EDGE_CURVE('',#1549,#1557,#1275,.T.);
+#3110=EDGE_CURVE('',#1581,#1719,#936,.T.);
+#3113=EDGE_CURVE('',#1917,#1568,#1041,.T.);
+#3115=EDGE_CURVE('',#1917,#1719,#961,.T.);
+#3125=EDGE_CURVE('',#1578,#1575,#944,.T.);
+#3127=EDGE_CURVE('',#1575,#1723,#949,.T.);
+#3129=EDGE_CURVE('',#1723,#1910,#1001,.T.);
+#3131=EDGE_CURVE('',#1910,#1893,#953,.T.);
+#3133=EDGE_CURVE('',#1893,#1894,#1209,.T.);
+#3135=EDGE_CURVE('',#1578,#1894,#1049,.T.);
+#3147=EDGE_CURVE('',#1719,#1720,#957,.T.);
+#3149=EDGE_CURVE('',#1582,#1720,#1242,.T.);
+#3161=EDGE_CURVE('',#1917,#1918,#965,.T.);
+#3163=EDGE_CURVE('',#1918,#1720,#969,.T.);
+#3173=EDGE_CURVE('',#1957,#1958,#973,.T.);
+#3175=EDGE_CURVE('',#1913,#1957,#977,.T.);
+#3177=EDGE_CURVE('',#1913,#1914,#981,.T.);
+#3179=EDGE_CURVE('',#1914,#1958,#985,.T.);
+#3189=EDGE_CURVE('',#1961,#1962,#989,.T.);
+#3191=EDGE_CURVE('',#1962,#1909,#993,.T.);
+#3193=EDGE_CURVE('',#1909,#1910,#997,.T.);
+#3196=EDGE_CURVE('',#1723,#1724,#1005,.T.);
+#3198=EDGE_CURVE('',#1724,#1902,#1009,.T.);
+#3200=EDGE_CURVE('',#1901,#1902,#1013,.T.);
+#3202=EDGE_CURVE('',#1901,#1708,#1225,.T.);
+#3204=EDGE_CURVE('',#1707,#1708,#1279,.T.);
+#3206=EDGE_CURVE('',#1905,#1707,#1287,.T.);
+#3208=EDGE_CURVE('',#1905,#1906,#1017,.T.);
+#3210=EDGE_CURVE('',#1961,#1906,#1021,.T.);
+#3220=EDGE_CURVE('',#1949,#1950,#1025,.T.);
+#3222=EDGE_CURVE('',#1950,#1888,#1029,.T.);
+#3224=EDGE_CURVE('',#1888,#1890,#1201,.T.);
+#3226=EDGE_CURVE('',#1711,#1890,#1283,.T.);
+#3229=EDGE_CURVE('',#1715,#1878,#1033,.T.);
+#3231=EDGE_CURVE('',#1878,#1880,#1189,.T.);
+#3233=EDGE_CURVE('',#1572,#1880,#1258,.T.);
+#3236=EDGE_CURVE('',#1918,#1570,#1246,.T.);
+#3242=EDGE_CURVE('',#1894,#1896,#1221,.T.);
+#3244=EDGE_CURVE('',#1954,#1896,#1160,.T.);
+#3246=EDGE_CURVE('',#1953,#1954,#1053,.T.);
+#3248=EDGE_CURVE('',#1914,#1953,#1148,.T.);
+#3251=EDGE_CURVE('',#1913,#1949,#1057,.T.);
+#3262=EDGE_CURVE('',#1942,#1925,#1061,.T.);
+#3264=EDGE_CURVE('',#1925,#1926,#1065,.T.);
+#3266=EDGE_CURVE('',#1926,#1941,#1069,.T.);
+#3277=EDGE_CURVE('',#1949,#1929,#1073,.T.);
+#3279=EDGE_CURVE('',#1929,#1930,#1077,.T.);
+#3281=EDGE_CURVE('',#1930,#1950,#1081,.T.);
+#3292=EDGE_CURVE('',#1946,#1933,#1093,.T.);
+#3294=EDGE_CURVE('',#1925,#1933,#1113,.T.);
+#3306=EDGE_CURVE('',#1934,#1945,#1085,.T.);
+#3308=EDGE_CURVE('',#1933,#1934,#1089,.T.);
+#3320=EDGE_CURVE('',#1938,#1954,#1097,.T.);
+#3322=EDGE_CURVE('',#1937,#1938,#1101,.T.);
+#3324=EDGE_CURVE('',#1953,#1937,#1105,.T.);
+#3336=EDGE_CURVE('',#1926,#1934,#1109,.T.);
+#3359=EDGE_CURVE('',#1950,#1966,#1130,.T.);
+#3362=EDGE_CURVE('',#1930,#1938,#1117,.T.);
+#3365=EDGE_CURVE('',#1954,#1970,#1156,.T.);
+#3367=EDGE_CURVE('',#1966,#1970,#1303,.T.);
+#3377=EDGE_CURVE('',#1964,#1957,#1122,.T.);
+#3379=EDGE_CURVE('',#1949,#1964,#1126,.T.);
+#3392=EDGE_CURVE('',#1966,#1961,#1135,.T.);
+#3395=EDGE_CURVE('',#1906,#1886,#1139,.T.);
+#3397=EDGE_CURVE('',#1886,#1888,#1205,.T.);
+#3409=EDGE_CURVE('',#1968,#1958,#1144,.T.);
+#3411=EDGE_CURVE('',#1964,#1968,#1169,.T.);
+#3425=EDGE_CURVE('',#1953,#1968,#1152,.T.);
+#3437=EDGE_CURVE('',#1896,#1898,#1217,.T.);
+#3439=EDGE_CURVE('',#1909,#1898,#1299,.T.);
+#3442=EDGE_CURVE('',#1970,#1962,#1165,.T.);
+#3456=EDGE_CURVE('',#1929,#1937,#1173,.T.);
+#3479=EDGE_CURVE('',#1877,#1878,#1177,.T.);
+#3481=EDGE_CURVE('',#1882,#1877,#1181,.T.);
+#3483=EDGE_CURVE('',#1880,#1882,#1185,.T.);
+#3494=EDGE_CURVE('',#1885,#1886,#1193,.T.);
+#3496=EDGE_CURVE('',#1890,#1885,#1197,.T.);
+#3509=EDGE_CURVE('',#1898,#1893,#1213,.T.);
+#3522=EDGE_CURVE('',#1901,#1877,#1229,.T.);
+#3527=EDGE_CURVE('',#1708,#1716,#1233,.T.);
+#3538=EDGE_CURVE('',#1902,#1882,#1237,.T.);
+#3562=EDGE_CURVE('',#1572,#1576,#1254,.T.);
+#3568=EDGE_CURVE('',#1576,#1724,#1263,.T.);
+#3580=EDGE_CURVE('',#1575,#1576,#1271,.T.);
+#3584=EDGE_CURVE('',#1550,#1558,#1267,.T.);
+#3619=EDGE_CURVE('',#1707,#1712,#1291,.T.);
+#3633=EDGE_CURVE('',#1905,#1885,#1295,.T.);
+#3682=EDGE_CURVE('',#1848,#1833,#1307,.T.);
+#3684=EDGE_CURVE('',#1848,#1748,#1311,.T.);
+#3696=EDGE_CURVE('',#1851,#1842,#1315,.T.);
+#3700=EDGE_CURVE('',#1742,#1851,#1319,.T.);
+#3710=EDGE_CURVE('',#1865,#1864,#1336,.T.);
+#3712=EDGE_CURVE('',#1828,#1864,#1340,.T.);
+#3715=EDGE_CURVE('',#1830,#1848,#1458,.T.);
+#3719=EDGE_CURVE('',#1834,#1865,#1454,.T.);
+#3729=EDGE_CURVE('',#1861,#1859,#1374,.T.);
+#3731=EDGE_CURVE('',#1862,#1861,#1498,.T.);
+#3733=EDGE_CURVE('',#1863,#1862,#1508,.T.);
+#3735=EDGE_CURVE('',#1864,#1863,#1518,.T.);
+#3738=EDGE_CURVE('',#1866,#1865,#1450,.T.);
+#3740=EDGE_CURVE('',#1867,#1866,#1440,.T.);
+#3742=EDGE_CURVE('',#1868,#1867,#1430,.T.);
+#3744=EDGE_CURVE('',#1870,#1868,#1420,.T.);
+#3746=EDGE_CURVE('',#1870,#1858,#1344,.T.);
+#3748=EDGE_CURVE('',#1855,#1858,#1389,.T.);
+#3750=EDGE_CURVE('',#1859,#1855,#1348,.T.);
+#3760=EDGE_CURVE('',#1859,#1860,#1361,.T.);
+#3762=EDGE_CURVE('',#1821,#1860,#1365,.T.);
+#3765=EDGE_CURVE('',#1822,#1861,#1502,.T.);
+#3778=EDGE_CURVE('',#1855,#1858,#1379,.T.);
+#3781=EDGE_CURVE('',#1869,#1870,#1409,.T.);
+#3783=EDGE_CURVE('',#1871,#1869,#1468,.T.);
+#3785=EDGE_CURVE('',#1872,#1871,#1478,.T.);
+#3787=EDGE_CURVE('',#1860,#1872,#1488,.T.);
+#3797=EDGE_CURVE('',#1855,#1856,#1384,.T.);
+#3799=EDGE_CURVE('',#1858,#1856,#1394,.T.);
+#3823=EDGE_CURVE('',#1840,#1868,#1424,.T.);
+#3827=EDGE_CURVE('',#1851,#1852,#1462,.T.);
+#3830=EDGE_CURVE('',#1874,#1869,#1472,.T.);
+#3841=EDGE_CURVE('',#1838,#1867,#1434,.T.);
+#3854=EDGE_CURVE('',#1836,#1866,#1444,.T.);
+#3905=EDGE_CURVE('',#1845,#1871,#1482,.T.);
+#3918=EDGE_CURVE('',#1846,#1872,#1492,.T.);
+#3943=EDGE_CURVE('',#1824,#1862,#1512,.T.);
+#3956=EDGE_CURVE('',#1826,#1863,#1522,.T.);
+#3978=EDGE_CURVE('',#1762,#1753,#1534,.T.);
+#3980=EDGE_CURVE('',#1922,#1753,#1542,.T.);
+#3992=EDGE_CURVE('',#1754,#1758,#1526,.T.);
+#3994=EDGE_CURVE('',#1753,#1754,#1530,.T.);
+#4009=EDGE_CURVE('',#1760,#1754,#1538,.T.);
+#4036=TRIMMED_CURVE('A_13',#4035,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE
+(1.E0)),.T.,.UNSPECIFIED.);
+#4042=TRIMMED_CURVE('A_14',#4041,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE
+(1.E0)),.T.,.UNSPECIFIED.);
+#4047=CIRCLE('',#4046,2.15E-2);
+#4048=TRIMMED_CURVE('',#4047,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4049=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4048);
+#4054=CIRCLE('',#4053,2.15E-2);
+#4055=TRIMMED_CURVE('',#4054,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4056=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4055);
+#4057=COMPOSITE_CURVE('',(#4049,#4056),.F.);
+#4062=CIRCLE('',#4061,2.15E-2);
+#4063=TRIMMED_CURVE('',#4062,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4064=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4063);
+#4069=CIRCLE('',#4068,2.15E-2);
+#4070=TRIMMED_CURVE('',#4069,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4071=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4070);
+#4072=COMPOSITE_CURVE('',(#4064,#4071),.F.);
+#4081=CIRCLE('',#4080,6.7E-2);
+#4082=TRIMMED_CURVE('',#4081,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4083=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4082);
+#4088=CIRCLE('',#4087,6.7E-2);
+#4089=TRIMMED_CURVE('',#4088,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4090=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4089);
+#4091=COMPOSITE_CURVE('',(#4083,#4090),.F.);
+#4096=CIRCLE('',#4095,6.7E-2);
+#4097=TRIMMED_CURVE('',#4096,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4098=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4097);
+#4103=CIRCLE('',#4102,6.7E-2);
+#4104=TRIMMED_CURVE('',#4103,(PARAMETER_VALUE(0.E0)),(PARAMETER_VALUE(1.8E2)),
+.T.,.UNSPECIFIED.);
+#4105=COMPOSITE_CURVE_SEGMENT(.CONTINUOUS.,.T.,#4104);
+#4106=COMPOSITE_CURVE('',(#4098,#4105),.F.);
+#4108=(LENGTH_UNIT()NAMED_UNIT(*)SI_UNIT(.MILLI.,.METRE.));
+#4112=(NAMED_UNIT(*)PLANE_ANGLE_UNIT()SI_UNIT($,.RADIAN.));
+#4115=(NAMED_UNIT(*)SI_UNIT($,.STERADIAN.)SOLID_ANGLE_UNIT());
+#4118=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#4027),#4117);
+#4119=GEOMETRICALLY_BOUNDED_SURFACE_SHAPE_REPRESENTATION('',(#4037),#4117);
+#4120=SHAPE_REPRESENTATION('',(#4031,#4076),#4117);
+#4129=PRODUCT_DEFINITION('design','',#4128,#4125);
+#4130=PRODUCT_DEFINITION_SHAPE('','SHAPE FOR 705550036.',#4129);
+#4131=SHAPE_DEFINITION_REPRESENTATION(#4130,#4120);
+ENDSEC;
+END-ISO-10303-21;
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_pcb b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_pcb
new file mode 100644
index 0000000..abc1fbf
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_pcb
@@ -0,0 +1,287 @@
+(kicad_pcb (version 20211014) (generator pcbnew)
+
+ (general
+ (thickness 1.6)
+ )
+
+ (paper "A4")
+ (layers
+ (0 "F.Cu" signal)
+ (31 "B.Cu" signal)
+ (32 "B.Adhes" user "B.Adhesive")
+ (33 "F.Adhes" user "F.Adhesive")
+ (34 "B.Paste" user)
+ (35 "F.Paste" user)
+ (36 "B.SilkS" user "B.Silkscreen")
+ (37 "F.SilkS" user "F.Silkscreen")
+ (38 "B.Mask" user)
+ (39 "F.Mask" user)
+ (40 "Dwgs.User" user "User.Drawings")
+ (41 "Cmts.User" user "User.Comments")
+ (42 "Eco1.User" user "User.Eco1")
+ (43 "Eco2.User" user "User.Eco2")
+ (44 "Edge.Cuts" user)
+ (45 "Margin" user)
+ (46 "B.CrtYd" user "B.Courtyard")
+ (47 "F.CrtYd" user "F.Courtyard")
+ (48 "B.Fab" user)
+ (49 "F.Fab" user)
+ (50 "User.1" user)
+ (51 "User.2" user)
+ (52 "User.3" user)
+ (53 "User.4" user)
+ (54 "User.5" user)
+ (55 "User.6" user)
+ (56 "User.7" user)
+ (57 "User.8" user)
+ (58 "User.9" user)
+ )
+
+ (setup
+ (pad_to_mask_clearance 0)
+ (grid_origin 160.528 112.395)
+ (pcbplotparams
+ (layerselection 0x00010fc_ffffffff)
+ (disableapertmacros false)
+ (usegerberextensions false)
+ (usegerberattributes true)
+ (usegerberadvancedattributes true)
+ (creategerberjobfile true)
+ (svguseinch false)
+ (svgprecision 6)
+ (excludeedgelayer true)
+ (plotframeref false)
+ (viasonmask false)
+ (mode 1)
+ (useauxorigin false)
+ (hpglpennumber 1)
+ (hpglpenspeed 20)
+ (hpglpendiameter 15.000000)
+ (dxfpolygonmode true)
+ (dxfimperialunits true)
+ (dxfusepcbnewfont true)
+ (psnegative false)
+ (psa4output false)
+ (plotreference false)
+ (plotvalue false)
+ (plotinvisibletext false)
+ (sketchpadsonfab false)
+ (subtractmaskfromsilk false)
+ (outputformat 1)
+ (mirror false)
+ (drillshape 0)
+ (scaleselection 1)
+ (outputdirectory "")
+ )
+ )
+
+ (net 0 "")
+ (net 1 "/CAN_H")
+ (net 2 "/CAN_L")
+
+ (footprint "CAN_terminator:70555-0036" (layer "F.Cu")
+ (tedit 0) (tstamp 8d0d01c9-18ba-497b-83f4-d68b8bd509e6)
+ (at 147.32 116.84 90)
+ (property "Sheetfile" "CAN_terminator.kicad_sch")
+ (property "Sheetname" "")
+ (path "/e8bffcfe-c870-4593-8a2b-7a667dd6382d")
+ (attr through_hole)
+ (fp_text reference "J1" (at -1.143 1.905 90) (layer "F.SilkS")
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp e04bb6f0-926f-4f5c-9e46-22f411f9535c)
+ )
+ (fp_text value "2 pin molex sl right angle" (at 0 -2.54 90) (layer "F.SilkS") hide
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp 0e178f07-4799-45e2-9612-a0b4b941366e)
+ )
+ (fp_text user "Copyright 2021 Accelerated Designs. All rights reserved." (at 0 0 90) (layer "Cmts.User")
+ (effects (font (size 0.127 0.127) (thickness 0.002)))
+ (tstamp b7886193-911b-4762-81a8-f396711fb4d1)
+ )
+ (fp_text user "*" (at 0 0 90) (layer "F.Fab")
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp 9d02aef6-b68e-47d5-ba6c-779a67d416ad)
+ )
+ (fp_line (start -5.7277 9.61133) (end -5.7277 13.066001) (layer "F.SilkS") (width 0.12) (tstamp 4ab750dd-e9a2-4fcc-a78a-174239fb8039))
+ (fp_line (start 3.1877 7.668671) (end 3.1877 -0.777) (layer "F.SilkS") (width 0.12) (tstamp 7739ac46-ca6b-43f5-9b39-883e29a2155f))
+ (fp_line (start -3.311185 -0.777) (end -5.7277 -0.777) (layer "F.SilkS") (width 0.12) (tstamp 8431235c-55eb-4742-9f6c-053041d297ad))
+ (fp_line (start -5.7277 -0.777) (end -5.7277 7.668671) (layer "F.SilkS") (width 0.12) (tstamp a699b636-e2e1-419a-a51e-ec6d6cc2fb70))
+ (fp_line (start 3.1877 13.066001) (end 3.1877 9.61133) (layer "F.SilkS") (width 0.12) (tstamp b3c7ca3a-97ef-43ae-96a9-28f4a4a5efc7))
+ (fp_line (start 3.1877 -0.777) (end 1.27 -0.777) (layer "F.SilkS") (width 0.12) (tstamp c7a3db25-81fe-4cc3-be0d-441c41ab85f4))
+ (fp_line (start -5.7277 13.066001) (end 3.1877 13.066001) (layer "F.SilkS") (width 0.12) (tstamp cf71b566-4811-4b0a-bab1-be46bd03652d))
+ (fp_line (start -14.9128 -0.396) (end -14.6588 -0.396) (layer "Cmts.User") (width 0.1) (tstamp 0451fd0c-c650-439a-a514-37d31ed2b220))
+ (fp_line (start -3.94 -8.636) (end 1.4 -8.636) (layer "Cmts.User") (width 0.1) (tstamp 0ab5104b-77cb-4bdc-9fe1-5eb13feace23))
+ (fp_line (start 5.4737 0.254) (end 5.7277 0.254) (layer "Cmts.User") (width 0.1) (tstamp 0b382ed5-eceb-4891-b0ae-2068b4d7f5e1))
+ (fp_line (start 3.0607 -11.176) (end 2.8067 -11.049) (layer "Cmts.User") (width 0.1) (tstamp 149b12b2-7d13-4fd4-b885-0bf4f6d5816c))
+ (fp_line (start 2.8067 -11.303) (end 2.8067 -11.049) (layer "Cmts.User") (width 0.1) (tstamp 1525e3c9-4ba2-4762-8905-9730daf4d016))
+ (fp_line (start 0 -5.461) (end 0.254 -5.334) (layer "Cmts.User") (width 0.1) (tstamp 19385824-3683-4bf5-aa2c-b0663eb4e603))
+ (fp_line (start -16.1828 -0.904) (end -15.9288 -0.904) (layer "Cmts.User") (width 0.1) (tstamp 1a197982-fa33-491a-8bbd-e38e27f0e74c))
+ (fp_line (start -16.0558 -0.65) (end -15.9288 -0.904) (layer "Cmts.User") (width 0.1) (tstamp 1a7425cb-8b4a-465a-bbac-f2220b58e655))
+ (fp_line (start -2.54 0) (end -2.54 -5.842) (layer "Cmts.User") (width 0.1) (tstamp 1b27a54e-6eee-44be-a486-5b10429360e9))
+ (fp_line (start 3.0607 -11.176) (end 2.8067 -11.303) (layer "Cmts.User") (width 0.1) (tstamp 20296511-e13c-449f-a421-fed6648b0759))
+ (fp_line (start 0 0) (end 5.9817 0) (layer "Cmts.User") (width 0.1) (tstamp 2a9908b3-69b3-4e9f-bb66-cfc673dbb1bc))
+ (fp_line (start -14.7858 12.939001) (end -14.6588 12.685001) (layer "Cmts.User") (width 0.1) (tstamp 2af10347-00c0-460e-9a95-217fa19788ce))
+ (fp_line (start -5.3467 -11.303) (end -5.3467 -11.049) (layer "Cmts.User") (width 0.1) (tstamp 2c09ffe8-cd48-46fd-b279-8c43d1a6bc1b))
+ (fp_line (start -16.0558 -0.65) (end -16.0558 -1.92) (layer "Cmts.User") (width 0.1) (tstamp 2cbccbfe-f0fd-4064-ab82-53a269f8b338))
+ (fp_line (start -7.9278 8.386001) (end -7.6738 8.386001) (layer "Cmts.User") (width 0.1) (tstamp 2e15f00e-83dd-478e-a7dd-98bc5a390430))
+ (fp_line (start 1.146 -8.763) (end 1.146 -8.509) (layer "Cmts.User") (width 0.1) (tstamp 37dcbdbc-b212-40eb-9fc4-bcbcf625f395))
+ (fp_line (start 1.4 8.640001) (end -8.1818 8.640001) (layer "Cmts.User") (width 0.1) (tstamp 3af25191-67ed-46b3-82d1-adfcab706aaa))
+ (fp_line (start -16.0558 0) (end -16.0558 1.27) (layer "Cmts.User") (width 0.1) (tstamp 3f2586e8-9966-47b8-90fc-5338ea9bd680))
+ (fp_line (start 5.6007 0) (end 5.4737 0.254) (layer "Cmts.User") (width 0.1) (tstamp 443e0bce-b507-44e0-bad1-6a0250193bc5))
+ (fp_line (start 5.6007 0) (end 5.6007 -1.27) (layer "Cmts.User") (width 0.1) (tstamp 4a968524-8474-465c-9a5e-e6f0f0ae44e9))
+ (fp_line (start -3.94 -8.636) (end -3.686 -8.763) (layer "Cmts.User") (width 0.1) (tstamp 4b0edace-c31a-473a-9307-1dd08e5e2a35))
+ (fp_line (start -5.6007 -0.65) (end -5.6007 -11.557) (layer "Cmts.User") (width 0.1) (tstamp 4d0b16bf-25fa-49af-8193-eee0d14d2b15))
+ (fp_line (start -14.9128 12.685001) (end -14.6588 12.685001) (layer "Cmts.User") (width 0.1) (tstamp 5ee6d8a4-1517-45f7-af0a-27aee9b9af74))
+ (fp_line (start 5.6007 0) (end 5.4737 -0.254) (layer "Cmts.User") (width 0.1) (tstamp 606c94e7-bc9c-4062-8758-a5431e41fdcb))
+ (fp_line (start -3.94 8.640001) (end -3.94 -9.017) (layer "Cmts.User") (width 0.1) (tstamp 66cc15ed-ab47-4158-aa71-ff8ebbf1711d))
+ (fp_line (start -7.9278 0.254) (end -7.6738 0.254) (layer "Cmts.User") (width 0.1) (tstamp 695dac98-0b16-4aa5-beca-21b55678f616))
+ (fp_line (start -5.6007 12.939001) (end -15.1668 12.939001) (layer "Cmts.User") (width 0.1) (tstamp 6d9a61e0-6d76-41bb-9649-b956b6fc3b34))
+ (fp_line (start -3.686 -8.763) (end -3.686 -8.509) (layer "Cmts.User") (width 0.1) (tstamp 71935ac6-1b80-42e9-848a-db3816287512))
+ (fp_line (start -5.6007 -11.176) (end -5.3467 -11.303) (layer "Cmts.User") (width 0.1) (tstamp 7201ebac-e2c0-4c7c-9fdc-e55b4cef1d76))
+ (fp_line (start -14.7858 -0.65) (end -14.7858 12.939001) (layer "Cmts.User") (width 0.1) (tstamp 83db32c0-f45c-456a-9ff7-62f7984db177))
+ (fp_line (start -2.54 -5.461) (end -2.794 -5.588) (layer "Cmts.User") (width 0.1) (tstamp 84ba8161-4339-4d7f-8366-f8f208e418a1))
+ (fp_line (start 5.6007 0) (end 5.6007 1.27) (layer "Cmts.User") (width 0.1) (tstamp 86728636-2d6b-4848-a8db-979cc5166ab5))
+ (fp_line (start -16.1828 0.254) (end -15.9288 0.254) (layer "Cmts.User") (width 0.1) (tstamp 88fe52dd-a3a7-42e2-b218-323cdae7baf6))
+ (fp_line (start 0.254 -5.588) (end 0.254 -5.334) (layer "Cmts.User") (width 0.1) (tstamp 8ce07e24-ce84-43c0-8aa1-98a5246b9ff4))
+ (fp_line (start 1.4 -8.636) (end 1.146 -8.509) (layer "Cmts.User") (width 0.1) (tstamp 91f9bcec-1b8d-4028-8714-4880368192f9))
+ (fp_line (start -5.6007 -11.176) (end -5.3467 -11.049) (layer "Cmts.User") (width 0.1) (tstamp 93bbfea5-84d7-4c47-860f-066cc90edbe3))
+ (fp_line (start -2.54 -5.461) (end -2.794 -5.334) (layer "Cmts.User") (width 0.1) (tstamp 95178b0f-9dc6-4d63-9357-cb46aa41867f))
+ (fp_line (start -1.27 0) (end -16.4368 0) (layer "Cmts.User") (width 0.1) (tstamp 952ba59a-449a-461b-ac1e-95e029b26601))
+ (fp_line (start -14.7858 12.939001) (end -14.9128 12.685001) (layer "Cmts.User") (width 0.1) (tstamp 994f91d0-f4d8-4f9d-b7c3-657bc14e66ca))
+ (fp_line (start -5.6007 -0.65) (end -16.4368 -0.65) (layer "Cmts.User") (width 0.1) (tstamp 9a6da229-63be-4997-a15c-94513be14718))
+ (fp_line (start -16.0558 0) (end -15.9288 0.254) (layer "Cmts.User") (width 0.1) (tstamp 9b7de360-5ffe-4798-ad3b-5c774ab09176))
+ (fp_line (start 5.6007 0) (end 5.7277 0.254) (layer "Cmts.User") (width 0.1) (tstamp 9d716695-cc63-411b-8c3f-3e9efdf5554c))
+ (fp_line (start 3.0607 -0.65) (end 3.0607 -11.557) (layer "Cmts.User") (width 0.1) (tstamp a1cd2766-582d-43b1-92a0-8a69b3c6a4e8))
+ (fp_line (start -7.8008 0) (end -7.6738 0.254) (layer "Cmts.User") (width 0.1) (tstamp a2099b73-8ad2-414e-a610-edbd301dcbcd))
+ (fp_line (start 1.4 -8.636) (end 1.146 -8.763) (layer "Cmts.User") (width 0.1) (tstamp a3d9ecc5-e32a-420c-8d8c-b3d489ba1d05))
+ (fp_line (start -14.7858 -0.65) (end -14.6588 -0.396) (layer "Cmts.User") (width 0.1) (tstamp a3f4caaf-0cef-4abe-ab87-a5bd56dfb401))
+ (fp_line (start -14.7858 -0.65) (end -14.9128 -0.396) (layer "Cmts.User") (width 0.1) (tstamp af588402-66b9-434f-bf75-00a638f94319))
+ (fp_line (start -5.6007 -11.176) (end 3.0607 -11.176) (layer "Cmts.User") (width 0.1) (tstamp b11b9fad-dd0a-4158-8fc4-5ed38ce5205b))
+ (fp_line (start -7.8008 8.640001) (end -7.9278 8.386001) (layer "Cmts.User") (width 0.1) (tstamp b5105f05-a85c-49d6-94bf-5713a472283a))
+ (fp_line (start 0 -5.461) (end 0.254 -5.588) (layer "Cmts.User") (width 0.1) (tstamp be096b2f-1286-4288-a881-759501c0cf77))
+ (fp_line (start -3.94 -8.636) (end -3.686 -8.509) (layer "Cmts.User") (width 0.1) (tstamp be1d98a7-5423-4259-99f5-f002cff439d1))
+ (fp_line (start -7.8008 8.640001) (end -7.6738 8.386001) (layer "Cmts.User") (width 0.1) (tstamp c313110d-b275-4d48-865c-446ddf750cac))
+ (fp_line (start -7.8008 0) (end -7.9278 0.254) (layer "Cmts.User") (width 0.1) (tstamp c71a0024-4414-4043-a86a-a6e107f4b2d1))
+ (fp_line (start 0 0) (end 0 -5.842) (layer "Cmts.User") (width 0.1) (tstamp c821b87e-6250-4a84-8d88-b99443b90c64))
+ (fp_line (start 0 0) (end 5.9817 0) (layer "Cmts.User") (width 0.1) (tstamp c9399dd8-5c90-4325-b389-bf3103624bec))
+ (fp_line (start 0 -5.461) (end 1.27 -5.461) (layer "Cmts.User") (width 0.1) (tstamp cb48d39d-3b2b-400a-9b46-c1d32718986b))
+ (fp_line (start -2.54 -5.461) (end -3.81 -5.461) (layer "Cmts.User") (width 0.1) (tstamp de557e3f-efed-47f9-90f8-ac5d9fce2601))
+ (fp_line (start -1.27 0) (end -8.1818 0) (layer "Cmts.User") (width 0.1) (tstamp df0777da-5b7f-4341-a6a5-5fc162a9a7ef))
+ (fp_line (start -16.0558 -0.65) (end -16.1828 -0.904) (layer "Cmts.User") (width 0.1) (tstamp e5c24f3a-2fcc-4559-97f3-e0b7cddb0c48))
+ (fp_line (start 1.4 8.640001) (end 1.4 -9.017) (layer "Cmts.User") (width 0.1) (tstamp e6c9e7f2-c898-4429-8902-78b402b76e4e))
+ (fp_line (start -5.6007 -0.65) (end -15.1668 -0.65) (layer "Cmts.User") (width 0.1) (tstamp eabec006-aad6-4e4b-b4d6-e688f33f7e1d))
+ (fp_line (start -16.0558 0) (end -16.1828 0.254) (layer "Cmts.User") (width 0.1) (tstamp f4147886-1d72-4fe1-b32a-57d9e7e86cf2))
+ (fp_line (start -7.8008 0) (end -7.8008 8.640001) (layer "Cmts.User") (width 0.1) (tstamp f5a9dc58-05e6-4677-9eed-deb5fd24965f))
+ (fp_line (start -2.794 -5.588) (end -2.794 -5.334) (layer "Cmts.User") (width 0.1) (tstamp f7220b3d-c3b5-46e2-98ca-e330b30390ca))
+ (fp_line (start 5.4737 -0.254) (end 5.7277 -0.254) (layer "Cmts.User") (width 0.1) (tstamp fabdd97b-fba7-4595-8cc6-ce801d292bf8))
+ (fp_line (start 5.6007 0) (end 5.7277 -0.254) (layer "Cmts.User") (width 0.1) (tstamp fd370dcf-dbc7-4117-96f0-a142e67e27d7))
+ (fp_line (start 3.3558 -1.016) (end -5.8958 -1.016) (layer "F.CrtYd") (width 0.05) (tstamp 15771939-fb81-4cff-a40e-65699aae3cf2))
+ (fp_line (start -5.8958 -1.016) (end -5.8958 13.193001) (layer "F.CrtYd") (width 0.05) (tstamp 2e44d9d5-1b1b-4cc1-afb6-adf703b02956))
+ (fp_line (start -5.8958 13.193001) (end 3.3558 13.193001) (layer "F.CrtYd") (width 0.05) (tstamp 9204eda4-1759-423e-8a98-81483dc07b5b))
+ (fp_line (start 3.3558 -1.016) (end -5.8958 -1.016) (layer "F.CrtYd") (width 0.05) (tstamp 96609c51-acda-483b-90f3-fbd8eea64e08))
+ (fp_line (start 3.3558 13.193001) (end 3.3558 -1.016) (layer "F.CrtYd") (width 0.05) (tstamp b4665ccb-5983-403b-87c3-b4f193b1ea43))
+ (fp_line (start 3.3558 13.193001) (end 3.3558 -1.016) (layer "F.CrtYd") (width 0.05) (tstamp b89111b5-5ee4-41ef-ba85-21abf67a1663))
+ (fp_line (start -5.8958 -1.016) (end -5.8958 13.193001) (layer "F.CrtYd") (width 0.05) (tstamp bed2a07d-6a29-4bb6-a711-bff108ed9b5a))
+ (fp_line (start -5.8958 13.193001) (end 3.3558 13.193001) (layer "F.CrtYd") (width 0.05) (tstamp d105aa25-9ccd-4ea5-8463-40d8efc30252))
+ (fp_line (start 3.0607 12.939001) (end 3.0607 -0.65) (layer "F.Fab") (width 0.1) (tstamp 11cc679d-69d4-44ee-a945-c556a0b393d2))
+ (fp_line (start -5.6007 12.939001) (end 3.0607 12.939001) (layer "F.Fab") (width 0.1) (tstamp 7d57d82b-6583-4958-8478-b4caeee3ba20))
+ (fp_line (start 3.0607 -0.65) (end -5.6007 -0.65) (layer "F.Fab") (width 0.1) (tstamp c9146377-97b3-49b3-b510-106f521ea531))
+ (fp_line (start -5.6007 -0.65) (end -5.6007 12.939001) (layer "F.Fab") (width 0.1) (tstamp d85f85a4-15fa-40cf-aab3-a16f7baeaf9d))
+ (fp_circle (center 0 -1.905) (end 0.381 -1.905) (layer "F.Fab") (width 0.1) (fill none) (tstamp f5e198df-6127-41a8-b74d-9cc10cbe6949))
+ (pad "1" thru_hole rect (at 0 0 90) (size 1.524 1.524) (drill 0.9652) (layers *.Cu *.Mask)
+ (net 1 "/CAN_H") (pinfunction "Pin_1") (pintype "passive") (tstamp 3aebf8b6-92e0-46ee-ac67-2c509c754a55))
+ (pad "2" thru_hole circle (at -2.54 0 90) (size 1.524 1.524) (drill 0.9652) (layers *.Cu *.Mask)
+ (net 2 "/CAN_L") (pinfunction "Pin_2") (pintype "passive") (tstamp c9d60483-08cc-43bc-beaf-a163675e6344))
+ (pad "3" thru_hole circle (at -3.94 8.640001 90) (size 3.4036 3.4036) (drill 3.4036) (layers *.Cu *.Mask) (tstamp 767fef50-24ac-4d80-ba31-60a68addd71c))
+ (pad "4" thru_hole circle (at 1.4 8.640001 90) (size 3.4036 3.4036) (drill 3.4036) (layers *.Cu *.Mask) (tstamp ec9f380e-5b8b-4225-8633-df2c495f3d57))
+ (model "${KIPRJMOD}/3D model/70555-0036_stp/705550036.stp"
+ (offset (xyz -1.27 -7.1882 2.794))
+ (scale (xyz 1 1 1))
+ (rotate (xyz -90 0 0))
+ )
+ )
+
+ (footprint "Resistor_SMD:R_0805_2012Metric" (layer "B.Cu")
+ (tedit 5F68FEEE) (tstamp fbf9bb3f-e722-456b-92cb-cae8f83948d0)
+ (at 150.876 118.0065 -90)
+ (descr "Resistor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 72, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf), generated with kicad-footprint-generator")
+ (tags "resistor")
+ (property "Sheetfile" "CAN_terminator.kicad_sch")
+ (property "Sheetname" "")
+ (path "/c78ffe14-863e-4c6e-af99-6d6cf6e857aa")
+ (attr smd)
+ (fp_text reference "R1" (at 0 1.65 90) (layer "B.SilkS")
+ (effects (font (size 1 1) (thickness 0.15)) (justify mirror))
+ (tstamp 62cebe0e-5aba-4ac0-bf4f-517466fd3628)
+ )
+ (fp_text value "120 ohm" (at 0 -1.65 90) (layer "B.Fab")
+ (effects (font (size 1 1) (thickness 0.15)) (justify mirror))
+ (tstamp b1559971-4af8-42b9-b8ff-c57891196380)
+ )
+ (fp_text user "${REFERENCE}" (at 0 0 90) (layer "B.Fab")
+ (effects (font (size 0.5 0.5) (thickness 0.08)) (justify mirror))
+ (tstamp d2f839d4-b74d-4900-8e06-437630edc13f)
+ )
+ (fp_line (start -0.227064 0.735) (end 0.227064 0.735) (layer "B.SilkS") (width 0.12) (tstamp aa852fef-25c5-4952-b6d2-4d55a79ea555))
+ (fp_line (start -0.227064 -0.735) (end 0.227064 -0.735) (layer "B.SilkS") (width 0.12) (tstamp c39a0fc1-d6ac-4bba-97ad-ea715058d535))
+ (fp_line (start 1.68 -0.95) (end -1.68 -0.95) (layer "B.CrtYd") (width 0.05) (tstamp 06d108ef-4e52-471b-9b08-82185fe7102d))
+ (fp_line (start 1.68 0.95) (end 1.68 -0.95) (layer "B.CrtYd") (width 0.05) (tstamp 26a1615f-7d49-4b0d-ab0f-3a499b651970))
+ (fp_line (start -1.68 0.95) (end 1.68 0.95) (layer "B.CrtYd") (width 0.05) (tstamp ac245935-1b6f-43a3-91b6-10168040ee49))
+ (fp_line (start -1.68 -0.95) (end -1.68 0.95) (layer "B.CrtYd") (width 0.05) (tstamp cff434b2-1fdf-4d2a-94fe-b6396a86563b))
+ (fp_line (start 1 -0.625) (end -1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 2b57236f-07a6-4412-a3a3-d7bf53a94854))
+ (fp_line (start -1 0.625) (end 1 0.625) (layer "B.Fab") (width 0.1) (tstamp 404a71a5-669d-48c0-a9b4-b7d86cd16ca6))
+ (fp_line (start -1 -0.625) (end -1 0.625) (layer "B.Fab") (width 0.1) (tstamp 8072f038-202d-4c08-85a2-b85b06b0c1d7))
+ (fp_line (start 1 0.625) (end 1 -0.625) (layer "B.Fab") (width 0.1) (tstamp f026ab72-b40c-4e66-99b8-4dfa1e3a8bc4))
+ (pad "1" smd roundrect (at -0.9125 0 270) (size 1.025 1.4) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.243902439)
+ (net 1 "/CAN_H") (pintype "passive") (tstamp 4d65a789-97ff-4775-b21f-65d823affd00))
+ (pad "2" smd roundrect (at 0.9125 0 270) (size 1.025 1.4) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.243902439)
+ (net 2 "/CAN_L") (pintype "passive") (tstamp 2c7bea0b-bf40-4a13-8ac7-f7cdb3ef4239))
+ (model "${KICAD6_3DMODEL_DIR}/Resistor_SMD.3dshapes/R_0805_2012Metric.wrl"
+ (offset (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (gr_poly
+ (pts
+ (xy 146.177 116.84)
+ (xy 145.542 116.205)
+ (xy 145.542 117.475)
+ ) (layer "F.SilkS") (width 0.15) (fill solid) (tstamp 2cedb083-c93c-4b1c-b36e-fbae4ac29a06))
+ (gr_arc (start 160.528 123.19) (mid 160.342013 123.639013) (end 159.893 123.825) (layer "Edge.Cuts") (width 0.1) (tstamp 049d31ed-c080-40c6-a27f-8f7f4b2fcb00))
+ (gr_arc (start 145.415 113.03) (mid 145.600987 112.580987) (end 146.05 112.395) (layer "Edge.Cuts") (width 0.1) (tstamp 183182d7-acc2-4f63-b8d1-5e98abdd2fdb))
+ (gr_line (start 145.415 113.03) (end 145.415 123.19) (layer "Edge.Cuts") (width 0.1) (tstamp 5601d0d4-083c-40bb-bb72-9163cf06877a))
+ (gr_line (start 160.528 113.03) (end 160.528 123.19) (layer "Edge.Cuts") (width 0.1) (tstamp 58133a47-f708-43ad-a5ee-a634a59b4b09))
+ (gr_line (start 146.05 123.825) (end 159.893 123.825) (layer "Edge.Cuts") (width 0.1) (tstamp 911f2eda-bbfe-4e1c-8cee-a449d0264f5a))
+ (gr_arc (start 146.05 123.825) (mid 145.600987 123.639013) (end 145.415 123.19) (layer "Edge.Cuts") (width 0.1) (tstamp aa08abe8-1f15-49aa-8bd2-671f43ff0144))
+ (gr_line (start 146.05 112.395) (end 159.893 112.395) (layer "Edge.Cuts") (width 0.1) (tstamp b1b733fd-ef3b-4971-848c-7ee41106c9da))
+ (gr_arc (start 159.893 112.395) (mid 160.342013 112.580987) (end 160.528 113.03) (layer "Edge.Cuts") (width 0.1) (tstamp d70741fb-9769-459d-85d7-06287068793b))
+ (gr_text "CAN Term" (at 152.9334 118.0846 90) (layer "B.SilkS") (tstamp 15dc4b2e-003f-454e-bdaf-e1febd8c55e0)
+ (effects (font (size 0.9906 0.9906) (thickness 0.127)) (justify mirror))
+ )
+ (gr_text "971" (at 155.9814 118.1862) (layer "B.SilkS") (tstamp 188ae16b-4163-436c-8af9-1112c99f2627)
+ (effects (font (size 0.889 0.889) (thickness 0.127)) (justify mirror))
+ )
+ (gr_text "V1.0 2022" (at 159.131 118.0084 90) (layer "B.SilkS") (tstamp 351426b8-c99c-48b2-b00a-5d5590d56c96)
+ (effects (font (size 1 1) (thickness 0.15)) (justify mirror))
+ )
+ (gr_text "CAN_H" (at 149.098 114.935) (layer "B.SilkS") (tstamp b6090a9a-81cf-4c25-90e4-f9b29d34d5b9)
+ (effects (font (size 1 1) (thickness 0.15)) (justify mirror))
+ )
+ (gr_text "CAN_L" (at 149.098 121.285) (layer "B.SilkS") (tstamp eef45872-f083-41bb-912f-de7a762baadb)
+ (effects (font (size 1 1) (thickness 0.15)) (justify mirror))
+ )
+
+ (segment (start 147.32 116.332) (end 147.32 116.84) (width 0.508) (layer "B.Cu") (net 1) (tstamp 4548c7ec-5e75-4078-a0d3-6fdc3ee75387))
+ (segment (start 149.606 115.062) (end 148.59 115.062) (width 0.508) (layer "B.Cu") (net 1) (tstamp 9a7279b8-4e1d-4cbb-894c-76ac725b7377))
+ (segment (start 150.876 116.332) (end 150.876 117.094) (width 0.508) (layer "B.Cu") (net 1) (tstamp f4ea9409-85a3-452f-88dd-f039b61cb093))
+ (arc (start 149.606 115.062) (mid 150.504026 115.433974) (end 150.876 116.332) (width 0.508) (layer "B.Cu") (net 1) (tstamp 87a6cea5-7eeb-4a7c-9676-cd20559c5b4c))
+ (arc (start 148.59 115.062) (mid 147.691974 115.433974) (end 147.32 116.332) (width 0.508) (layer "B.Cu") (net 1) (tstamp 92918a26-f3e7-4ab1-b6fe-c7c8e0d4fcc9))
+ (segment (start 148.59 121.158) (end 149.606 121.158) (width 0.508) (layer "B.Cu") (net 2) (tstamp 1cff39b5-dffa-45c8-9d9e-ad67501381ac))
+ (segment (start 147.32 119.38) (end 147.32 119.888) (width 0.508) (layer "B.Cu") (net 2) (tstamp 24f848e3-8dee-4c26-bb11-62626cc4794d))
+ (segment (start 150.876 119.888) (end 150.876 118.919) (width 0.508) (layer "B.Cu") (net 2) (tstamp 51bb6c19-543d-4f8b-8732-511f3eeb46a8))
+ (arc (start 147.32 119.888) (mid 147.691974 120.786026) (end 148.59 121.158) (width 0.508) (layer "B.Cu") (net 2) (tstamp 691576cc-9fda-40be-bc7d-3fe2fa68f19a))
+ (arc (start 149.606 121.158) (mid 150.504026 120.786026) (end 150.876 119.888) (width 0.508) (layer "B.Cu") (net 2) (tstamp 97686675-611c-4b29-9007-70bf5369feb4))
+
+)
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_prl b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_prl
new file mode 100644
index 0000000..a71949c
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_prl
@@ -0,0 +1,75 @@
+{
+ "board": {
+ "active_layer": 36,
+ "active_layer_preset": "",
+ "auto_track_width": true,
+ "hidden_nets": [],
+ "high_contrast_mode": 0,
+ "net_color_mode": 1,
+ "opacity": {
+ "pads": 1.0,
+ "tracks": 1.0,
+ "vias": 1.0,
+ "zones": 0.6
+ },
+ "ratsnest_display_mode": 0,
+ "selection_filter": {
+ "dimensions": true,
+ "footprints": true,
+ "graphics": true,
+ "keepouts": true,
+ "lockedItems": true,
+ "otherItems": true,
+ "pads": true,
+ "text": true,
+ "tracks": true,
+ "vias": true,
+ "zones": true
+ },
+ "visible_items": [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 32,
+ 33,
+ 34,
+ 35,
+ 36
+ ],
+ "visible_layers": "ffffdff_ffffffff",
+ "zone_display_mode": 0
+ },
+ "meta": {
+ "filename": "CAN_terminator.kicad_prl",
+ "version": 3
+ },
+ "project": {
+ "files": []
+ }
+}
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_pro b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_pro
new file mode 100644
index 0000000..de08af6
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_pro
@@ -0,0 +1,326 @@
+{
+ "board": {
+ "design_settings": {
+ "defaults": {
+ "board_outline_line_width": 0.1,
+ "copper_line_width": 0.2,
+ "copper_text_size_h": 1.5,
+ "copper_text_size_v": 1.5,
+ "copper_text_thickness": 0.3,
+ "other_line_width": 0.15,
+ "silk_line_width": 0.15,
+ "silk_text_size_h": 1.0,
+ "silk_text_size_v": 1.0,
+ "silk_text_thickness": 0.15
+ },
+ "diff_pair_dimensions": [],
+ "drc_exclusions": [],
+ "rules": {
+ "min_copper_edge_clearance": 0.0,
+ "solder_mask_clearance": 0.0,
+ "solder_mask_min_width": 0.0
+ },
+ "track_widths": [],
+ "via_dimensions": []
+ },
+ "layer_presets": []
+ },
+ "boards": [],
+ "cvpcb": {
+ "equivalence_files": []
+ },
+ "erc": {
+ "erc_exclusions": [],
+ "meta": {
+ "version": 0
+ },
+ "pin_map": [
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 2,
+ 2,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 2
+ ],
+ [
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 2,
+ 1,
+ 1,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 1,
+ 2,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 2,
+ 2,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ ],
+ "rule_severities": {
+ "bus_definition_conflict": "error",
+ "bus_entry_needed": "error",
+ "bus_label_syntax": "error",
+ "bus_to_bus_conflict": "error",
+ "bus_to_net_conflict": "error",
+ "different_unit_footprint": "error",
+ "different_unit_net": "error",
+ "duplicate_reference": "error",
+ "duplicate_sheet_names": "error",
+ "extra_units": "error",
+ "global_label_dangling": "warning",
+ "hier_label_mismatch": "error",
+ "label_dangling": "error",
+ "lib_symbol_issues": "warning",
+ "multiple_net_names": "warning",
+ "net_not_bus_member": "warning",
+ "no_connect_connected": "warning",
+ "no_connect_dangling": "warning",
+ "pin_not_connected": "error",
+ "pin_not_driven": "error",
+ "pin_to_pin": "warning",
+ "power_pin_not_driven": "error",
+ "similar_labels": "warning",
+ "unannotated": "error",
+ "unit_value_mismatch": "error",
+ "unresolved_variable": "error",
+ "wire_dangling": "error"
+ }
+ },
+ "libraries": {
+ "pinned_footprint_libs": [],
+ "pinned_symbol_libs": []
+ },
+ "meta": {
+ "filename": "CAN_terminator.kicad_pro",
+ "version": 1
+ },
+ "net_settings": {
+ "classes": [
+ {
+ "bus_width": 12.0,
+ "clearance": 0.2,
+ "diff_pair_gap": 0.25,
+ "diff_pair_via_gap": 0.25,
+ "diff_pair_width": 0.2,
+ "line_style": 0,
+ "microvia_diameter": 0.3,
+ "microvia_drill": 0.1,
+ "name": "Default",
+ "pcb_color": "rgba(0, 0, 0, 0.000)",
+ "schematic_color": "rgba(0, 0, 0, 0.000)",
+ "track_width": 0.25,
+ "via_diameter": 0.8,
+ "via_drill": 0.4,
+ "wire_width": 6.0
+ }
+ ],
+ "meta": {
+ "version": 2
+ },
+ "net_colors": null
+ },
+ "pcbnew": {
+ "last_paths": {
+ "gencad": "",
+ "idf": "",
+ "netlist": "",
+ "specctra_dsn": "",
+ "step": "",
+ "vrml": ""
+ },
+ "page_layout_descr_file": ""
+ },
+ "schematic": {
+ "annotate_start_num": 0,
+ "drawing": {
+ "default_line_thickness": 6.0,
+ "default_text_size": 50.0,
+ "field_names": [],
+ "intersheets_ref_own_page": false,
+ "intersheets_ref_prefix": "",
+ "intersheets_ref_short": false,
+ "intersheets_ref_show": false,
+ "intersheets_ref_suffix": "",
+ "junction_size_choice": 3,
+ "label_size_ratio": 0.375,
+ "pin_symbol_size": 25.0,
+ "text_offset_ratio": 0.15
+ },
+ "legacy_lib_dir": "",
+ "legacy_lib_list": [],
+ "meta": {
+ "version": 1
+ },
+ "net_format_name": "",
+ "ngspice": {
+ "fix_include_paths": true,
+ "fix_passive_vals": false,
+ "meta": {
+ "version": 0
+ },
+ "model_mode": 0,
+ "workbook_filename": ""
+ },
+ "page_layout_descr_file": "",
+ "plot_directory": "./",
+ "spice_adjust_passive_values": false,
+ "spice_external_command": "spice \"%I\"",
+ "subpart_first_id": 65,
+ "subpart_id_separator": 0
+ },
+ "sheets": [
+ [
+ "8caaff45-1713-4013-a97e-9b4f033294ef",
+ ""
+ ]
+ ],
+ "text_variables": {}
+}
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_sch b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_sch
new file mode 100644
index 0000000..54cf30c
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.kicad_sch
@@ -0,0 +1,239 @@
+(kicad_sch (version 20211123) (generator eeschema)
+
+ (uuid 8caaff45-1713-4013-a97e-9b4f033294ef)
+
+ (paper "USLetter")
+
+ (title_block
+ (title "CAN Terminator")
+ (date "2022-08-20")
+ (rev "V1.0")
+ (company "971 Spartan Robotics")
+ )
+
+ (lib_symbols
+ (symbol "Connector_Generic:Conn_01x02" (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
+ (property "Reference" "J" (id 0) (at 0 2.54 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Value" "Conn_01x02" (id 1) (at 0 -5.08 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Footprint" "" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "~" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_keywords" "connector" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_description" "Generic connector, single row, 01x02, script generated (kicad-library-utils/schlib/autogen/connector/)" (id 5) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_fp_filters" "Connector*:*_1x??_*" (id 6) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (symbol "Conn_01x02_1_1"
+ (rectangle (start -1.27 -2.413) (end 0 -2.667)
+ (stroke (width 0.1524) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (rectangle (start -1.27 0.127) (end 0 -0.127)
+ (stroke (width 0.1524) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (rectangle (start -1.27 1.27) (end 1.27 -3.81)
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type background))
+ )
+ (pin passive line (at -5.08 0 0) (length 3.81)
+ (name "Pin_1" (effects (font (size 1.27 1.27))))
+ (number "1" (effects (font (size 1.27 1.27))))
+ )
+ (pin passive line (at -5.08 -2.54 0) (length 3.81)
+ (name "Pin_2" (effects (font (size 1.27 1.27))))
+ (number "2" (effects (font (size 1.27 1.27))))
+ )
+ )
+ )
+ (symbol "Device:R_US" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+ (property "Reference" "R" (id 0) (at 2.54 0 90)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Value" "R_US" (id 1) (at -2.54 0 90)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Footprint" "" (id 2) (at 1.016 -0.254 90)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "~" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_keywords" "R res resistor" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_description" "Resistor, US symbol" (id 5) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_fp_filters" "R_*" (id 6) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (symbol "R_US_0_1"
+ (polyline
+ (pts
+ (xy 0 -2.286)
+ (xy 0 -2.54)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 2.286)
+ (xy 0 2.54)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 -0.762)
+ (xy 1.016 -1.143)
+ (xy 0 -1.524)
+ (xy -1.016 -1.905)
+ (xy 0 -2.286)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 0.762)
+ (xy 1.016 0.381)
+ (xy 0 0)
+ (xy -1.016 -0.381)
+ (xy 0 -0.762)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 2.286)
+ (xy 1.016 1.905)
+ (xy 0 1.524)
+ (xy -1.016 1.143)
+ (xy 0 0.762)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ )
+ (symbol "R_US_1_1"
+ (pin passive line (at 0 3.81 270) (length 1.27)
+ (name "~" (effects (font (size 1.27 1.27))))
+ (number "1" (effects (font (size 1.27 1.27))))
+ )
+ (pin passive line (at 0 -3.81 90) (length 1.27)
+ (name "~" (effects (font (size 1.27 1.27))))
+ (number "2" (effects (font (size 1.27 1.27))))
+ )
+ )
+ )
+ )
+
+
+ (wire (pts (xy 128.27 111.76) (xy 128.27 105.41))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 132da87c-3baa-426d-9066-829f7a1c28ca)
+ )
+ (wire (pts (xy 105.41 105.41) (xy 105.41 109.22))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 16080253-9679-4f09-ae3c-0d227a31af37)
+ )
+ (wire (pts (xy 128.27 120.65) (xy 128.27 114.3))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 1c2bb4d1-25e3-4267-b31c-15fa51f79efa)
+ )
+ (wire (pts (xy 105.41 120.65) (xy 128.27 120.65))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 2b854e78-cf10-434e-80f3-55de5d8462ca)
+ )
+ (wire (pts (xy 128.27 105.41) (xy 105.41 105.41))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 42c66820-779d-4567-afe2-0ac717e72ae9)
+ )
+ (wire (pts (xy 133.35 111.76) (xy 128.27 111.76))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 8ce9985d-a05c-4d8e-a69f-f4915fbf5366)
+ )
+ (wire (pts (xy 105.41 116.84) (xy 105.41 120.65))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid d46455f0-49d8-4114-b429-ae1e6176838d)
+ )
+ (wire (pts (xy 128.27 114.3) (xy 133.35 114.3))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid d82afd32-f227-4439-bb6a-506a8bdec942)
+ )
+
+ (label "CAN_L" (at 113.03 120.65 0)
+ (effects (font (size 1.27 1.27)) (justify left bottom))
+ (uuid 00c76874-07d7-4d12-98ee-a7a628c5ea6b)
+ )
+ (label "CAN_H" (at 113.03 105.41 0)
+ (effects (font (size 1.27 1.27)) (justify left bottom))
+ (uuid a15fc3cb-764b-4b2b-b58a-28846c8afa34)
+ )
+
+ (symbol (lib_id "Device:R_US") (at 105.41 113.03 0) (unit 1)
+ (in_bom yes) (on_board yes) (fields_autoplaced)
+ (uuid c78ffe14-863e-4c6e-af99-6d6cf6e857aa)
+ (property "Reference" "R1" (id 0) (at 107.95 111.7599 0)
+ (effects (font (size 1.27 1.27)) (justify left))
+ )
+ (property "Value" "120 ohm" (id 1) (at 107.95 114.2999 0)
+ (effects (font (size 1.27 1.27)) (justify left))
+ )
+ (property "Footprint" "Resistor_SMD:R_0805_2012Metric" (id 2) (at 106.426 113.284 90)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "~" (id 3) (at 105.41 113.03 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 8571995a-c61d-4156-aab5-9044acb9c951))
+ (pin "2" (uuid a0e40ed7-58f5-46d0-95bb-25409f92731d))
+ )
+
+ (symbol (lib_id "Connector_Generic:Conn_01x02") (at 138.43 111.76 0) (unit 1)
+ (in_bom yes) (on_board yes) (fields_autoplaced)
+ (uuid e8bffcfe-c870-4593-8a2b-7a667dd6382d)
+ (property "Reference" "J1" (id 0) (at 140.97 111.7599 0)
+ (effects (font (size 1.27 1.27)) (justify left))
+ )
+ (property "Value" "2 pin molex sl right angle" (id 1) (at 140.97 114.2999 0)
+ (effects (font (size 1.27 1.27)) (justify left))
+ )
+ (property "Footprint" "CAN_terminator:70555-0036" (id 2) (at 138.43 111.76 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "~" (id 3) (at 138.43 111.76 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 50d9b5e2-32db-4f4d-a17c-99ca7d722b41))
+ (pin "2" (uuid 0bf9eb86-5378-4e96-91ca-e45822cd719e))
+ )
+
+ (sheet_instances
+ (path "/" (page "1"))
+ )
+
+ (symbol_instances
+ (path "/e8bffcfe-c870-4593-8a2b-7a667dd6382d"
+ (reference "J1") (unit 1) (value "2 pin molex sl right angle") (footprint "CAN_terminator:70555-0036")
+ )
+ (path "/c78ffe14-863e-4c6e-af99-6d6cf6e857aa"
+ (reference "R1") (unit 1) (value "120 ohm") (footprint "Resistor_SMD:R_0805_2012Metric")
+ )
+ )
+)
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.pdf b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.pdf
new file mode 100644
index 0000000..6c32349
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.pdf
Binary files differ
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.pretty/70555-0036.kicad_mod b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.pretty/70555-0036.kicad_mod
new file mode 100644
index 0000000..dbaaf03
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/CAN_terminator.pretty/70555-0036.kicad_mod
@@ -0,0 +1,104 @@
+(footprint "70555-0036" (version 20211014) (generator pcbnew)
+ (layer "F.Cu")
+ (tedit 0)
+ (attr through_hole)
+ (fp_text reference "REF**" (at -1.27 -2.54) (layer "F.SilkS") hide
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp a637ec7c-75ad-40cc-bf25-c9128703e18e)
+ )
+ (fp_text value "CONN_70555-0036_MOL" (at -1.27 -2.54) (layer "F.SilkS") hide
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp 4fc22d93-c3d7-4028-98ce-997a45d349f0)
+ )
+ (fp_line (start 3.1877 7.668671) (end 3.1877 -0.777) (layer "F.SilkS") (width 0.12) (tstamp 0055cacd-42a1-4b46-9ede-aec3cb861e96))
+ (fp_line (start -5.7277 9.61133) (end -5.7277 13.066001) (layer "F.SilkS") (width 0.12) (tstamp 13fd91cb-1534-4e0c-9633-a112903a58be))
+ (fp_line (start -5.7277 13.066001) (end 3.1877 13.066001) (layer "F.SilkS") (width 0.12) (tstamp 3fe370f9-d589-4d90-bb35-419cf6f2007c))
+ (fp_line (start 3.1877 -0.777) (end 1.27 -0.777) (layer "F.SilkS") (width 0.12) (tstamp a1955cab-c40f-4eb5-b49a-9e55b820c051))
+ (fp_line (start 3.1877 13.066001) (end 3.1877 9.61133) (layer "F.SilkS") (width 0.12) (tstamp b1c4628e-4ca7-47d3-a192-095c5d42a51c))
+ (fp_line (start -3.311185 -0.777) (end -5.7277 -0.777) (layer "F.SilkS") (width 0.12) (tstamp c708b7e6-0f15-4151-8f31-12b7dba4ce4e))
+ (fp_line (start -5.7277 -0.777) (end -5.7277 7.668671) (layer "F.SilkS") (width 0.12) (tstamp cde8d358-6018-4574-84ff-062501c7ea31))
+ (fp_line (start -16.0558 0) (end -16.1828 0.254) (layer "Cmts.User") (width 0.1) (tstamp 04ae59ee-7a5f-42e0-8b3c-12636c325aee))
+ (fp_line (start -3.94 -8.636) (end -3.686 -8.509) (layer "Cmts.User") (width 0.1) (tstamp 05ef887f-4540-49c7-be70-43c58844c91f))
+ (fp_line (start 1.4 8.640001) (end -8.1818 8.640001) (layer "Cmts.User") (width 0.1) (tstamp 086b87e8-bfef-4521-baa4-9ee2ded580c9))
+ (fp_line (start -2.54 0) (end -2.54 -5.842) (layer "Cmts.User") (width 0.1) (tstamp 096ead2f-3032-410c-9480-3f5584fdd801))
+ (fp_line (start 3.0607 -11.176) (end 2.8067 -11.303) (layer "Cmts.User") (width 0.1) (tstamp 11b3f72a-26ec-4967-a815-144a5592b95c))
+ (fp_line (start -16.0558 -0.65) (end -15.9288 -0.904) (layer "Cmts.User") (width 0.1) (tstamp 11b8f84d-7fac-48e4-bb25-0f97c8fc70f2))
+ (fp_line (start -5.6007 12.939001) (end -15.1668 12.939001) (layer "Cmts.User") (width 0.1) (tstamp 11d481ec-b267-4e5c-b519-bb7d01c6da97))
+ (fp_line (start 0 -5.461) (end 0.254 -5.334) (layer "Cmts.User") (width 0.1) (tstamp 1ec515b2-8a95-4b3e-85c8-389200ccf123))
+ (fp_line (start -16.1828 -0.904) (end -15.9288 -0.904) (layer "Cmts.User") (width 0.1) (tstamp 24bbb1ee-9b37-4dc1-9128-ddf9c1b809a0))
+ (fp_line (start -7.9278 0.254) (end -7.6738 0.254) (layer "Cmts.User") (width 0.1) (tstamp 28f54e94-ec22-4cd2-8029-d541b7f0f513))
+ (fp_line (start -16.0558 -0.65) (end -16.0558 -1.92) (layer "Cmts.User") (width 0.1) (tstamp 378009e1-b239-433e-8679-51e5961ad562))
+ (fp_line (start 5.6007 0) (end 5.4737 0.254) (layer "Cmts.User") (width 0.1) (tstamp 381834cc-3b73-43a9-8c39-b915d8c6099c))
+ (fp_line (start 0.254 -5.588) (end 0.254 -5.334) (layer "Cmts.User") (width 0.1) (tstamp 38a048bd-3dd1-42c8-8463-fc2b1b59d751))
+ (fp_line (start -14.9128 -0.396) (end -14.6588 -0.396) (layer "Cmts.User") (width 0.1) (tstamp 40ab74e1-98e2-4646-bf5b-23606a24427a))
+ (fp_line (start -16.0558 -0.65) (end -16.1828 -0.904) (layer "Cmts.User") (width 0.1) (tstamp 40c1eb3e-3696-40e3-9674-5db7853a51a2))
+ (fp_line (start -3.686 -8.763) (end -3.686 -8.509) (layer "Cmts.User") (width 0.1) (tstamp 41981f46-b053-4690-ab8d-137ca090545d))
+ (fp_line (start -5.6007 -11.176) (end -5.3467 -11.303) (layer "Cmts.User") (width 0.1) (tstamp 42008337-8863-4b41-b559-aae67c980ac8))
+ (fp_line (start 3.0607 -11.176) (end 2.8067 -11.049) (layer "Cmts.User") (width 0.1) (tstamp 4ce4c298-707b-4516-964d-f74e3aea4d52))
+ (fp_line (start -5.6007 -11.176) (end 3.0607 -11.176) (layer "Cmts.User") (width 0.1) (tstamp 50e78a7b-2763-4de7-8724-6296f95282fc))
+ (fp_line (start -5.6007 -0.65) (end -16.4368 -0.65) (layer "Cmts.User") (width 0.1) (tstamp 52234db8-4ba9-43e2-9b57-f95144b2a41b))
+ (fp_line (start 0 0) (end 5.9817 0) (layer "Cmts.User") (width 0.1) (tstamp 5e41804b-414e-4a5e-a4e8-2bafc930e1a9))
+ (fp_line (start -2.794 -5.588) (end -2.794 -5.334) (layer "Cmts.User") (width 0.1) (tstamp 5f05f809-7aae-4fa0-bde1-19292a9bc622))
+ (fp_line (start 5.6007 0) (end 5.6007 -1.27) (layer "Cmts.User") (width 0.1) (tstamp 6760ce19-7881-46ec-9487-399928257ff6))
+ (fp_line (start 1.4 -8.636) (end 1.146 -8.509) (layer "Cmts.User") (width 0.1) (tstamp 6a3b209d-ffa3-4cce-b869-df2a1eb58b21))
+ (fp_line (start -14.7858 -0.65) (end -14.7858 12.939001) (layer "Cmts.User") (width 0.1) (tstamp 6fb5189d-b997-4bb3-870d-29119785040b))
+ (fp_line (start -7.8008 8.640001) (end -7.9278 8.386001) (layer "Cmts.User") (width 0.1) (tstamp 72742d07-33e8-46fc-b079-4be459be1d61))
+ (fp_line (start 5.6007 0) (end 5.6007 1.27) (layer "Cmts.User") (width 0.1) (tstamp 7882e5ec-1952-4fe4-a435-3e01dd9d8644))
+ (fp_line (start -1.27 0) (end -16.4368 0) (layer "Cmts.User") (width 0.1) (tstamp 7969d0a4-70da-43b7-9298-d0d962b319f4))
+ (fp_line (start -14.7858 12.939001) (end -14.6588 12.685001) (layer "Cmts.User") (width 0.1) (tstamp 859be0b6-7ae7-4cc1-b5d3-4e4bb54ac7b4))
+ (fp_line (start 0 0) (end 5.9817 0) (layer "Cmts.User") (width 0.1) (tstamp 884deb15-7009-4afb-a7b5-8a6f2e44da55))
+ (fp_line (start -5.6007 -0.65) (end -15.1668 -0.65) (layer "Cmts.User") (width 0.1) (tstamp 89ea3951-c0a0-40fc-9546-54cbf97559ef))
+ (fp_line (start 1.146 -8.763) (end 1.146 -8.509) (layer "Cmts.User") (width 0.1) (tstamp 8b3c93fd-90a8-40aa-bcb2-b63067e41902))
+ (fp_line (start -3.94 -8.636) (end -3.686 -8.763) (layer "Cmts.User") (width 0.1) (tstamp 8d56ad97-87f6-495a-9ea0-d5eacd8ac8be))
+ (fp_line (start -7.8008 8.640001) (end -7.6738 8.386001) (layer "Cmts.User") (width 0.1) (tstamp 8f839a12-f6f4-47ac-aa9e-670c126bae8f))
+ (fp_line (start 1.4 -8.636) (end 1.146 -8.763) (layer "Cmts.User") (width 0.1) (tstamp 9336c6e0-89b1-47b6-a338-18b48e3eccdb))
+ (fp_line (start -5.6007 -0.65) (end -5.6007 -11.557) (layer "Cmts.User") (width 0.1) (tstamp 9a92e568-5b1b-48d8-843a-d115fac522d1))
+ (fp_line (start -3.94 -8.636) (end 1.4 -8.636) (layer "Cmts.User") (width 0.1) (tstamp 9c524846-b502-40f2-8c1e-7f0ca29abcce))
+ (fp_line (start 3.0607 -0.65) (end 3.0607 -11.557) (layer "Cmts.User") (width 0.1) (tstamp a9b50f99-5e96-49e2-94c4-69771c4fab6d))
+ (fp_line (start 2.8067 -11.303) (end 2.8067 -11.049) (layer "Cmts.User") (width 0.1) (tstamp abf9614e-67ac-40dd-a0cf-62bb43ba6390))
+ (fp_line (start -7.8008 0) (end -7.8008 8.640001) (layer "Cmts.User") (width 0.1) (tstamp ada78ddd-0212-4df5-993e-6a96b08962c8))
+ (fp_line (start -5.6007 -11.176) (end -5.3467 -11.049) (layer "Cmts.User") (width 0.1) (tstamp af35deb4-61bd-4ffa-835a-1088e4086461))
+ (fp_line (start -16.1828 0.254) (end -15.9288 0.254) (layer "Cmts.User") (width 0.1) (tstamp b15a20d3-2f8e-4e0c-ae47-a0ff0d9ba72d))
+ (fp_line (start 5.4737 0.254) (end 5.7277 0.254) (layer "Cmts.User") (width 0.1) (tstamp b2225a88-9450-49ef-aa59-6dcda4bc793a))
+ (fp_line (start 0 0) (end 0 -5.842) (layer "Cmts.User") (width 0.1) (tstamp b566ea23-5d96-4503-8b21-6ff130407837))
+ (fp_line (start -3.94 8.640001) (end -3.94 -9.017) (layer "Cmts.User") (width 0.1) (tstamp b84d37c1-0f6b-4959-a96e-2ec215df2f55))
+ (fp_line (start 0 -5.461) (end 0.254 -5.588) (layer "Cmts.User") (width 0.1) (tstamp b857338b-7569-4a1f-a2e5-33863b256df6))
+ (fp_line (start -7.8008 0) (end -7.9278 0.254) (layer "Cmts.User") (width 0.1) (tstamp bcf26687-fa97-4825-8279-aeb9f3ebf98e))
+ (fp_line (start -14.7858 -0.65) (end -14.6588 -0.396) (layer "Cmts.User") (width 0.1) (tstamp bd8d5c78-fb56-42aa-9471-49f80b981d41))
+ (fp_line (start 5.6007 0) (end 5.7277 -0.254) (layer "Cmts.User") (width 0.1) (tstamp c0267093-3637-4e52-8ff6-fb45a153a6cf))
+ (fp_line (start -7.8008 0) (end -7.6738 0.254) (layer "Cmts.User") (width 0.1) (tstamp c487eed0-25d5-4cb9-8e98-41dc4d62f0cb))
+ (fp_line (start -5.3467 -11.303) (end -5.3467 -11.049) (layer "Cmts.User") (width 0.1) (tstamp d274b4f7-b31e-462d-9cae-7f1c6ed5a3d7))
+ (fp_line (start -2.54 -5.461) (end -3.81 -5.461) (layer "Cmts.User") (width 0.1) (tstamp d7d5f5cd-5c60-4d54-819e-a0ef8dc4022e))
+ (fp_line (start 5.6007 0) (end 5.7277 0.254) (layer "Cmts.User") (width 0.1) (tstamp dab0cfb2-4c57-4f6d-907c-5502896a59f6))
+ (fp_line (start -16.0558 0) (end -15.9288 0.254) (layer "Cmts.User") (width 0.1) (tstamp dafa37ff-e739-45d8-9e55-e09577a94397))
+ (fp_line (start -14.7858 12.939001) (end -14.9128 12.685001) (layer "Cmts.User") (width 0.1) (tstamp dc73bb49-e5e5-4062-887b-79f0bff305e1))
+ (fp_line (start 5.4737 -0.254) (end 5.7277 -0.254) (layer "Cmts.User") (width 0.1) (tstamp e44a7830-31a3-48bd-96d5-863125ee7e25))
+ (fp_line (start -1.27 0) (end -8.1818 0) (layer "Cmts.User") (width 0.1) (tstamp e5a2238b-5459-4d8c-bb93-f5262d5086e8))
+ (fp_line (start -7.9278 8.386001) (end -7.6738 8.386001) (layer "Cmts.User") (width 0.1) (tstamp e959cc5a-d690-4e74-a4a0-ad0be6db0eca))
+ (fp_line (start -2.54 -5.461) (end -2.794 -5.334) (layer "Cmts.User") (width 0.1) (tstamp eb85b359-bde4-46ff-8294-dfe1e63f1ec8))
+ (fp_line (start 5.6007 0) (end 5.4737 -0.254) (layer "Cmts.User") (width 0.1) (tstamp efc7c558-4deb-4150-bebc-9c103d33280f))
+ (fp_line (start 1.4 8.640001) (end 1.4 -9.017) (layer "Cmts.User") (width 0.1) (tstamp f1403736-0953-4201-a37f-a75754507117))
+ (fp_line (start -16.0558 0) (end -16.0558 1.27) (layer "Cmts.User") (width 0.1) (tstamp f2b12c8b-695e-41fd-ab8d-a84f0af72566))
+ (fp_line (start -14.9128 12.685001) (end -14.6588 12.685001) (layer "Cmts.User") (width 0.1) (tstamp fa7212a2-3046-4606-9a1a-14e7b8a52907))
+ (fp_line (start -2.54 -5.461) (end -2.794 -5.588) (layer "Cmts.User") (width 0.1) (tstamp fd3438dc-1570-4d59-8a64-56e57c425b93))
+ (fp_line (start 0 -5.461) (end 1.27 -5.461) (layer "Cmts.User") (width 0.1) (tstamp febb73c9-ccaa-4a28-be70-3792d5c07fc5))
+ (fp_line (start -14.7858 -0.65) (end -14.9128 -0.396) (layer "Cmts.User") (width 0.1) (tstamp ff0552ae-0194-4155-b5f2-8a4fa1645e29))
+ (fp_line (start -5.842 -1.016) (end -5.842 13.208) (layer "F.CrtYd") (width 0.05) (tstamp 0eb2133e-f922-42b1-8334-81a9257d1b04))
+ (fp_line (start 3.302 13.208) (end 3.302 -1.016) (layer "F.CrtYd") (width 0.05) (tstamp 389cf10b-5e4c-458a-b100-30d6fee48ae1))
+ (fp_line (start -5.842 13.208) (end 3.302 13.208) (layer "F.CrtYd") (width 0.05) (tstamp 38bd06ba-0acd-4765-9dd9-7db0a93c3b06))
+ (fp_line (start 3.302 -1.016) (end -5.842 -1.016) (layer "F.CrtYd") (width 0.05) (tstamp 6f780366-0098-4a09-9d36-8e89ccb494e4))
+ (fp_line (start 3.0607 12.939001) (end 3.0607 -0.65) (layer "F.Fab") (width 0.1) (tstamp 247e8377-ffdd-494d-a3a6-b3e8589d068d))
+ (fp_line (start -5.6007 -0.65) (end -5.6007 12.939001) (layer "F.Fab") (width 0.1) (tstamp 91bbe933-a48f-4848-b09e-babd44079522))
+ (fp_line (start 3.0607 -0.65) (end -5.6007 -0.65) (layer "F.Fab") (width 0.1) (tstamp b73ad2b5-47c3-4925-95a7-df028fb933ee))
+ (fp_line (start -5.6007 12.939001) (end 3.0607 12.939001) (layer "F.Fab") (width 0.1) (tstamp c465551b-e82a-4556-a907-cdd41b17bab0))
+ (fp_circle (center 0 -1.905) (end 0.381 -1.905) (layer "F.Fab") (width 0.1) (fill none) (tstamp 09c78b92-293a-4eac-aa0d-01023d43de6a))
+ (pad "1" thru_hole rect (at 0 0) (size 1.524 1.524) (drill 0.9652) (layers *.Cu *.Mask) (tstamp 5e7583cd-f0e4-4f71-8690-fcca5cd247d0))
+ (pad "2" thru_hole circle (at -2.54 0) (size 1.524 1.524) (drill 0.9652) (layers *.Cu *.Mask) (tstamp 1029a2f1-6126-4559-a7d0-2ba50ff85553))
+ (pad "3" thru_hole circle (at -3.94 8.640001) (size 3.4036 3.4036) (drill 3.4036) (layers *.Cu *.Mask) (tstamp 91f31940-2bb5-4543-9fe4-bebc2171c010))
+ (pad "4" thru_hole circle (at 1.4 8.640001) (size 3.4036 3.4036) (drill 3.4036) (layers *.Cu *.Mask) (tstamp c82e0ef2-1666-473e-98f1-5cb9bb1c6ca1))
+ (model "${KIPRJMOD}/3D model/70555-0036_stp/705550036.stp"
+ (offset (xyz -1.27 -7.1882 2.794))
+ (scale (xyz 1 1 1))
+ (rotate (xyz -90 0 0))
+ )
+)
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/fp-info-cache b/circuit_boards/CAN_boards/CAN_Terminator/fp-info-cache
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/fp-info-cache
@@ -0,0 +1 @@
+0
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/fp-lib-table b/circuit_boards/CAN_boards/CAN_Terminator/fp-lib-table
new file mode 100644
index 0000000..c2ac0ea
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/fp-lib-table
@@ -0,0 +1,3 @@
+(fp_lib_table
+ (lib (name "CAN_terminator")(type "KiCad")(uri "${KIPRJMOD}/CAN_terminator.pretty")(options "")(descr ""))
+)
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Cu.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Cu.gbr
new file mode 100644
index 0000000..7be1df4
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Cu.gbr
@@ -0,0 +1,107 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Copper,L2,Bot*%
+%TF.FilePolarity,Positive*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+G04 Aperture macros list*
+%AMRoundRect*
+0 Rectangle with rounded corners*
+0 $1 Rounding radius*
+0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
+0 Add a 4 corners polygon primitive as box body*
+4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
+0 Add four circle primitives for the rounded corners*
+1,1,$1+$1,$2,$3*
+1,1,$1+$1,$4,$5*
+1,1,$1+$1,$6,$7*
+1,1,$1+$1,$8,$9*
+0 Add four rect primitives between the rounded corners*
+20,1,$1+$1,$2,$3,$4,$5,0*
+20,1,$1+$1,$4,$5,$6,$7,0*
+20,1,$1+$1,$6,$7,$8,$9,0*
+20,1,$1+$1,$8,$9,$2,$3,0*%
+G04 Aperture macros list end*
+%TA.AperFunction,ComponentPad*%
+%ADD10R,1.524000X1.524000*%
+%TD*%
+%TA.AperFunction,ComponentPad*%
+%ADD11C,1.524000*%
+%TD*%
+%TA.AperFunction,ComponentPad*%
+%ADD12C,3.403600*%
+%TD*%
+%TA.AperFunction,SMDPad,CuDef*%
+%ADD13RoundRect,0.250000X-0.450000X0.262500X-0.450000X-0.262500X0.450000X-0.262500X0.450000X0.262500X0*%
+%TD*%
+%TA.AperFunction,Conductor*%
+%ADD14C,0.508000*%
+%TD*%
+G04 APERTURE END LIST*
+D10*
+%TO.P,J1,1,Pin_1*%
+%TO.N,/CAN_H*%
+X147320000Y-116840000D03*
+D11*
+%TO.P,J1,2,Pin_2*%
+%TO.N,/CAN_L*%
+X147320000Y-119380000D03*
+D12*
+%TO.P,J1,3*%
+%TO.N,N/C*%
+X155960001Y-120780000D03*
+%TO.P,J1,4*%
+X155960001Y-115440000D03*
+%TD*%
+D13*
+%TO.P,R1,1*%
+%TO.N,/CAN_H*%
+X150876000Y-117094000D03*
+%TO.P,R1,2*%
+%TO.N,/CAN_L*%
+X150876000Y-118919000D03*
+%TD*%
+D14*
+%TO.N,/CAN_H*%
+X147320000Y-116332000D02*
+X147320000Y-116840000D01*
+X149606000Y-115062000D02*
+X148590000Y-115062000D01*
+X150876000Y-116332000D02*
+X150876000Y-117094000D01*
+X150876000Y-116332000D02*
+G75*
+G03*
+X149606000Y-115062000I-1270000J0D01*
+G01*
+X147320000Y-116332000D02*
+G75*
+G02*
+X148590000Y-115062000I1270000J0D01*
+G01*
+%TO.N,/CAN_L*%
+X148590000Y-121158000D02*
+X149606000Y-121158000D01*
+X147320000Y-119380000D02*
+X147320000Y-119888000D01*
+X150876000Y-119888000D02*
+X150876000Y-118919000D01*
+X148590000Y-121158000D02*
+G75*
+G02*
+X147320000Y-119888000I0J1270000D01*
+G01*
+X150876000Y-119888000D02*
+G75*
+G02*
+X149606000Y-121158000I-1270000J0D01*
+G01*
+%TD*%
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Mask.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Mask.gbr
new file mode 100644
index 0000000..14a0e36
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Mask.gbr
@@ -0,0 +1,51 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Soldermask,Bot*%
+%TF.FilePolarity,Negative*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+G04 Aperture macros list*
+%AMRoundRect*
+0 Rectangle with rounded corners*
+0 $1 Rounding radius*
+0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
+0 Add a 4 corners polygon primitive as box body*
+4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
+0 Add four circle primitives for the rounded corners*
+1,1,$1+$1,$2,$3*
+1,1,$1+$1,$4,$5*
+1,1,$1+$1,$6,$7*
+1,1,$1+$1,$8,$9*
+0 Add four rect primitives between the rounded corners*
+20,1,$1+$1,$2,$3,$4,$5,0*
+20,1,$1+$1,$4,$5,$6,$7,0*
+20,1,$1+$1,$6,$7,$8,$9,0*
+20,1,$1+$1,$8,$9,$2,$3,0*%
+G04 Aperture macros list end*
+%ADD10R,1.524000X1.524000*%
+%ADD11C,1.524000*%
+%ADD12C,3.403600*%
+%ADD13RoundRect,0.250000X-0.450000X0.262500X-0.450000X-0.262500X0.450000X-0.262500X0.450000X0.262500X0*%
+G04 APERTURE END LIST*
+D10*
+%TO.C,J1*%
+X147320000Y-116840000D03*
+D11*
+X147320000Y-119380000D03*
+D12*
+X155960001Y-120780000D03*
+X155960001Y-115440000D03*
+%TD*%
+D13*
+%TO.C,R1*%
+X150876000Y-117094000D03*
+X150876000Y-118919000D03*
+%TD*%
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Paste.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Paste.gbr
new file mode 100644
index 0000000..5285611
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Paste.gbr
@@ -0,0 +1,39 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Paste,Bot*%
+%TF.FilePolarity,Positive*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+G04 Aperture macros list*
+%AMRoundRect*
+0 Rectangle with rounded corners*
+0 $1 Rounding radius*
+0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
+0 Add a 4 corners polygon primitive as box body*
+4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
+0 Add four circle primitives for the rounded corners*
+1,1,$1+$1,$2,$3*
+1,1,$1+$1,$4,$5*
+1,1,$1+$1,$6,$7*
+1,1,$1+$1,$8,$9*
+0 Add four rect primitives between the rounded corners*
+20,1,$1+$1,$2,$3,$4,$5,0*
+20,1,$1+$1,$4,$5,$6,$7,0*
+20,1,$1+$1,$6,$7,$8,$9,0*
+20,1,$1+$1,$8,$9,$2,$3,0*%
+G04 Aperture macros list end*
+%ADD10RoundRect,0.250000X-0.450000X0.262500X-0.450000X-0.262500X0.450000X-0.262500X0.450000X0.262500X0*%
+G04 APERTURE END LIST*
+D10*
+%TO.C,R1*%
+X150876000Y-117094000D03*
+X150876000Y-118919000D03*
+%TD*%
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Silkscreen.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Silkscreen.gbr
new file mode 100644
index 0000000..803aa18
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-B_Silkscreen.gbr
@@ -0,0 +1,279 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Legend,Bot*%
+%TF.FilePolarity,Positive*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+%ADD10C,0.150000*%
+%ADD11C,0.127000*%
+%ADD12C,0.120000*%
+G04 APERTURE END LIST*
+D10*
+X158583380Y-114198876D02*
+X159583380Y-114532209D01*
+X158583380Y-114865542D01*
+X159583380Y-115722685D02*
+X159583380Y-115151257D01*
+X159583380Y-115436971D02*
+X158583380Y-115436971D01*
+X158726238Y-115341733D01*
+X158821476Y-115246495D01*
+X158869095Y-115151257D01*
+X159488142Y-116151257D02*
+X159535761Y-116198876D01*
+X159583380Y-116151257D01*
+X159535761Y-116103638D01*
+X159488142Y-116151257D01*
+X159583380Y-116151257D01*
+X158583380Y-116817923D02*
+X158583380Y-116913161D01*
+X158631000Y-117008400D01*
+X158678619Y-117056019D01*
+X158773857Y-117103638D01*
+X158964333Y-117151257D01*
+X159202428Y-117151257D01*
+X159392904Y-117103638D01*
+X159488142Y-117056019D01*
+X159535761Y-117008400D01*
+X159583380Y-116913161D01*
+X159583380Y-116817923D01*
+X159535761Y-116722685D01*
+X159488142Y-116675066D01*
+X159392904Y-116627447D01*
+X159202428Y-116579828D01*
+X158964333Y-116579828D01*
+X158773857Y-116627447D01*
+X158678619Y-116675066D01*
+X158631000Y-116722685D01*
+X158583380Y-116817923D01*
+X158678619Y-118294114D02*
+X158631000Y-118341733D01*
+X158583380Y-118436971D01*
+X158583380Y-118675066D01*
+X158631000Y-118770304D01*
+X158678619Y-118817923D01*
+X158773857Y-118865542D01*
+X158869095Y-118865542D01*
+X159011952Y-118817923D01*
+X159583380Y-118246495D01*
+X159583380Y-118865542D01*
+X158583380Y-119484590D02*
+X158583380Y-119579828D01*
+X158631000Y-119675066D01*
+X158678619Y-119722685D01*
+X158773857Y-119770304D01*
+X158964333Y-119817923D01*
+X159202428Y-119817923D01*
+X159392904Y-119770304D01*
+X159488142Y-119722685D01*
+X159535761Y-119675066D01*
+X159583380Y-119579828D01*
+X159583380Y-119484590D01*
+X159535761Y-119389352D01*
+X159488142Y-119341733D01*
+X159392904Y-119294114D01*
+X159202428Y-119246495D01*
+X158964333Y-119246495D01*
+X158773857Y-119294114D01*
+X158678619Y-119341733D01*
+X158631000Y-119389352D01*
+X158583380Y-119484590D01*
+X158678619Y-120198876D02*
+X158631000Y-120246495D01*
+X158583380Y-120341733D01*
+X158583380Y-120579828D01*
+X158631000Y-120675066D01*
+X158678619Y-120722685D01*
+X158773857Y-120770304D01*
+X158869095Y-120770304D01*
+X159011952Y-120722685D01*
+X159583380Y-120151257D01*
+X159583380Y-120770304D01*
+X158678619Y-121151257D02*
+X158631000Y-121198876D01*
+X158583380Y-121294114D01*
+X158583380Y-121532209D01*
+X158631000Y-121627447D01*
+X158678619Y-121675066D01*
+X158773857Y-121722685D01*
+X158869095Y-121722685D01*
+X159011952Y-121675066D01*
+X159583380Y-121103638D01*
+X159583380Y-121722685D01*
+D11*
+X153287185Y-115301485D02*
+X153334357Y-115254314D01*
+X153381528Y-115112800D01*
+X153381528Y-115018457D01*
+X153334357Y-114876942D01*
+X153240014Y-114782600D01*
+X153145671Y-114735428D01*
+X152956985Y-114688257D01*
+X152815471Y-114688257D01*
+X152626785Y-114735428D01*
+X152532442Y-114782600D01*
+X152438100Y-114876942D01*
+X152390928Y-115018457D01*
+X152390928Y-115112800D01*
+X152438100Y-115254314D01*
+X152485271Y-115301485D01*
+X153098500Y-115678857D02*
+X153098500Y-116150571D01*
+X153381528Y-115584514D02*
+X152390928Y-115914714D01*
+X153381528Y-116244914D01*
+X153381528Y-116575114D02*
+X152390928Y-116575114D01*
+X153381528Y-117141171D01*
+X152390928Y-117141171D01*
+X152390928Y-118226114D02*
+X152390928Y-118792171D01*
+X153381528Y-118509142D02*
+X152390928Y-118509142D01*
+X153334357Y-119499742D02*
+X153381528Y-119405400D01*
+X153381528Y-119216714D01*
+X153334357Y-119122371D01*
+X153240014Y-119075200D01*
+X152862642Y-119075200D01*
+X152768300Y-119122371D01*
+X152721128Y-119216714D01*
+X152721128Y-119405400D01*
+X152768300Y-119499742D01*
+X152862642Y-119546914D01*
+X152956985Y-119546914D01*
+X153051328Y-119075200D01*
+X153381528Y-119971457D02*
+X152721128Y-119971457D01*
+X152909814Y-119971457D02*
+X152815471Y-120018628D01*
+X152768300Y-120065800D01*
+X152721128Y-120160142D01*
+X152721128Y-120254485D01*
+X153381528Y-120584685D02*
+X152721128Y-120584685D01*
+X152815471Y-120584685D02*
+X152768300Y-120631857D01*
+X152721128Y-120726200D01*
+X152721128Y-120867714D01*
+X152768300Y-120962057D01*
+X152862642Y-121009228D01*
+X153381528Y-121009228D01*
+X152862642Y-121009228D02*
+X152768300Y-121056400D01*
+X152721128Y-121150742D01*
+X152721128Y-121292257D01*
+X152768300Y-121386600D01*
+X152862642Y-121433771D01*
+X153381528Y-121433771D01*
+X156997400Y-118588366D02*
+X156828066Y-118588366D01*
+X156743400Y-118546033D01*
+X156701066Y-118503700D01*
+X156616400Y-118376700D01*
+X156574066Y-118207366D01*
+X156574066Y-117868700D01*
+X156616400Y-117784033D01*
+X156658733Y-117741700D01*
+X156743400Y-117699366D01*
+X156912733Y-117699366D01*
+X156997400Y-117741700D01*
+X157039733Y-117784033D01*
+X157082066Y-117868700D01*
+X157082066Y-118080366D01*
+X157039733Y-118165033D01*
+X156997400Y-118207366D01*
+X156912733Y-118249700D01*
+X156743400Y-118249700D01*
+X156658733Y-118207366D01*
+X156616400Y-118165033D01*
+X156574066Y-118080366D01*
+X156277733Y-117699366D02*
+X155685066Y-117699366D01*
+X156066066Y-118588366D01*
+X154880733Y-118588366D02*
+X155388733Y-118588366D01*
+X155134733Y-118588366D02*
+X155134733Y-117699366D01*
+X155219400Y-117826366D01*
+X155304066Y-117911033D01*
+X155388733Y-117953366D01*
+D10*
+X150645619Y-115292142D02*
+X150693238Y-115339761D01*
+X150836095Y-115387380D01*
+X150931333Y-115387380D01*
+X151074190Y-115339761D01*
+X151169428Y-115244523D01*
+X151217047Y-115149285D01*
+X151264666Y-114958809D01*
+X151264666Y-114815952D01*
+X151217047Y-114625476D01*
+X151169428Y-114530238D01*
+X151074190Y-114435000D01*
+X150931333Y-114387380D01*
+X150836095Y-114387380D01*
+X150693238Y-114435000D01*
+X150645619Y-114482619D01*
+X150264666Y-115101666D02*
+X149788476Y-115101666D01*
+X150359904Y-115387380D02*
+X150026571Y-114387380D01*
+X149693238Y-115387380D01*
+X149359904Y-115387380D02*
+X149359904Y-114387380D01*
+X148788476Y-115387380D01*
+X148788476Y-114387380D01*
+X148550380Y-115482619D02*
+X147788476Y-115482619D01*
+X147550380Y-115387380D02*
+X147550380Y-114387380D01*
+X147550380Y-114863571D02*
+X146978952Y-114863571D01*
+X146978952Y-115387380D02*
+X146978952Y-114387380D01*
+X150526571Y-121642142D02*
+X150574190Y-121689761D01*
+X150717047Y-121737380D01*
+X150812285Y-121737380D01*
+X150955142Y-121689761D01*
+X151050380Y-121594523D01*
+X151098000Y-121499285D01*
+X151145619Y-121308809D01*
+X151145619Y-121165952D01*
+X151098000Y-120975476D01*
+X151050380Y-120880238D01*
+X150955142Y-120785000D01*
+X150812285Y-120737380D01*
+X150717047Y-120737380D01*
+X150574190Y-120785000D01*
+X150526571Y-120832619D01*
+X150145619Y-121451666D02*
+X149669428Y-121451666D01*
+X150240857Y-121737380D02*
+X149907523Y-120737380D01*
+X149574190Y-121737380D01*
+X149240857Y-121737380D02*
+X149240857Y-120737380D01*
+X148669428Y-121737380D01*
+X148669428Y-120737380D01*
+X148431333Y-121832619D02*
+X147669428Y-121832619D01*
+X146955142Y-121737380D02*
+X147431333Y-121737380D01*
+X147431333Y-120737380D01*
+D12*
+%TO.C,R1*%
+X150141000Y-117779436D02*
+X150141000Y-118233564D01*
+X151611000Y-117779436D02*
+X151611000Y-118233564D01*
+%TD*%
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-Edge_Cuts.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-Edge_Cuts.gbr
new file mode 100644
index 0000000..21a8b1d
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-Edge_Cuts.gbr
@@ -0,0 +1,46 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Profile,NP*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+%TA.AperFunction,Profile*%
+%ADD10C,0.100000*%
+%TD*%
+G04 APERTURE END LIST*
+D10*
+X159893000Y-123825000D02*
+G75*
+G03*
+X160528000Y-123190000I0J635000D01*
+G01*
+X146050000Y-112395000D02*
+G75*
+G03*
+X145415000Y-113030000I0J-635000D01*
+G01*
+X145415000Y-113030000D02*
+X145415000Y-123190000D01*
+X160528000Y-113030000D02*
+X160528000Y-123190000D01*
+X146050000Y-123825000D02*
+X159893000Y-123825000D01*
+X145415000Y-123190000D02*
+G75*
+G03*
+X146050000Y-123825000I635000J0D01*
+G01*
+X146050000Y-112395000D02*
+X159893000Y-112395000D01*
+X160528000Y-113030000D02*
+G75*
+G03*
+X159893000Y-112395000I-635000J0D01*
+G01*
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Cu.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Cu.gbr
new file mode 100644
index 0000000..8b1efbe
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Cu.gbr
@@ -0,0 +1,39 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Copper,L1,Top*%
+%TF.FilePolarity,Positive*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+%TA.AperFunction,ComponentPad*%
+%ADD10R,1.524000X1.524000*%
+%TD*%
+%TA.AperFunction,ComponentPad*%
+%ADD11C,1.524000*%
+%TD*%
+%TA.AperFunction,ComponentPad*%
+%ADD12C,3.403600*%
+%TD*%
+G04 APERTURE END LIST*
+D10*
+%TO.P,J1,1,Pin_1*%
+%TO.N,/CAN_H*%
+X147320000Y-116840000D03*
+D11*
+%TO.P,J1,2,Pin_2*%
+%TO.N,/CAN_L*%
+X147320000Y-119380000D03*
+D12*
+%TO.P,J1,3*%
+%TO.N,N/C*%
+X155960001Y-120780000D03*
+%TO.P,J1,4*%
+X155960001Y-115440000D03*
+%TD*%
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Mask.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Mask.gbr
new file mode 100644
index 0000000..21565ee
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Mask.gbr
@@ -0,0 +1,27 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Soldermask,Top*%
+%TF.FilePolarity,Negative*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+%ADD10R,1.524000X1.524000*%
+%ADD11C,1.524000*%
+%ADD12C,3.403600*%
+G04 APERTURE END LIST*
+D10*
+%TO.C,J1*%
+X147320000Y-116840000D03*
+D11*
+X147320000Y-119380000D03*
+D12*
+X155960001Y-120780000D03*
+X155960001Y-115440000D03*
+%TD*%
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Paste.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Paste.gbr
new file mode 100644
index 0000000..d2674b0
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Paste.gbr
@@ -0,0 +1,15 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Paste,Top*%
+%TF.FilePolarity,Positive*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+G04 APERTURE END LIST*
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Silkscreen.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Silkscreen.gbr
new file mode 100644
index 0000000..2fbc550
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-F_Silkscreen.gbr
@@ -0,0 +1,46 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T13:59:53-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Legend,Top*%
+%TF.FilePolarity,Positive*%
+%FSLAX46Y46*%
+G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 13:59:53*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+%ADD10C,0.150000*%
+%ADD11C,0.120000*%
+G04 APERTURE END LIST*
+D10*
+G36*
+X146177000Y-116840000D02*
+G01*
+X145542000Y-117475000D01*
+X145542000Y-116205000D01*
+X146177000Y-116840000D01*
+G37*
+X146177000Y-116840000D02*
+X145542000Y-117475000D01*
+X145542000Y-116205000D01*
+X146177000Y-116840000D01*
+D11*
+%TO.C,J1*%
+X156931330Y-122567700D02*
+X160386001Y-122567700D01*
+X154988671Y-113652300D02*
+X146543000Y-113652300D01*
+X146543000Y-120151185D02*
+X146543000Y-122567700D01*
+X146543000Y-122567700D02*
+X154988671Y-122567700D01*
+X160386001Y-113652300D02*
+X156931330Y-113652300D01*
+X146543000Y-113652300D02*
+X146543000Y-115570000D01*
+X160386001Y-122567700D02*
+X160386001Y-113652300D01*
+%TD*%
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH-drl_map.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH-drl_map.gbr
new file mode 100644
index 0000000..839ba5c
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH-drl_map.gbr
@@ -0,0 +1,130 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T14:00:31-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Drillmap*%
+%TF.FilePolarity,Positive*%
+%FSLAX45Y45*%
+G04 Gerber Fmt 4.5, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 14:00:31*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+%ADD10C,0.100000*%
+%ADD11C,0.200000*%
+G04 APERTURE END LIST*
+D10*
+X15989300Y-12382500D02*
+G75*
+G03*
+X16052800Y-12319000I0J63500D01*
+G01*
+X14605000Y-11239500D02*
+G75*
+G03*
+X14541500Y-11303000I0J-63500D01*
+G01*
+X14541500Y-11303000D02*
+X14541500Y-12319000D01*
+X16052800Y-11303000D02*
+X16052800Y-12319000D01*
+X14605000Y-12382500D02*
+X15989300Y-12382500D01*
+X14541500Y-12319000D02*
+G75*
+G03*
+X14605000Y-12382500I63500J0D01*
+G01*
+X14605000Y-11239500D02*
+X15989300Y-11239500D01*
+X16052800Y-11303000D02*
+G75*
+G03*
+X15989300Y-11239500I-63500J0D01*
+G01*
+D11*
+X14794119Y-12697976D02*
+X14794119Y-12497976D01*
+X14841738Y-12497976D01*
+X14870309Y-12507500D01*
+X14889357Y-12526548D01*
+X14898881Y-12545595D01*
+X14908405Y-12583690D01*
+X14908405Y-12612262D01*
+X14898881Y-12650357D01*
+X14889357Y-12669405D01*
+X14870309Y-12688452D01*
+X14841738Y-12697976D01*
+X14794119Y-12697976D01*
+X14994119Y-12697976D02*
+X14994119Y-12564643D01*
+X14994119Y-12602738D02*
+X15003643Y-12583690D01*
+X15013167Y-12574167D01*
+X15032214Y-12564643D01*
+X15051262Y-12564643D01*
+X15117928Y-12697976D02*
+X15117928Y-12564643D01*
+X15117928Y-12497976D02*
+X15108405Y-12507500D01*
+X15117928Y-12517024D01*
+X15127452Y-12507500D01*
+X15117928Y-12497976D01*
+X15117928Y-12517024D01*
+X15241738Y-12697976D02*
+X15222690Y-12688452D01*
+X15213167Y-12669405D01*
+X15213167Y-12497976D01*
+X15346500Y-12697976D02*
+X15327452Y-12688452D01*
+X15317928Y-12669405D01*
+X15317928Y-12497976D01*
+X15575071Y-12697976D02*
+X15575071Y-12497976D01*
+X15641738Y-12640833D01*
+X15708405Y-12497976D01*
+X15708405Y-12697976D01*
+X15889357Y-12697976D02*
+X15889357Y-12593214D01*
+X15879833Y-12574167D01*
+X15860786Y-12564643D01*
+X15822690Y-12564643D01*
+X15803643Y-12574167D01*
+X15889357Y-12688452D02*
+X15870309Y-12697976D01*
+X15822690Y-12697976D01*
+X15803643Y-12688452D01*
+X15794119Y-12669405D01*
+X15794119Y-12650357D01*
+X15803643Y-12631309D01*
+X15822690Y-12621786D01*
+X15870309Y-12621786D01*
+X15889357Y-12612262D01*
+X15984595Y-12564643D02*
+X15984595Y-12764643D01*
+X15984595Y-12574167D02*
+X16003643Y-12564643D01*
+X16041738Y-12564643D01*
+X16060786Y-12574167D01*
+X16070309Y-12583690D01*
+X16079833Y-12602738D01*
+X16079833Y-12659881D01*
+X16070309Y-12678928D01*
+X16060786Y-12688452D01*
+X16041738Y-12697976D01*
+X16003643Y-12697976D01*
+X15984595Y-12688452D01*
+X16165548Y-12678928D02*
+X16175071Y-12688452D01*
+X16165548Y-12697976D01*
+X16156024Y-12688452D01*
+X16165548Y-12678928D01*
+X16165548Y-12697976D01*
+X16165548Y-12574167D02*
+X16175071Y-12583690D01*
+X16165548Y-12593214D01*
+X16156024Y-12583690D01*
+X16165548Y-12574167D01*
+X16165548Y-12593214D01*
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH-drl_map.pdf b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH-drl_map.pdf
new file mode 100644
index 0000000..7b6eddc
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH-drl_map.pdf
Binary files differ
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH.drl b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH.drl
new file mode 100644
index 0000000..f9b3ac6
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-NPTH.drl
@@ -0,0 +1,13 @@
+M48
+; DRILL file {KiCad 6.0.7-f9a2dced07~116~ubuntu20.04.1} date Sat 17 Sep 2022 02:00:29 PM PDT
+; FORMAT={-:-/ absolute / inch / decimal}
+; #@! TF.CreationDate,2022-09-17T14:00:29-07:00
+; #@! TF.GenerationSoftware,Kicad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1
+; #@! TF.FileFunction,NonPlated,1,2,NPTH
+FMAT,2
+INCH
+%
+G90
+G05
+T0
+M30
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH-drl_map.gbr b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH-drl_map.gbr
new file mode 100644
index 0000000..d3847cb
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH-drl_map.gbr
@@ -0,0 +1,755 @@
+%TF.GenerationSoftware,KiCad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1*%
+%TF.CreationDate,2022-09-17T14:00:31-07:00*%
+%TF.ProjectId,CAN_terminator,43414e5f-7465-4726-9d69-6e61746f722e,rev?*%
+%TF.SameCoordinates,Original*%
+%TF.FileFunction,Drillmap*%
+%TF.FilePolarity,Positive*%
+%FSLAX45Y45*%
+G04 Gerber Fmt 4.5, Leading zero omitted, Abs format (unit mm)*
+G04 Created by KiCad (PCBNEW 6.0.7-f9a2dced07~116~ubuntu20.04.1) date 2022-09-17 14:00:31*
+%MOMM*%
+%LPD*%
+G01*
+G04 APERTURE LIST*
+%ADD10C,0.100000*%
+%ADD11C,0.200000*%
+%ADD12C,0.096520*%
+%ADD13C,0.340360*%
+G04 APERTURE END LIST*
+D10*
+X15989300Y-12382500D02*
+G75*
+G03*
+X16052800Y-12319000I0J63500D01*
+G01*
+X14605000Y-11239500D02*
+G75*
+G03*
+X14541500Y-11303000I0J-63500D01*
+G01*
+X14541500Y-11303000D02*
+X14541500Y-12319000D01*
+X16052800Y-11303000D02*
+X16052800Y-12319000D01*
+X14605000Y-12382500D02*
+X15989300Y-12382500D01*
+X14541500Y-12319000D02*
+G75*
+G03*
+X14605000Y-12382500I63500J0D01*
+G01*
+X14605000Y-11239500D02*
+X15989300Y-11239500D01*
+X16052800Y-11303000D02*
+G75*
+G03*
+X15989300Y-11239500I-63500J0D01*
+G01*
+D11*
+D12*
+X14683740Y-11635740D02*
+X14780260Y-11732260D01*
+X14780260Y-11635740D02*
+X14683740Y-11732260D01*
+X14683740Y-11889740D02*
+X14780260Y-11986260D01*
+X14780260Y-11889740D02*
+X14683740Y-11986260D01*
+D13*
+X15766180Y-11544000D02*
+G75*
+G03*
+X15766180Y-11544000I-170180J0D01*
+G01*
+X15766180Y-12078000D02*
+G75*
+G03*
+X15766180Y-12078000I-170180J0D01*
+G01*
+D11*
+X14794119Y-12697976D02*
+X14794119Y-12497976D01*
+X14841738Y-12497976D01*
+X14870309Y-12507500D01*
+X14889357Y-12526548D01*
+X14898881Y-12545595D01*
+X14908405Y-12583690D01*
+X14908405Y-12612262D01*
+X14898881Y-12650357D01*
+X14889357Y-12669405D01*
+X14870309Y-12688452D01*
+X14841738Y-12697976D01*
+X14794119Y-12697976D01*
+X14994119Y-12697976D02*
+X14994119Y-12564643D01*
+X14994119Y-12602738D02*
+X15003643Y-12583690D01*
+X15013167Y-12574167D01*
+X15032214Y-12564643D01*
+X15051262Y-12564643D01*
+X15117928Y-12697976D02*
+X15117928Y-12564643D01*
+X15117928Y-12497976D02*
+X15108405Y-12507500D01*
+X15117928Y-12517024D01*
+X15127452Y-12507500D01*
+X15117928Y-12497976D01*
+X15117928Y-12517024D01*
+X15241738Y-12697976D02*
+X15222690Y-12688452D01*
+X15213167Y-12669405D01*
+X15213167Y-12497976D01*
+X15346500Y-12697976D02*
+X15327452Y-12688452D01*
+X15317928Y-12669405D01*
+X15317928Y-12497976D01*
+X15575071Y-12697976D02*
+X15575071Y-12497976D01*
+X15641738Y-12640833D01*
+X15708405Y-12497976D01*
+X15708405Y-12697976D01*
+X15889357Y-12697976D02*
+X15889357Y-12593214D01*
+X15879833Y-12574167D01*
+X15860786Y-12564643D01*
+X15822690Y-12564643D01*
+X15803643Y-12574167D01*
+X15889357Y-12688452D02*
+X15870309Y-12697976D01*
+X15822690Y-12697976D01*
+X15803643Y-12688452D01*
+X15794119Y-12669405D01*
+X15794119Y-12650357D01*
+X15803643Y-12631309D01*
+X15822690Y-12621786D01*
+X15870309Y-12621786D01*
+X15889357Y-12612262D01*
+X15984595Y-12564643D02*
+X15984595Y-12764643D01*
+X15984595Y-12574167D02*
+X16003643Y-12564643D01*
+X16041738Y-12564643D01*
+X16060786Y-12574167D01*
+X16070309Y-12583690D01*
+X16079833Y-12602738D01*
+X16079833Y-12659881D01*
+X16070309Y-12678928D01*
+X16060786Y-12688452D01*
+X16041738Y-12697976D01*
+X16003643Y-12697976D01*
+X15984595Y-12688452D01*
+X16165548Y-12678928D02*
+X16175071Y-12688452D01*
+X16165548Y-12697976D01*
+X16156024Y-12688452D01*
+X16165548Y-12678928D01*
+X16165548Y-12697976D01*
+X16165548Y-12574167D02*
+X16175071Y-12583690D01*
+X16165548Y-12593214D01*
+X16156024Y-12583690D01*
+X16165548Y-12574167D01*
+X16165548Y-12593214D01*
+D12*
+X14439980Y-12979240D02*
+X14536500Y-13075760D01*
+X14536500Y-12979240D02*
+X14439980Y-13075760D01*
+D11*
+X14832214Y-12917976D02*
+X14851262Y-12917976D01*
+X14870309Y-12927500D01*
+X14879833Y-12937024D01*
+X14889357Y-12956071D01*
+X14898881Y-12994167D01*
+X14898881Y-13041786D01*
+X14889357Y-13079881D01*
+X14879833Y-13098928D01*
+X14870309Y-13108452D01*
+X14851262Y-13117976D01*
+X14832214Y-13117976D01*
+X14813167Y-13108452D01*
+X14803643Y-13098928D01*
+X14794119Y-13079881D01*
+X14784595Y-13041786D01*
+X14784595Y-12994167D01*
+X14794119Y-12956071D01*
+X14803643Y-12937024D01*
+X14813167Y-12927500D01*
+X14832214Y-12917976D01*
+X14984595Y-13098928D02*
+X14994119Y-13108452D01*
+X14984595Y-13117976D01*
+X14975071Y-13108452D01*
+X14984595Y-13098928D01*
+X14984595Y-13117976D01*
+X15089357Y-13117976D02*
+X15127452Y-13117976D01*
+X15146500Y-13108452D01*
+X15156024Y-13098928D01*
+X15175071Y-13070357D01*
+X15184595Y-13032262D01*
+X15184595Y-12956071D01*
+X15175071Y-12937024D01*
+X15165548Y-12927500D01*
+X15146500Y-12917976D01*
+X15108405Y-12917976D01*
+X15089357Y-12927500D01*
+X15079833Y-12937024D01*
+X15070309Y-12956071D01*
+X15070309Y-13003690D01*
+X15079833Y-13022738D01*
+X15089357Y-13032262D01*
+X15108405Y-13041786D01*
+X15146500Y-13041786D01*
+X15165548Y-13032262D01*
+X15175071Y-13022738D01*
+X15184595Y-13003690D01*
+X15356024Y-12917976D02*
+X15317928Y-12917976D01*
+X15298881Y-12927500D01*
+X15289357Y-12937024D01*
+X15270309Y-12965595D01*
+X15260786Y-13003690D01*
+X15260786Y-13079881D01*
+X15270309Y-13098928D01*
+X15279833Y-13108452D01*
+X15298881Y-13117976D01*
+X15336976Y-13117976D01*
+X15356024Y-13108452D01*
+X15365548Y-13098928D01*
+X15375071Y-13079881D01*
+X15375071Y-13032262D01*
+X15365548Y-13013214D01*
+X15356024Y-13003690D01*
+X15336976Y-12994167D01*
+X15298881Y-12994167D01*
+X15279833Y-13003690D01*
+X15270309Y-13013214D01*
+X15260786Y-13032262D01*
+X15556024Y-12917976D02*
+X15460786Y-12917976D01*
+X15451262Y-13013214D01*
+X15460786Y-13003690D01*
+X15479833Y-12994167D01*
+X15527452Y-12994167D01*
+X15546500Y-13003690D01*
+X15556024Y-13013214D01*
+X15565548Y-13032262D01*
+X15565548Y-13079881D01*
+X15556024Y-13098928D01*
+X15546500Y-13108452D01*
+X15527452Y-13117976D01*
+X15479833Y-13117976D01*
+X15460786Y-13108452D01*
+X15451262Y-13098928D01*
+X15651262Y-13117976D02*
+X15651262Y-12984643D01*
+X15651262Y-13003690D02*
+X15660786Y-12994167D01*
+X15679833Y-12984643D01*
+X15708405Y-12984643D01*
+X15727452Y-12994167D01*
+X15736976Y-13013214D01*
+X15736976Y-13117976D01*
+X15736976Y-13013214D02*
+X15746500Y-12994167D01*
+X15765548Y-12984643D01*
+X15794119Y-12984643D01*
+X15813167Y-12994167D01*
+X15822690Y-13013214D01*
+X15822690Y-13117976D01*
+X15917928Y-13117976D02*
+X15917928Y-12984643D01*
+X15917928Y-13003690D02*
+X15927452Y-12994167D01*
+X15946500Y-12984643D01*
+X15975071Y-12984643D01*
+X15994119Y-12994167D01*
+X16003643Y-13013214D01*
+X16003643Y-13117976D01*
+X16003643Y-13013214D02*
+X16013167Y-12994167D01*
+X16032214Y-12984643D01*
+X16060786Y-12984643D01*
+X16079833Y-12994167D01*
+X16089357Y-13013214D01*
+X16089357Y-13117976D01*
+X16479833Y-12908452D02*
+X16308405Y-13165595D01*
+X16736976Y-12917976D02*
+X16756024Y-12917976D01*
+X16775071Y-12927500D01*
+X16784595Y-12937024D01*
+X16794119Y-12956071D01*
+X16803643Y-12994167D01*
+X16803643Y-13041786D01*
+X16794119Y-13079881D01*
+X16784595Y-13098928D01*
+X16775071Y-13108452D01*
+X16756024Y-13117976D01*
+X16736976Y-13117976D01*
+X16717928Y-13108452D01*
+X16708405Y-13098928D01*
+X16698881Y-13079881D01*
+X16689357Y-13041786D01*
+X16689357Y-12994167D01*
+X16698881Y-12956071D01*
+X16708405Y-12937024D01*
+X16717928Y-12927500D01*
+X16736976Y-12917976D01*
+X16889357Y-13098928D02*
+X16898881Y-13108452D01*
+X16889357Y-13117976D01*
+X16879833Y-13108452D01*
+X16889357Y-13098928D01*
+X16889357Y-13117976D01*
+X17022690Y-12917976D02*
+X17041738Y-12917976D01*
+X17060786Y-12927500D01*
+X17070310Y-12937024D01*
+X17079833Y-12956071D01*
+X17089357Y-12994167D01*
+X17089357Y-13041786D01*
+X17079833Y-13079881D01*
+X17070310Y-13098928D01*
+X17060786Y-13108452D01*
+X17041738Y-13117976D01*
+X17022690Y-13117976D01*
+X17003643Y-13108452D01*
+X16994119Y-13098928D01*
+X16984595Y-13079881D01*
+X16975071Y-13041786D01*
+X16975071Y-12994167D01*
+X16984595Y-12956071D01*
+X16994119Y-12937024D01*
+X17003643Y-12927500D01*
+X17022690Y-12917976D01*
+X17156024Y-12917976D02*
+X17279833Y-12917976D01*
+X17213167Y-12994167D01*
+X17241738Y-12994167D01*
+X17260786Y-13003690D01*
+X17270310Y-13013214D01*
+X17279833Y-13032262D01*
+X17279833Y-13079881D01*
+X17270310Y-13098928D01*
+X17260786Y-13108452D01*
+X17241738Y-13117976D01*
+X17184595Y-13117976D01*
+X17165548Y-13108452D01*
+X17156024Y-13098928D01*
+X17394119Y-13003690D02*
+X17375071Y-12994167D01*
+X17365548Y-12984643D01*
+X17356024Y-12965595D01*
+X17356024Y-12956071D01*
+X17365548Y-12937024D01*
+X17375071Y-12927500D01*
+X17394119Y-12917976D01*
+X17432214Y-12917976D01*
+X17451262Y-12927500D01*
+X17460786Y-12937024D01*
+X17470310Y-12956071D01*
+X17470310Y-12965595D01*
+X17460786Y-12984643D01*
+X17451262Y-12994167D01*
+X17432214Y-13003690D01*
+X17394119Y-13003690D01*
+X17375071Y-13013214D01*
+X17365548Y-13022738D01*
+X17356024Y-13041786D01*
+X17356024Y-13079881D01*
+X17365548Y-13098928D01*
+X17375071Y-13108452D01*
+X17394119Y-13117976D01*
+X17432214Y-13117976D01*
+X17451262Y-13108452D01*
+X17460786Y-13098928D01*
+X17470310Y-13079881D01*
+X17470310Y-13041786D01*
+X17460786Y-13022738D01*
+X17451262Y-13013214D01*
+X17432214Y-13003690D01*
+X17594119Y-12917976D02*
+X17613167Y-12917976D01*
+X17632214Y-12927500D01*
+X17641738Y-12937024D01*
+X17651262Y-12956071D01*
+X17660786Y-12994167D01*
+X17660786Y-13041786D01*
+X17651262Y-13079881D01*
+X17641738Y-13098928D01*
+X17632214Y-13108452D01*
+X17613167Y-13117976D01*
+X17594119Y-13117976D01*
+X17575071Y-13108452D01*
+X17565548Y-13098928D01*
+X17556024Y-13079881D01*
+X17546500Y-13041786D01*
+X17546500Y-12994167D01*
+X17556024Y-12956071D01*
+X17565548Y-12937024D01*
+X17575071Y-12927500D01*
+X17594119Y-12917976D01*
+X17736976Y-12917976D02*
+X17736976Y-12956071D01*
+X17813167Y-12917976D02*
+X17813167Y-12956071D01*
+X18108405Y-13194167D02*
+X18098881Y-13184643D01*
+X18079833Y-13156071D01*
+X18070310Y-13137024D01*
+X18060786Y-13108452D01*
+X18051262Y-13060833D01*
+X18051262Y-13022738D01*
+X18060786Y-12975119D01*
+X18070310Y-12946548D01*
+X18079833Y-12927500D01*
+X18098881Y-12898928D01*
+X18108405Y-12889405D01*
+X18175071Y-12937024D02*
+X18184595Y-12927500D01*
+X18203643Y-12917976D01*
+X18251262Y-12917976D01*
+X18270310Y-12927500D01*
+X18279833Y-12937024D01*
+X18289357Y-12956071D01*
+X18289357Y-12975119D01*
+X18279833Y-13003690D01*
+X18165548Y-13117976D01*
+X18289357Y-13117976D01*
+X18527452Y-13117976D02*
+X18527452Y-12917976D01*
+X18613167Y-13117976D02*
+X18613167Y-13013214D01*
+X18603643Y-12994167D01*
+X18584595Y-12984643D01*
+X18556024Y-12984643D01*
+X18536976Y-12994167D01*
+X18527452Y-13003690D01*
+X18736976Y-13117976D02*
+X18717929Y-13108452D01*
+X18708405Y-13098928D01*
+X18698881Y-13079881D01*
+X18698881Y-13022738D01*
+X18708405Y-13003690D01*
+X18717929Y-12994167D01*
+X18736976Y-12984643D01*
+X18765548Y-12984643D01*
+X18784595Y-12994167D01*
+X18794119Y-13003690D01*
+X18803643Y-13022738D01*
+X18803643Y-13079881D01*
+X18794119Y-13098928D01*
+X18784595Y-13108452D01*
+X18765548Y-13117976D01*
+X18736976Y-13117976D01*
+X18917929Y-13117976D02*
+X18898881Y-13108452D01*
+X18889357Y-13089405D01*
+X18889357Y-12917976D01*
+X19070310Y-13108452D02*
+X19051262Y-13117976D01*
+X19013167Y-13117976D01*
+X18994119Y-13108452D01*
+X18984595Y-13089405D01*
+X18984595Y-13013214D01*
+X18994119Y-12994167D01*
+X19013167Y-12984643D01*
+X19051262Y-12984643D01*
+X19070310Y-12994167D01*
+X19079833Y-13013214D01*
+X19079833Y-13032262D01*
+X18984595Y-13051309D01*
+X19156024Y-13108452D02*
+X19175071Y-13117976D01*
+X19213167Y-13117976D01*
+X19232214Y-13108452D01*
+X19241738Y-13089405D01*
+X19241738Y-13079881D01*
+X19232214Y-13060833D01*
+X19213167Y-13051309D01*
+X19184595Y-13051309D01*
+X19165548Y-13041786D01*
+X19156024Y-13022738D01*
+X19156024Y-13013214D01*
+X19165548Y-12994167D01*
+X19184595Y-12984643D01*
+X19213167Y-12984643D01*
+X19232214Y-12994167D01*
+X19308405Y-13194167D02*
+X19317929Y-13184643D01*
+X19336976Y-13156071D01*
+X19346500Y-13137024D01*
+X19356024Y-13108452D01*
+X19365548Y-13060833D01*
+X19365548Y-13022738D01*
+X19356024Y-12975119D01*
+X19346500Y-12946548D01*
+X19336976Y-12927500D01*
+X19317929Y-12898928D01*
+X19308405Y-12889405D01*
+X14536500Y-13291500D02*
+G75*
+G03*
+X14536500Y-13291500I-100000J0D01*
+G01*
+X14775071Y-13181976D02*
+X14898881Y-13181976D01*
+X14832214Y-13258167D01*
+X14860786Y-13258167D01*
+X14879833Y-13267690D01*
+X14889357Y-13277214D01*
+X14898881Y-13296262D01*
+X14898881Y-13343881D01*
+X14889357Y-13362928D01*
+X14879833Y-13372452D01*
+X14860786Y-13381976D01*
+X14803643Y-13381976D01*
+X14784595Y-13372452D01*
+X14775071Y-13362928D01*
+X14984595Y-13362928D02*
+X14994119Y-13372452D01*
+X14984595Y-13381976D01*
+X14975071Y-13372452D01*
+X14984595Y-13362928D01*
+X14984595Y-13381976D01*
+X15165548Y-13248643D02*
+X15165548Y-13381976D01*
+X15117928Y-13172452D02*
+X15070309Y-13315309D01*
+X15194119Y-13315309D01*
+X15308405Y-13181976D02*
+X15327452Y-13181976D01*
+X15346500Y-13191500D01*
+X15356024Y-13201024D01*
+X15365548Y-13220071D01*
+X15375071Y-13258167D01*
+X15375071Y-13305786D01*
+X15365548Y-13343881D01*
+X15356024Y-13362928D01*
+X15346500Y-13372452D01*
+X15327452Y-13381976D01*
+X15308405Y-13381976D01*
+X15289357Y-13372452D01*
+X15279833Y-13362928D01*
+X15270309Y-13343881D01*
+X15260786Y-13305786D01*
+X15260786Y-13258167D01*
+X15270309Y-13220071D01*
+X15279833Y-13201024D01*
+X15289357Y-13191500D01*
+X15308405Y-13181976D01*
+X15546500Y-13248643D02*
+X15546500Y-13381976D01*
+X15498881Y-13172452D02*
+X15451262Y-13315309D01*
+X15575071Y-13315309D01*
+X15651262Y-13381976D02*
+X15651262Y-13248643D01*
+X15651262Y-13267690D02*
+X15660786Y-13258167D01*
+X15679833Y-13248643D01*
+X15708405Y-13248643D01*
+X15727452Y-13258167D01*
+X15736976Y-13277214D01*
+X15736976Y-13381976D01*
+X15736976Y-13277214D02*
+X15746500Y-13258167D01*
+X15765548Y-13248643D01*
+X15794119Y-13248643D01*
+X15813167Y-13258167D01*
+X15822690Y-13277214D01*
+X15822690Y-13381976D01*
+X15917928Y-13381976D02*
+X15917928Y-13248643D01*
+X15917928Y-13267690D02*
+X15927452Y-13258167D01*
+X15946500Y-13248643D01*
+X15975071Y-13248643D01*
+X15994119Y-13258167D01*
+X16003643Y-13277214D01*
+X16003643Y-13381976D01*
+X16003643Y-13277214D02*
+X16013167Y-13258167D01*
+X16032214Y-13248643D01*
+X16060786Y-13248643D01*
+X16079833Y-13258167D01*
+X16089357Y-13277214D01*
+X16089357Y-13381976D01*
+X16479833Y-13172452D02*
+X16308405Y-13429595D01*
+X16736976Y-13181976D02*
+X16756024Y-13181976D01*
+X16775071Y-13191500D01*
+X16784595Y-13201024D01*
+X16794119Y-13220071D01*
+X16803643Y-13258167D01*
+X16803643Y-13305786D01*
+X16794119Y-13343881D01*
+X16784595Y-13362928D01*
+X16775071Y-13372452D01*
+X16756024Y-13381976D01*
+X16736976Y-13381976D01*
+X16717928Y-13372452D01*
+X16708405Y-13362928D01*
+X16698881Y-13343881D01*
+X16689357Y-13305786D01*
+X16689357Y-13258167D01*
+X16698881Y-13220071D01*
+X16708405Y-13201024D01*
+X16717928Y-13191500D01*
+X16736976Y-13181976D01*
+X16889357Y-13362928D02*
+X16898881Y-13372452D01*
+X16889357Y-13381976D01*
+X16879833Y-13372452D01*
+X16889357Y-13362928D01*
+X16889357Y-13381976D01*
+X17089357Y-13381976D02*
+X16975071Y-13381976D01*
+X17032214Y-13381976D02*
+X17032214Y-13181976D01*
+X17013167Y-13210548D01*
+X16994119Y-13229595D01*
+X16975071Y-13239119D01*
+X17156024Y-13181976D02*
+X17279833Y-13181976D01*
+X17213167Y-13258167D01*
+X17241738Y-13258167D01*
+X17260786Y-13267690D01*
+X17270310Y-13277214D01*
+X17279833Y-13296262D01*
+X17279833Y-13343881D01*
+X17270310Y-13362928D01*
+X17260786Y-13372452D01*
+X17241738Y-13381976D01*
+X17184595Y-13381976D01*
+X17165548Y-13372452D01*
+X17156024Y-13362928D01*
+X17451262Y-13248643D02*
+X17451262Y-13381976D01*
+X17403643Y-13172452D02*
+X17356024Y-13315309D01*
+X17479833Y-13315309D01*
+X17594119Y-13181976D02*
+X17613167Y-13181976D01*
+X17632214Y-13191500D01*
+X17641738Y-13201024D01*
+X17651262Y-13220071D01*
+X17660786Y-13258167D01*
+X17660786Y-13305786D01*
+X17651262Y-13343881D01*
+X17641738Y-13362928D01*
+X17632214Y-13372452D01*
+X17613167Y-13381976D01*
+X17594119Y-13381976D01*
+X17575071Y-13372452D01*
+X17565548Y-13362928D01*
+X17556024Y-13343881D01*
+X17546500Y-13305786D01*
+X17546500Y-13258167D01*
+X17556024Y-13220071D01*
+X17565548Y-13201024D01*
+X17575071Y-13191500D01*
+X17594119Y-13181976D01*
+X17736976Y-13181976D02*
+X17736976Y-13220071D01*
+X17813167Y-13181976D02*
+X17813167Y-13220071D01*
+X18108405Y-13458167D02*
+X18098881Y-13448643D01*
+X18079833Y-13420071D01*
+X18070310Y-13401024D01*
+X18060786Y-13372452D01*
+X18051262Y-13324833D01*
+X18051262Y-13286738D01*
+X18060786Y-13239119D01*
+X18070310Y-13210548D01*
+X18079833Y-13191500D01*
+X18098881Y-13162928D01*
+X18108405Y-13153405D01*
+X18175071Y-13201024D02*
+X18184595Y-13191500D01*
+X18203643Y-13181976D01*
+X18251262Y-13181976D01*
+X18270310Y-13191500D01*
+X18279833Y-13201024D01*
+X18289357Y-13220071D01*
+X18289357Y-13239119D01*
+X18279833Y-13267690D01*
+X18165548Y-13381976D01*
+X18289357Y-13381976D01*
+X18527452Y-13381976D02*
+X18527452Y-13181976D01*
+X18613167Y-13381976D02*
+X18613167Y-13277214D01*
+X18603643Y-13258167D01*
+X18584595Y-13248643D01*
+X18556024Y-13248643D01*
+X18536976Y-13258167D01*
+X18527452Y-13267690D01*
+X18736976Y-13381976D02*
+X18717929Y-13372452D01*
+X18708405Y-13362928D01*
+X18698881Y-13343881D01*
+X18698881Y-13286738D01*
+X18708405Y-13267690D01*
+X18717929Y-13258167D01*
+X18736976Y-13248643D01*
+X18765548Y-13248643D01*
+X18784595Y-13258167D01*
+X18794119Y-13267690D01*
+X18803643Y-13286738D01*
+X18803643Y-13343881D01*
+X18794119Y-13362928D01*
+X18784595Y-13372452D01*
+X18765548Y-13381976D01*
+X18736976Y-13381976D01*
+X18917929Y-13381976D02*
+X18898881Y-13372452D01*
+X18889357Y-13353405D01*
+X18889357Y-13181976D01*
+X19070310Y-13372452D02*
+X19051262Y-13381976D01*
+X19013167Y-13381976D01*
+X18994119Y-13372452D01*
+X18984595Y-13353405D01*
+X18984595Y-13277214D01*
+X18994119Y-13258167D01*
+X19013167Y-13248643D01*
+X19051262Y-13248643D01*
+X19070310Y-13258167D01*
+X19079833Y-13277214D01*
+X19079833Y-13296262D01*
+X18984595Y-13315309D01*
+X19156024Y-13372452D02*
+X19175071Y-13381976D01*
+X19213167Y-13381976D01*
+X19232214Y-13372452D01*
+X19241738Y-13353405D01*
+X19241738Y-13343881D01*
+X19232214Y-13324833D01*
+X19213167Y-13315309D01*
+X19184595Y-13315309D01*
+X19165548Y-13305786D01*
+X19156024Y-13286738D01*
+X19156024Y-13277214D01*
+X19165548Y-13258167D01*
+X19184595Y-13248643D01*
+X19213167Y-13248643D01*
+X19232214Y-13258167D01*
+X19308405Y-13458167D02*
+X19317929Y-13448643D01*
+X19336976Y-13420071D01*
+X19346500Y-13401024D01*
+X19356024Y-13372452D01*
+X19365548Y-13324833D01*
+X19365548Y-13286738D01*
+X19356024Y-13239119D01*
+X19346500Y-13210548D01*
+X19336976Y-13191500D01*
+X19317929Y-13162928D01*
+X19308405Y-13153405D01*
+M02*
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH-drl_map.pdf b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH-drl_map.pdf
new file mode 100644
index 0000000..11056fe
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH-drl_map.pdf
Binary files differ
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH.drl b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH.drl
new file mode 100644
index 0000000..2ae858f
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-PTH.drl
@@ -0,0 +1,23 @@
+M48
+; DRILL file {KiCad 6.0.7-f9a2dced07~116~ubuntu20.04.1} date Sat 17 Sep 2022 02:00:29 PM PDT
+; FORMAT={-:-/ absolute / inch / decimal}
+; #@! TF.CreationDate,2022-09-17T14:00:29-07:00
+; #@! TF.GenerationSoftware,Kicad,Pcbnew,6.0.7-f9a2dced07~116~ubuntu20.04.1
+; #@! TF.FileFunction,Plated,1,2,PTH
+FMAT,2
+INCH
+; #@! TA.AperFunction,Plated,PTH,ComponentDrill
+T1C0.0380
+; #@! TA.AperFunction,Plated,PTH,ComponentDrill
+T2C0.1340
+%
+G90
+G05
+T1
+X5.8Y-4.6
+X5.8Y-4.7
+T2
+X6.1402Y-4.5449
+X6.1402Y-4.7551
+T0
+M30
diff --git a/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-job.gbrjob b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-job.gbrjob
new file mode 100644
index 0000000..5fb6bcb
--- /dev/null
+++ b/circuit_boards/CAN_boards/CAN_Terminator/gerbers/CAN_terminator-job.gbrjob
@@ -0,0 +1,120 @@
+{
+ "Header": {
+ "GenerationSoftware": {
+ "Vendor": "KiCad",
+ "Application": "Pcbnew",
+ "Version": "6.0.7-f9a2dced07~116~ubuntu20.04.1"
+ },
+ "CreationDate": "2022-09-17T13:59:53-07:00"
+ },
+ "GeneralSpecs": {
+ "ProjectId": {
+ "Name": "CAN_terminator",
+ "GUID": "43414e5f-7465-4726-9d69-6e61746f722e",
+ "Revision": "rev?"
+ },
+ "Size": {
+ "X": 15.213,
+ "Y": 11.53
+ },
+ "LayerNumber": 2,
+ "BoardThickness": 1.6,
+ "Finish": "None"
+ },
+ "DesignRules": [
+ {
+ "Layers": "Outer",
+ "PadToPad": 0.0,
+ "PadToTrack": 0.0,
+ "TrackToTrack": 0.2,
+ "MinLineWidth": 0.508
+ }
+ ],
+ "FilesAttributes": [
+ {
+ "Path": "CAN_terminator-F_Cu.gbr",
+ "FileFunction": "Copper,L1,Top",
+ "FilePolarity": "Positive"
+ },
+ {
+ "Path": "CAN_terminator-B_Cu.gbr",
+ "FileFunction": "Copper,L2,Bot",
+ "FilePolarity": "Positive"
+ },
+ {
+ "Path": "CAN_terminator-F_Paste.gbr",
+ "FileFunction": "SolderPaste,Top",
+ "FilePolarity": "Positive"
+ },
+ {
+ "Path": "CAN_terminator-B_Paste.gbr",
+ "FileFunction": "SolderPaste,Bot",
+ "FilePolarity": "Positive"
+ },
+ {
+ "Path": "CAN_terminator-F_Silkscreen.gbr",
+ "FileFunction": "Legend,Top",
+ "FilePolarity": "Positive"
+ },
+ {
+ "Path": "CAN_terminator-B_Silkscreen.gbr",
+ "FileFunction": "Legend,Bot",
+ "FilePolarity": "Positive"
+ },
+ {
+ "Path": "CAN_terminator-F_Mask.gbr",
+ "FileFunction": "SolderMask,Top",
+ "FilePolarity": "Negative"
+ },
+ {
+ "Path": "CAN_terminator-B_Mask.gbr",
+ "FileFunction": "SolderMask,Bot",
+ "FilePolarity": "Negative"
+ },
+ {
+ "Path": "CAN_terminator-Edge_Cuts.gbr",
+ "FileFunction": "Profile",
+ "FilePolarity": "Positive"
+ }
+ ],
+ "MaterialStackup": [
+ {
+ "Type": "Legend",
+ "Name": "Top Silk Screen"
+ },
+ {
+ "Type": "SolderPaste",
+ "Name": "Top Solder Paste"
+ },
+ {
+ "Type": "SolderMask",
+ "Name": "Top Solder Mask"
+ },
+ {
+ "Type": "Copper",
+ "Name": "F.Cu"
+ },
+ {
+ "Type": "Dielectric",
+ "Material": "FR4",
+ "Name": "F.Cu/B.Cu",
+ "Notes": "Type: dielectric layer 1 (from F.Cu to B.Cu)"
+ },
+ {
+ "Type": "Copper",
+ "Name": "B.Cu"
+ },
+ {
+ "Type": "SolderMask",
+ "Name": "Bottom Solder Mask"
+ },
+ {
+ "Type": "SolderPaste",
+ "Name": "Bottom Solder Paste"
+ },
+ {
+ "Type": "Legend",
+ "Name": "Bottom Silk Screen"
+ }
+ ]
+}
diff --git a/debian/BUILD.nghttp2.bazel b/debian/BUILD.nghttp2.bazel
new file mode 100644
index 0000000..cd055cd
--- /dev/null
+++ b/debian/BUILD.nghttp2.bazel
@@ -0,0 +1,71 @@
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+
+licenses(["notice"]) # MIT/X derivative license
+
+exports_files(["COPYING"])
+
+write_file(
+ name = "nghttp2ver.h",
+ out = "lib/includes/nghttp2/nghttp2ver.h",
+ content = """
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP2VER_H
+#define NGHTTP2VER_H
+
+/**
+ * @macro
+ * Version number of the nghttp2 library release
+ */
+#define NGHTTP2_VERSION "1.58.0"
+
+/**
+ * @macro
+ * Numerical representation of the version number of the nghttp2 library
+ * release. This is a 24 bit number with 8 bits for major number, 8 bits
+ * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
+ */
+#define NGHTTP2_VERSION_NUM 0x013a00
+
+#endif /* NGHTTP2VER_H */
+""".splitlines(),
+)
+
+cc_library(
+ name = "nghttp2",
+ srcs = glob(["lib/*.c"]),
+ hdrs = glob(["lib/*.h"]) + [
+ "lib/includes/nghttp2/nghttp2.h",
+ ":nghttp2ver.h",
+ ],
+ defines = [
+ "HAVE_TIME_H",
+ "HAVE_ARPA_INET_H",
+ ],
+ includes = [
+ "lib/includes/",
+ ],
+ visibility = ["//visibility:public"],
+)
diff --git a/debian/curl.BUILD b/debian/curl.BUILD
index a4f6526..1292c5e 100644
--- a/debian/curl.BUILD
+++ b/debian/curl.BUILD
@@ -422,6 +422,7 @@
"-DCURL_DISABLE_NTLM", # turning it off in configure is not enough
"-DHAVE_LIBZ",
"-DHAVE_ZLIB_H",
+ "-DUSE_NGHTTP2",
"-Wno-string-plus-int",
"-Wno-cast-qual",
"-Wno-format-nonliteral",
@@ -466,6 +467,7 @@
# Use the same version of zlib that gRPC does.
"@zlib",
":define-ca-bundle-location",
+ "@com_github_nghttp2_nghttp2//:nghttp2",
] + select({
":windows": [],
"//conditions:default": [
diff --git a/documentation/aos/docs/flatbuffers.md b/documentation/aos/docs/flatbuffers.md
index 132348d..9c82ee3 100644
--- a/documentation/aos/docs/flatbuffers.md
+++ b/documentation/aos/docs/flatbuffers.md
@@ -157,13 +157,30 @@
type using the regular generated flatbuffer API and a `FromFlatbuffer()` method
which attempts to copy the specified flatbuffer into the current object.
+The `FromFlatbuffer()` method works on both the "raw" flatbuffer type, as well
+as on the [Flatbuffer Object
+API](https://flatbuffers.dev/flatbuffers_guide_use_cpp.html) (i.e. the
+`FlatbufferT` types). When copying
+flatbuffers from the object-based API, we apply the same semantics that that the
+`Pack()` method does in the raw flatbuffer type. Namely, all non-table fields
+will be set:
+
+ * Scalar fields are always populated, even if their value is equal to the
+ default.
+ * Vectors are set to zero-length vectors if there is no data in the vector.
+ * Strings are set to the empty string if there is no data in the string.
+
+These limitations are a consequence of how flatbuffers are represented in the
+object API, and is not an issue when copying from regular flatbuffer types.
+For copying from raw flatbuffer objects (which is what most existing code
+uses), these caveats do not apply, and there is no loss of information.
+
### Sample Usage
The below example constructs a table of the above example `TestTable`:
```cpp
-aos::FixedAllocator allocator(TestTableStatic::kUnalignedBufferSize);
-Builder<TestTableStatic> builder(&allocator);
+Builder<TestTableStatic> builder;
TestTableStatic *object = builder.get();
object->set_scalar(123);
{
@@ -436,10 +453,24 @@
space allocated for the vector; returns false on failure (e.g., if you are in
a fixed-size allocator that does not support increasing the size past a
certain point).
-* `bool FromFlatbuffer(const flatbuffers::Vector<>*)`: Attempts to copy an
+* `bool FromFlatbuffer(const flatbuffers::Vector<>&)`: Attempts to copy an
existing vector into this `Vector`. This may attempt to call `reserve()`
if the new vector is longer than `capacity()`. If the copy fails for
any reason, returns `false`.
+* `bool FromFlatbuffer(const std::vector<>&)`: Attempts to copy an
+ existing vector into this `Vector`. This may attempt to call `reserve()`
+ if the new vector is longer than `capacity()`. If the copy fails for
+ any reason, returns `false`. This is called "`FromFlatbuffer`" because
+ the [Flatbuffer Object
+ API](https://flatbuffers.dev/flatbuffers_guide_use_cpp.html) uses
+ `std::vector<>` to represent vectors.
+* `bool FromData(const T*, size_t)`: Attempts to copy a contiguous set of data
+ from the provided pointer. Only applies to inline types. This may attempt to
+ call `reserve()`, and if the call fails, it returns `false`.
+* `bool FromIterator(It begin, It end)`: Attempts to copy data from [begin, end)
+ into the vector. Does not assume that the data is stored contiguously in
+ memory. Only applies to inline types. This may attempt to
+ call `reserve()`, and if the call fails, it returns `false`.
#### Managing Resizing of Vectors
@@ -510,12 +541,25 @@
# upgraded to static_flatbuffer rules.
static_flatbuffer(
name = "test_message_fbs",
- src = "test_message.fbs",
+ srcs = ["test_message.fbs"],
)
```
+Then you must update the `#include` to use a `test_message_static.h` instead of
+the standard `test_message_generated.h` (the `_static.h` will include the
+`_generated.h` itself). Any C++ code can then be updated, noting that any
+AOS Senders that need to use the new API need to have their definitions updated
+to use the `TestMessageStatic` and must call `MakeStaticBuilder` instead of
+`MakeBuilder`. There is currently no support for using static flatbuffers with
+the AOS Fetcher or Watcher interfaces, as these only present immutable objects
+and so do not benefit from the API additions (and attempting to convert them to
+the new API would require additional overhead associated with copying the
+serialized flatbuffer into the correct format).
+
Before:
```cpp
+#include "aos/events/test_message_generated.h"
+...
aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
loop->OnRun([&]() {
@@ -528,6 +572,8 @@
After:
```cpp
+#include "aos/events/test_message_static.h"
+...
aos::Sender<TestMessageStatic> sender =
loop1->MakeSender<TestMessageStatic>("/test");
diff --git a/documentation/tutorials/rio-gdb.md b/documentation/tutorials/rio-gdb.md
new file mode 100644
index 0000000..eb7bc16
--- /dev/null
+++ b/documentation/tutorials/rio-gdb.md
@@ -0,0 +1,29 @@
+# Roborio GDB
+
+The roborio has a gdb-server command so we need to set the rio as our server and connect to it with some host device.
+
+## Steps
+
+### Rio
+This command will start the gdb-server program on the rio.
+```bash
+gdbserver localhost:8898 program
+```
+
+### Host
+```bash
+# this is the gdb-multiarch package on debian. Its needed because your host machine
+# likely isn't using the architecture the rio uses.
+gdb-multiarch
+# This connects us using an extended remote to the roborio, which lets us use the run command.
+target extended-remote ip:8898
+# Here we specify the name of program from the rio side
+set remote exec-file program
+# This makes it so we don't stop when getting realtime signals
+handle SIG33 pass nostop noprint
+# From here on you can use gdb like you normally would
+# So we can run our program with some args
+r args
+# To exit the gdb-server program on the rio side we need to run this command.
+monitor exit
+```
diff --git a/frc971/analysis/BUILD b/frc971/analysis/BUILD
index b179ad9..9c236c8 100644
--- a/frc971/analysis/BUILD
+++ b/frc971/analysis/BUILD
@@ -31,6 +31,9 @@
"//y2023/control_loops/superstructure:superstructure_plotter",
"//y2023/localizer:corrections_plotter",
"//y2023/localizer:localizer_plotter",
+ "//y2024/control_loops/superstructure:superstructure_plotter",
+ "//y2024/localizer:corrections_plotter",
+ "//y2024/localizer:localizer_plotter",
],
)
diff --git a/frc971/analysis/plot_index.ts b/frc971/analysis/plot_index.ts
index 1ee85d1..610ac4b 100644
--- a/frc971/analysis/plot_index.ts
+++ b/frc971/analysis/plot_index.ts
@@ -21,46 +21,32 @@
// using JSON rather than requiring people to write a script just to create
// a plot.
import {Configuration} from '../../aos/configuration_generated';
-import {Connection} from '../../aos/network/www/proxy';
-import {plotImu} from '../wpilib/imu_plotter';
-import {plotDrivetrain} from '../control_loops/drivetrain/drivetrain_plotter';
-import {plotSpline} from '../control_loops/drivetrain/spline_plotter';
-import {plotDownEstimator} from '../control_loops/drivetrain/down_estimator_plotter';
-import {plotRobotState} from
- '../control_loops/drivetrain/robot_state_plotter'
-import {plotFinisher as plot2020Finisher} from
- '../../y2020/control_loops/superstructure/finisher_plotter'
-import {plotTurret as plot2020Turret} from
- '../../y2020/control_loops/superstructure/turret_plotter'
-import {plotLocalizer as plot2020Localizer} from
- '../../y2020/control_loops/drivetrain/localizer_plotter'
-import {plotAccelerator as plot2020Accelerator} from
- '../../y2020/control_loops/superstructure/accelerator_plotter'
-import {plotHood as plot2020Hood} from
- '../../y2020/control_loops/superstructure/hood_plotter'
-import {plotSuperstructure as plot2021Superstructure} from
- '../../y2021_bot3/control_loops/superstructure/superstructure_plotter';
-import {plotTurret as plot2022Turret} from
- '../../y2022/control_loops/superstructure/turret_plotter'
-import {plotSuperstructure as plot2022Superstructure} from
- '../../y2022/control_loops/superstructure/superstructure_plotter'
-import {plotSuperstructure as plot2023Superstructure} from
- '../../y2023/control_loops/superstructure/superstructure_plotter'
-import {plotCatapult as plot2022Catapult} from
- '../../y2022/control_loops/superstructure/catapult_plotter'
-import {plotIntakeFront as plot2022IntakeFront, plotIntakeBack as plot2022IntakeBack} from
- '../../y2022/control_loops/superstructure/intake_plotter'
-import {plotClimber as plot2022Climber} from
- '../../y2022/control_loops/superstructure/climber_plotter'
-import {plotLocalizer as plot2022Localizer} from
- '../../y2022/localizer/localizer_plotter'
-import {plotLocalizer as plot2023Localizer} from
- '../../y2023/localizer/localizer_plotter'
-import {plotVision as plot2022Vision} from
- '../../y2022/vision/vision_plotter'
-import {plotVision as plot2023Corrections} from
- '../../y2023/localizer/corrections_plotter'
import {plotDemo} from '../../aos/network/www/demo_plot';
+import {Connection} from '../../aos/network/www/proxy';
+import {plotLocalizer as plot2020Localizer} from '../../y2020/control_loops/drivetrain/localizer_plotter'
+import {plotAccelerator as plot2020Accelerator} from '../../y2020/control_loops/superstructure/accelerator_plotter'
+import {plotFinisher as plot2020Finisher} from '../../y2020/control_loops/superstructure/finisher_plotter'
+import {plotHood as plot2020Hood} from '../../y2020/control_loops/superstructure/hood_plotter'
+import {plotTurret as plot2020Turret} from '../../y2020/control_loops/superstructure/turret_plotter'
+import {plotSuperstructure as plot2021Superstructure} from '../../y2021_bot3/control_loops/superstructure/superstructure_plotter';
+import {plotCatapult as plot2022Catapult} from '../../y2022/control_loops/superstructure/catapult_plotter'
+import {plotClimber as plot2022Climber} from '../../y2022/control_loops/superstructure/climber_plotter'
+import {plotIntakeBack as plot2022IntakeBack, plotIntakeFront as plot2022IntakeFront} from '../../y2022/control_loops/superstructure/intake_plotter'
+import {plotSuperstructure as plot2022Superstructure} from '../../y2022/control_loops/superstructure/superstructure_plotter'
+import {plotTurret as plot2022Turret} from '../../y2022/control_loops/superstructure/turret_plotter'
+import {plotLocalizer as plot2022Localizer} from '../../y2022/localizer/localizer_plotter'
+import {plotVision as plot2022Vision} from '../../y2022/vision/vision_plotter'
+import {plotSuperstructure as plot2023Superstructure} from '../../y2023/control_loops/superstructure/superstructure_plotter'
+import {plotVision as plot2023Corrections} from '../../y2023/localizer/corrections_plotter'
+import {plotLocalizer as plot2023Localizer} from '../../y2023/localizer/localizer_plotter'
+import {plotClimber as plot2024Climber, plotIntake as plot2024Intake, plotSuperstructure as plot2024Superstructure} from '../../y2024/control_loops/superstructure/superstructure_plotter'
+import {plotVision as plot2024Corrections} from '../../y2024/localizer/corrections_plotter'
+import {plotLocalizer as plot2024Localizer} from '../../y2024/localizer/localizer_plotter'
+import {plotDownEstimator} from '../control_loops/drivetrain/down_estimator_plotter';
+import {plotDrivetrain} from '../control_loops/drivetrain/drivetrain_plotter';
+import {plotRobotState} from '../control_loops/drivetrain/robot_state_plotter'
+import {plotSpline} from '../control_loops/drivetrain/spline_plotter';
+import {plotImu} from '../wpilib/imu_plotter';
const rootDiv = document.createElement('div');
rootDiv.style.width = '100%';
@@ -118,6 +104,11 @@
['Spline Debug', new PlotState(plotDiv, plotSpline)],
['Down Estimator', new PlotState(plotDiv, plotDownEstimator)],
['Robot State', new PlotState(plotDiv, plotRobotState)],
+ ['2024 Vision', new PlotState(plotDiv, plot2024Corrections)],
+ ['2024 Localizer', new PlotState(plotDiv, plot2024Localizer)],
+ ['2024 Superstructure', new PlotState(plotDiv, plot2024Superstructure)],
+ ['2024 Climber', new PlotState(plotDiv, plot2024Climber)],
+ ['2024 Intake', new PlotState(plotDiv, plot2024Intake)],
['2023 Vision', new PlotState(plotDiv, plot2023Corrections)],
['2023 Localizer', new PlotState(plotDiv, plot2023Localizer)],
['2023 Superstructure', new PlotState(plotDiv, plot2023Superstructure)],
diff --git a/frc971/can_logger/asc_logger.cc b/frc971/can_logger/asc_logger.cc
index 6848938..5282d13 100644
--- a/frc971/can_logger/asc_logger.cc
+++ b/frc971/can_logger/asc_logger.cc
@@ -12,11 +12,11 @@
}
void AscLogger::HandleFrame(const CanFrame &frame) {
- if (!first_frame_monotonic_) {
- aos::monotonic_clock::time_point time(
- std::chrono::nanoseconds(frame.monotonic_timestamp_ns()));
+ if (!first_frame_realtime_) {
+ aos::realtime_clock::time_point time(
+ std::chrono::nanoseconds(frame.realtime_timestamp_ns()));
- first_frame_monotonic_ = time;
+ first_frame_realtime_ = time;
WriteHeader(output_, event_loop_->realtime_now());
}
@@ -60,11 +60,11 @@
} // namespace
void AscLogger::WriteFrame(std::ostream &file, const CanFrame &frame) {
- aos::monotonic_clock::time_point frame_timestamp(
- std::chrono::nanoseconds(frame.monotonic_timestamp_ns()));
+ aos::realtime_clock::time_point frame_timestamp(
+ std::chrono::nanoseconds(frame.realtime_timestamp_ns()));
std::chrono::duration<double> time(frame_timestamp -
- first_frame_monotonic_.value());
+ first_frame_realtime_.value());
// TODO: maybe this should not be hardcoded
const int device_id = 1;
diff --git a/frc971/can_logger/asc_logger.h b/frc971/can_logger/asc_logger.h
index 2be2eb2..e7d4e74 100644
--- a/frc971/can_logger/asc_logger.h
+++ b/frc971/can_logger/asc_logger.h
@@ -25,7 +25,7 @@
static void WriteHeader(std::ostream &file,
aos::realtime_clock::time_point start_time);
- std::optional<aos::monotonic_clock::time_point> first_frame_monotonic_;
+ std::optional<aos::realtime_clock::time_point> first_frame_realtime_;
std::ofstream output_;
diff --git a/frc971/can_logger/can_logger.cc b/frc971/can_logger/can_logger.cc
index cfc3dd8..380410f 100644
--- a/frc971/can_logger/can_logger.cc
+++ b/frc971/can_logger/can_logger.cc
@@ -1,5 +1,9 @@
#include "frc971/can_logger/can_logger.h"
+DEFINE_bool(poll, false,
+ "If true, poll the CAN bus every 100ms. If false, wake up for "
+ "each frame and publish it.");
+
namespace frc971::can_logger {
CanLogger::CanLogger(aos::ShmEventLoop *event_loop,
@@ -8,8 +12,10 @@
: shm_event_loop_(event_loop),
fd_(socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)),
frames_sender_(shm_event_loop_->MakeSender<CanFrame>(channel_name)) {
- // TOOD(max): Figure out a proper priority
- shm_event_loop_->SetRuntimeRealtimePriority(10);
+ // TODO(max): Figure out a proper priority
+ if (!FLAGS_poll) {
+ shm_event_loop_->SetRuntimeRealtimePriority(10);
+ }
struct ifreq ifr;
strcpy(ifr.ifr_name, interface_name.data());
PCHECK(ioctl(fd_.get(), SIOCGIFINDEX, &ifr) == 0)
@@ -35,7 +41,14 @@
CHECK_EQ(opt_size, sizeof(recieve_buffer_size));
VLOG(0) << "CAN recieve bufffer is " << recieve_buffer_size << " bytes large";
- shm_event_loop_->epoll()->OnReadable(fd_.get(), [this]() { Poll(); });
+ if (FLAGS_poll) {
+ aos::TimerHandler *timer_handler =
+ shm_event_loop_->AddTimer([this]() { Poll(); });
+ timer_handler->set_name("CAN logging Loop");
+ timer_handler->Schedule(event_loop->monotonic_now(), kPollPeriod);
+ } else {
+ shm_event_loop_->epoll()->OnReadable(fd_.get(), [this]() { Poll(); });
+ }
}
void CanLogger::Poll() {
@@ -75,8 +88,8 @@
can_frame_builder.add_can_id(frame.can_id);
can_frame_builder.add_flags(frame.flags);
can_frame_builder.add_data(frame_data);
- can_frame_builder.add_monotonic_timestamp_ns(
- static_cast<std::chrono::nanoseconds>(
+ can_frame_builder.add_realtime_timestamp_ns(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::seconds(tv.tv_sec) +
std::chrono::microseconds(tv.tv_usec))
.count());
diff --git a/frc971/can_logger/can_logging.fbs b/frc971/can_logger/can_logging.fbs
index ba60241..c7a82f2 100644
--- a/frc971/can_logger/can_logging.fbs
+++ b/frc971/can_logger/can_logging.fbs
@@ -7,7 +7,7 @@
// The body of the CAN frame up to 64 bytes long.
data:[ubyte] (id: 1);
// The hardware timestamp of when the frame came in
- monotonic_timestamp_ns:uint64 (id: 2);
+ realtime_timestamp_ns:uint64 (id: 2);
// Additional flags for CAN FD
flags: ubyte (id: 3);
}
diff --git a/frc971/control_loops/aiming/aiming.cc b/frc971/control_loops/aiming/aiming.cc
index 4cf9255..572ed1c 100644
--- a/frc971/control_loops/aiming/aiming.cc
+++ b/frc971/control_loops/aiming/aiming.cc
@@ -54,6 +54,8 @@
} // namespace
TurretGoal AimerGoal(const ShotConfig &config, const RobotState &state) {
+ CHECK(config.ball_speed_over_ground > 0.0)
+ << ": Ball speed must be positive.";
TurretGoal result;
// This code manages compensating the goal turret heading for the robot's
// current velocity, to allow for shooting on-the-fly.
diff --git a/frc971/control_loops/aiming/aiming_test.cc b/frc971/control_loops/aiming/aiming_test.cc
index 4ec7eb9..a4f7bf2 100644
--- a/frc971/control_loops/aiming/aiming_test.cc
+++ b/frc971/control_loops/aiming/aiming_test.cc
@@ -158,4 +158,17 @@
EXPECT_FLOAT_EQ(0.0, goal.velocity);
}
+// Tests that we fail if the ballspeed is zero. Can't hit anything if the ball
+// isn't moving.
+TEST(AimerDeathTest, ZeroBallSpeed) {
+ const Pose target({0.0, 0.0, 0.0}, 0.0);
+ Pose robot_pose({1.0, 1.0, 1.0}, 0.0);
+ const constants::Range range{-2.36, 2.36, -2.16, 2.16};
+ const double kBallSpeed = 0.0;
+ EXPECT_DEATH(AimerGoal(ShotConfig{target, ShotMode::kShootOnTheFly, range,
+ kBallSpeed, 0.0, 0.0},
+ RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0}),
+ "Ball speed must be positive.");
+}
+
} // namespace frc971::control_loops::aiming::testing
diff --git a/frc971/control_loops/catapult/catapult.cc b/frc971/control_loops/catapult/catapult.cc
index 2e1867c..a827dc9 100644
--- a/frc971/control_loops/catapult/catapult.cc
+++ b/frc971/control_loops/catapult/catapult.cc
@@ -102,9 +102,11 @@
case CatapultState::RESETTING:
if (catapult_.controller().R(1, 0) > 7.0) {
- catapult_.AdjustProfile(7.0, 2000.0);
+ catapult_.mutable_profile()->set_maximum_velocity(7.0);
+ catapult_.mutable_profile()->set_maximum_acceleration(2000.0);
} else if (catapult_.controller().R(1, 0) > 0.0) {
- catapult_.AdjustProfile(7.0, 1000.0);
+ catapult_.mutable_profile()->set_maximum_velocity(7.0);
+ catapult_.mutable_profile()->set_maximum_acceleration(1000.0);
} else {
catapult_state_ = CatapultState::PROFILE;
}
@@ -135,4 +137,4 @@
return catapult_.MakeStatus(fbb);
}
-} // namespace frc971::control_loops::catapult
\ No newline at end of file
+} // namespace frc971::control_loops::catapult
diff --git a/frc971/control_loops/drivetrain/BUILD b/frc971/control_loops/drivetrain/BUILD
index afbce9b..789de2b 100644
--- a/frc971/control_loops/drivetrain/BUILD
+++ b/frc971/control_loops/drivetrain/BUILD
@@ -30,6 +30,11 @@
)
static_flatbuffer(
+ name = "rio_localizer_inputs_fbs",
+ srcs = ["rio_localizer_inputs.fbs"],
+)
+
+static_flatbuffer(
name = "drivetrain_output_fbs",
srcs = ["drivetrain_output.fbs"],
)
@@ -42,7 +47,10 @@
static_flatbuffer(
name = "drivetrain_status_fbs",
srcs = ["drivetrain_status.fbs"],
- deps = ["//frc971/control_loops:control_loops_fbs"],
+ deps = [
+ "//frc971/control_loops:control_loops_fbs",
+ "//frc971/control_loops:encoder_fault_status_fbs",
+ ],
)
static_flatbuffer(
@@ -62,7 +70,10 @@
name = "drivetrain_status_ts_fbs",
srcs = ["drivetrain_status.fbs"],
target_compatible_with = ["@platforms//os:linux"],
- deps = ["//frc971/control_loops:control_loops_ts_fbs"],
+ deps = [
+ "//frc971/control_loops:control_loops_ts_fbs",
+ "//frc971/control_loops:encoder_fault_status_ts_fbs",
+ ],
)
flatbuffer_ts_library(
@@ -128,7 +139,10 @@
static_flatbuffer(
name = "drivetrain_status_float_fbs",
srcs = ["drivetrain_status_float.fbs"],
- deps = ["//frc971/control_loops:control_loops_fbs"],
+ deps = [
+ "//frc971/control_loops:control_loops_fbs",
+ "//frc971/control_loops:encoder_fault_status_fbs",
+ ],
)
aos_config(
@@ -136,6 +150,7 @@
src = "drivetrain_simulation_channels.json",
flatbuffers = [
":drivetrain_status_fbs",
+ "//frc971/control_loops:encoder_fault_status_fbs",
],
target_compatible_with = ["@platforms//os:linux"],
visibility = ["//visibility:public"],
@@ -162,6 +177,7 @@
":drivetrain_output_fbs",
":drivetrain_status_fbs",
":drivetrain_position_fbs",
+ ":drivetrain_can_position_fbs",
":localizer_fbs",
"//frc971/queues:gyro_fbs",
"//frc971/queues:gyro_uid_fbs",
@@ -444,6 +460,7 @@
":localizer",
":localizer_fbs",
":polydrivetrain",
+ ":rio_localizer_inputs_fbs",
":spline_goal_fbs",
":splinedrivetrain",
":ssdrivetrain",
@@ -789,6 +806,34 @@
],
)
+cc_library(
+ name = "drivetrain_encoder_fault_detector",
+ srcs = ["drivetrain_encoder_fault_detector.cc"],
+ hdrs = ["drivetrain_encoder_fault_detector.h"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ "//aos/events:event_loop",
+ "//frc971/control_loops:encoder_fault_detector",
+ "//frc971/control_loops:encoder_fault_status_fbs",
+ "//frc971/control_loops/drivetrain:drivetrain_can_position_fbs",
+ "//frc971/control_loops/drivetrain:drivetrain_position_fbs",
+ "//frc971/control_loops/drivetrain:drivetrain_status_fbs",
+ ],
+)
+
+cc_test(
+ name = "drivetrain_encoder_fault_detector_test",
+ srcs = ["drivetrain_encoder_fault_detector_test.cc"],
+ data = [":simulation_config"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":drivetrain_encoder_fault_detector",
+ "//aos:json_to_flatbuffer",
+ "//aos/events:simulated_event_loop",
+ "//aos/testing:googletest",
+ ],
+)
+
cc_test(
name = "camera_test",
srcs = ["camera_test.cc"],
diff --git a/frc971/control_loops/drivetrain/drivetrain.cc b/frc971/control_loops/drivetrain/drivetrain.cc
index 2b84a9d..95fab0a2 100644
--- a/frc971/control_loops/drivetrain/drivetrain.cc
+++ b/frc971/control_loops/drivetrain/drivetrain.cc
@@ -350,7 +350,11 @@
dt_openloop_(dt_config_, filters_.kf()),
dt_closedloop_(dt_config_, filters_.kf(), localizer),
dt_spline_(dt_config_),
- dt_line_follow_(dt_config_, localizer->target_selector()) {
+ dt_line_follow_(dt_config_, localizer->target_selector()),
+ localizer_input_sender_(
+ event_loop->TryMakeSender<
+ frc971::control_loops::drivetrain::RioLocalizerInputsStatic>(
+ "/drivetrain")) {
event_loop->SetRuntimeRealtimePriority(30);
for (size_t ii = 0; ii < trajectory_fetchers_.size(); ++ii) {
trajectory_fetchers_[ii].fetcher =
@@ -613,6 +617,17 @@
if (output) {
output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
}
+
+ if (localizer_input_sender_.valid()) {
+ auto localizer_input_builder = localizer_input_sender_.MakeStaticBuilder();
+ localizer_input_builder->set_left_encoder(position->left_encoder());
+ localizer_input_builder->set_right_encoder(position->right_encoder());
+ if (output) {
+ localizer_input_builder->set_left_voltage(output_struct.left_voltage);
+ localizer_input_builder->set_right_voltage(output_struct.right_voltage);
+ }
+ localizer_input_builder.CheckOk(localizer_input_builder.Send());
+ }
}
flatbuffers::Offset<Output> DrivetrainLoop::Zero(
diff --git a/frc971/control_loops/drivetrain/drivetrain.h b/frc971/control_loops/drivetrain/drivetrain.h
index 87266f1..332b117 100644
--- a/frc971/control_loops/drivetrain/drivetrain.h
+++ b/frc971/control_loops/drivetrain/drivetrain.h
@@ -18,6 +18,7 @@
#include "frc971/control_loops/drivetrain/localizer.h"
#include "frc971/control_loops/drivetrain/localizer_generated.h"
#include "frc971/control_loops/drivetrain/polydrivetrain.h"
+#include "frc971/control_loops/drivetrain/rio_localizer_inputs_static.h"
#include "frc971/control_loops/drivetrain/splinedrivetrain.h"
#include "frc971/control_loops/drivetrain/ssdrivetrain.h"
#include "frc971/control_loops/polytope.h"
@@ -191,6 +192,8 @@
bool has_been_enabled_ = false;
aos::SendFailureCounter status_failure_counter_;
+ aos::Sender<::frc971::control_loops::drivetrain::RioLocalizerInputsStatic>
+ localizer_input_sender_;
};
} // namespace frc971::control_loops::drivetrain
diff --git a/frc971/control_loops/drivetrain/drivetrain_can_position.fbs b/frc971/control_loops/drivetrain/drivetrain_can_position.fbs
index 29c0b85..539b18e 100644
--- a/frc971/control_loops/drivetrain/drivetrain_can_position.fbs
+++ b/frc971/control_loops/drivetrain/drivetrain_can_position.fbs
@@ -14,6 +14,11 @@
// The ctre::phoenix::StatusCode of the measurement
// Should be OK = 0
status:int (id: 2);
+
+ // These values aren't used yet but are
+ // arrays that represent falcons on left/right sides of the drivetrain
+ left_falcons: [CANTalonFX] (id: 3);
+ right_falcons: [CANTalonFX] (id: 4);
}
root_type CANPosition;
diff --git a/frc971/control_loops/drivetrain/drivetrain_config.json b/frc971/control_loops/drivetrain/drivetrain_config.json
index a74d416..71c7628 100644
--- a/frc971/control_loops/drivetrain/drivetrain_config.json
+++ b/frc971/control_loops/drivetrain/drivetrain_config.json
@@ -43,6 +43,11 @@
},
{
"name": "/drivetrain",
+ "type": "frc971.control_loops.drivetrain.CANPosition",
+ "frequency": 200
+ },
+ {
+ "name": "/drivetrain",
"type": "frc971.control_loops.drivetrain.Status",
"frequency": 200,
"max_size": 2000
diff --git a/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.cc b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.cc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.cc
diff --git a/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h
new file mode 100644
index 0000000..7513033
--- /dev/null
+++ b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h
@@ -0,0 +1,115 @@
+#ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_ENCODER_FAULT_DETECTOR_H_
+#define FRC971_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_ENCODER_FAULT_DETECTOR_H_
+
+#include "aos/events/event_loop.h"
+#include "frc971/control_loops/drivetrain/drivetrain_can_position_generated.h"
+#include "frc971/control_loops/drivetrain/drivetrain_position_generated.h"
+#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
+#include "frc971/control_loops/encoder_fault_detector.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace drivetrain {
+
+// This class will be used to detect any encoder faults within the drivetrain
+// using values from the left/right encoders and motors
+
+template <uint8_t NumberofDrivetrainMotors>
+class DrivetrainEncoderFaultDetector {
+ public:
+ DrivetrainEncoderFaultDetector(aos::EventLoop *event_loop);
+
+ void UpdateDetectors();
+
+ void SendMessage();
+
+ flatbuffers::Offset<drivetrain::Faults> PopulateFaults(
+ flatbuffers::FlatBufferBuilder *fbb);
+
+ private:
+ aos::EventLoop *event_loop_;
+ EncoderFaultDetector<NumberofDrivetrainMotors> left_encoder_fault_detector_;
+ EncoderFaultDetector<NumberofDrivetrainMotors> right_encoder_fault_detector_;
+ aos::Sender<frc971::control_loops::drivetrain::Status>
+ drivetrain_status_sender_;
+ aos::Fetcher<control_loops::drivetrain::Position>
+ drivetrain_position_fetcher_;
+ aos::Fetcher<control_loops::drivetrain::CANPosition>
+ drivetrain_can_position_fetcher_;
+};
+
+template <uint8_t NumberofDrivetrainMotors>
+DrivetrainEncoderFaultDetector<NumberofDrivetrainMotors>::
+ DrivetrainEncoderFaultDetector(aos::EventLoop *event_loop)
+ : event_loop_(event_loop),
+ drivetrain_status_sender_(
+ event_loop->MakeSender<drivetrain::Status>("/drivetrain")),
+ drivetrain_position_fetcher_(
+ event_loop->MakeFetcher<drivetrain::Position>("/drivetrain")),
+ drivetrain_can_position_fetcher_(
+ event_loop->MakeFetcher<drivetrain::CANPosition>("/drivetrain")) {
+ event_loop
+ ->AddTimer([this]() {
+ UpdateDetectors();
+ SendMessage();
+ })
+ ->Schedule(event_loop->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+}
+
+template <uint8_t NumberofDrivetrainMotors>
+void DrivetrainEncoderFaultDetector<
+ NumberofDrivetrainMotors>::UpdateDetectors() {
+ drivetrain_position_fetcher_.Fetch();
+ drivetrain_can_position_fetcher_.Fetch();
+
+ if (drivetrain_position_fetcher_.get() &&
+ drivetrain_can_position_fetcher_.get()) {
+ left_encoder_fault_detector_.Iterate(
+ drivetrain_position_fetcher_->left_encoder(),
+ drivetrain_can_position_fetcher_->left_falcons(),
+ event_loop_->monotonic_now());
+ right_encoder_fault_detector_.Iterate(
+ drivetrain_position_fetcher_->right_encoder(),
+ drivetrain_can_position_fetcher_->right_falcons(),
+ event_loop_->monotonic_now());
+ }
+}
+
+template <uint8_t NumberofDrivetrainMotors>
+void DrivetrainEncoderFaultDetector<NumberofDrivetrainMotors>::SendMessage() {
+ auto builder = drivetrain_status_sender_.MakeBuilder();
+ auto offset = PopulateFaults(builder.fbb());
+
+ drivetrain::Status::Builder drivetrain_status_builder =
+ builder.MakeBuilder<drivetrain::Status>();
+
+ drivetrain_status_builder.add_encoder_faults(offset);
+ builder.CheckOk(builder.Send(drivetrain_status_builder.Finish()));
+}
+
+template <uint8_t NumberofDrivetrainMotors>
+flatbuffers::Offset<drivetrain::Faults>
+DrivetrainEncoderFaultDetector<NumberofDrivetrainMotors>::PopulateFaults(
+ flatbuffers::FlatBufferBuilder *fbb) {
+ flatbuffers::Offset<EncoderFaultStatus> left_encoder_fault_detector_offset =
+ left_encoder_fault_detector_.PopulateStatus(fbb);
+ flatbuffers::Offset<EncoderFaultStatus> right_encoder_fault_detector_offset =
+ right_encoder_fault_detector_.PopulateStatus(fbb);
+
+ drivetrain::Faults::Builder drivetrain_faults_builder(*fbb);
+
+ drivetrain_faults_builder.add_left_faulted(
+ left_encoder_fault_detector_offset);
+ drivetrain_faults_builder.add_right_faulted(
+ right_encoder_fault_detector_offset);
+
+ return drivetrain_faults_builder.Finish();
+}
+
+} // namespace drivetrain
+} // namespace control_loops
+} // namespace frc971
+
+#endif
\ No newline at end of file
diff --git a/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector_test.cc b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector_test.cc
new file mode 100644
index 0000000..adc8972
--- /dev/null
+++ b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector_test.cc
@@ -0,0 +1,416 @@
+#include "frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/events/simulated_event_loop.h"
+#include "aos/json_to_flatbuffer.h"
+
+namespace frc971::control_loops::drivetrain::testing {
+
+class EncoderFaultDetectorTest : public ::testing::Test {
+ public:
+ EncoderFaultDetectorTest()
+ : config_(aos::configuration::ReadConfig(
+ "frc971/control_loops/drivetrain/simulation_config.json")),
+ event_loop_factory_(&config_.message()),
+ event_loop_(event_loop_factory_.MakeEventLoop(
+ "drivetrain_encoder_fault_detector", nullptr)),
+ drivetrain_status_fetcher_(
+ event_loop_->MakeFetcher<frc971::control_loops::drivetrain::Status>(
+ "/drivetrain")),
+ drivetrain_position_sender_(
+ event_loop_->MakeSender<drivetrain::Position>("/drivetrain")),
+ drivetrain_can_position_sender_(
+ event_loop_->MakeSender<drivetrain::CANPosition>("/drivetrain")),
+ fault_detector_(event_loop_.get()) {}
+ void ResetEncoders() {
+ left_encoder_ = 0.0;
+ right_encoder_ = 0.0;
+ right_falcons_ = {0.0, 0.0};
+ left_falcons_ = {0.0, 0.0};
+ }
+ aos::FlatbufferDetachedBuffer<control_loops::drivetrain::Position>
+ ConstructPositionFlatBuffer(double left_encoder, double right_encoder) {
+ return aos::JsonToFlatbuffer<control_loops::drivetrain::Position>(
+ absl::StrFormat("{ \"left_encoder\": %f, \"right_encoder\": %f }",
+ left_encoder, right_encoder));
+ }
+
+ aos::FlatbufferDetachedBuffer<control_loops::drivetrain::CANPosition>
+ ConstructCANPositionFlatBuffer(const std::vector<double> &left_falcons,
+ const std::vector<double> &right_falcons) {
+ if (left_falcons.size() == right_falcons.size()) {
+ const size_t num_falcons = left_falcons.size();
+ std::string json = "{ \"left_falcons\":[";
+
+ for (size_t i = 0; i < num_falcons; ++i) {
+ json += absl::StrFormat("{ \"position\": %f }", left_falcons[i]);
+ if (i + 1 < num_falcons) {
+ json += ", ";
+ }
+ }
+
+ json += "], \"right_falcons\":[";
+
+ for (size_t i = 0; i < num_falcons; ++i) {
+ json += absl::StrFormat("{ \"position\": %f }", right_falcons[i]);
+ if (i + 1 < num_falcons) {
+ json += ", ";
+ }
+ }
+
+ json += "]}";
+ return aos::JsonToFlatbuffer<control_loops::drivetrain::CANPosition>(
+ json);
+ }
+ LOG(FATAL) << "You must provide two falcon arrays of equal length";
+ return aos::JsonToFlatbuffer<control_loops::drivetrain::CANPosition>("");
+ }
+
+ void SendPositionMessages() {
+ drivetrain_position_sender_.CheckOk(drivetrain_position_sender_.Send(
+ ConstructPositionFlatBuffer(left_encoder_, right_encoder_)));
+ drivetrain_can_position_sender_.CheckOk(
+ drivetrain_can_position_sender_.Send(
+ ConstructCANPositionFlatBuffer(left_falcons_, right_falcons_)));
+ }
+
+ protected:
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+ aos::SimulatedEventLoopFactory event_loop_factory_;
+ std::unique_ptr<aos::EventLoop> event_loop_;
+
+ aos::Fetcher<frc971::control_loops::drivetrain::Status>
+ drivetrain_status_fetcher_;
+ aos::Sender<control_loops::drivetrain::Position> drivetrain_position_sender_;
+ aos::Sender<control_loops::drivetrain::CANPosition>
+ drivetrain_can_position_sender_;
+
+ DrivetrainEncoderFaultDetector<2> fault_detector_;
+ double left_encoder_ = 0.0;
+ double right_encoder_ = 0.0;
+ std::vector<double> right_falcons_;
+ std::vector<double> left_falcons_;
+};
+
+// Test simulates if drivetrain encoders are idle
+TEST_F(EncoderFaultDetectorTest, Idle) {
+ ResetEncoders();
+
+ event_loop_->AddTimer([this]() { SendPositionMessages(); })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(1));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if drivetrain encoders are increasing
+TEST_F(EncoderFaultDetectorTest, Increasing) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ += 0.1;
+ right_encoder_ += 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon += 0.1;
+ }
+ for (double &falcon : right_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if drivetrain encoders are decreasing
+TEST_F(EncoderFaultDetectorTest, Decreasing) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ -= 0.1;
+ right_encoder_ -= 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon -= 0.1;
+ }
+ for (double &falcon : right_falcons_) {
+ falcon -= 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if only the right drivetrain encoders are increasing
+TEST_F(EncoderFaultDetectorTest, OnlyIncreaseRightSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ right_encoder_ += 0.1;
+ for (double &falcon : right_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if only the left drivetrain encoders are increasing
+TEST_F(EncoderFaultDetectorTest, OnlyIncreaseLeftSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ += 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if only the right drivetrain encoders are decreasing
+TEST_F(EncoderFaultDetectorTest, OnlyDecreaseRightSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ right_encoder_ -= 0.1;
+ for (double &falcon : right_falcons_) {
+ falcon -= 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+// Test simulates if only the left drivetrain encoders are decreasing
+TEST_F(EncoderFaultDetectorTest, OnlyDecreaseLeftSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ -= 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon -= 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that if there is no data for one second that their will be no
+// faults
+TEST_F(EncoderFaultDetectorTest, NoDataForOneSecond) {
+ ResetEncoders();
+
+ SendPositionMessages();
+
+ event_loop_factory_.RunFor(std::chrono::seconds(1));
+
+ left_encoder_ = 1.0;
+ right_encoder_ = 2.0;
+ for (double &falcon : left_falcons_) {
+ falcon = 3.0;
+ }
+ for (double &falcon : right_falcons_) {
+ falcon = 4.0;
+ }
+
+ SendPositionMessages();
+
+ event_loop_factory_.RunFor(std::chrono::seconds(1));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that only the left encoder is increasing
+TEST_F(EncoderFaultDetectorTest, LeftEncoderFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ += 0.1;
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+// Test simulates that only the right encoder is increasing
+TEST_F(EncoderFaultDetectorTest, RightEncoderFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ right_encoder_ += 0.1;
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that only the left falcons are increasing
+TEST_F(EncoderFaultDetectorTest, LeftFalconsFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ for (double &falcon : left_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that only the right falcons are increasing
+TEST_F(EncoderFaultDetectorTest, RightFalconsFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ for (double &falcon : right_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+} // namespace frc971::control_loops::drivetrain::testing
\ No newline at end of file
diff --git a/frc971/control_loops/drivetrain/drivetrain_status.fbs b/frc971/control_loops/drivetrain/drivetrain_status.fbs
index 8158e75..8e18915 100644
--- a/frc971/control_loops/drivetrain/drivetrain_status.fbs
+++ b/frc971/control_loops/drivetrain/drivetrain_status.fbs
@@ -1,4 +1,5 @@
include "frc971/control_loops/control_loops.fbs";
+include "frc971/control_loops/encoder_fault_status.fbs";
namespace frc971.control_loops.drivetrain;
@@ -210,6 +211,11 @@
accel_z_average:float (id: 8);
}
+table Faults {
+ right_faulted:EncoderFaultStatus (id: 0);
+ left_faulted:EncoderFaultStatus (id: 1);
+}
+
table Status {
// Estimated speed of the center of the robot in m/s (positive forwards).
robot_speed:double (id: 0);
@@ -271,6 +277,8 @@
// Total number of status send failures.
send_failures:uint64 (id: 28);
+
+ encoder_faults:Faults (id: 29);
}
root_type Status;
diff --git a/frc971/control_loops/drivetrain/hybrid_ekf.h b/frc971/control_loops/drivetrain/hybrid_ekf.h
index f784f2e..2f89a0c 100644
--- a/frc971/control_loops/drivetrain/hybrid_ekf.h
+++ b/frc971/control_loops/drivetrain/hybrid_ekf.h
@@ -757,8 +757,9 @@
aos::monotonic_clock::time_point t) {
CHECK(!observations_.empty());
if (!observations_.full() && t < observations_.begin()->t) {
- LOG(ERROR) << "Dropped an observation that was received before we "
- "initialized.\n";
+ AOS_LOG(ERROR,
+ "Dropped an observation that was received before we "
+ "initialized.\n");
return;
}
auto cur_it = observations_.PushFromBottom(
diff --git a/frc971/control_loops/drivetrain/localization/BUILD b/frc971/control_loops/drivetrain/localization/BUILD
index 7b86f09..1cf83d4 100644
--- a/frc971/control_loops/drivetrain/localization/BUILD
+++ b/frc971/control_loops/drivetrain/localization/BUILD
@@ -9,7 +9,11 @@
deps = [
"//aos/events:event_loop",
"//aos/network:message_bridge_server_fbs",
+ "//frc971/control_loops:pose",
"//frc971/control_loops/drivetrain:drivetrain_output_fbs",
+ "//frc971/control_loops/drivetrain:drivetrain_position_fbs",
+ "//frc971/control_loops/drivetrain:hybrid_ekf",
+ "//frc971/control_loops/drivetrain:rio_localizer_inputs_fbs",
"//frc971/input:joystick_state_fbs",
"//frc971/vision:calibration_fbs",
"@org_tuxfamily_eigen//:eigen",
diff --git a/frc971/control_loops/drivetrain/localization/utils.cc b/frc971/control_loops/drivetrain/localization/utils.cc
index 10ee445..0986037 100644
--- a/frc971/control_loops/drivetrain/localization/utils.cc
+++ b/frc971/control_loops/drivetrain/localization/utils.cc
@@ -3,24 +3,63 @@
namespace frc971::control_loops::drivetrain {
LocalizationUtils::LocalizationUtils(aos::EventLoop *event_loop)
- : output_fetcher_(event_loop->MakeFetcher<Output>("/drivetrain")),
+ : event_loop_(event_loop),
+ output_fetcher_(event_loop->TryMakeFetcher<Output>("/drivetrain")),
+ position_fetcher_(event_loop->TryMakeFetcher<Position>("/drivetrain")),
+ combined_fetcher_(
+ event_loop->TryMakeFetcher<RioLocalizerInputs>("/drivetrain")),
clock_offset_fetcher_(
event_loop->MakeFetcher<aos::message_bridge::ServerStatistics>(
"/aos")),
joystick_state_fetcher_(
event_loop->MakeFetcher<aos::JoystickState>("/roborio/aos")) {}
+namespace {
+template <typename T>
+Eigen::Vector2d GetVoltage(T &fetcher, aos::monotonic_clock::time_point now) {
+ fetcher.Fetch();
+ // Determine if the robot is likely to be disabled currently.
+ const bool disabled =
+ (fetcher.get() == nullptr) ||
+ (fetcher.context().monotonic_event_time + std::chrono::milliseconds(10) <
+ now);
+ return disabled ? Eigen::Vector2d::Zero()
+ : Eigen::Vector2d{fetcher->left_voltage(),
+ fetcher->right_voltage()};
+}
+} // namespace
Eigen::Vector2d LocalizationUtils::VoltageOrZero(
aos::monotonic_clock::time_point now) {
- output_fetcher_.Fetch();
- // Determine if the robot is likely to be disabled currently.
- const bool disabled = (output_fetcher_.get() == nullptr) ||
- (output_fetcher_.context().monotonic_event_time +
- std::chrono::milliseconds(10) <
- now);
- return disabled ? Eigen::Vector2d::Zero()
- : Eigen::Vector2d{output_fetcher_->left_voltage(),
- output_fetcher_->right_voltage()};
+ if (output_fetcher_.valid()) {
+ return GetVoltage(output_fetcher_, now);
+ } else {
+ CHECK(combined_fetcher_.valid());
+ return GetVoltage(combined_fetcher_, now);
+ }
+}
+namespace {
+template <typename T>
+std::optional<Eigen::Vector2d> GetPosition(
+ T &fetcher, aos::monotonic_clock::time_point now) {
+ fetcher.Fetch();
+ const bool stale =
+ (fetcher.get() == nullptr) ||
+ (fetcher.context().monotonic_event_time + std::chrono::milliseconds(10) <
+ now);
+ return stale ? std::nullopt
+ : std::make_optional<Eigen::Vector2d>(fetcher->left_encoder(),
+ fetcher->right_encoder());
+}
+} // namespace
+
+std::optional<Eigen::Vector2d> LocalizationUtils::Encoders(
+ aos::monotonic_clock::time_point now) {
+ if (position_fetcher_.valid()) {
+ return GetPosition(position_fetcher_, now);
+ } else {
+ CHECK(combined_fetcher_.valid());
+ return GetPosition(combined_fetcher_, now);
+ }
}
bool LocalizationUtils::MaybeInAutonomous() {
@@ -39,6 +78,9 @@
std::optional<aos::monotonic_clock::duration> LocalizationUtils::ClockOffset(
std::string_view node) {
+ if (node == event_loop_->node()->name()->string_view()) {
+ return std::chrono::seconds(0);
+ }
std::optional<aos::monotonic_clock::duration> monotonic_offset;
clock_offset_fetcher_.Fetch();
if (clock_offset_fetcher_.get() != nullptr) {
diff --git a/frc971/control_loops/drivetrain/localization/utils.h b/frc971/control_loops/drivetrain/localization/utils.h
index cfd443a..4d21e3b 100644
--- a/frc971/control_loops/drivetrain/localization/utils.h
+++ b/frc971/control_loops/drivetrain/localization/utils.h
@@ -5,6 +5,10 @@
#include "aos/events/event_loop.h"
#include "aos/network/message_bridge_server_generated.h"
#include "frc971/control_loops/drivetrain/drivetrain_output_generated.h"
+#include "frc971/control_loops/drivetrain/drivetrain_position_generated.h"
+#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
+#include "frc971/control_loops/drivetrain/rio_localizer_inputs_generated.h"
+#include "frc971/control_loops/pose.h"
#include "frc971/input/joystick_state_generated.h"
#include "frc971/vision/calibration_generated.h"
@@ -16,6 +20,7 @@
// has timed out.
// * Offsets between monotonic clocks on different devices.
// * Whether we are in autonomous mode.
+// * Drivetrain encoder voltages, as reported by the roborio.
class LocalizationUtils {
public:
LocalizationUtils(aos::EventLoop *event_loop);
@@ -26,6 +31,11 @@
// [left_voltage, right_voltage]
Eigen::Vector2d VoltageOrZero(aos::monotonic_clock::time_point now);
+ // Returns the latest drivetrain encoder values (in meters), or nullopt if no
+ // position message is available (or if the message is stale).
+ // Returns encoders as [left_position, right_position]
+ std::optional<Eigen::Vector2d> Encoders(aos::monotonic_clock::time_point now);
+
// Returns true if either there is no JoystickState message available or if
// we are currently in autonomous mode.
bool MaybeInAutonomous();
@@ -38,7 +48,11 @@
std::string_view node);
private:
+ aos::EventLoop *const event_loop_;
aos::Fetcher<frc971::control_loops::drivetrain::Output> output_fetcher_;
+ aos::Fetcher<frc971::control_loops::drivetrain::Position> position_fetcher_;
+ aos::Fetcher<frc971::control_loops::drivetrain::RioLocalizerInputs>
+ combined_fetcher_;
aos::Fetcher<aos::message_bridge::ServerStatistics> clock_offset_fetcher_;
aos::Fetcher<aos::JoystickState> joystick_state_fetcher_;
};
@@ -47,6 +61,63 @@
Eigen::Matrix<double, 4, 4> FlatbufferToTransformationMatrix(
const frc971::vision::calibration::TransformationMatrix &flatbuffer);
+// This approximates the Jacobian of a vector of [heading, distance, skew]
+// of a target with respect to the full state of a drivetrain EKF.
+// Note that the only nonzero values in the returned matrix will be in the
+// columns corresponding to the X, Y, and Theta components of the state.
+// This is suitable for use as the H matrix in the kalman updates of the EKF,
+// although due to the approximation it should not be used to actually
+// calculate the expected measurement.
+// target_pose is the global pose of the target that we have identified.
+// camera_pose is the current estimate of the global pose of
+// the camera that can see the target.
+template <typename Scalar>
+Eigen::Matrix<double, 3, HybridEkf<Scalar>::kNStates>
+HMatrixForCameraHeadingDistanceSkew(const TypedPose<Scalar> &target_pose,
+ const TypedPose<Scalar> &camera_pose) {
+ // For all of the below calculations, we will assume to a first
+ // approximation that:
+ //
+ // dcamera_theta / dtheta ~= 1
+ // dcamera_x / dx ~= 1
+ // dcamera_y / dy ~= 1
+ //
+ // For cameras sufficiently far from the robot's origin, or if the robot were
+ // spinning extremely rapidly, this would not hold.
+
+ // To calculate dheading/d{x,y,theta}:
+ // heading = arctan2(target_pos - camera_pos) - camera_theta
+ Eigen::Matrix<Scalar, 3, 1> target_pos = target_pose.abs_pos();
+ Eigen::Matrix<Scalar, 3, 1> camera_pos = camera_pose.abs_pos();
+ Scalar diffx = target_pos.x() - camera_pos.x();
+ Scalar diffy = target_pos.y() - camera_pos.y();
+ Scalar norm2 = diffx * diffx + diffy * diffy;
+ Scalar dheadingdx = diffy / norm2;
+ Scalar dheadingdy = -diffx / norm2;
+ Scalar dheadingdtheta = -1.0;
+
+ // To calculate ddistance/d{x,y}:
+ // distance = sqrt(diffx^2 + diffy^2)
+ Scalar distance = ::std::sqrt(norm2);
+ Scalar ddistdx = -diffx / distance;
+ Scalar ddistdy = -diffy / distance;
+
+ // Skew = target.theta - camera.theta - heading
+ // = target.theta - arctan2(target_pos - camera_pos)
+ Scalar dskewdx = -dheadingdx;
+ Scalar dskewdy = -dheadingdy;
+ Eigen::Matrix<Scalar, 3, HybridEkf<Scalar>::kNStates> H;
+ H.setZero();
+ H(0, HybridEkf<Scalar>::kX) = dheadingdx;
+ H(0, HybridEkf<Scalar>::kY) = dheadingdy;
+ H(0, HybridEkf<Scalar>::kTheta) = dheadingdtheta;
+ H(1, HybridEkf<Scalar>::kX) = ddistdx;
+ H(1, HybridEkf<Scalar>::kY) = ddistdy;
+ H(2, HybridEkf<Scalar>::kX) = dskewdx;
+ H(2, HybridEkf<Scalar>::kY) = dskewdy;
+ return H;
+}
+
} // namespace frc971::control_loops::drivetrain
#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_LOCALIZATION_UTILS_H_
diff --git a/frc971/control_loops/drivetrain/rio_localizer_inputs.fbs b/frc971/control_loops/drivetrain/rio_localizer_inputs.fbs
new file mode 100644
index 0000000..b9dc359
--- /dev/null
+++ b/frc971/control_loops/drivetrain/rio_localizer_inputs.fbs
@@ -0,0 +1,12 @@
+namespace frc971.control_loops.drivetrain;
+
+// This combines the drivetrian Position and Output messages
+// to be forwarded to the imu orin.
+table RioLocalizerInputs {
+ left_voltage:double (id: 0);
+ right_voltage:double (id: 1);
+ left_encoder:double (id: 2);
+ right_encoder:double (id: 3);
+}
+
+root_type RioLocalizerInputs;
diff --git a/frc971/control_loops/profiled_subsystem.h b/frc971/control_loops/profiled_subsystem.h
index 7a0b7fa..599c4da 100644
--- a/frc971/control_loops/profiled_subsystem.h
+++ b/frc971/control_loops/profiled_subsystem.h
@@ -148,7 +148,8 @@
};
template <typename ZeroingEstimator =
- ::frc971::zeroing::PotAndIndexPulseZeroingEstimator>
+ ::frc971::zeroing::PotAndIndexPulseZeroingEstimator,
+ class Profile = aos::util::TrapezoidProfile>
class SingleDOFProfiledSubsystem
: public ::frc971::control_loops::ProfiledSubsystem<3, 1,
ZeroingEstimator> {
@@ -209,6 +210,9 @@
double default_velocity() const { return default_velocity_; }
double default_acceleration() const { return default_acceleration_; }
+ // Returns a pointer to the profile in use.
+ Profile *mutable_profile() { return &profile_; }
+
protected:
// Limits the provided goal to the soft limits. Prints "name" when it fails
// to aid debugging.
@@ -218,7 +222,7 @@
private:
void UpdateOffset(double offset);
- aos::util::TrapezoidProfile profile_;
+ Profile profile_;
bool enable_profile_ = true;
// Current measurement.
@@ -240,12 +244,13 @@
} // namespace internal
-template <class ZeroingEstimator>
-SingleDOFProfiledSubsystem<ZeroingEstimator>::SingleDOFProfiledSubsystem(
- ::std::unique_ptr<SimpleCappedStateFeedbackLoop<3, 1, 1>> loop,
- const typename ZeroingEstimator::ZeroingConstants &zeroing_constants,
- const ::frc971::constants::Range &range, double default_velocity,
- double default_acceleration)
+template <class ZeroingEstimator, class Profile>
+SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::
+ SingleDOFProfiledSubsystem(
+ ::std::unique_ptr<SimpleCappedStateFeedbackLoop<3, 1, 1>> loop,
+ const typename ZeroingEstimator::ZeroingConstants &zeroing_constants,
+ const ::frc971::constants::Range &range, double default_velocity,
+ double default_acceleration)
: ProfiledSubsystem<3, 1, ZeroingEstimator>(
::std::move(loop), {{ZeroingEstimator(zeroing_constants)}}),
profile_(this->loop_->plant().coefficients().dt),
@@ -254,11 +259,11 @@
default_acceleration_(default_acceleration) {
Y_.setZero();
offset_.setZero();
- AdjustProfile(0.0, 0.0);
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::UpdateOffset(double offset) {
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::UpdateOffset(
+ double offset) {
const double doffset = offset - offset_(0, 0);
AOS_LOG(INFO, "Adjusting offset from %f to %f\n", offset_(0, 0), offset);
@@ -273,9 +278,10 @@
CapGoal("R", &this->loop_->mutable_R());
}
-template <class ZeroingEstimator>
+template <class ZeroingEstimator, class Profile>
template <class StatusTypeBuilder>
-StatusTypeBuilder SingleDOFProfiledSubsystem<ZeroingEstimator>::BuildStatus(
+StatusTypeBuilder
+SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::BuildStatus(
flatbuffers::FlatBufferBuilder *fbb) {
flatbuffers::Offset<typename ZeroingEstimator::State> estimator_state =
this->EstimatorState(fbb, 0);
@@ -306,8 +312,8 @@
return builder;
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::Correct(
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::Correct(
const typename ZeroingEstimator::Position &new_position) {
this->estimators_[0].UpdateEstimate(new_position);
@@ -336,8 +342,8 @@
this->X_hat_ = this->loop_->X_hat();
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::CapGoal(
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::CapGoal(
const char *name, Eigen::Matrix<double, 3, 1> *goal, bool print) {
// Limit the goal to min/max allowable positions.
if ((*goal)(0, 0) > range_.upper) {
@@ -356,8 +362,8 @@
}
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::ForceGoal(
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::ForceGoal(
double goal, double goal_velocity) {
set_unprofiled_goal(goal, goal_velocity, false);
this->loop_->mutable_R() = this->unprofiled_goal_;
@@ -367,8 +373,8 @@
this->profile_.MoveCurrentState(R.block<2, 1>(0, 0));
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::set_unprofiled_goal(
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::set_unprofiled_goal(
double unprofiled_goal, double unprofiled_goal_velocity, bool print) {
this->unprofiled_goal_(0, 0) = unprofiled_goal;
this->unprofiled_goal_(1, 0) = unprofiled_goal_velocity;
@@ -376,8 +382,8 @@
CapGoal("unprofiled R", &this->unprofiled_goal_, print);
}
-template <class ZeroingEstimator>
-double SingleDOFProfiledSubsystem<ZeroingEstimator>::UpdateController(
+template <class ZeroingEstimator, class Profile>
+double SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::UpdateController(
bool disable) {
// TODO(austin): What do we want to do with the profile on reset? Also, we
// should probably reset R, the offset, the profile, etc.
@@ -418,22 +424,23 @@
return this->loop_->U(0, 0);
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::UpdateObserver(
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::UpdateObserver(
double voltage) {
this->loop_->mutable_U(0, 0) = voltage;
this->loop_->UpdateObserver(this->loop_->U(), this->loop_->plant().dt());
}
-template <class ZeroingEstimator>
-double SingleDOFProfiledSubsystem<ZeroingEstimator>::Update(bool disable) {
+template <class ZeroingEstimator, class Profile>
+double SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::Update(
+ bool disable) {
const double voltage = UpdateController(disable);
UpdateObserver(voltage);
return voltage;
}
-template <class ZeroingEstimator>
-bool SingleDOFProfiledSubsystem<ZeroingEstimator>::CheckHardLimits() {
+template <class ZeroingEstimator, class Profile>
+bool SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::CheckHardLimits() {
// Returns whether hard limits have been exceeded.
if (position() > range_.upper_hard || position() < range_.lower_hard) {
@@ -447,8 +454,8 @@
return false;
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::AdjustProfile(
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::AdjustProfile(
const ::frc971::ProfileParameters *profile_parameters) {
AdjustProfile(
profile_parameters != nullptr ? profile_parameters->max_velocity() : 0.0,
@@ -456,8 +463,8 @@
: 0.0);
}
-template <class ZeroingEstimator>
-void SingleDOFProfiledSubsystem<ZeroingEstimator>::AdjustProfile(
+template <class ZeroingEstimator, class Profile>
+void SingleDOFProfiledSubsystem<ZeroingEstimator, Profile>::AdjustProfile(
double max_angular_velocity, double max_angular_acceleration) {
profile_.set_maximum_velocity(
internal::UseUnlessZero(max_angular_velocity, default_velocity_));
diff --git a/frc971/control_loops/python/angular_system.py b/frc971/control_loops/python/angular_system.py
index 551f3d0..16773fa 100755
--- a/frc971/control_loops/python/angular_system.py
+++ b/frc971/control_loops/python/angular_system.py
@@ -22,7 +22,9 @@
kalman_q_voltage,
kalman_r_position,
radius=None,
- dt=0.00505):
+ dt=0.00505,
+ enable_voltage_error=True,
+ delayed_u=0):
"""Constructs an AngularSystemParams object.
Args:
@@ -42,6 +44,8 @@
self.kalman_r_position = kalman_r_position
self.radius = radius
self.dt = dt
+ self.enable_voltage_error = enable_voltage_error
+ self.delayed_u = delayed_u
class AngularSystem(control_loop.ControlLoop):
@@ -56,7 +60,8 @@
self.G = params.G
# Moment of inertia in kg m^2
- self.J = params.J + self.motor.motor_inertia / (self.G**2.0)
+ self.J_motor = self.motor.motor_inertia / (self.G**2.0)
+ self.J = params.J + self.J_motor
# Control loop time step
self.dt = params.dt
@@ -182,7 +187,8 @@
self.K_unaugmented = self.K
self.K = numpy.matrix(numpy.zeros((1, 3)))
self.K[0, 0:2] = self.K_unaugmented
- self.K[0, 2] = 1
+ if params.enable_voltage_error:
+ self.K[0, 2] = 1
self.Kff = numpy.concatenate(
(self.Kff, numpy.matrix(numpy.zeros((1, 1)))), axis=1)
@@ -225,6 +231,10 @@
v_goal_plot = []
x_hat_plot = []
u_plot = []
+ power_rotor_plot = []
+ power_mechanism_plot = []
+ power_overall_plot = []
+ power_electrical_plot = []
offset_plot = []
if controller is None:
@@ -273,8 +283,18 @@
motor_current = (U[0, 0] - plant.X[1, 0] / plant.G /
plant.motor.Kv) / plant.motor.resistance
motor_current_plot.append(motor_current)
- battery_current = U[0, 0] * motor_current / 12.0
+ battery_current = U[0, 0] * motor_current / vbat
+ power_electrical_plot.append(battery_current * vbat)
battery_current_plot.append(battery_current)
+
+ # Instantaneous acceleration.
+ X_dot = plant.A_continuous * plant.X + plant.B_continuous * U
+ # Torque = J * alpha (accel).
+ power_rotor_plot.append(X_dot[1, 0] * plant.J_motor * plant.X[1, 0])
+ power_mechanism_plot.append(X_dot[1, 0] * plant.params.J *
+ plant.X[1, 0])
+ power_overall_plot.append(X_dot[1, 0] * plant.J * plant.X[1, 0])
+
x_plot.append(plant.X[0, 0])
if v_plot:
@@ -306,21 +326,34 @@
glog.debug('goal_error %s', repr(end_goal - goal))
glog.debug('error %s', repr(observer.X_hat - end_goal))
- pylab.subplot(3, 1, 1)
- pylab.plot(t_plot, x_plot, label='x')
- pylab.plot(t_plot, x_hat_plot, label='x_hat')
- pylab.plot(t_plot, x_goal_plot, label='x_goal')
- pylab.legend()
+ pylab.suptitle(f'Gear ratio {plant.G}')
+ position_ax1 = pylab.subplot(3, 1, 1)
+ position_ax1.plot(t_plot, x_plot, label='x')
+ position_ax1.plot(t_plot, x_hat_plot, label='x_hat')
+ position_ax1.plot(t_plot, x_goal_plot, label='x_goal')
- pylab.subplot(3, 1, 2)
- pylab.plot(t_plot, u_plot, label='u')
- pylab.plot(t_plot, offset_plot, label='voltage_offset')
- pylab.legend()
+ power_ax2 = position_ax1.twinx()
+ power_ax2.set_xlabel("time(s)")
+ power_ax2.set_ylabel("Power (W)")
+ power_ax2.plot(t_plot, power_rotor_plot, label='Rotor power')
+ power_ax2.plot(t_plot, power_mechanism_plot, label='Mechanism power')
+ power_ax2.plot(t_plot,
+ power_overall_plot,
+ label='Overall mechanical power')
+ power_ax2.plot(t_plot, power_electrical_plot, label='Electrical power')
+
+ position_ax1.legend()
+ power_ax2.legend(loc='lower right')
+
+ voltage_ax1 = pylab.subplot(3, 1, 2)
+ voltage_ax1.plot(t_plot, u_plot, label='u')
+ voltage_ax1.plot(t_plot, offset_plot, label='voltage_offset')
+ voltage_ax1.legend()
ax1 = pylab.subplot(3, 1, 3)
ax1.set_xlabel("time(s)")
ax1.set_ylabel("rad/s^2")
- ax1.plot(t_plot, a_plot, label='a')
+ ax1.plot(t_plot, a_plot, label='acceleration')
ax2 = ax1.twinx()
ax2.set_xlabel("time(s)")
diff --git a/frc971/control_loops/python/constants.py b/frc971/control_loops/python/constants.py
index e4ff6b7..260a6ad 100644
--- a/frc971/control_loops/python/constants.py
+++ b/frc971/control_loops/python/constants.py
@@ -37,8 +37,7 @@
Robot2021 = Robot2020
Robot2022 = RobotType(width=0.8763, length=0.96647)
Robot2023 = RobotType(width=0.6061, length=0.77581)
-#TODO (Nathan): Update 2024 robot dimensions when CAD is done
-Robot2024 = RobotType(width=0.9017, length=0.9525) # 35.5 in x 37.5 in
+Robot2024 = RobotType(width=0.85979, length=0.9906) # 33.85 in x 39.0 in
FIELDS = {
"2019 Field":
diff --git a/frc971/control_loops/python/linear_system.py b/frc971/control_loops/python/linear_system.py
index e28c422..aa8251e 100755
--- a/frc971/control_loops/python/linear_system.py
+++ b/frc971/control_loops/python/linear_system.py
@@ -50,8 +50,9 @@
self.radius = params.radius
# Mass in kg
- self.mass = params.mass + self.motor.motor_inertia / (
+ self.mass_motor = self.motor.motor_inertia / (
(self.G * self.radius)**2.0)
+ self.mass = params.mass + self.mass_motor
# Control loop time step
self.dt = params.dt
@@ -80,13 +81,22 @@
controllability = controls.ctrb(self.A, self.B)
glog.debug('Controllability of %d',
numpy.linalg.matrix_rank(controllability))
- glog.debug('Mass: %f', self.mass)
- glog.debug('Stall force: %f',
+ glog.debug('Mass: %f kg', self.mass)
+ glog.debug('Stall force: %f N',
self.motor.stall_torque / self.G / self.radius)
- glog.debug('Stall acceleration: %f',
+ glog.debug('Stall acceleration: %f m/s^2',
self.motor.stall_torque / self.G / self.radius / self.mass)
- glog.debug('Free speed is %f',
+ holding_force = 500
+
+ holding_current = holding_force * self.radius * self.G / self.motor.Kt
+ glog.debug('Motor current to hold %f N: %f A', holding_force,
+ holding_current)
+ glog.debug(
+ 'Battery current to hold %f N: %f A', holding_force,
+ holding_current * holding_current * self.motor.resistance / 12.0)
+
+ glog.debug('Free speed is %f m/s',
-self.B_continuous[1, 0] / self.A_continuous[1, 1] * 12.0)
self.Q = numpy.matrix([[(1.0 / (self.params.q_pos**2.0)), 0.0],
@@ -202,10 +212,16 @@
x_plot = []
v_plot = []
a_plot = []
+ motor_current_plot = []
+ battery_current_plot = []
x_goal_plot = []
v_goal_plot = []
x_hat_plot = []
u_plot = []
+ power_rotor_plot = []
+ power_mechanism_plot = []
+ power_overall_plot = []
+ power_electrical_plot = []
offset_plot = []
if controller is None:
@@ -249,6 +265,22 @@
U = U_uncapped.copy()
U[0, 0] = numpy.clip(U[0, 0], -vbat, vbat)
+
+ motor_current = (U[0, 0] - plant.X[1, 0] / plant.radius / plant.G /
+ plant.motor.Kv) / plant.motor.resistance
+ motor_current_plot.append(motor_current)
+ battery_current = U[0, 0] * motor_current / vbat
+ power_electrical_plot.append(battery_current * vbat)
+ battery_current_plot.append(battery_current)
+
+ # Instantaneous acceleration.
+ X_dot = plant.A_continuous * plant.X + plant.B_continuous * U
+ # Torque = J * alpha (accel).
+ power_rotor_plot.append(X_dot[1, 0] * plant.mass_motor * plant.X[1, 0])
+ power_mechanism_plot.append(X_dot[1, 0] * plant.params.mass *
+ plant.X[1, 0])
+ power_overall_plot.append(X_dot[1, 0] * plant.mass * plant.X[1, 0])
+
x_plot.append(plant.X[0, 0])
if v_plot:
@@ -280,20 +312,42 @@
glog.debug('goal_error %s', repr(end_goal - goal))
glog.debug('error %s', repr(observer.X_hat - end_goal))
- pylab.subplot(3, 1, 1)
- pylab.plot(t_plot, x_plot, label='x')
- pylab.plot(t_plot, x_hat_plot, label='x_hat')
- pylab.plot(t_plot, x_goal_plot, label='x_goal')
- pylab.legend()
+ pylab.suptitle(f'Gear ratio {plant.G}')
+ position_ax1 = pylab.subplot(3, 1, 1)
+ position_ax1.plot(t_plot, x_plot, label='x')
+ position_ax1.plot(t_plot, x_hat_plot, label='x_hat')
+ position_ax1.plot(t_plot, x_goal_plot, label='x_goal')
- pylab.subplot(3, 1, 2)
- pylab.plot(t_plot, u_plot, label='u')
- pylab.plot(t_plot, offset_plot, label='voltage_offset')
- pylab.legend()
+ power_ax2 = position_ax1.twinx()
+ power_ax2.set_xlabel("time(s)")
+ power_ax2.set_ylabel("Power (W)")
+ power_ax2.plot(t_plot, power_rotor_plot, label='Rotor power')
+ power_ax2.plot(t_plot, power_mechanism_plot, label='Mechanism power')
+ power_ax2.plot(t_plot,
+ power_overall_plot,
+ label='Overall mechanical power')
+ power_ax2.plot(t_plot, power_electrical_plot, label='Electrical power')
- pylab.subplot(3, 1, 3)
- pylab.plot(t_plot, a_plot, label='a')
- pylab.legend()
+ position_ax1.legend()
+ power_ax2.legend(loc='lower right')
+
+ voltage_ax1 = pylab.subplot(3, 1, 2)
+ voltage_ax1.plot(t_plot, u_plot, label='u')
+ voltage_ax1.plot(t_plot, offset_plot, label='voltage_offset')
+ voltage_ax1.legend()
+
+ ax1 = pylab.subplot(3, 1, 3)
+ ax1.set_xlabel("time(s)")
+ ax1.set_ylabel("m/s^2")
+ ax1.plot(t_plot, a_plot, label='acceleration')
+
+ ax2 = ax1.twinx()
+ ax2.set_xlabel("time(s)")
+ ax2.set_ylabel("Amps")
+ ax2.plot(t_plot, battery_current_plot, 'g', label='battery')
+ ax2.plot(t_plot, motor_current_plot, 'r', label='motor')
+ ax2.legend()
+ ax1.legend(loc='lower left')
pylab.show()
@@ -377,7 +431,7 @@
end_goal=augmented_R,
controller=controller,
observer=observer,
- duration=2.0,
+ duration=1.0,
use_profile=True,
max_velocity=max_velocity,
max_acceleration=max_acceleration)
diff --git a/frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h b/frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h
index 069914e..c084047 100644
--- a/frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h
+++ b/frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h
@@ -96,7 +96,8 @@
// Class for controlling and motion profiling a single degree of freedom
// subsystem with a zeroing strategy of not moving.
template <typename TZeroingEstimator, typename TProfiledJointStatus,
- typename TSubsystemParams = TZeroingEstimator>
+ typename TSubsystemParams = TZeroingEstimator,
+ typename TProfile = aos::util::TrapezoidProfile>
class StaticZeroingSingleDOFProfiledSubsystem {
public:
// Constructs the subsystem from flatbuffer types (appropriate when using the
@@ -147,8 +148,10 @@
// Sets the unprofiled goal which UpdateController will go to.
void set_unprofiled_goal(double position, double velocity);
- // Changes the profile parameters for UpdateController to track.
- void AdjustProfile(double velocity, double acceleration);
+
+ double unprofiled_goal(int row, int col) const {
+ return profiled_subsystem_.unprofiled_goal(row, col);
+ }
// Returns the current position
double position() const { return profiled_subsystem_.position(); }
@@ -178,6 +181,12 @@
flatbuffers::Offset<ProfiledJointStatus> MakeStatus(
flatbuffers::FlatBufferBuilder *status_fbb);
+ // Sets whether to use the trapezoidal profiler or whether to just bypass it
+ // and pass the unprofiled goal through directly.
+ void set_enable_profile(bool enable) {
+ profiled_subsystem_.set_enable_profile(enable);
+ }
+
// Iterates the controller with the provided goal.
flatbuffers::Offset<ProfiledJointStatus> Iterate(
const StaticZeroingSingleDOFProfiledSubsystemGoal *goal,
@@ -220,6 +229,9 @@
return profiled_subsystem_.controller();
}
+ // Returns a pointer to the profile in use.
+ TProfile *mutable_profile() { return profiled_subsystem_.mutable_profile(); }
+
private:
State state_ = State::UNINITIALIZED;
double min_position_, max_position_;
@@ -227,14 +239,15 @@
const StaticZeroingSingleDOFProfiledSubsystemParams<TSubsystemParams> params_;
- ::frc971::control_loops::SingleDOFProfiledSubsystem<ZeroingEstimator>
+ ::frc971::control_loops::SingleDOFProfiledSubsystem<ZeroingEstimator,
+ TProfile>
profiled_subsystem_;
};
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
StaticZeroingSingleDOFProfiledSubsystem<ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::
+ SubsystemParams, Profile>::
StaticZeroingSingleDOFProfiledSubsystem(
const StaticZeroingSingleDOFProfiledSubsystemParams<SubsystemParams>
¶ms)
@@ -251,9 +264,9 @@
};
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
void StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus, SubsystemParams>::Reset() {
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams, Profile>::Reset() {
state_ = State::UNINITIALIZED;
clear_min_position();
clear_max_position();
@@ -261,12 +274,12 @@
}
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
bool StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus, SubsystemParams>::
- Correct(const StaticZeroingSingleDOFProfiledSubsystemGoal *goal,
- const typename ZeroingEstimator::Position *position,
- bool disabled) {
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams,
+ Profile>::Correct(const StaticZeroingSingleDOFProfiledSubsystemGoal *goal,
+ const typename ZeroingEstimator::Position *position,
+ bool disabled) {
CHECK_NOTNULL(position);
profiled_subsystem_.Correct(*position);
@@ -296,8 +309,9 @@
// jump.
profiled_subsystem_.ForceGoal(profiled_subsystem_.position());
// Set up the profile to be the zeroing profile.
- profiled_subsystem_.AdjustProfile(
- params_.zeroing_profile_params.max_velocity,
+ mutable_profile()->set_maximum_velocity(
+ params_.zeroing_profile_params.max_velocity);
+ mutable_profile()->set_maximum_acceleration(
params_.zeroing_profile_params.max_acceleration);
// We are not ready to start doing anything yet.
@@ -321,11 +335,20 @@
if (goal) {
if (goal->profile_params()) {
- AdjustProfile(goal->profile_params()->max_velocity(),
- goal->profile_params()->max_acceleration());
+ mutable_profile()->set_maximum_velocity(
+ internal::UseUnlessZero(goal->profile_params()->max_velocity(),
+ profiled_subsystem_.default_velocity()));
+ mutable_profile()->set_maximum_acceleration(
+ std::min(static_cast<double>(max_acceleration_),
+ internal::UseUnlessZero(
+ goal->profile_params()->max_acceleration(),
+ profiled_subsystem_.default_acceleration())));
} else {
- AdjustProfile(profiled_subsystem_.default_velocity(),
- profiled_subsystem_.default_acceleration());
+ mutable_profile()->set_maximum_velocity(
+ profiled_subsystem_.default_velocity());
+ mutable_profile()->set_maximum_acceleration(
+ std::min(static_cast<double>(max_acceleration_),
+ profiled_subsystem_.default_acceleration()));
}
if (goal->has_ignore_profile()) {
@@ -352,10 +375,10 @@
}
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
void StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::set_unprofiled_goal(double goal, double goal_velocity) {
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams,
+ Profile>::set_unprofiled_goal(double goal, double goal_velocity) {
if (goal < min_position_) {
VLOG(1) << "Limiting to " << min_position_ << " from " << goal;
goal = min_position_;
@@ -368,22 +391,14 @@
}
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
-void StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::AdjustProfile(double velocity, double acceleration) {
- profiled_subsystem_.AdjustProfile(
- velocity, std::min(acceleration, static_cast<double>(max_acceleration_)));
-}
-
-template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
flatbuffers::Offset<ProfiledJointStatus>
-StaticZeroingSingleDOFProfiledSubsystem<ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::
- Iterate(const StaticZeroingSingleDOFProfiledSubsystemGoal *goal,
- const typename ZeroingEstimator::Position *position, double *output,
- flatbuffers::FlatBufferBuilder *status_fbb) {
+StaticZeroingSingleDOFProfiledSubsystem<
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams,
+ Profile>::Iterate(const StaticZeroingSingleDOFProfiledSubsystemGoal *goal,
+ const typename ZeroingEstimator::Position *position,
+ double *output,
+ flatbuffers::FlatBufferBuilder *status_fbb) {
const bool disabled = Correct(goal, position, output == nullptr);
// Calculate the loops for a cycle.
@@ -400,35 +415,35 @@
}
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
double StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::UpdateController(bool disabled) {
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams,
+ Profile>::UpdateController(bool disabled) {
return profiled_subsystem_.UpdateController(disabled);
}
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
void StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::UpdateObserver(double voltage) {
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams,
+ Profile>::UpdateObserver(double voltage) {
profiled_subsystem_.UpdateObserver(voltage);
}
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
void StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::ForceGoal(double goal, double goal_velocity) {
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams,
+ Profile>::ForceGoal(double goal, double goal_velocity) {
profiled_subsystem_.ForceGoal(goal, goal_velocity);
}
template <typename ZeroingEstimator, typename ProfiledJointStatus,
- typename SubsystemParams>
+ typename SubsystemParams, typename Profile>
flatbuffers::Offset<ProfiledJointStatus>
StaticZeroingSingleDOFProfiledSubsystem<
- ZeroingEstimator, ProfiledJointStatus,
- SubsystemParams>::MakeStatus(flatbuffers::FlatBufferBuilder *status_fbb) {
+ ZeroingEstimator, ProfiledJointStatus, SubsystemParams,
+ Profile>::MakeStatus(flatbuffers::FlatBufferBuilder *status_fbb) {
CHECK_NOTNULL(status_fbb);
typename ProfiledJointStatus::Builder status_builder =
diff --git a/frc971/control_loops/subsystem_simulator.h b/frc971/control_loops/subsystem_simulator.h
index 0a23a38..3dc44f7 100644
--- a/frc971/control_loops/subsystem_simulator.h
+++ b/frc971/control_loops/subsystem_simulator.h
@@ -79,6 +79,9 @@
PositionSensorSimulator *encoder() { return &encoder_; }
+ double position() const { return plant_->X(0, 0); }
+ double velocity() const { return plant_->X(1, 0); }
+
private:
std::unique_ptr<CappedTestPlant> plant_;
PositionSensorSimulator encoder_;
diff --git a/frc971/imu/BUILD b/frc971/imu/BUILD
index b90f844..3bccf20 100644
--- a/frc971/imu/BUILD
+++ b/frc971/imu/BUILD
@@ -61,3 +61,46 @@
uf2_from_elf(
name = "ADIS16505",
)
+
+cc_library(
+ name = "imu_calibrator",
+ srcs = ["imu_calibrator.cc"],
+ hdrs = [
+ "imu_calibrator.h",
+ "imu_calibrator-tmpl.h",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ "//aos/time",
+ "//frc971/math:interpolate",
+ "@com_google_absl//absl/strings:str_format",
+ "@com_google_ceres_solver//:ceres",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
+
+cc_library(
+ name = "imu_calibrator_solver",
+ srcs = [
+ "imu_calibrator_solver.cc",
+ ],
+ hdrs = [
+ "imu_calibrator_solver.h",
+ ],
+ deps = [
+ ":imu_calibrator",
+ "@com_google_ceres_solver//:ceres",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
+
+cc_test(
+ name = "imu_calibrator_test",
+ srcs = ["imu_calibrator_test.cc"],
+ shard_count = 3,
+ deps = [
+ ":imu_calibrator",
+ ":imu_calibrator_solver",
+ "//aos/testing:googletest",
+ ],
+)
diff --git a/frc971/imu/imu_calibrator-tmpl.h b/frc971/imu/imu_calibrator-tmpl.h
new file mode 100644
index 0000000..6743106
--- /dev/null
+++ b/frc971/imu/imu_calibrator-tmpl.h
@@ -0,0 +1,248 @@
+#include "frc971/imu/imu_calibrator.h"
+#include "frc971/math/interpolate.h"
+
+DECLARE_int32(imu_zeroing_buffer);
+
+namespace frc971::imu {
+
+inline constexpr double kGravityGs = 1.0;
+// rad / sec
+inline constexpr double kGyroMaxZeroingValue = 0.1;
+
+template <typename Scalar>
+void ImuCalibrator<Scalar>::InsertImu(size_t imu_index,
+ const RawImuReading &reading) {
+ CHECK_LT(imu_index, imu_readings_.size());
+ std::vector<ImuReading> &readings = imu_readings_[imu_index];
+ if (readings.size() > 0u) {
+ CHECK_LT(readings.back().capture_time_raw, reading.capture_time)
+ << ": Readings must be inserted in time order per IMU.";
+ }
+ // Execute the stationary logic. We identify if this reading is plausibly
+ // stationary, then if it is not stationary, we go back in time to any
+ // potentially relevant readings and mark them as not stationary. Finally, we
+ // go through and as values exit the FLAGS_imu_zeroing_buffer window we do any
+ // processing that we can given that we know it must be stationary.
+ const bool plausibly_stationary =
+ reading.gyro.squaredNorm() < kGyroMaxZeroingValue * kGyroMaxZeroingValue;
+ bool stationary = plausibly_stationary;
+ int earliest_affected_index = readings.size() - FLAGS_imu_zeroing_buffer;
+ for (size_t index = std::max(0, earliest_affected_index);
+ index < readings.size(); ++index) {
+ if (!plausibly_stationary) {
+ readings[index].stationary = false;
+ }
+ if (!readings[index].plausibly_stationary) {
+ stationary = false;
+ }
+ }
+
+ // Since we don't have data from before the start, assume that we may have
+ // been moving.
+ if (earliest_affected_index < 0) {
+ stationary = false;
+ }
+
+ if (earliest_affected_index >= 0) {
+ ImuReading &earliest_reading = readings[earliest_affected_index];
+ // The stationary flag for this reading can no longer change, so we can
+ // start to do things based on it.
+ earliest_reading.stationary_is_locked = true;
+ if (earliest_reading.stationary) {
+ earliest_reading.parameter_residuals.gravity =
+ earliest_reading.accel.norm() - kGravityGs;
+ earliest_reading.parameter_residuals.gyro_zero = earliest_reading.gyro;
+ LOG(INFO) << earliest_reading.gyro.transpose();
+ }
+ }
+
+ const ImuConfig<Scalar> &config = imu_configs_[imu_index];
+ Scalar capture_time_adjusted =
+ static_cast<Scalar>(aos::time::DurationInSeconds(
+ reading.capture_time.time_since_epoch())) -
+ (config.parameters.has_value() ? config.parameters->time_offset
+ : static_cast<Scalar>(0.0));
+
+ imu_readings_[imu_index].emplace_back(
+ reading.capture_time, capture_time_adjusted,
+ reading.gyro - config.dynamic_params.gyro_zero,
+ reading.accel / config.dynamic_params.gravity,
+ DynamicImuParameters<Scalar>{static_cast<Scalar>(0.0),
+ Eigen::Matrix<Scalar, 3, 1>::Zero()},
+ plausibly_stationary, stationary, false, std::nullopt, std::nullopt);
+}
+
+template <typename Scalar>
+void ImuCalibrator<Scalar>::EvaluateRelativeResiduals() {
+ for (const auto &readings : imu_readings_) {
+ CHECK_LT(static_cast<size_t>(FLAGS_imu_zeroing_buffer * 2), readings.size())
+ << ": Insufficient readings to perform calibration.";
+ }
+ Scalar base_clock = imu_readings_[origin_index_][0].capture_time_adjusted;
+ // Current index corresponding to the base_clock time.
+ std::vector<size_t> reading_indices(imu_configs_.size(), 0);
+ // The for loops are set up so that we:
+ // 1. Iterate over every pair of readings from the origin/base IMU.
+ // 2. For each other IMU, we identify 0 or 1 readings that fall between those
+ // two readings of the origin IMU. We then calculate the residuals for
+ // that IMU relative to the origin IMU, linearly interpolating between
+ // the pair of readings from (1) (by doing a linear interpolation, we can
+ // get sub-cycle accuracy on time offsets).
+ for (;
+ reading_indices[origin_index_] < imu_readings_[origin_index_].size() - 1;
+ ++reading_indices[origin_index_]) {
+ const ImuReading &base_reading =
+ imu_readings_[origin_index_][reading_indices[origin_index_]];
+ const ImuReading &next_base_reading =
+ imu_readings_[origin_index_][reading_indices[origin_index_] + 1];
+ base_clock = base_reading.capture_time_adjusted;
+ const Scalar next_base_clock = next_base_reading.capture_time_adjusted;
+ for (size_t imu_index = 0; imu_index < imu_configs_.size(); ++imu_index) {
+ const ImuConfig<Scalar> &imu_config = imu_configs_[imu_index];
+ // We don't care about calculating the offsets from the origin IMU to
+ // itself...
+ if (imu_config.is_origin) {
+ continue;
+ }
+ auto &readings = imu_readings_[imu_index];
+ bool done_with_imu = false;
+ // This will put the index for the current IMU just past the base_clock
+ // timepoint, allowing us to interpolate between
+ // reading_indices[origin_index_] and reading_indices[origin_index_] + 1.
+ while (readings[reading_indices[imu_index]].capture_time_adjusted <
+ base_clock) {
+ if (reading_indices[imu_index] == imu_readings_[imu_index].size() - 1) {
+ done_with_imu = true;
+ break;
+ }
+ ++reading_indices[imu_index];
+ }
+ // If we've run out of readings on this imu, stop doing anything.
+ if (done_with_imu) {
+ continue;
+ }
+ ImuReading &reading = readings[reading_indices[imu_index]];
+ const Scalar reading_time = reading.capture_time_adjusted;
+ if (reading_time >= next_base_clock) {
+ // There is a gap in readings for this imu; we can't meaningfully
+ // populate the residuals.
+ continue;
+ }
+ // Sanity check the above logic.
+ CHECK_LE(base_clock, reading_time);
+ CHECK_LT(reading_time, next_base_clock);
+ CHECK(imu_config.parameters.has_value());
+ reading.gyro_residual =
+ imu_config.parameters.value().rotation * reading.gyro -
+ frc971::math::Interpolate<Eigen::Matrix<Scalar, 3, 1>, Scalar>(
+ base_clock, next_base_clock, base_reading.gyro,
+ next_base_reading.gyro, reading_time);
+ if (!reading.stationary_is_locked || !reading.stationary) {
+ continue;
+ }
+ // In order to calculate the accelerometer residual, we are assuming that
+ // the two IMUs "should" produce identical accelerations. This is only
+ // true when not rotating. Future changes may account for coriolis
+ // effects.
+ reading.accel_residual =
+ imu_config.parameters.value().rotation * reading.accel -
+ frc971::math::Interpolate(base_clock, next_base_clock,
+ base_reading.accel, next_base_reading.accel,
+ reading_time);
+ }
+ }
+}
+
+// Helpers to accommodate serializing residuals into the ceres buffer. These
+// helpers all return a buffer that points to the next value to be populated.
+namespace internal {
+template <typename Scalar>
+std::span<Scalar> SerializeScalar(Scalar value, std::span<Scalar> out) {
+ DCHECK(!out.empty());
+ out[0] = value;
+ return out.subspan(1);
+}
+template <typename Scalar, int kSize>
+std::span<Scalar> SerializeVector(const Eigen::Matrix<Scalar, kSize, 1> &value,
+ std::span<Scalar> out) {
+ DCHECK_LE(static_cast<size_t>(value.size()), out.size());
+ for (int index = 0; index < kSize; ++index) {
+ out[index] = value(index);
+ }
+ return out.subspan(kSize);
+}
+template <typename Scalar>
+std::span<Scalar> SerializeParams(const DynamicImuParameters<Scalar> ¶ms,
+ std::span<Scalar> out) {
+ return SerializeVector(params.gyro_zero,
+ SerializeScalar(params.gravity, out));
+}
+inline constexpr int kResidualsPerReading = 10u;
+} // namespace internal
+
+template <typename Scalar>
+void ImuCalibrator<Scalar>::CalculateResiduals(std::span<Scalar> residuals) {
+ EvaluateRelativeResiduals();
+ for (size_t imu_index = 0; imu_index < imu_configs_.size(); ++imu_index) {
+ const auto &readings = imu_readings_[imu_index];
+ double valid_gyro_reading_count = 0;
+ double valid_accel_reading_count = 0;
+ for (size_t reading_index = 0; reading_index < readings.size();
+ ++reading_index) {
+ const auto &reading = readings[reading_index];
+ if (reading.gyro_residual.has_value()) {
+ ++valid_gyro_reading_count;
+ }
+ if (reading.accel_residual.has_value()) {
+ ++valid_accel_reading_count;
+ }
+ }
+ if (!imu_configs_[imu_index].is_origin) {
+ CHECK_LT(0, valid_gyro_reading_count);
+ CHECK_LT(0, valid_accel_reading_count);
+ } else {
+ valid_gyro_reading_count = readings.size();
+ valid_accel_reading_count = readings.size();
+ }
+ // Adjust the residuals of the readings to ensure that the solver doesn't
+ // cheat by just making it so that the time-offsets are completely
+ // misaligned and we can say that all the residuals are "zero".
+ const Scalar gyro_reading_scalar =
+ static_cast<Scalar>(readings.size() / valid_gyro_reading_count);
+ const Scalar accel_reading_scalar =
+ static_cast<Scalar>(readings.size() / valid_accel_reading_count);
+ for (size_t reading_index = 0; reading_index < readings.size();
+ ++reading_index) {
+ const auto &reading = readings[reading_index];
+ const Scalar *const start_residual = residuals.data();
+ // 4 residuals (gravity scalar; gyro zeroes)
+ residuals =
+ internal::SerializeParams(reading.parameter_residuals, residuals);
+ const Eigen::Matrix<Scalar, 3, 1> gyro_residual =
+ reading.gyro_residual.value_or(Eigen::Matrix<Scalar, 3, 1>::Zero()) *
+ gyro_reading_scalar;
+ // 3 residuals
+ residuals = internal::SerializeVector(gyro_residual, residuals);
+ const Eigen::Matrix<Scalar, 3, 1> accel_residual =
+ reading.accel_residual.value_or(Eigen::Matrix<Scalar, 3, 1>::Zero()) *
+ accel_reading_scalar;
+ // 3 residuals
+ residuals = internal::SerializeVector(accel_residual, residuals);
+ CHECK_EQ(internal::kResidualsPerReading,
+ residuals.data() - start_residual)
+ << ": Need to update kResidualsPerReading.";
+ }
+ }
+}
+
+template <typename Scalar>
+size_t ImuCalibrator<Scalar>::CalculateNumResiduals(
+ const std::vector<size_t> &num_readings) {
+ size_t num_residuals = 0;
+ for (const size_t count : num_readings) {
+ num_residuals += internal::kResidualsPerReading * count;
+ }
+ return num_residuals;
+}
+
+} // namespace frc971::imu
diff --git a/frc971/imu/imu_calibrator.cc b/frc971/imu/imu_calibrator.cc
new file mode 100644
index 0000000..03fa377
--- /dev/null
+++ b/frc971/imu/imu_calibrator.cc
@@ -0,0 +1,6 @@
+#include "frc971/imu/imu_calibrator.h"
+
+DEFINE_int32(
+ imu_zeroing_buffer, 100,
+ "We will only consider readings stationary for purposes if calibration if "
+ "this many readings to either side appear to be stationary.");
diff --git a/frc971/imu/imu_calibrator.h b/frc971/imu/imu_calibrator.h
new file mode 100644
index 0000000..be4c4d4
--- /dev/null
+++ b/frc971/imu/imu_calibrator.h
@@ -0,0 +1,331 @@
+#ifndef FRC971_IMU_IMU_CALIBRATOR_H_
+#define FRC971_IMU_IMU_CALIBRATOR_H_
+
+#include <optional>
+#include <span>
+#include <tuple>
+#include <vector>
+
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_join.h"
+#include "ceres/ceres.h"
+#include "glog/logging.h"
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+#include "aos/time/time.h"
+
+namespace frc971::imu {
+
+// Contains a reading that comes directly from an IMU.
+// These should not be zeroed or corrected for yet.
+struct RawImuReading {
+ aos::monotonic_clock::time_point capture_time;
+ // gyro readings are in radians / sec; accelerometer readings are in g's.
+ const Eigen::Vector3d gyro;
+ const Eigen::Vector3d accel;
+};
+
+// Note on how we manage populating ceres parameters:
+// * We use dynamically-sized parameter lists for convenience.
+// * For every struct that we have that corresponds to problem parameters,
+// there is a PopulateParameters() method that takes a:
+// * ceres::Problem* that is used for setting parameter constraints.
+// * ceres::DynamicCostFunction* that is used to mark that we have added
+// a parameter block.
+// * A std::vector<double*> that will later be passed to AddResidualBlock.
+// We add any parameter blocks which we add to the problem.
+// * post_populate_methods is a list of std::function's that will get called
+// after everything is populated and added (this is necessary because
+// we cannot add constraints to the Problem until after everything
+// has been added).
+// * Additionally, there is a PopulateFromParameters() method which is used
+// in the ceres cost functor and:
+// * Takes a parameters double-pointer where parameters[X] is the
+// parameter block X.
+// * Returns a pair where the first value is a populated instance of the
+// struct and an updated parameters pointer which points to the next
+// set of pointers.
+// * All the Populate* methods must be called in the same order every
+// time so that they result in the raw pointers getting populated
+// consistently.
+
+// These are the parameters corresponding to things which will vary at every
+// power-on of the IMU.
+template <typename Scalar>
+struct DynamicImuParameters {
+ // A scalar to represent the current magnitude of gravity, in g's.
+ // This currently compensates both for any variations in local gravity as well
+ // as for some amount of variations in the IMU itself. In the future we may
+ // expand this to have a local gravity number that is global to all IMUs while
+ // separately calibrating per-axis information.
+ Scalar gravity;
+ // Current gyro zero, in radians / sec.
+ // These are the per-axis values that the gyro will read when it is sitting
+ // still. Technically these zeroes will drift over time; however, the
+ // time-windows that we expect to use for calibration are short enough that
+ // this should be a non-issue.
+ Eigen::Matrix<Scalar, 3, 1> gyro_zero;
+ void PopulateParameters(
+ ceres::Problem *problem, ceres::DynamicCostFunction *cost_function,
+ std::vector<double *> *parameters,
+ std::vector<std::function<void()>> *post_populate_methods) {
+ cost_function->AddParameterBlock(1);
+ parameters->push_back(&gravity);
+ cost_function->AddParameterBlock(3);
+ parameters->push_back(gyro_zero.data());
+ post_populate_methods->emplace_back([this, problem]() {
+ // Gravity shouldn't vary by much (these bounds are significantly larger
+ // than any real variations which we will experience).
+ problem->SetParameterLowerBound(&gravity, 0, 0.95);
+ problem->SetParameterUpperBound(&gravity, 0, 1.05);
+ for (int i = 0; i < 3; ++i) {
+ problem->SetParameterLowerBound(gyro_zero.data(), i, -0.05);
+ problem->SetParameterUpperBound(gyro_zero.data(), i, 0.05);
+ }
+ });
+ }
+ static std::tuple<DynamicImuParameters<Scalar>, const Scalar *const *>
+ PopulateFromParameters(const Scalar *const *parameters) {
+ const Scalar *const gravity = parameters[0];
+ ++parameters;
+ const Scalar *const gyro = parameters[0];
+ ++parameters;
+ return std::make_tuple(
+ DynamicImuParameters<Scalar>{
+ *gravity, Eigen::Matrix<Scalar, 3, 1>(gyro[0], gyro[1], gyro[2])},
+ parameters);
+ }
+ std::string ToString() const {
+ std::stringstream out;
+ out << "gravity: " << gravity << " gyro_zero: " << gyro_zero.transpose();
+ return out.str();
+ }
+};
+
+// These parameters correspond to IMU parameters which will not vary between
+// boots. Namely, the relative positions and time offsets of the IMU(s).
+template <typename Scalar>
+struct StaticImuParameters {
+ // If you have a point p_imu in this IMU's frame then (rotation *
+ // p_imu + position) will give you that point's position in the board frame
+ // (the "board" frame refers to the frame associated with the entire PCB,
+ // where the PCB itself contains multiple IMUs. The board frame will be
+ // attached to whichever IMU is being treated as the origin).
+ Eigen::Quaternion<Scalar> rotation;
+ // position is currently unused because it is only observeable when there
+ // are coriolis effects, and we currently only make use of accelerometer
+ // readings from when the IMU is sat still.
+ // As such, we currently assume that all position offsets are zero.
+ // Eigen::Matrix<Scalar, 3, 1> position;
+ // The "true" time at which the event occurred is equal to the capture time -
+ // time_offset. I.e., a more positive time offset implies that the there is a
+ // large delay between the imu readings being taken and us observing them.
+ Scalar time_offset;
+
+ void PopulateParameters(
+ ceres::EigenQuaternionParameterization *quaternion_local_parameterization,
+ ceres::Problem *problem, ceres::DynamicCostFunction *cost_function,
+ std::vector<double *> *parameters,
+ std::vector<std::function<void()>> *post_populate_methods) {
+ cost_function->AddParameterBlock(4);
+ parameters->push_back(rotation.coeffs().data());
+ cost_function->AddParameterBlock(1);
+ parameters->push_back(&time_offset);
+ post_populate_methods->emplace_back(
+ [this, problem, quaternion_local_parameterization]() {
+ problem->SetParameterization(rotation.coeffs().data(),
+ quaternion_local_parameterization);
+ problem->SetParameterLowerBound(&time_offset, 0, -0.03);
+ problem->SetParameterUpperBound(&time_offset, 0, 0.03);
+ });
+ }
+ static std::tuple<StaticImuParameters, const Scalar *const *>
+ PopulateFromParameters(const Scalar *const *parameters) {
+ const Scalar *const quat = parameters[0];
+ ++parameters;
+ const Scalar *const time = parameters[0];
+ ++parameters;
+ return std::make_tuple(
+ StaticImuParameters{
+ Eigen::Quaternion<Scalar>(quat[3], quat[0], quat[1], quat[2]),
+ *time},
+ parameters);
+ }
+ std::string ToString() const {
+ std::stringstream out;
+ out << "quat: " << rotation.coeffs().transpose()
+ << " time_offset: " << time_offset;
+ return out.str();
+ }
+};
+
+// Represents the calibration for a single IMU.
+template <typename Scalar>
+struct ImuConfig {
+ // Set to true if this IMU is to be considered the origin of the coordinate
+ // system. This will also mean that this IMU is treated as the source-of-truth
+ // for clock offsets.
+ bool is_origin;
+ // Will be nullopt if is_origin is true (the corresponding rotations and
+ // offsets will all be the identity/zero as appropriate).
+ std::optional<StaticImuParameters<Scalar>> parameters;
+
+ DynamicImuParameters<Scalar> dynamic_params{
+ .gravity = static_cast<Scalar>(1.0),
+ .gyro_zero = Eigen::Matrix<Scalar, 3, 1>::Zero()};
+ std::string ToString() const {
+ return absl::StrFormat(
+ "is_origin: %d params: %s dynamic: %s", is_origin,
+ parameters.has_value() ? parameters->ToString() : std::string("<null>"),
+ dynamic_params.ToString());
+ }
+};
+
+// Represents all of the configuration parameters for the entire system.
+template <typename Scalar>
+struct AllParameters {
+ // Each entry in the imus vector will be a single imu.
+ std::vector<ImuConfig<Scalar>> imus;
+ std::tuple<std::vector<double *>, std::vector<std::function<void()>>>
+ PopulateParameters(
+ ceres::EigenQuaternionParameterization *quaternion_local_parameterization,
+ ceres::Problem *problem, ceres::DynamicCostFunction *cost_function) {
+ std::vector<std::function<void()>> post_populate_methods;
+ std::vector<double *> parameters;
+ for (auto &imu : imus) {
+ if (imu.parameters.has_value()) {
+ imu.parameters.value().PopulateParameters(
+ quaternion_local_parameterization, problem, cost_function,
+ ¶meters, &post_populate_methods);
+ }
+ imu.dynamic_params.PopulateParameters(problem, cost_function, ¶meters,
+ &post_populate_methods);
+ }
+ return std::make_tuple(parameters, post_populate_methods);
+ }
+ static AllParameters PopulateFromParameters(
+ const std::vector<ImuConfig<double>> &nominal_configs,
+ const Scalar *const *parameters) {
+ std::vector<ImuConfig<Scalar>> result;
+ for (size_t imu_index = 0; imu_index < nominal_configs.size();
+ ++imu_index) {
+ ImuConfig<Scalar> config;
+ config.is_origin = nominal_configs[imu_index].is_origin;
+ if (!config.is_origin) {
+ std::tie(config.parameters, parameters) =
+ StaticImuParameters<Scalar>::PopulateFromParameters(parameters);
+ }
+ std::tie(config.dynamic_params, parameters) =
+ DynamicImuParameters<Scalar>::PopulateFromParameters(parameters);
+ result.emplace_back(std::move(config));
+ }
+ return {.imus = std::move(result)};
+ }
+ std::string ToString() const {
+ std::vector<std::string> imu_strings;
+ for (const auto &imu : imus) {
+ std::vector<std::string> dynamic_params;
+ imu_strings.push_back(absl::StrFormat("config: %s", imu.ToString()));
+ }
+ return absl::StrJoin(imu_strings, "\n");
+ }
+};
+
+// This class does the hard work to support calibrating multiple IMU's
+// orientations relative to one another. It is set up to readily be used with a
+// ceres solver (see imu_calibrator.*).
+//
+// The current theory of operation is to have some number of imus, one of which
+// we will consider to be fixed in position. We have a fixed set of data that we
+// feed into this class, which can be parameterized based on:
+// * The current zeroes for each IMU.
+// * The orientation of each non-fixed IMU.
+// * The time-offset of each non-fixed IMU.
+//
+// When run under ceres, ceres can then vary these parameters to solve for the
+// current calibrations of the IMUs.
+//
+// When solving, the main complexity is that some internal state has to be
+// tracked to try to determine when we should be calculating zeroes and when we
+// can calibrate out the magnitude of gravity. This is done by tracking when the
+// IMUs are "stationary". For a reading to be stationary, all values with
+// FLAGS_imu_zeroing_buffer readings of the reading must be "plausibly
+// stationary". Readings are plausibly stationary if they have sufficiently low
+// gyro values.
+//
+// TODO: Provide some utilities to plot the results of a calibration.
+template <typename Scalar>
+class ImuCalibrator {
+ public:
+ ImuCalibrator(const std::vector<ImuConfig<Scalar>> &imu_configs)
+ : imu_configs_(imu_configs), imu_readings_(imu_configs.size()) {
+ origin_index_ = -1;
+ for (size_t imu_index = 0; imu_index < imu_configs_.size(); ++imu_index) {
+ if (imu_configs_[imu_index].is_origin) {
+ CHECK_EQ(origin_index_, -1)
+ << ": Can't have more than one IMU specified as the origin.";
+ origin_index_ = imu_index;
+ }
+ }
+ CHECK_NE(origin_index_, -1)
+ << ": Must have at least one IMU specified as the origin.";
+ }
+
+ // These gyro readings will be "raw"---i.e., they still need to get
+ // transformed by nominal_transform.
+ // gyro readings are in radians / sec; accelerometer readings are in g's.
+ // Within a given imu, must be called in time order.
+ void InsertImu(size_t imu_index, const RawImuReading &reading);
+
+ // Populates all the residuals that we use for the cost in ceres.
+ void CalculateResiduals(std::span<Scalar> residuals);
+
+ // Returns the total number of residuals that this problem will have, given
+ // the number of readings for each IMU.
+ static size_t CalculateNumResiduals(const std::vector<size_t> &num_readings);
+
+ private:
+ // Represents an imu reading. The values in this are generally already
+ // adjusted for the provided parameters.
+ struct ImuReading {
+ // The "actual" provided capture time. Used for debugging.
+ aos::monotonic_clock::time_point capture_time_raw;
+ // The capture time, adjusted for this IMU's time offset.
+ Scalar capture_time_adjusted;
+ // gyro reading, adjusted for gyro zero but NOT rotation.
+ // In radians / sec.
+ Eigen::Matrix<Scalar, 3, 1> gyro;
+ // accelerometer reading, adjusted for gravity value but NOT rotation.
+ // In g's.
+ Eigen::Matrix<Scalar, 3, 1> accel;
+ // Residuals associated with the DynamicImuParameters for this imu.
+ DynamicImuParameters<Scalar> parameter_residuals;
+ // Set if this measurement *could* be part of a segment of time where the
+ // IMU is stationary.
+ bool plausibly_stationary;
+ // Set to true if all values with FLAGS_imu_zeroing_buffer of this reading
+ // are plausibly_stationary.
+ bool stationary;
+ // We set stationary_is_locked once we have enough readings to either side
+ // of this value to guarantee that it is stationary.
+ bool stationary_is_locked;
+ // Residuals that are used for calibrating the rotation values. These are
+ // nullopt if we can't calibrate for some reason (e.g., this is the origin
+ // IMU, or for the accelerometer residual, we don't populate it if we are
+ // not stationary).
+ std::optional<Eigen::Matrix<Scalar, 3, 1>> gyro_residual;
+ std::optional<Eigen::Matrix<Scalar, 3, 1>> accel_residual;
+ };
+ void EvaluateRelativeResiduals();
+
+ const std::vector<ImuConfig<Scalar>> imu_configs_;
+ // Index of the IMU which is the origin IMU.
+ int origin_index_;
+ std::vector<std::vector<ImuReading>> imu_readings_;
+};
+
+} // namespace frc971::imu
+
+#include "frc971/imu/imu_calibrator-tmpl.h"
+#endif // FRC971_IMU_IMU_CALIBRATOR_H_
diff --git a/frc971/imu/imu_calibrator_solver.cc b/frc971/imu/imu_calibrator_solver.cc
new file mode 100644
index 0000000..19b82e5
--- /dev/null
+++ b/frc971/imu/imu_calibrator_solver.cc
@@ -0,0 +1,89 @@
+#include "frc971/imu/imu_calibrator_solver.h"
+namespace frc971::imu {
+
+struct ImuCalibratorCostFunctor {
+ ImuCalibratorCostFunctor(
+ const std::vector<std::vector<RawImuReading>> &readings,
+ const std::vector<ImuConfig<double>> &nominal_config,
+ const size_t num_residuals)
+ : readings_(readings),
+ nominal_config_(nominal_config),
+ num_residuals_(num_residuals) {}
+ template <typename S>
+ bool operator()(const S *const *const parameters_ptr, S *residual) const {
+ AllParameters<S> params = AllParameters<S>::PopulateFromParameters(
+ nominal_config_, parameters_ptr);
+ std::vector<ImuConfig<S>> imu_configs;
+ for (const auto ¶m : params.imus) {
+ imu_configs.push_back(param);
+ }
+ ImuCalibrator<S> calibrator(imu_configs);
+ for (size_t imu_index = 0; imu_index < readings_.size(); ++imu_index) {
+ const auto imu_readings = readings_[imu_index];
+ for (size_t reading_index = 0; reading_index < imu_readings.size();
+ ++reading_index) {
+ calibrator.InsertImu(imu_index, imu_readings[reading_index]);
+ }
+ }
+ calibrator.CalculateResiduals({residual, num_residuals_});
+ return true;
+ }
+ const std::vector<std::vector<RawImuReading>> readings_;
+ const std::vector<ImuConfig<double>> nominal_config_;
+ const size_t num_residuals_;
+};
+
+AllParameters<double> Solve(
+ const std::vector<std::vector<RawImuReading>> &readings,
+ const std::vector<ImuConfig<double>> &nominal_config) {
+ ceres::Problem problem;
+
+ ceres::EigenQuaternionParameterization *quaternion_local_parameterization =
+ new ceres::EigenQuaternionParameterization();
+ AllParameters<double> parameters;
+ std::vector<size_t> num_readings;
+ CHECK_EQ(nominal_config.size(), readings.size());
+ for (size_t imu_index = 0; imu_index < nominal_config.size(); ++imu_index) {
+ const size_t num_params = readings[imu_index].size();
+ parameters.imus.emplace_back(nominal_config[imu_index]);
+ num_readings.push_back(num_params);
+ }
+ // Set up the only cost function (also known as residual). This uses
+ // auto-differentiation to obtain the derivative (jacobian).
+
+ {
+ const size_t num_residuals =
+ ImuCalibrator<double>::CalculateNumResiduals(num_readings);
+ ceres::DynamicCostFunction *cost_function =
+ new ceres::DynamicAutoDiffCostFunction<ImuCalibratorCostFunctor>(
+ new ImuCalibratorCostFunctor(readings, nominal_config,
+ num_residuals));
+
+ auto [vector_parameters, post_populate_methods] =
+ parameters.PopulateParameters(quaternion_local_parameterization,
+ &problem, cost_function);
+
+ cost_function->SetNumResiduals(num_residuals);
+
+ problem.AddResidualBlock(cost_function, new ceres::HuberLoss(1.0),
+ vector_parameters);
+ for (auto &method : post_populate_methods) {
+ method();
+ }
+ }
+
+ // Run the solver!
+ ceres::Solver::Options options;
+ options.minimizer_progress_to_stdout = true;
+ options.gradient_tolerance = 1e-12;
+ options.function_tolerance = 1e-6;
+ options.parameter_tolerance = 1e-6;
+ ceres::Solver::Summary summary;
+ Solve(options, &problem, &summary);
+ LOG(INFO) << summary.FullReport();
+ LOG(INFO) << "Solution is " << (summary.IsSolutionUsable() ? "" : "NOT ")
+ << "usable";
+ LOG(INFO) << "Solution:\n" << parameters.ToString();
+ return parameters;
+}
+} // namespace frc971::imu
diff --git a/frc971/imu/imu_calibrator_solver.h b/frc971/imu/imu_calibrator_solver.h
new file mode 100644
index 0000000..148fcc4
--- /dev/null
+++ b/frc971/imu/imu_calibrator_solver.h
@@ -0,0 +1,14 @@
+#ifndef FRC971_IMU_IMU_CALIBRATOR_SOLVER_H_
+#define FRC971_IMU_IMU_CALIBRATOR_SOLVER_H_
+
+#include "frc971/imu/imu_calibrator.h"
+
+namespace frc971::imu {
+
+// Stores all the IMU data from a log so that we can feed it into the
+// ImuCalibrator readily.
+AllParameters<double> Solve(
+ const std::vector<std::vector<RawImuReading>> &readings,
+ const std::vector<ImuConfig<double>> &nominal_config);
+} // namespace frc971::imu
+#endif // FRC971_IMU_IMU_CALIBRATOR_SOLVER_H_
diff --git a/frc971/imu/imu_calibrator_test.cc b/frc971/imu/imu_calibrator_test.cc
new file mode 100644
index 0000000..a341b2a
--- /dev/null
+++ b/frc971/imu/imu_calibrator_test.cc
@@ -0,0 +1,215 @@
+#include "frc971/imu/imu_calibrator.h"
+
+#include <random>
+
+#include "gtest/gtest.h"
+
+#include "frc971/imu/imu_calibrator_solver.h"
+
+namespace frc971::imu::testing {
+class ImuSimulator {
+ public:
+ ImuSimulator(const std::vector<ImuConfig<double>> &imu_configs)
+ : imu_configs_(imu_configs), readings_(imu_configs.size()) {}
+ void SimulateForTime(aos::monotonic_clock::duration duration,
+ aos::monotonic_clock::duration dt,
+ const Eigen::Vector3d &gravity_vector,
+ const Eigen::Vector3d &accel,
+ const Eigen::Vector3d &gyro) {
+ for (const aos::monotonic_clock::time_point end_time = now + duration;
+ now < end_time; now += dt) {
+ for (size_t imu_index = 0; imu_index < imu_configs_.size(); ++imu_index) {
+ const ImuConfig<double> &config = imu_configs_[imu_index];
+ const Eigen::Quaterniond rotation =
+ config.is_origin ? Eigen::Quaterniond::Identity()
+ : config.parameters->rotation.inverse();
+ const std::chrono::nanoseconds time_offset{
+ config.is_origin
+ ? 0
+ : static_cast<uint64_t>(config.parameters->time_offset * 1e9)};
+ readings_[imu_index].emplace_back(
+ now + time_offset,
+ rotation * gyro + config.dynamic_params.gyro_zero + GyroNoise(),
+ rotation *
+ (accel + gravity_vector * config.dynamic_params.gravity) +
+ AccelNoise());
+ }
+ }
+ }
+ Eigen::Vector3d GyroNoise() {
+ return (enable_noise_ ? 1.0 : 0.0) *
+ Eigen::Vector3d(distribution_(generator_), distribution_(generator_),
+ distribution_(generator_));
+ }
+ Eigen::Vector3d AccelNoise() { return GyroNoise() * 2.0; }
+ void set_enable_noise(bool enable) { enable_noise_ = enable; }
+ std::vector<std::vector<RawImuReading>> readings() const {
+ return readings_;
+ };
+
+ private:
+ const std::vector<ImuConfig<double>> imu_configs_;
+ std::vector<std::vector<RawImuReading>> readings_;
+ aos::monotonic_clock::time_point now = aos::monotonic_clock::epoch();
+
+ std::mt19937 generator_;
+ std::normal_distribution<> distribution_{0.0, 0.00025};
+ bool enable_noise_ = false;
+};
+
+namespace {
+void VerifyParameters(const std::vector<ImuConfig<double>> &imus,
+ const AllParameters<double> ¶ms, double eps = 1e-8) {
+ ASSERT_EQ(imus.size(), params.imus.size());
+ for (size_t imu_index = 0; imu_index < imus.size(); ++imu_index) {
+ SCOPED_TRACE(imu_index);
+ const ImuConfig<double> &expected = imus[imu_index];
+ const ImuConfig<double> &calculated = params.imus[imu_index];
+ EXPECT_EQ(expected.parameters.has_value(),
+ calculated.parameters.has_value());
+ EXPECT_NEAR(expected.dynamic_params.gravity,
+ calculated.dynamic_params.gravity, eps);
+ EXPECT_LT((expected.dynamic_params.gyro_zero -
+ calculated.dynamic_params.gyro_zero)
+ .norm(),
+ eps)
+ << expected.dynamic_params.gyro_zero.transpose() << " vs. "
+ << calculated.dynamic_params.gyro_zero.transpose();
+ if (expected.parameters.has_value()) {
+ EXPECT_NEAR(expected.parameters->time_offset,
+ calculated.parameters->time_offset, eps);
+ EXPECT_LT(((expected.parameters->rotation *
+ calculated.parameters->rotation.inverse())
+ .coeffs() -
+ Eigen::Quaterniond::Identity().coeffs())
+ .norm(),
+ eps)
+ << expected.parameters->rotation.coeffs().transpose() << " vs. "
+ << calculated.parameters->rotation.coeffs().transpose();
+ }
+ }
+}
+} // namespace
+
+// Confirms that we can calibrate in a relatively simple scenario where we have
+// some gyro/accelerometer offsets and a small rotation that is not accounted
+// for in the nominal parameters.
+TEST(ImuCalibratorTest, BasicCalibrationTest) {
+ std::vector<ImuConfig<double>> nominal_imus = {
+ ImuConfig<double>{true, std::nullopt},
+ ImuConfig<double>{false, std::make_optional<StaticImuParameters<double>>(
+ Eigen::Quaterniond::Identity(), 0.0)}};
+
+ std::vector<ImuConfig<double>> real_imus = nominal_imus;
+ real_imus[0].dynamic_params.gravity = 1.005;
+ real_imus[0].dynamic_params.gyro_zero << 0.001, 0.002, 0.003;
+ real_imus[1].dynamic_params.gyro_zero << -0.009, -0.007, -0.001;
+ real_imus[1].parameters->rotation =
+ Eigen::AngleAxisd(0.01, Eigen::Vector3d::UnitZ());
+ ImuSimulator simulator(real_imus);
+ simulator.SimulateForTime(std::chrono::seconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 0, 1), Eigen::Vector3d(0, 0, 0),
+ Eigen::Vector3d(0.0, 0.0, 0.0));
+ simulator.SimulateForTime(std::chrono::seconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 1, 0), Eigen::Vector3d(0, 0, 0),
+ Eigen::Vector3d(0.0, 0.0, 0.0));
+ simulator.SimulateForTime(std::chrono::seconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(1, 0, 0), Eigen::Vector3d(0, 0, 0),
+ Eigen::Vector3d(0.0, 0.0, 0.0));
+ simulator.SimulateForTime(
+ std::chrono::seconds(1), std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 0, 1), Eigen::Vector3d(0.1, 0.2, 0.3),
+ Eigen::Vector3d(1.0, 0.0, 0.0));
+ simulator.SimulateForTime(
+ std::chrono::seconds(1), std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 0, 1), Eigen::Vector3d(0.1, 0.2, 0.3),
+ Eigen::Vector3d(0.0, 1.0, 0.0));
+ auto params = Solve(simulator.readings(), nominal_imus);
+ LOG(INFO) << params.ToString();
+ LOG(INFO) << real_imus[0].ToString();
+ LOG(INFO) << real_imus[1].ToString();
+ VerifyParameters(real_imus, params);
+}
+
+// Separately test the estimation of the time offset between IMUs.
+// This is done separately because the optimization problem is poorly condition
+// for estimating time offsets when there is just a handful of step changes in
+// the IMU inputs; feeding in a sine wave works much better for allowing the
+// solver to estimate the offset.
+TEST(ImuCalibratorTest, TimeOffsetTest) {
+ gflags::FlagSaver flag_saver;
+
+ std::vector<ImuConfig<double>> nominal_imus = {
+ ImuConfig<double>{true, std::nullopt},
+ ImuConfig<double>{false, std::make_optional<StaticImuParameters<double>>(
+ Eigen::Quaterniond::Identity(), 0.0)}};
+
+ std::vector<ImuConfig<double>> real_imus = nominal_imus;
+ real_imus[1].parameters->time_offset = 0.0255;
+ ImuSimulator simulator(real_imus);
+ // Note on convergence: It is easy to end up in situations where the problem
+ // is not outstandingly well conditioned and we can end up with local minima
+ // where changes to physical calibration attributes can explain the time
+ // offset.
+ simulator.SimulateForTime(std::chrono::seconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 0, 1), Eigen::Vector3d(0, 0, 0),
+ Eigen::Vector3d(0.0, 0.0, 0.0));
+ for (size_t ii = 0; ii < 10000; ++ii) {
+ simulator.SimulateForTime(
+ std::chrono::milliseconds(1), std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 0, 1), Eigen::Vector3d(0.0, 0.0, 0.0),
+ Eigen::Vector3d(std::sin(ii / 1000.0), 0.0, 0.0));
+ }
+ auto params = Solve(simulator.readings(), nominal_imus);
+ LOG(INFO) << params.ToString();
+ LOG(INFO) << real_imus[0].ToString();
+ LOG(INFO) << real_imus[1].ToString();
+ VerifyParameters(real_imus, params, 1e-6);
+}
+
+// Test that if we add in some random noise that the solver still behaves
+// itself.
+TEST(ImuCalibratorTest, RandomNoise) {
+ std::vector<ImuConfig<double>> nominal_imus = {
+ ImuConfig<double>{true, std::nullopt},
+ ImuConfig<double>{false, std::make_optional<StaticImuParameters<double>>(
+ Eigen::Quaterniond::Identity(), 0.0)}};
+
+ std::vector<ImuConfig<double>> real_imus = nominal_imus;
+ real_imus[0].dynamic_params.gravity = 0.999;
+ real_imus[0].dynamic_params.gyro_zero << 0.001, 0.002, 0.003;
+ real_imus[1].dynamic_params.gyro_zero << -0.009, -0.007, -0.001;
+ real_imus[1].parameters->rotation =
+ Eigen::AngleAxisd(0.01, Eigen::Vector3d::UnitZ());
+ ImuSimulator simulator(real_imus);
+ simulator.set_enable_noise(true);
+ simulator.SimulateForTime(std::chrono::seconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 0, 1), Eigen::Vector3d(0, 0, 0),
+ Eigen::Vector3d(0.0, 0.0, 0.0));
+ simulator.SimulateForTime(std::chrono::seconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 1, 0), Eigen::Vector3d(0, 0, 0),
+ Eigen::Vector3d(0.0, 0.0, 0.0));
+ simulator.SimulateForTime(std::chrono::seconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(1, 0, 0), Eigen::Vector3d(0, 0, 0),
+ Eigen::Vector3d(0.0, 0.0, 0.0));
+ for (size_t ii = 0; ii < 6000; ++ii) {
+ simulator.SimulateForTime(std::chrono::milliseconds(1),
+ std::chrono::milliseconds(1),
+ Eigen::Vector3d(0, 0, 1),
+ Eigen::Vector3d(std::sin(ii / 1000.0), 0.0, 0.0),
+ Eigen::Vector3d(1.0, 0.0, 0.0));
+ }
+ auto params = Solve(simulator.readings(), nominal_imus);
+ LOG(INFO) << params.ToString();
+ LOG(INFO) << real_imus[0].ToString();
+ LOG(INFO) << real_imus[1].ToString();
+ VerifyParameters(real_imus, params, 1e-4);
+}
+} // namespace frc971::imu::testing
diff --git a/frc971/imu_fdcan/BUILD b/frc971/imu_fdcan/BUILD
index deb6a7e..c8a6cd7 100644
--- a/frc971/imu_fdcan/BUILD
+++ b/frc971/imu_fdcan/BUILD
@@ -1,3 +1,4 @@
+load("//aos:config.bzl", "aos_config")
load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
static_flatbuffer(
@@ -5,3 +6,125 @@
srcs = ["dual_imu.fbs"],
visibility = ["//visibility:public"],
)
+
+static_flatbuffer(
+ name = "can_translator_status_fbs",
+ srcs = ["can_translator_status.fbs"],
+ visibility = ["//visibility:public"],
+)
+
+static_flatbuffer(
+ name = "dual_imu_blender_status_fbs",
+ srcs = ["dual_imu_blender_status.fbs"],
+ visibility = ["//visibility:public"],
+)
+
+cc_binary(
+ name = "can_translator",
+ srcs = ["can_translator_main.cc"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":can_translator_lib",
+ "//aos:init",
+ "//aos/events:shm_event_loop",
+ ],
+)
+
+cc_library(
+ name = "can_translator_lib",
+ srcs = [
+ "can_translator_lib.cc",
+ ],
+ hdrs = [
+ "can_translator_lib.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":can_translator_status_fbs",
+ ":dual_imu_fbs",
+ "//aos/events:event_loop",
+ "//frc971/can_logger:can_logging_fbs",
+ ],
+)
+
+cc_binary(
+ name = "dual_imu_blender",
+ srcs = ["dual_imu_blender_main.cc"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":dual_imu_blender_lib",
+ "//aos:init",
+ "//aos/events:shm_event_loop",
+ ],
+)
+
+cc_library(
+ name = "dual_imu_blender_lib",
+ srcs = [
+ "dual_imu_blender_lib.cc",
+ ],
+ hdrs = [
+ "dual_imu_blender_lib.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":dual_imu_blender_status_fbs",
+ ":dual_imu_fbs",
+ "//aos/events:event_loop",
+ "//frc971/can_logger:can_logging_fbs",
+ "//frc971/wpilib:imu_batch_fbs",
+ ],
+)
+
+cc_test(
+ name = "can_translator_lib_test",
+ srcs = [
+ "can_translator_lib_test.cc",
+ ],
+ data = [
+ ":dual_imu_test_config",
+ ],
+ deps = [
+ ":can_translator_lib",
+ ":can_translator_status_fbs",
+ ":dual_imu_fbs",
+ "//aos/events:simulated_event_loop",
+ "//aos/testing:googletest",
+ "//frc971/can_logger:can_logging_fbs",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
+cc_test(
+ name = "dual_imu_blender_lib_test",
+ srcs = [
+ "dual_imu_blender_lib_test.cc",
+ ],
+ data = [
+ ":dual_imu_test_config",
+ ],
+ deps = [
+ ":dual_imu_blender_lib",
+ ":dual_imu_blender_status_fbs",
+ ":dual_imu_fbs",
+ "//aos/events:simulated_event_loop",
+ "//aos/testing:googletest",
+ "//frc971/can_logger:can_logging_fbs",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
+aos_config(
+ name = "dual_imu_test_config",
+ src = "dual_imu_test_config_source.json",
+ flatbuffers = [
+ "//aos/logging:log_message_fbs",
+ ":dual_imu_fbs",
+ ":can_translator_status_fbs",
+ "//frc971/can_logger:can_logging_fbs",
+ ":dual_imu_blender_status_fbs",
+ "//frc971/wpilib:imu_batch_fbs",
+ "//aos/events:event_loop_fbs",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+)
diff --git a/frc971/imu_fdcan/can_translator_lib.cc b/frc971/imu_fdcan/can_translator_lib.cc
new file mode 100644
index 0000000..e4a927e
--- /dev/null
+++ b/frc971/imu_fdcan/can_translator_lib.cc
@@ -0,0 +1,138 @@
+#include "frc971/imu_fdcan/can_translator_lib.h"
+
+using frc971::imu_fdcan::CANTranslator;
+
+constexpr std::size_t kCanFrameSize = 64;
+constexpr int kImuCanId = 1;
+
+CANTranslator::CANTranslator(aos::EventLoop *event_loop,
+ std::string_view canframe_channel)
+ : event_loop_(event_loop),
+ dual_imu_sender_(
+ event_loop->MakeSender<frc971::imu::DualImuStatic>("/imu")),
+ can_translator_status_sender_(
+ event_loop->MakeSender<frc971::imu::CanTranslatorStatusStatic>(
+ "/imu")) {
+ // TODO(max): Update this with a proper priority
+ event_loop->SetRuntimeRealtimePriority(15);
+
+ event_loop->MakeWatcher(
+ canframe_channel, [this](const frc971::can_logger::CanFrame &can_frame) {
+ if (can_frame.data()->size() / sizeof(uint8_t) != 64) {
+ invalid_packet_count_++;
+ }
+
+ if (can_frame.can_id() != kImuCanId) {
+ invalid_can_id_count_++;
+ return;
+ }
+
+ if (can_frame.data()->size() / sizeof(uint8_t) == 64) {
+ valid_packet_count_++;
+ HandleFrame(&can_frame);
+ }
+ });
+
+ event_loop->AddPhasedLoop(
+ [this](int) {
+ aos::Sender<frc971::imu::CanTranslatorStatusStatic>::StaticBuilder
+ status_builder = can_translator_status_sender_.MakeStaticBuilder();
+
+ status_builder->set_valid_packet_count(valid_packet_count_);
+ status_builder->set_invalid_packet_count(invalid_packet_count_);
+ status_builder->set_invalid_can_id_count(invalid_can_id_count_);
+
+ status_builder.CheckOk(status_builder.Send());
+ },
+ std::chrono::milliseconds(100));
+}
+
+// Gets the data from the span and iterates it to the next section of bytes.
+template <typename T>
+T GetAndPopDataFromBuffer(std::span<const uint8_t> &span) {
+ T value = 0;
+
+ std::memcpy(&value, span.data(), sizeof(T));
+ span = span.subspan(sizeof(T));
+
+ return value;
+}
+
+// Values from the data field mapping table in
+// https://docs.google.com/document/d/12AJUruW7DZ2pIrDzTyPC0qqFoia4QOSVlax6Jd7m4H0/edit?usp=sharing
+void CANTranslator::HandleFrame(const frc971::can_logger::CanFrame *can_frame) {
+ aos::Sender<frc971::imu::DualImuStatic>::StaticBuilder dual_imu_builder =
+ dual_imu_sender_.MakeStaticBuilder();
+
+ std::span can_data(can_frame->data()->data(), kCanFrameSize);
+
+ frc971::imu::SingleImuStatic *murata = dual_imu_builder->add_murata();
+
+ auto *murata_chip_states = murata->add_chip_states();
+ frc971::imu::ChipStateStatic *murata_uno_chip_state =
+ murata_chip_states->emplace_back();
+ frc971::imu::ChipStateStatic *murata_due_chip_state =
+ murata_chip_states->emplace_back();
+
+ frc971::imu::SingleImuStatic *tdk = dual_imu_builder->add_tdk();
+
+ auto tdk_chip_states = tdk->add_chip_states();
+ frc971::imu::ChipStateStatic *tdk_chip_state =
+ tdk_chip_states->emplace_back();
+
+ dual_imu_builder->set_board_timestamp_us(
+ GetAndPopDataFromBuffer<uint32_t>(can_data));
+
+ dual_imu_builder->set_packet_counter(
+ GetAndPopDataFromBuffer<uint16_t>(can_data));
+
+ tdk_chip_state->set_counter(GetAndPopDataFromBuffer<uint16_t>(can_data));
+ murata_uno_chip_state->set_counter(
+ GetAndPopDataFromBuffer<uint16_t>(can_data));
+ murata_due_chip_state->set_counter(
+ GetAndPopDataFromBuffer<uint16_t>(can_data));
+
+ tdk->set_accelerometer_x(GetAndPopDataFromBuffer<float>(can_data));
+ tdk->set_accelerometer_y(GetAndPopDataFromBuffer<float>(can_data));
+ tdk->set_accelerometer_z(GetAndPopDataFromBuffer<float>(can_data));
+
+ tdk->set_gyro_x(GetAndPopDataFromBuffer<float>(can_data));
+ tdk->set_gyro_y(GetAndPopDataFromBuffer<float>(can_data));
+ tdk->set_gyro_z(GetAndPopDataFromBuffer<float>(can_data));
+
+ murata->set_accelerometer_x(GetAndPopDataFromBuffer<float>(can_data));
+ murata->set_accelerometer_y(GetAndPopDataFromBuffer<float>(can_data));
+ murata->set_accelerometer_z(GetAndPopDataFromBuffer<float>(can_data));
+
+ murata->set_gyro_x(GetAndPopDataFromBuffer<float>(can_data));
+ murata->set_gyro_y(GetAndPopDataFromBuffer<float>(can_data));
+ murata->set_gyro_z(GetAndPopDataFromBuffer<float>(can_data));
+
+ tdk_chip_state->set_temperature(GetAndPopDataFromBuffer<uint8_t>(can_data));
+ murata_uno_chip_state->set_temperature(
+ GetAndPopDataFromBuffer<uint8_t>(can_data));
+ murata_due_chip_state->set_temperature(
+ GetAndPopDataFromBuffer<uint8_t>(can_data));
+
+ murata_uno_chip_state->set_max_counter(std::numeric_limits<uint16_t>::max());
+ murata_due_chip_state->set_max_counter(std::numeric_limits<uint16_t>::max());
+ tdk_chip_state->set_max_counter(std::numeric_limits<uint16_t>::max());
+ dual_imu_builder->set_max_packet_counter(
+ std::numeric_limits<uint16_t>::max());
+
+ // The timestamp coming from the CanFrame is a realtime timestamp. Calculate
+ // the offset into a monotonic time based on the clock samples from when the
+ // CanFrame was sent (this may not be ultra principled, as there are no strict
+ // bounds on how much time can pass between the clock samples we are using; in
+ // practice, the differences should be negligible).
+ const int64_t realtime_offset =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(
+ event_loop_->context().monotonic_event_time.time_since_epoch() -
+ event_loop_->context().realtime_event_time.time_since_epoch())
+ .count();
+
+ dual_imu_builder->set_kernel_timestamp(can_frame->realtime_timestamp_ns() +
+ realtime_offset);
+
+ dual_imu_builder.CheckOk(dual_imu_builder.Send());
+}
diff --git a/frc971/imu_fdcan/can_translator_lib.h b/frc971/imu_fdcan/can_translator_lib.h
new file mode 100644
index 0000000..12acb0a
--- /dev/null
+++ b/frc971/imu_fdcan/can_translator_lib.h
@@ -0,0 +1,31 @@
+#ifndef FRC971_IMU_FDCAN_CAN_TRANSLATOR_LIB_H_
+#define FRC971_IMU_FDCAN_CAN_TRANSLATOR_LIB_H_
+#include "aos/events/event_loop.h"
+#include "frc971/can_logger/can_logging_generated.h"
+#include "frc971/imu_fdcan/can_translator_status_static.h"
+#include "frc971/imu_fdcan/dual_imu_static.h"
+
+namespace frc971::imu_fdcan {
+
+// Translates the CanFrames from the IMU into a DualIMU message based on the
+// spec defined in this doc:
+// https://docs.google.com/document/d/12AJUruW7DZ2pIrDzTyPC0qqFoia4QOSVlax6Jd7m4H0/edit?usp=sharing
+class CANTranslator {
+ public:
+ CANTranslator(aos::EventLoop *event_loop, std::string_view canframe_channel);
+
+ private:
+ void HandleFrame(const can_logger::CanFrame *can_frame);
+
+ aos::EventLoop *event_loop_;
+ aos::Sender<imu::DualImuStatic> dual_imu_sender_;
+ aos::Sender<imu::CanTranslatorStatusStatic> can_translator_status_sender_;
+
+ uint64_t valid_packet_count_ = 0;
+ uint64_t invalid_packet_count_ = 0;
+ uint64_t invalid_can_id_count_ = 0;
+};
+
+} // namespace frc971::imu_fdcan
+
+#endif // FRC971_IMU_FDCAN_CAN_TRANSLATOR_LIB_H_
diff --git a/frc971/imu_fdcan/can_translator_lib_test.cc b/frc971/imu_fdcan/can_translator_lib_test.cc
new file mode 100644
index 0000000..f8e6d85
--- /dev/null
+++ b/frc971/imu_fdcan/can_translator_lib_test.cc
@@ -0,0 +1,197 @@
+#include "frc971/imu_fdcan/can_translator_lib.h"
+
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+#include "aos/events/simulated_event_loop.h"
+#include "frc971/can_logger/can_logging_static.h"
+#include "frc971/imu_fdcan/can_translator_status_generated.h"
+#include "frc971/imu_fdcan/dual_imu_generated.h"
+
+class CANTranslatorTest : public ::testing::Test {
+ public:
+ CANTranslatorTest()
+ : config_(aos::configuration::ReadConfig(
+ "frc971/imu_fdcan/dual_imu_test_config.json")),
+ event_loop_factory_(&config_.message()),
+ can_translator_event_loop_(
+ event_loop_factory_.MakeEventLoop("can_translator")),
+ can_frame_event_loop_(event_loop_factory_.MakeEventLoop("can_frame")),
+ dual_imu_fetcher_(
+ can_translator_event_loop_->MakeFetcher<frc971::imu::DualImu>(
+ "/imu")),
+ can_translator_status_fetcher_(
+ can_translator_event_loop_
+ ->MakeFetcher<frc971::imu::CanTranslatorStatus>("/imu")),
+ can_frame_sender_(
+ can_frame_event_loop_
+ ->MakeSender<frc971::can_logger::CanFrameStatic>("/can")),
+ can_translator_(can_translator_event_loop_.get(), "/can") {}
+
+ protected:
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+ aos::SimulatedEventLoopFactory event_loop_factory_;
+
+ std::unique_ptr<aos::EventLoop> can_translator_event_loop_;
+ std::unique_ptr<aos::EventLoop> can_frame_event_loop_;
+
+ aos::Fetcher<frc971::imu::DualImu> dual_imu_fetcher_;
+ aos::Fetcher<frc971::imu::CanTranslatorStatus> can_translator_status_fetcher_;
+
+ aos::Sender<frc971::can_logger::CanFrameStatic> can_frame_sender_;
+
+ frc971::imu_fdcan::CANTranslator can_translator_;
+};
+
+TEST_F(CANTranslatorTest, CheckValidFrame) {
+ event_loop_factory_.GetNodeEventLoopFactory(can_frame_event_loop_->node())
+ ->SetRealtimeOffset(
+ aos::monotonic_clock::epoch() + std::chrono::seconds(0),
+ aos::realtime_clock::epoch() + std::chrono::seconds(100));
+ can_frame_event_loop_->OnRun([this] {
+ aos::Sender<frc971::can_logger::CanFrameStatic>::StaticBuilder
+ can_frame_builder = can_frame_sender_.MakeStaticBuilder();
+
+ can_frame_builder->set_can_id(1);
+ can_frame_builder->set_realtime_timestamp_ns(100e9 + 971);
+ auto can_data = can_frame_builder->add_data();
+ CHECK(can_data->reserve(sizeof(uint8_t) * 64));
+
+ CHECK(can_data->emplace_back(226));
+ CHECK(can_data->emplace_back(100));
+ CHECK(can_data->emplace_back(108));
+ CHECK(can_data->emplace_back(8));
+ CHECK(can_data->emplace_back(152));
+ CHECK(can_data->emplace_back(40));
+ CHECK(can_data->emplace_back(202));
+ CHECK(can_data->emplace_back(121));
+ CHECK(can_data->emplace_back(202));
+ CHECK(can_data->emplace_back(121));
+ CHECK(can_data->emplace_back(202));
+ CHECK(can_data->emplace_back(121));
+ CHECK(can_data->emplace_back(85));
+ CHECK(can_data->emplace_back(85));
+ CHECK(can_data->emplace_back(81));
+ CHECK(can_data->emplace_back(189));
+ CHECK(can_data->emplace_back(0));
+ CHECK(can_data->emplace_back(0));
+ CHECK(can_data->emplace_back(8));
+ CHECK(can_data->emplace_back(189));
+ CHECK(can_data->emplace_back(85));
+ CHECK(can_data->emplace_back(213));
+ CHECK(can_data->emplace_back(127));
+ CHECK(can_data->emplace_back(191));
+ CHECK(can_data->emplace_back(12));
+ CHECK(can_data->emplace_back(189));
+ CHECK(can_data->emplace_back(34));
+ CHECK(can_data->emplace_back(187));
+ CHECK(can_data->emplace_back(255));
+ CHECK(can_data->emplace_back(219));
+ CHECK(can_data->emplace_back(220));
+ CHECK(can_data->emplace_back(59));
+ CHECK(can_data->emplace_back(147));
+ CHECK(can_data->emplace_back(173));
+ CHECK(can_data->emplace_back(5));
+ CHECK(can_data->emplace_back(61));
+ CHECK(can_data->emplace_back(88));
+ CHECK(can_data->emplace_back(68));
+ CHECK(can_data->emplace_back(205));
+ CHECK(can_data->emplace_back(188));
+ CHECK(can_data->emplace_back(230));
+ CHECK(can_data->emplace_back(92));
+ CHECK(can_data->emplace_back(24));
+ CHECK(can_data->emplace_back(189));
+ CHECK(can_data->emplace_back(235));
+ CHECK(can_data->emplace_back(1));
+ CHECK(can_data->emplace_back(127));
+ CHECK(can_data->emplace_back(191));
+ CHECK(can_data->emplace_back(210));
+ CHECK(can_data->emplace_back(7));
+ CHECK(can_data->emplace_back(34));
+ CHECK(can_data->emplace_back(54));
+ CHECK(can_data->emplace_back(86));
+ CHECK(can_data->emplace_back(103));
+ CHECK(can_data->emplace_back(133));
+ CHECK(can_data->emplace_back(186));
+ CHECK(can_data->emplace_back(100));
+ CHECK(can_data->emplace_back(205));
+ CHECK(can_data->emplace_back(101));
+ CHECK(can_data->emplace_back(185));
+ CHECK(can_data->emplace_back(29));
+ CHECK(can_data->emplace_back(26));
+ CHECK(can_data->emplace_back(26));
+ CHECK(can_data->emplace_back(0));
+
+ can_frame_builder.CheckOk(can_frame_builder.Send());
+ });
+
+ event_loop_factory_.RunFor(std::chrono::milliseconds(200));
+
+ ASSERT_TRUE(can_translator_status_fetcher_.Fetch());
+ ASSERT_TRUE(dual_imu_fetcher_.Fetch());
+
+ ASSERT_FALSE(can_translator_status_fetcher_->invalid_packet_count() > 0);
+ ASSERT_FALSE(can_translator_status_fetcher_->invalid_can_id_count() > 0);
+ EXPECT_EQ(can_translator_status_fetcher_->valid_packet_count(), 1);
+
+ EXPECT_EQ(dual_imu_fetcher_->board_timestamp_us(), 141321442);
+ EXPECT_EQ(dual_imu_fetcher_->packet_counter(), 10392);
+
+ EXPECT_NEAR(dual_imu_fetcher_->murata()->gyro_x(), 2.41444e-06, 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->murata()->gyro_y(), -0.00101779, 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->murata()->gyro_z(), -0.000219157, 0.00001);
+
+ EXPECT_NEAR(dual_imu_fetcher_->murata()->accelerometer_x(), -0.025057,
+ 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->murata()->accelerometer_y(), -0.037198,
+ 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->murata()->accelerometer_z(), -0.996123,
+ 0.00001);
+
+ EXPECT_EQ(dual_imu_fetcher_->murata()->chip_states()->Get(0)->counter(),
+ 31178);
+ EXPECT_EQ(dual_imu_fetcher_->murata()->chip_states()->Get(0)->temperature(),
+ 26);
+
+ EXPECT_EQ(dual_imu_fetcher_->murata()->chip_states()->Get(1)->counter(),
+ 31178);
+ EXPECT_EQ(dual_imu_fetcher_->murata()->chip_states()->Get(1)->temperature(),
+ 26);
+
+ EXPECT_NEAR(dual_imu_fetcher_->tdk()->gyro_x(), -0.00248319, 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->tdk()->gyro_y(), 0.00674009, 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->tdk()->gyro_z(), 0.0326362, 0.00001);
+
+ EXPECT_NEAR(dual_imu_fetcher_->tdk()->accelerometer_x(), -0.0511068, 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->tdk()->accelerometer_y(), -0.0332031, 0.00001);
+ EXPECT_NEAR(dual_imu_fetcher_->tdk()->accelerometer_z(), -0.999349, 0.00001);
+
+ EXPECT_EQ(dual_imu_fetcher_->tdk()->chip_states()->Get(0)->counter(), 31178);
+ EXPECT_EQ(dual_imu_fetcher_->tdk()->chip_states()->Get(0)->temperature(), 29);
+
+ EXPECT_EQ(dual_imu_fetcher_->kernel_timestamp(), 971);
+}
+
+TEST_F(CANTranslatorTest, CheckInvalidFrame) {
+ can_frame_event_loop_->OnRun([this] {
+ aos::Sender<frc971::can_logger::CanFrameStatic>::StaticBuilder
+ can_frame_builder = can_frame_sender_.MakeStaticBuilder();
+
+ can_frame_builder->set_can_id(2);
+ can_frame_builder->set_realtime_timestamp_ns(100);
+ auto can_data = can_frame_builder->add_data();
+ CHECK(can_data->reserve(sizeof(uint8_t) * 1));
+
+ CHECK(can_data->emplace_back(0));
+
+ can_frame_builder.CheckOk(can_frame_builder.Send());
+ });
+
+ event_loop_factory_.RunFor(std::chrono::milliseconds(200));
+
+ ASSERT_TRUE(can_translator_status_fetcher_.Fetch());
+ ASSERT_FALSE(dual_imu_fetcher_.Fetch());
+
+ EXPECT_EQ(can_translator_status_fetcher_->invalid_packet_count(), 1);
+ EXPECT_EQ(can_translator_status_fetcher_->invalid_can_id_count(), 1);
+}
diff --git a/frc971/imu_fdcan/can_translator_main.cc b/frc971/imu_fdcan/can_translator_main.cc
new file mode 100644
index 0000000..fdb7107
--- /dev/null
+++ b/frc971/imu_fdcan/can_translator_main.cc
@@ -0,0 +1,22 @@
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "frc971/imu_fdcan/can_translator_lib.h"
+
+DEFINE_string(channel, "/can", "The CAN channel to use");
+
+using frc971::imu_fdcan::CANTranslator;
+
+int main(int argc, char **argv) {
+ ::aos::InitGoogle(&argc, &argv);
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig("aos_config.json");
+
+ ::aos::ShmEventLoop event_loop(&config.message());
+
+ CANTranslator translator(&event_loop, FLAGS_channel);
+
+ event_loop.Run();
+
+ return 0;
+}
diff --git a/frc971/imu_fdcan/can_translator_status.fbs b/frc971/imu_fdcan/can_translator_status.fbs
new file mode 100644
index 0000000..232f3ba
--- /dev/null
+++ b/frc971/imu_fdcan/can_translator_status.fbs
@@ -0,0 +1,12 @@
+namespace frc971.imu;
+
+table CanTranslatorStatus {
+ // Number of times we've gotten valid packets at 64 bytes
+ valid_packet_count: uint64 (id: 0);
+ // Number of times we've gotten packets under 64 bytes
+ invalid_packet_count: uint64 (id: 1);
+ // Number of times we've gotten an invalid can id
+ invalid_can_id_count: uint64 (id: 2);
+}
+
+root_type CanTranslatorStatus;
diff --git a/frc971/imu_fdcan/dual_imu.fbs b/frc971/imu_fdcan/dual_imu.fbs
index 7236acf..61cc976 100644
--- a/frc971/imu_fdcan/dual_imu.fbs
+++ b/frc971/imu_fdcan/dual_imu.fbs
@@ -1,5 +1,7 @@
namespace frc971.imu;
+attribute "static_length";
+
table ChipState {
// Counter indicating how many samples have been taken on this IMU.
// Note that because averaging occurs on the microcontroller, this
@@ -28,14 +30,14 @@
accelerometer_z:double (id: 5);
// State for the individual ASIC(s) for this IMU.
- chip_states:[ChipState] (id: 6);
+ chip_states:[ChipState] (id: 6, static_length: 2);
}
table DualImu {
- // Timestamp from the board corresponding to these samples.
+ // Timestamp from the board corresponding to these samples in nanoseconds.
// Future changes may lead us to add per-chip timestamps, but for now
// we treat them as being sampled at the same time.
- board_timestamp_us:uint32 (id: 0);
+ kernel_timestamp:int64 (id: 0);
// Packet counter that should increment by 1 for every incoming packet.
packet_counter:uint64 (id: 1);
// Value at which the packet counter wraps, such that
@@ -45,6 +47,8 @@
murata:SingleImu (id: 3);
// Readings associated with the TDK IMU.
tdk:SingleImu (id: 4);
+ // Timestamp from the IMU in microseconds
+ board_timestamp_us:uint32 (id: 5);
}
root_type DualImu;
diff --git a/frc971/imu_fdcan/dual_imu_blender_lib.cc b/frc971/imu_fdcan/dual_imu_blender_lib.cc
new file mode 100644
index 0000000..09655e3
--- /dev/null
+++ b/frc971/imu_fdcan/dual_imu_blender_lib.cc
@@ -0,0 +1,128 @@
+#include "frc971/imu_fdcan/dual_imu_blender_lib.h"
+
+#include "gflags/gflags.h"
+
+DEFINE_bool(murata_only, false,
+ "If true then only use the murata value and ignore the tdk.");
+
+// Saturation for the gyro is measured in +- radians/s
+static constexpr double kMurataGyroSaturation = (300.0 * M_PI) / 180;
+
+// Measured in gs
+static constexpr double kMurataAccelSaturation = 6.0;
+
+// Coefficient to multiply the saturation values by to give some room on where
+// we switch to tdk.
+static constexpr double kSaturationCoeff = 0.9;
+
+using frc971::imu_fdcan::DualImuBlender;
+
+DualImuBlender::DualImuBlender(aos::EventLoop *event_loop)
+ : imu_values_batch_sender_(
+ event_loop->MakeSender<frc971::IMUValuesBatchStatic>("/localizer")),
+ dual_imu_blender_status_sender_(
+ event_loop->MakeSender<frc971::imu::DualImuBlenderStatusStatic>(
+ "/imu")) {
+ // TODO(max): Give this a proper priority
+ event_loop->SetRuntimeRealtimePriority(15);
+
+ event_loop->MakeWatcher("/imu", [this](const frc971::imu::DualImu &dual_imu) {
+ HandleDualImu(&dual_imu);
+ });
+}
+
+void DualImuBlender::HandleDualImu(const frc971::imu::DualImu *dual_imu) {
+ aos::Sender<frc971::IMUValuesBatchStatic>::StaticBuilder
+ imu_values_batch_builder_ = imu_values_batch_sender_.MakeStaticBuilder();
+
+ aos::Sender<frc971::imu::DualImuBlenderStatusStatic>::StaticBuilder
+ dual_imu_blender_status_builder =
+ dual_imu_blender_status_sender_.MakeStaticBuilder();
+
+ frc971::IMUValuesStatic *imu_values =
+ CHECK_NOTNULL(imu_values_batch_builder_->add_readings()->emplace_back());
+
+ imu_values->set_pico_timestamp_us(dual_imu->board_timestamp_us());
+ imu_values->set_monotonic_timestamp_ns(dual_imu->kernel_timestamp());
+ imu_values->set_data_counter(dual_imu->packet_counter());
+
+ if (std::abs(dual_imu->tdk()->gyro_x()) >=
+ kSaturationCoeff * kMurataGyroSaturation) {
+ dual_imu_blender_status_builder->set_gyro_x(imu::ImuType::TDK);
+ imu_values->set_gyro_x(dual_imu->tdk()->gyro_x());
+ } else {
+ dual_imu_blender_status_builder->set_gyro_x(imu::ImuType::MURATA);
+ imu_values->set_gyro_x(dual_imu->murata()->gyro_x());
+ }
+
+ if (std::abs(dual_imu->tdk()->gyro_y()) >=
+ kSaturationCoeff * kMurataGyroSaturation) {
+ dual_imu_blender_status_builder->set_gyro_y(imu::ImuType::TDK);
+ imu_values->set_gyro_y(dual_imu->tdk()->gyro_y());
+ } else {
+ dual_imu_blender_status_builder->set_gyro_y(imu::ImuType::MURATA);
+ imu_values->set_gyro_y(dual_imu->murata()->gyro_y());
+ }
+
+ if (std::abs(dual_imu->tdk()->gyro_z()) >=
+ kSaturationCoeff * kMurataGyroSaturation) {
+ dual_imu_blender_status_builder->set_gyro_z(imu::ImuType::TDK);
+ imu_values->set_gyro_z(dual_imu->tdk()->gyro_z());
+ } else {
+ dual_imu_blender_status_builder->set_gyro_z(imu::ImuType::MURATA);
+ imu_values->set_gyro_z(dual_imu->murata()->gyro_z());
+ }
+
+ if (std::abs(dual_imu->tdk()->accelerometer_x()) >=
+ kSaturationCoeff * kMurataAccelSaturation) {
+ dual_imu_blender_status_builder->set_accelerometer_x(imu::ImuType::TDK);
+ imu_values->set_accelerometer_x(dual_imu->tdk()->accelerometer_x());
+ } else {
+ dual_imu_blender_status_builder->set_accelerometer_x(imu::ImuType::MURATA);
+ imu_values->set_accelerometer_x(dual_imu->murata()->accelerometer_x());
+ }
+
+ if (std::abs(dual_imu->tdk()->accelerometer_y()) >=
+ kSaturationCoeff * kMurataAccelSaturation) {
+ dual_imu_blender_status_builder->set_accelerometer_y(imu::ImuType::TDK);
+ imu_values->set_accelerometer_y(dual_imu->tdk()->accelerometer_y());
+ } else {
+ dual_imu_blender_status_builder->set_accelerometer_y(imu::ImuType::MURATA);
+ imu_values->set_accelerometer_y(dual_imu->murata()->accelerometer_y());
+ }
+
+ if (std::abs(dual_imu->tdk()->accelerometer_z()) >=
+ kSaturationCoeff * kMurataAccelSaturation) {
+ dual_imu_blender_status_builder->set_accelerometer_z(imu::ImuType::TDK);
+ imu_values->set_accelerometer_z(dual_imu->tdk()->accelerometer_z());
+ } else {
+ dual_imu_blender_status_builder->set_accelerometer_z(imu::ImuType::MURATA);
+ imu_values->set_accelerometer_z(dual_imu->murata()->accelerometer_z());
+ }
+
+ if (FLAGS_murata_only) {
+ imu_values->set_gyro_x(dual_imu->murata()->gyro_x());
+ imu_values->set_gyro_y(dual_imu->murata()->gyro_y());
+ imu_values->set_gyro_z(dual_imu->murata()->gyro_z());
+
+ imu_values->set_accelerometer_x(dual_imu->murata()->accelerometer_x());
+ imu_values->set_accelerometer_y(dual_imu->murata()->accelerometer_y());
+ imu_values->set_accelerometer_z(dual_imu->murata()->accelerometer_z());
+
+ dual_imu_blender_status_builder->set_gyro_x(imu::ImuType::MURATA);
+ dual_imu_blender_status_builder->set_gyro_y(imu::ImuType::MURATA);
+ dual_imu_blender_status_builder->set_gyro_z(imu::ImuType::MURATA);
+
+ dual_imu_blender_status_builder->set_accelerometer_x(imu::ImuType::MURATA);
+ dual_imu_blender_status_builder->set_accelerometer_y(imu::ImuType::MURATA);
+ dual_imu_blender_status_builder->set_accelerometer_z(imu::ImuType::MURATA);
+ }
+
+ dual_imu_blender_status_builder.CheckOk(
+ dual_imu_blender_status_builder.Send());
+
+ imu_values->set_temperature(
+ dual_imu->murata()->chip_states()->Get(0)->temperature());
+
+ imu_values_batch_builder_.CheckOk(imu_values_batch_builder_.Send());
+}
diff --git a/frc971/imu_fdcan/dual_imu_blender_lib.h b/frc971/imu_fdcan/dual_imu_blender_lib.h
new file mode 100644
index 0000000..04044d2
--- /dev/null
+++ b/frc971/imu_fdcan/dual_imu_blender_lib.h
@@ -0,0 +1,27 @@
+#ifndef FRC971_IMU_FDCAN_DUAL_IMU_BLENDER_H_
+#define FRC971_IMU_FDCAN_DUAL_IMU_BLENDER_H_
+
+#include "aos/events/event_loop.h"
+#include "frc971/imu_fdcan/dual_imu_blender_status_static.h"
+#include "frc971/imu_fdcan/dual_imu_generated.h"
+#include "frc971/wpilib/imu_batch_static.h"
+
+namespace frc971::imu_fdcan {
+
+// Takes in the values from the dual_imu and creates an IMUValuesBatch. Will use
+// the murata until we've hit saturation according to the tdk, then we will
+// switch to using tdk IMU values.
+class DualImuBlender {
+ public:
+ DualImuBlender(aos::EventLoop *event_loop);
+
+ void HandleDualImu(const frc971::imu::DualImu *dual_imu);
+
+ private:
+ aos::Sender<IMUValuesBatchStatic> imu_values_batch_sender_;
+ aos::Sender<imu::DualImuBlenderStatusStatic> dual_imu_blender_status_sender_;
+};
+
+} // namespace frc971::imu_fdcan
+
+#endif // FRC971_IMU_FDCAN_DUAL_IMU_BLENDER_H_
diff --git a/frc971/imu_fdcan/dual_imu_blender_lib_test.cc b/frc971/imu_fdcan/dual_imu_blender_lib_test.cc
new file mode 100644
index 0000000..b3a3015
--- /dev/null
+++ b/frc971/imu_fdcan/dual_imu_blender_lib_test.cc
@@ -0,0 +1,302 @@
+#include "frc971/imu_fdcan/dual_imu_blender_lib.h"
+
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+#include "aos/events/simulated_event_loop.h"
+#include "frc971/imu_fdcan/dual_imu_blender_lib.h"
+#include "frc971/imu_fdcan/dual_imu_blender_status_generated.h"
+#include "frc971/imu_fdcan/dual_imu_generated.h"
+#include "frc971/imu_fdcan/dual_imu_static.h"
+
+class DualImuBlenderTest : public ::testing::Test {
+ public:
+ DualImuBlenderTest()
+ : config_(aos::configuration::ReadConfig(
+ "frc971/imu_fdcan/dual_imu_test_config.json")),
+ event_loop_factory_(&config_.message()),
+ dual_imu_blender_event_loop_(
+ event_loop_factory_.MakeEventLoop("dual_imu_blender")),
+ dual_imu_event_loop_(event_loop_factory_.MakeEventLoop("dual_imu")),
+ imu_values_batch_fetcher_(
+ dual_imu_event_loop_->MakeFetcher<frc971::IMUValuesBatch>(
+ "/localizer")),
+ dual_imu_blender_status_fetcher_(
+ dual_imu_blender_event_loop_
+ ->MakeFetcher<frc971::imu::DualImuBlenderStatus>("/imu")),
+ dual_imu_sender_(
+ dual_imu_event_loop_->MakeSender<frc971::imu::DualImuStatic>(
+ "/imu")),
+ dual_imu_blender_(dual_imu_blender_event_loop_.get()) {}
+
+ void CheckImuType(frc971::imu::ImuType type) {
+ EXPECT_EQ(dual_imu_blender_status_fetcher_->gyro_x(), type);
+ EXPECT_EQ(dual_imu_blender_status_fetcher_->gyro_y(), type);
+ EXPECT_EQ(dual_imu_blender_status_fetcher_->gyro_z(), type);
+ EXPECT_EQ(dual_imu_blender_status_fetcher_->accelerometer_x(), type);
+ EXPECT_EQ(dual_imu_blender_status_fetcher_->accelerometer_y(), type);
+ EXPECT_EQ(dual_imu_blender_status_fetcher_->accelerometer_z(), type);
+ }
+
+ protected:
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+ aos::SimulatedEventLoopFactory event_loop_factory_;
+
+ std::unique_ptr<aos::EventLoop> dual_imu_blender_event_loop_;
+ std::unique_ptr<aos::EventLoop> dual_imu_event_loop_;
+
+ aos::Fetcher<frc971::IMUValuesBatch> imu_values_batch_fetcher_;
+ aos::Fetcher<frc971::imu::DualImuBlenderStatus>
+ dual_imu_blender_status_fetcher_;
+
+ aos::Sender<frc971::imu::DualImuStatic> dual_imu_sender_;
+
+ frc971::imu_fdcan::DualImuBlender dual_imu_blender_;
+};
+
+// Sanity check that some sane values in are the same values out
+TEST_F(DualImuBlenderTest, SanityCheck) {
+ dual_imu_blender_event_loop_->OnRun([this] {
+ aos::Sender<frc971::imu::DualImuStatic>::StaticBuilder dual_imu_builder =
+ dual_imu_sender_.MakeStaticBuilder();
+
+ frc971::imu::SingleImuStatic *murata = dual_imu_builder->add_murata();
+
+ auto *murata_chip_states = murata->add_chip_states();
+ frc971::imu::ChipStateStatic *murata_uno_chip_state =
+ murata_chip_states->emplace_back();
+ frc971::imu::ChipStateStatic *murata_due_chip_state =
+ murata_chip_states->emplace_back();
+
+ frc971::imu::SingleImuStatic *tdk = dual_imu_builder->add_tdk();
+
+ dual_imu_builder->set_board_timestamp_us(0);
+ dual_imu_builder->set_kernel_timestamp(0);
+
+ tdk->set_gyro_x(0.3);
+ tdk->set_gyro_y(0.2);
+ tdk->set_gyro_z(0.2);
+
+ murata->set_gyro_x(0.351);
+ murata->set_gyro_y(0.284);
+ murata->set_gyro_z(0.293);
+
+ tdk->set_accelerometer_x(1.5);
+ tdk->set_accelerometer_y(1.5);
+ tdk->set_accelerometer_z(1.5);
+
+ murata->set_accelerometer_x(1.58);
+ murata->set_accelerometer_y(1.51);
+ murata->set_accelerometer_z(1.52);
+
+ murata_uno_chip_state->set_temperature(20);
+ murata_due_chip_state->set_temperature(10);
+
+ dual_imu_builder.CheckOk(dual_imu_builder.Send());
+ });
+
+ event_loop_factory_.RunFor(std::chrono::milliseconds(200));
+
+ ASSERT_TRUE(imu_values_batch_fetcher_.Fetch());
+ ASSERT_TRUE(dual_imu_blender_status_fetcher_.Fetch());
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_x(), 0.351,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_y(), 0.284,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_z(), 0.293,
+ 0.0001);
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_x(),
+ 1.58, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_y(),
+ 1.51, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_z(),
+ 1.52, 0.0001);
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->temperature(), 20,
+ 0.0001);
+
+ CheckImuType(frc971::imu::ImuType::MURATA);
+}
+
+TEST_F(DualImuBlenderTest, Saturation) {
+ dual_imu_blender_event_loop_->OnRun([this] {
+ aos::Sender<frc971::imu::DualImuStatic>::StaticBuilder dual_imu_builder =
+ dual_imu_sender_.MakeStaticBuilder();
+
+ frc971::imu::SingleImuStatic *murata = dual_imu_builder->add_murata();
+
+ auto *murata_chip_states = murata->add_chip_states();
+ frc971::imu::ChipStateStatic *murata_uno_chip_state =
+ murata_chip_states->emplace_back();
+
+ frc971::imu::SingleImuStatic *tdk = dual_imu_builder->add_tdk();
+
+ dual_imu_builder->set_board_timestamp_us(0);
+ dual_imu_builder->set_kernel_timestamp(0);
+
+ tdk->set_gyro_x(0.7);
+ tdk->set_gyro_y(0.7);
+ tdk->set_gyro_z(0.7);
+
+ murata->set_gyro_x(0.71);
+ murata->set_gyro_y(0.79);
+ murata->set_gyro_z(0.78);
+
+ tdk->set_accelerometer_x(1.0);
+ tdk->set_accelerometer_y(1.0);
+ tdk->set_accelerometer_z(1.0);
+
+ murata->set_accelerometer_x(1.3);
+ murata->set_accelerometer_y(1.1);
+ murata->set_accelerometer_z(1.1);
+
+ murata_uno_chip_state->set_temperature(20);
+
+ dual_imu_builder.CheckOk(dual_imu_builder.Send());
+ });
+
+ event_loop_factory_.RunFor(std::chrono::milliseconds(200));
+
+ ASSERT_TRUE(imu_values_batch_fetcher_.Fetch());
+ ASSERT_TRUE(dual_imu_blender_status_fetcher_.Fetch());
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_x(), 0.71,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_y(), 0.79,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_z(), 0.78,
+ 0.0001);
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_x(),
+ 1.3, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_y(),
+ 1.1, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_z(),
+ 1.1, 0.0001);
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->temperature(), 20,
+ 0.0001);
+
+ CheckImuType(frc971::imu::ImuType::MURATA);
+
+ // Make sure we switch to TDK on saturation
+ dual_imu_blender_event_loop_->OnRun([this] {
+ aos::Sender<frc971::imu::DualImuStatic>::StaticBuilder dual_imu_builder =
+ dual_imu_sender_.MakeStaticBuilder();
+
+ frc971::imu::SingleImuStatic *murata = dual_imu_builder->add_murata();
+
+ auto *murata_chip_states = murata->add_chip_states();
+ frc971::imu::ChipStateStatic *murata_uno_chip_state =
+ murata_chip_states->emplace_back();
+
+ frc971::imu::SingleImuStatic *tdk = dual_imu_builder->add_tdk();
+
+ dual_imu_builder->set_board_timestamp_us(1);
+ dual_imu_builder->set_kernel_timestamp(1);
+
+ tdk->set_gyro_x(6.0);
+ tdk->set_gyro_y(6.0);
+ tdk->set_gyro_z(6.0);
+
+ murata->set_gyro_x(5.2);
+ murata->set_gyro_y(5.2);
+ murata->set_gyro_z(5.2);
+
+ tdk->set_accelerometer_x(6.2);
+ tdk->set_accelerometer_y(6.3);
+ tdk->set_accelerometer_z(6.5);
+
+ murata->set_accelerometer_x(5.5);
+ murata->set_accelerometer_y(5.5);
+ murata->set_accelerometer_z(5.5);
+
+ murata_uno_chip_state->set_temperature(20);
+
+ dual_imu_builder.CheckOk(dual_imu_builder.Send());
+ });
+
+ event_loop_factory_.RunFor(std::chrono::milliseconds(200));
+
+ ASSERT_TRUE(imu_values_batch_fetcher_.Fetch());
+ ASSERT_TRUE(dual_imu_blender_status_fetcher_.Fetch());
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_x(), 6.0,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_y(), 6.0,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_z(), 6.0,
+ 0.0001);
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_x(),
+ 6.2, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_y(),
+ 6.3, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_z(),
+ 6.5, 0.0001);
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->temperature(), 20,
+ 0.0001);
+
+ CheckImuType(frc971::imu::ImuType::TDK);
+
+ // Check negative values as well
+ dual_imu_blender_event_loop_->OnRun([this] {
+ aos::Sender<frc971::imu::DualImuStatic>::StaticBuilder dual_imu_builder =
+ dual_imu_sender_.MakeStaticBuilder();
+
+ frc971::imu::SingleImuStatic *murata = dual_imu_builder->add_murata();
+
+ auto *murata_chip_states = murata->add_chip_states();
+ frc971::imu::ChipStateStatic *murata_uno_chip_state =
+ murata_chip_states->emplace_back();
+
+ frc971::imu::SingleImuStatic *tdk = dual_imu_builder->add_tdk();
+
+ dual_imu_builder->set_board_timestamp_us(1);
+ dual_imu_builder->set_kernel_timestamp(1);
+
+ tdk->set_gyro_x(-6.0);
+ tdk->set_gyro_y(-6.0);
+ tdk->set_gyro_z(-6.0);
+
+ murata->set_gyro_x(-5.2);
+ murata->set_gyro_y(-5.2);
+ murata->set_gyro_z(-5.2);
+
+ tdk->set_accelerometer_x(-6.2);
+ tdk->set_accelerometer_y(-6.3);
+ tdk->set_accelerometer_z(-6.5);
+
+ murata->set_accelerometer_x(-5.5);
+ murata->set_accelerometer_y(-5.5);
+ murata->set_accelerometer_z(-5.5);
+
+ murata_uno_chip_state->set_temperature(20);
+
+ dual_imu_builder.CheckOk(dual_imu_builder.Send());
+ });
+
+ event_loop_factory_.RunFor(std::chrono::milliseconds(200));
+
+ ASSERT_TRUE(imu_values_batch_fetcher_.Fetch());
+ ASSERT_TRUE(dual_imu_blender_status_fetcher_.Fetch());
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_x(), -6.0,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_y(), -6.0,
+ 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->gyro_z(), -6.0,
+ 0.0001);
+
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_x(),
+ -6.2, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_y(),
+ -6.3, 0.0001);
+ EXPECT_NEAR(imu_values_batch_fetcher_->readings()->Get(0)->accelerometer_z(),
+ -6.5, 0.0001);
+
+ CheckImuType(frc971::imu::ImuType::TDK);
+}
diff --git a/frc971/imu_fdcan/dual_imu_blender_main.cc b/frc971/imu_fdcan/dual_imu_blender_main.cc
new file mode 100644
index 0000000..1d5ee22
--- /dev/null
+++ b/frc971/imu_fdcan/dual_imu_blender_main.cc
@@ -0,0 +1,20 @@
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "frc971/imu_fdcan/dual_imu_blender_lib.h"
+
+using frc971::imu_fdcan::DualImuBlender;
+
+int main(int argc, char **argv) {
+ ::aos::InitGoogle(&argc, &argv);
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig("aos_config.json");
+
+ ::aos::ShmEventLoop event_loop(&config.message());
+
+ DualImuBlender blender(&event_loop);
+
+ event_loop.Run();
+
+ return 0;
+}
diff --git a/frc971/imu_fdcan/dual_imu_blender_status.fbs b/frc971/imu_fdcan/dual_imu_blender_status.fbs
new file mode 100644
index 0000000..e3c9893
--- /dev/null
+++ b/frc971/imu_fdcan/dual_imu_blender_status.fbs
@@ -0,0 +1,19 @@
+namespace frc971.imu;
+
+enum ImuType : ubyte {
+ MURATA = 0,
+ TDK = 1,
+}
+
+table DualImuBlenderStatus {
+ // These values explain if we're using the tdk or the murata for our accelerometers and gyro.
+ gyro_x: ImuType (id: 0);
+ gyro_y: ImuType (id: 1);
+ gyro_z: ImuType (id: 2);
+
+ accelerometer_x: ImuType (id: 3);
+ accelerometer_y: ImuType (id: 4);
+ accelerometer_z: ImuType (id: 5);
+}
+
+root_type DualImuBlenderStatus;
diff --git a/frc971/imu_fdcan/dual_imu_test_config_source.json b/frc971/imu_fdcan/dual_imu_test_config_source.json
new file mode 100644
index 0000000..eda06da
--- /dev/null
+++ b/frc971/imu_fdcan/dual_imu_test_config_source.json
@@ -0,0 +1,38 @@
+{
+ "channels": [
+ {
+ "name": "/aos",
+ "type": "aos.timing.Report"
+ },
+ {
+ "name": "/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "frequency": 400
+ },
+ {
+ "name": "/imu",
+ "type": "frc971.imu.DualImu",
+ "frequency": 200
+ },
+ {
+ "name": "/can",
+ "type": "frc971.can_logger.CanFrame",
+ "frequency": 200
+ },
+ {
+ "name": "/localizer",
+ "type": "frc971.IMUValuesBatch",
+ "frequency": 1100
+ },
+ {
+ "name": "/imu",
+ "type": "frc971.imu.CanTranslatorStatus",
+ "frequency": 100
+ },
+ {
+ "name": "/imu",
+ "type": "frc971.imu.DualImuBlenderStatus",
+ "frequency": 100
+ },
+ ]
+}
diff --git a/frc971/imu_reader/imu_watcher.cc b/frc971/imu_reader/imu_watcher.cc
index 153b84f..ae69996 100644
--- a/frc971/imu_reader/imu_watcher.cc
+++ b/frc971/imu_reader/imu_watcher.cc
@@ -37,13 +37,15 @@
if (zeroer_.Faulted()) {
if (value->checksum_failed()) {
imu_fault_tracker_.pico_to_pi_checksum_mismatch++;
- } else if (value->previous_reading_diag_stat()->checksum_mismatch()) {
+ } else if (value->has_previous_reading_diag_stat() &&
+ value->previous_reading_diag_stat()->checksum_mismatch()) {
imu_fault_tracker_.imu_to_pico_checksum_mismatch++;
} else {
imu_fault_tracker_.other_zeroing_faults++;
}
} else {
- if (!first_valid_data_counter_.has_value()) {
+ if (!first_valid_data_counter_.has_value() &&
+ value->has_data_counter()) {
first_valid_data_counter_ = value->data_counter();
}
}
@@ -68,12 +70,14 @@
}
// Set encoders to nullopt if we are faulted at all (faults may include
// checksum mismatches).
+ const bool have_encoders = !zeroer_.Faulted() &&
+ value->has_left_encoder() &&
+ value->has_right_encoder();
const std::optional<Eigen::Vector2d> encoders =
- zeroer_.Faulted()
- ? std::nullopt
- : std::make_optional(Eigen::Vector2d{
- left_encoder_.Unwrap(value->left_encoder()),
- right_encoder_.Unwrap(value->right_encoder())});
+ have_encoders ? std::make_optional(Eigen::Vector2d{
+ left_encoder_.Unwrap(value->left_encoder()),
+ right_encoder_.Unwrap(value->right_encoder())})
+ : std::nullopt;
{
const aos::monotonic_clock::time_point pi_read_timestamp =
aos::monotonic_clock::time_point(
diff --git a/frc971/orin/BUILD b/frc971/orin/BUILD
index 6383423..06b1d6c 100644
--- a/frc971/orin/BUILD
+++ b/frc971/orin/BUILD
@@ -61,6 +61,8 @@
],
data = [
"@apriltag_test_bfbs_images",
+ "@orin_capture_24_04//file",
+ "@orin_capture_24_04_side//file",
"@orin_image_apriltag//file",
"@orin_large_image_apriltag//file",
],
diff --git a/frc971/orin/apriltag.cc b/frc971/orin/apriltag.cc
index dd253d1..92513c2 100644
--- a/frc971/orin/apriltag.cc
+++ b/frc971/orin/apriltag.cc
@@ -113,7 +113,9 @@
} // namespace
GpuDetector::GpuDetector(size_t width, size_t height,
- apriltag_detector_t *tag_detector)
+ apriltag_detector_t *tag_detector,
+ CameraMatrix camera_matrix,
+ DistCoeffs distortion_coefficients)
: width_(width),
height_(height),
tag_detector_(tag_detector),
@@ -141,6 +143,8 @@
compressed_peaks_device_(line_fit_points_device_.size()),
sorted_compressed_peaks_device_(line_fit_points_device_.size()),
peak_extents_device_(kMaxBlobs),
+ camera_matrix_(camera_matrix),
+ distortion_coefficients_(distortion_coefficients),
fit_quads_device_(kMaxBlobs),
radix_sort_tmpstorage_device_(RadixSortScratchSpace<QuadBoundaryPoint>(
sorted_union_marker_pair_device_.size())),
diff --git a/frc971/orin/apriltag.h b/frc971/orin/apriltag.h
index d3e041b..3309996 100644
--- a/frc971/orin/apriltag.h
+++ b/frc971/orin/apriltag.h
@@ -59,6 +59,21 @@
uint32_t blob_index;
};
+struct CameraMatrix {
+ double fx;
+ double cx;
+ double fy;
+ double cy;
+};
+
+struct DistCoeffs {
+ double k1;
+ double k2;
+ double p1;
+ double p2;
+ double k3;
+};
+
// GPU based april tag detector.
class GpuDetector {
public:
@@ -67,8 +82,8 @@
// Constructs a detector, reserving space for detecting tags of the provided
// with and height, using the provided detector options.
- GpuDetector(size_t width, size_t height, apriltag_detector_t *tag_detector);
-
+ GpuDetector(size_t width, size_t height, apriltag_detector_t *tag_detector,
+ CameraMatrix camera_matrix, DistCoeffs distortion_coefficients);
virtual ~GpuDetector();
// Detects april tags in the provided image.
@@ -168,6 +183,16 @@
void AdjustCenter(float corners[4][2]) const;
+ // TODO(max): We probably don't want to use these after our test images are
+ // just orin images
+ void SetCameraMatrix(CameraMatrix camera_matrix) {
+ camera_matrix_ = camera_matrix;
+ }
+
+ void SetDistortionCoefficients(DistCoeffs distortion_coefficients) {
+ distortion_coefficients_ = distortion_coefficients;
+ }
+
private:
void UpdateFitQuads();
@@ -292,6 +317,9 @@
GpuMemory<int> num_quad_peaked_quads_device_{/* allocate 1 integer...*/ 1};
GpuMemory<PeakExtents> peak_extents_device_;
+ CameraMatrix camera_matrix_;
+ DistCoeffs distortion_coefficients_;
+
GpuMemory<FitQuad> fit_quads_device_;
std::vector<FitQuad> fit_quads_host_;
diff --git a/frc971/orin/apriltag_detect.cc b/frc971/orin/apriltag_detect.cc
index 69c93cc..41b687e 100644
--- a/frc971/orin/apriltag_detect.cc
+++ b/frc971/orin/apriltag_detect.cc
@@ -295,11 +295,79 @@
zarray_t *detections;
image_u8_t *im_samples;
+
+ CameraMatrix *camera_matrix;
+ DistCoeffs *distortion_coefficients;
};
+// Dewarps points from the image based on various constants
+// Algorithm mainly taken from
+// https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html
+void ReDistort(double *x, double *y, CameraMatrix *camera_matrix,
+ DistCoeffs *distortion_coefficients) {
+ double k1 = distortion_coefficients->k1;
+ double k2 = distortion_coefficients->k2;
+ double p1 = distortion_coefficients->p1;
+ double p2 = distortion_coefficients->p2;
+ double k3 = distortion_coefficients->k3;
+
+ double fx = camera_matrix->fx;
+ double cx = camera_matrix->cx;
+ double fy = camera_matrix->fy;
+ double cy = camera_matrix->cy;
+
+ double xP = (*x - cx) / fx;
+ double yP = (*y - cy) / fy;
+
+ double rSq = xP * xP + yP * yP;
+
+ double linCoef = 1 + k1 * rSq + k2 * rSq * rSq + k3 * rSq * rSq * rSq;
+ double xPP = xP * linCoef + 2 * p1 * xP * yP + p2 * (rSq + 2 * xP * xP);
+ double yPP = yP * linCoef + p1 * (rSq + 2 * yP * yP) + 2 * p2 * xP * yP;
+
+ *x = xPP * fx + cx;
+ *y = yPP * fy + cy;
+}
+
+// We're undistorting using math found from this github page
+// https://yangyushi.github.io/code/2020/03/04/opencv-undistort.html
+void UnDistort(double *x, double *y, CameraMatrix *camera_matrix,
+ DistCoeffs *distortion_coefficients) {
+ double k1 = distortion_coefficients->k1;
+ double k2 = distortion_coefficients->k2;
+ double p1 = distortion_coefficients->p1;
+ double p2 = distortion_coefficients->p2;
+ double k3 = distortion_coefficients->k3;
+
+ double fx = camera_matrix->fx;
+ double cx = camera_matrix->cx;
+ double fy = camera_matrix->fy;
+ double cy = camera_matrix->cy;
+
+ double xP = (*x - cx) / fx;
+ double yP = (*y - cy) / fy;
+
+ double x0 = xP;
+ double y0 = yP;
+
+ for (int i = 0; i < 10; i++) {
+ double rSq = xP * xP + yP * yP;
+ double linCoef = 1 + k1 * rSq + k2 * rSq * rSq + k3 * rSq * rSq * rSq;
+ double kInv = 1 / linCoef;
+ double dx = 2 * p1 * xP * yP + p2 * (rSq + k3 * rSq * rSq * rSq);
+ double dy = p1 * (rSq + 2 * yP * yP) + 2 * p2 * xP * yP;
+ xP = (x0 - dx) * kInv;
+ yP = (y0 - dy) * kInv;
+ }
+
+ *x = xP * fx + cx;
+ *y = yP * fy + cy;
+}
+
// Mostly stolen from aprilrobotics, but modified to implement the dewarp.
void RefineEdges(apriltag_detector_t *td, image_u8_t *im_orig,
- struct quad *quad) {
+ struct quad *quad, CameraMatrix *camera_matrix,
+ DistCoeffs *distortion_coefficients) {
double lines[4][4]; // for each line, [Ex Ey nx ny]
for (int edge = 0; edge < 4; edge++) {
@@ -395,6 +463,8 @@
double bestx = x0 + n0 * nx;
double besty = y0 + n0 * ny;
+ UnDistort(&bestx, &besty, camera_matrix, distortion_coefficients);
+
// update our line fit statistics
Mx += bestx;
My += besty;
@@ -440,8 +510,12 @@
// Compute intersection. Note that line i represents the line from corner
// i to (i+1)&3, so the intersection of line i with line (i+1)&3
// represents corner (i+1)&3.
- quad->p[(i + 1) & 3][0] = lines[i][0] + L0 * A00;
- quad->p[(i + 1) & 3][1] = lines[i][1] + L0 * A10;
+ double px = lines[i][0] + L0 * A00;
+ double py = lines[i][1] + L0 * A10;
+
+ ReDistort(&px, &py, camera_matrix, distortion_coefficients);
+ quad->p[(i + 1) & 3][0] = px;
+ quad->p[(i + 1) & 3][1] = py;
} else {
// this is a bad sign. We'll just keep the corner we had.
// debug_print("bad det: %15f %15f %15f %15f %15f\n", A00, A11,
@@ -465,7 +539,36 @@
quad_original.Hinv = nullptr;
if (td->refine_edges) {
- RefineEdges(td, im, &quad_original);
+ RefineEdges(td, im, &quad_original, task->camera_matrix,
+ task->distortion_coefficients);
+ }
+
+ if (td->debug) {
+ image_u8_t *im_quads = image_u8_copy(im);
+ image_u8_darken(im_quads);
+ image_u8_darken(im_quads);
+
+ srandom(0);
+
+ const int bias = 100;
+ int color = bias + (random() % (255 - bias));
+
+ image_u8_draw_line(im_quads, quad_original.p[0][0], quad_original.p[0][1],
+ quad_original.p[1][0], quad_original.p[1][1], color,
+ 1);
+ image_u8_draw_line(im_quads, quad_original.p[1][0], quad_original.p[1][1],
+ quad_original.p[2][0], quad_original.p[2][1], color,
+ 1);
+ image_u8_draw_line(im_quads, quad_original.p[2][0], quad_original.p[2][1],
+ quad_original.p[3][0], quad_original.p[3][1], color,
+ 1);
+ image_u8_draw_line(im_quads, quad_original.p[3][0], quad_original.p[3][1],
+ quad_original.p[0][0], quad_original.p[0][1], color,
+ 1);
+
+ image_u8_write_pnm(
+ im_quads,
+ std::string("/tmp/quad" + std::to_string(quadidx) + ".pnm").c_str());
}
quad_decode_index(td, &quad_original, im, task->im_samples,
@@ -504,6 +607,8 @@
tasks[ntasks].td = tag_detector_;
tasks[ntasks].im = &im_orig;
tasks[ntasks].detections = detections_;
+ tasks[ntasks].camera_matrix = &camera_matrix_;
+ tasks[ntasks].distortion_coefficients = &distortion_coefficients_;
tasks[ntasks].im_samples = nullptr;
diff --git a/frc971/orin/argus_camera.cc b/frc971/orin/argus_camera.cc
index 37f7e7d..a4dca06 100644
--- a/frc971/orin/argus_camera.cc
+++ b/frc971/orin/argus_camera.cc
@@ -431,7 +431,6 @@
const Argus::CaptureMetadata *metadata = ibuffer->getMetadata();
const Argus::ICaptureMetadata *imetadata =
Argus::interface_cast<const Argus::ICaptureMetadata>(metadata);
- CHECK(imetadata);
return imetadata;
}
@@ -566,51 +565,60 @@
if (buffer.nvbuf_surf() == nullptr) {
// TODO(austin): Control-C isn't working for some reason, debug it...
+ // We're restarting nvargus-daemon here because if we exit like this its
+ // likely that nvargus-daemon has run into an error that it can't
+ // recover from. Which means even if this program restarts it can't get
+ // new camera images.
+ CHECK_EQ(std::system("sudo systemctl restart nvargus-daemon.service"),
+ 0);
event_loop.Exit();
return;
}
const Argus::ICaptureMetadata *imetadata = buffer.imetadata();
- aos::Sender<frc971::vision::CameraImage>::Builder builder =
- sender.MakeBuilder();
+ if (imetadata) {
+ aos::Sender<frc971::vision::CameraImage>::Builder builder =
+ sender.MakeBuilder();
- uint8_t *data_pointer = nullptr;
- builder.fbb()->StartIndeterminateVector(FLAGS_width * FLAGS_height * 2, 1,
- 64, &data_pointer);
+ uint8_t *data_pointer = nullptr;
+ builder.fbb()->StartIndeterminateVector(FLAGS_width * FLAGS_height * 2,
+ 1, 64, &data_pointer);
- YCbCr422(buffer.nvbuf_surf(), data_pointer);
- flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data_offset =
- builder.fbb()->EndIndeterminateVector(FLAGS_width * FLAGS_height * 2,
- 1);
+ YCbCr422(buffer.nvbuf_surf(), data_pointer);
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data_offset =
+ builder.fbb()->EndIndeterminateVector(
+ FLAGS_width * FLAGS_height * 2, 1);
- auto image_builder = builder.MakeBuilder<frc971::vision::CameraImage>();
- image_builder.add_data(data_offset);
- image_builder.add_rows(FLAGS_height);
- image_builder.add_cols(FLAGS_width);
- {
- aos::ScopedNotRealtime nrt;
- image_builder.add_monotonic_timestamp_ns(
- imetadata->getSensorTimestamp());
+ auto image_builder = builder.MakeBuilder<frc971::vision::CameraImage>();
+ image_builder.add_data(data_offset);
+ image_builder.add_rows(FLAGS_height);
+ image_builder.add_cols(FLAGS_width);
+ {
+ aos::ScopedNotRealtime nrt;
+ image_builder.add_monotonic_timestamp_ns(
+ imetadata->getSensorTimestamp());
+ }
+ builder.CheckOk(builder.Send(image_builder.Finish()));
+
+ const aos::monotonic_clock::time_point after_send =
+ aos::monotonic_clock::now();
+
+ VLOG(1)
+ << "Got " << imetadata->getCaptureId() << " delay "
+ << chrono::duration<double>(
+ chrono::nanoseconds(
+ (buffer.start_time().time_since_epoch().count() -
+ (imetadata->getSensorTimestamp() +
+ imetadata->getFrameReadoutTime()))))
+ .count()
+ << " mmap "
+ << chrono::duration<double>(after_send - buffer.start_time())
+ .count()
+ << "sec dt "
+ << chrono::duration<double>(buffer.start_time() - last_time).count()
+ << "sec, exposure " << imetadata->getSensorExposureTime();
}
- builder.CheckOk(builder.Send(image_builder.Finish()));
-
- const aos::monotonic_clock::time_point after_send =
- aos::monotonic_clock::now();
-
- VLOG(1)
- << "Got " << imetadata->getCaptureId() << " delay "
- << chrono::duration<double>(
- chrono::nanoseconds(
- (buffer.start_time().time_since_epoch().count() -
- (imetadata->getSensorTimestamp() +
- imetadata->getFrameReadoutTime()))))
- .count()
- << " mmap "
- << chrono::duration<double>(after_send - buffer.start_time()).count()
- << "sec dt "
- << chrono::duration<double>(buffer.start_time() - last_time).count()
- << "sec, exposure " << imetadata->getSensorExposureTime();
last_time = buffer.start_time();
timer->Schedule(event_loop.monotonic_now());
diff --git a/frc971/orin/build_rootfs.py b/frc971/orin/build_rootfs.py
index ea15cf8..e0559a1 100755
--- a/frc971/orin/build_rootfs.py
+++ b/frc971/orin/build_rootfs.py
@@ -1240,6 +1240,8 @@
copyfile("root:root", "700", "root/trace.sh")
copyfile("root:root", "440", "etc/sudoers")
copyfile("root:root", "644", "etc/fstab")
+ copyfile("root:root", "644", "etc/modprobe.d/audio.conf")
+ copyfile("root:root", "644", "etc/modprobe.d/can.conf")
copyfile("root:root", "644",
"var/nvidia/nvcam/settings/camera_overrides.isp")
copyfile("root:root", "644", "/etc/ld.so.conf.d/yocto.conf")
@@ -1259,6 +1261,14 @@
target(["systemctl", "enable", "frc971"])
target(["systemctl", "enable", "frc971chrt"])
+ # Set up HW clock to use /dev/rtc0 and install hwclock service
+ target(["ln", "-sf", "/dev/rtc0", "/dev/rtc"])
+ target_unescaped(
+ "sed -i s/ATTR{hctosys}==\\\"1\\\"/ATTR{hctosys}==\\\"0\\\"/ /lib/udev/rules.d/50-udev-default.rules"
+ )
+ copyfile("root:root", "644", "etc/systemd/system/hwclock.service")
+ target(["systemctl", "enable", "hwclock"])
+
target(["apt-file", "update"])
target(["ldconfig"])
diff --git a/frc971/orin/contents/etc/modprobe.d/audio.conf b/frc971/orin/contents/etc/modprobe.d/audio.conf
new file mode 100644
index 0000000..1a0f72b
--- /dev/null
+++ b/frc971/orin/contents/etc/modprobe.d/audio.conf
@@ -0,0 +1 @@
+blacklist snd_hda_tegra
diff --git a/frc971/orin/contents/etc/modprobe.d/can.conf b/frc971/orin/contents/etc/modprobe.d/can.conf
new file mode 100644
index 0000000..4dd5db2
--- /dev/null
+++ b/frc971/orin/contents/etc/modprobe.d/can.conf
@@ -0,0 +1 @@
+options peak_pciefd irq_batch_max_messages=1
diff --git a/frc971/orin/contents/etc/systemd/network/80-cana.network b/frc971/orin/contents/etc/systemd/network/80-cana.network
index 0e4ad11..56f692a 100644
--- a/frc971/orin/contents/etc/systemd/network/80-cana.network
+++ b/frc971/orin/contents/etc/systemd/network/80-cana.network
@@ -5,3 +5,6 @@
BitRate=1M
RestartSec=1000ms
BusErrorReporting=yes
+
+[Link]
+RequiredForOnline=no
diff --git a/frc971/orin/contents/etc/systemd/network/80-canb.network b/frc971/orin/contents/etc/systemd/network/80-canb.network
index 1bef0ee..55bc297 100644
--- a/frc971/orin/contents/etc/systemd/network/80-canb.network
+++ b/frc971/orin/contents/etc/systemd/network/80-canb.network
@@ -7,3 +7,6 @@
RestartSec=1000ms
BusErrorReporting=yes
FDMode=yes
+
+[Link]
+RequiredForOnline=no
diff --git a/frc971/orin/contents/etc/systemd/network/80-canc.network b/frc971/orin/contents/etc/systemd/network/80-canc.network
index cdcbe81..4d43e47 100644
--- a/frc971/orin/contents/etc/systemd/network/80-canc.network
+++ b/frc971/orin/contents/etc/systemd/network/80-canc.network
@@ -7,3 +7,6 @@
RestartSec=1000ms
BusErrorReporting=yes
FDMode=yes
+
+[Link]
+RequiredForOnline=no
diff --git a/frc971/orin/contents/etc/systemd/system/hwclock.service b/frc971/orin/contents/etc/systemd/system/hwclock.service
new file mode 100644
index 0000000..40c446f
--- /dev/null
+++ b/frc971/orin/contents/etc/systemd/system/hwclock.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=Hardware clock synchronization
+[Service]
+Type=oneshot
+ExecStart=/sbin/hwclock --hctosys --utc --noadjfile
+ExecStop=/sbin/hwclock --systohc --utc --noadjfile
+[Install]
+WantedBy=multi-user.target
diff --git a/frc971/orin/cuda_april_tag_test.cc b/frc971/orin/cuda_april_tag_test.cc
index b30943f..638bf6a 100644
--- a/frc971/orin/cuda_april_tag_test.cc
+++ b/frc971/orin/cuda_april_tag_test.cc
@@ -8,6 +8,8 @@
#include "third_party/apriltag/apriltag.h"
#include "third_party/apriltag/common/unionfind.h"
#include "third_party/apriltag/tag16h5.h"
+#include "third_party/apriltag/tag36h11.h"
+#include <opencv2/calib3d.hpp>
#include <opencv2/highgui.hpp>
#include "aos/flatbuffer_merge.h"
@@ -243,17 +245,35 @@
return tag_detector;
}
+// TODO(max): Create a function which will take in the calibration data
+CameraMatrix create_camera_matrix() {
+ return CameraMatrix{
+ 1,
+ 1,
+ 1,
+ 1,
+ };
+}
+
+DistCoeffs create_distortion_coefficients() {
+ return DistCoeffs{
+ 0, 0, 0, 0, 0,
+ };
+}
+
class CudaAprilTagDetector {
public:
- CudaAprilTagDetector(size_t width, size_t height)
- : tag_family_(tag16h5_create()),
+ CudaAprilTagDetector(size_t width, size_t height,
+ apriltag_family_t *tag_family = tag16h5_create())
+ : tag_family_(tag_family),
tag_detector_(MakeTagDetector(tag_family_)),
gray_cuda_(cv::Size(width, height), CV_8UC1),
decimated_cuda_(gray_cuda_.size() / 2, CV_8UC1),
thresholded_cuda_(decimated_cuda_.size(), CV_8UC1),
union_markers_(decimated_cuda_.size(), CV_32SC1),
union_markers_size_(decimated_cuda_.size(), CV_32SC1),
- gpu_detector_(width, height, tag_detector_),
+ gpu_detector_(width, height, tag_detector_, create_camera_matrix(),
+ create_distortion_coefficients()),
width_(width),
height_(height) {
// Report out info about our GPU.
@@ -1716,6 +1736,8 @@
}
}
+ void set_undistort(bool value) { undistort_ = value; }
+
void CheckDetections(zarray_t *aprilrobotics_detections,
const zarray_t *_gpu_detections) {
zarray_t *gpu_detections = zarray_copy(_gpu_detections);
@@ -1757,7 +1779,7 @@
// TODO(austin): Crank down the thresholds and figure out why these
// deviate. It should be the same function for both at this point.
- const double threshold = valid ? 2e-3 : 1e-1;
+ const double threshold = undistort_ ? 15.0 : (valid ? 2e-3 : 1e-1);
CHECK_EQ(aprilrobotics_detection->id, gpu_detection->id);
CHECK_EQ(aprilrobotics_detection->hamming, gpu_detection->hamming);
@@ -1869,7 +1891,9 @@
zarray_t *april_clusters =
OrderAprilroboticsLikeCuda(thresholded_im, uf, cuda_grouped_points);
- CheckQuads(april_clusters, sorted_selected_blobs_cuda_, fit_quads_);
+ if (!undistort_) {
+ CheckQuads(april_clusters, sorted_selected_blobs_cuda_, fit_quads_);
+ }
const zarray_t *gpu_detections = gpu_detector_.Detections();
CheckDetections(aprilrobotics_detections_, gpu_detections);
@@ -2045,6 +2069,14 @@
}
}
+ // Sets the camera constants for camera 24-04
+ void SetCameraFourConstants() {
+ gpu_detector_.SetCameraMatrix(
+ CameraMatrix{642.80365, 718.017517, 642.83667, 555.022461});
+ gpu_detector_.SetDistortionCoefficients(
+ DistCoeffs{-0.239969, 0.055889, 0.000086, 0.000099, -0.005468});
+ }
+
private:
apriltag_family_t *tag_family_;
apriltag_detector_t *tag_detector_;
@@ -2082,6 +2114,8 @@
bool normal_border_ = false;
bool reversed_border_ = false;
+
+ bool undistort_ = false;
int min_tag_width_ = 1000000;
};
@@ -2236,4 +2270,52 @@
EXPECT_EQ(overall_count, MaxRankedIndex());
}
+// Tests our Undistort is working properly
+TEST_F(AprilDetectionTest, Undistort) {
+ auto image = ReadImage("orin_capture_24_04/file/orin_capture_24_04.bfbs");
+
+ LOG(INFO) << "Image is: " << image.message().cols() << " x "
+ << image.message().rows();
+
+ CudaAprilTagDetector cuda_detector(image.message().cols(),
+ image.message().rows(), tag36h11_create());
+
+ cuda_detector.set_undistort(true);
+
+ const cv::Mat color_image = ToMat(&image.message());
+
+ cuda_detector.SetCameraFourConstants();
+
+ cuda_detector.DetectGPU(color_image.clone());
+ cuda_detector.DetectCPU(color_image.clone());
+ cuda_detector.Check(color_image.clone());
+ if (FLAGS_debug) {
+ cuda_detector.WriteDebug(color_image);
+ }
+}
+
+// Tests our Undistort is working properly with a tag at the edge of the image
+TEST_F(AprilDetectionTest, UndistortEdge) {
+ auto image =
+ ReadImage("orin_capture_24_04_side/file/orin_capture_24_04_side.bfbs");
+
+ LOG(INFO) << "Image is: " << image.message().cols() << " x "
+ << image.message().rows();
+
+ CudaAprilTagDetector cuda_detector(image.message().cols(),
+ image.message().rows(), tag36h11_create());
+
+ cuda_detector.set_undistort(true);
+
+ const cv::Mat color_image = ToMat(&image.message());
+
+ cuda_detector.SetCameraFourConstants();
+
+ cuda_detector.DetectGPU(color_image.clone());
+ cuda_detector.DetectCPU(color_image.clone());
+ cuda_detector.Check(color_image.clone());
+ if (FLAGS_debug) {
+ cuda_detector.WriteDebug(color_image);
+ }
+}
} // namespace frc971::apriltag::testing
diff --git a/frc971/orin/gpu_apriltag.cc b/frc971/orin/gpu_apriltag.cc
index 89625b6..27cab4d 100644
--- a/frc971/orin/gpu_apriltag.cc
+++ b/frc971/orin/gpu_apriltag.cc
@@ -40,15 +40,42 @@
namespace chrono = std::chrono;
+CameraMatrix GetCameraMatrix(
+ const frc971::vision::calibration::CameraCalibration *calibration) {
+ auto intrinsics = calibration->intrinsics();
+ return CameraMatrix{
+ .fx = intrinsics->Get(0),
+ .cx = intrinsics->Get(2),
+ .fy = intrinsics->Get(4),
+ .cy = intrinsics->Get(5),
+ };
+}
+
+DistCoeffs GetDistCoeffs(
+ const frc971::vision::calibration::CameraCalibration *calibration) {
+ auto dist_coeffs = calibration->dist_coeffs();
+ return DistCoeffs{
+ .k1 = dist_coeffs->Get(0),
+ .k2 = dist_coeffs->Get(1),
+ .p1 = dist_coeffs->Get(2),
+ .p2 = dist_coeffs->Get(3),
+ .k3 = dist_coeffs->Get(4),
+ };
+}
+
ApriltagDetector::ApriltagDetector(
aos::EventLoop *event_loop, std::string_view channel_name,
const frc971::vision::calibration::CameraCalibration *calibration,
size_t width, size_t height)
: tag_family_(tag36h11_create()),
tag_detector_(MakeTagDetector(tag_family_)),
- gpu_detector_(width, height, tag_detector_),
node_name_(event_loop->node()->name()->string_view()),
calibration_(calibration),
+ intrinsics_(frc971::vision::CameraIntrinsics(calibration_)),
+ extrinsics_(frc971::vision::CameraExtrinsics(calibration_)),
+ dist_coeffs_(frc971::vision::CameraDistCoeffs(calibration_)),
+ gpu_detector_(width, height, tag_detector_, GetCameraMatrix(calibration_),
+ GetDistCoeffs(calibration_)),
image_callback_(
event_loop, channel_name,
[this](cv::Mat image_color_mat,
@@ -63,10 +90,6 @@
rejections_(0) {
image_callback_.set_format(frc971::vision::ImageCallback::Format::YUYV2);
- extrinsics_ = frc971::vision::CameraExtrinsics(calibration_);
- intrinsics_ = frc971::vision::CameraIntrinsics(calibration_);
- dist_coeffs_ = frc971::vision::CameraDistCoeffs(calibration_);
-
projection_matrix_ = cv::Mat::zeros(3, 4, CV_64F);
intrinsics_.rowRange(0, 3).colRange(0, 3).copyTo(
projection_matrix_.rowRange(0, 3).colRange(0, 3));
@@ -245,7 +268,7 @@
// parameters.
apriltag_detection_info_t info;
info.det = gpu_detection;
- info.tagsize = 0.1524;
+ info.tagsize = 6.5 * 0.0254;
info.fx = intrinsics_.at<double>(0, 0);
info.fy = intrinsics_.at<double>(1, 1);
diff --git a/frc971/orin/gpu_apriltag.h b/frc971/orin/gpu_apriltag.h
index 09c8579..39d2b60 100644
--- a/frc971/orin/gpu_apriltag.h
+++ b/frc971/orin/gpu_apriltag.h
@@ -73,7 +73,6 @@
private:
apriltag_family_t *tag_family_;
apriltag_detector_t *tag_detector_;
- frc971::apriltag::GpuDetector gpu_detector_;
std::string_view node_name_;
const frc971::vision::calibration::CameraCalibration *calibration_;
@@ -81,6 +80,8 @@
cv::Mat projection_matrix_;
std::optional<cv::Mat> extrinsics_;
cv::Mat dist_coeffs_;
+
+ frc971::apriltag::GpuDetector gpu_detector_;
cv::Size image_size_;
frc971::vision::ImageCallback image_callback_;
diff --git a/frc971/orin/orin_irq_config.json b/frc971/orin/orin_irq_config.json
new file mode 100644
index 0000000..0edb72b
--- /dev/null
+++ b/frc971/orin/orin_irq_config.json
@@ -0,0 +1,413 @@
+{
+ "irqs": [
+ {
+ /* Control BackBone */
+ "name": "13a00000.cbb-fabric",
+ "affinity": [0]
+ },
+ {
+ /* AON controller */
+ "name": "c600000.aon-fabric",
+ "affinity": [0]
+ },
+ {
+ /* Boot and Power Management Processor */
+ "name": "d600000.bpmp-fabric",
+ "affinity": [0]
+ },
+ {
+ /* Display Controller Engine */
+ "name": "d600000.bpmp-fabric",
+ "affinity": [0]
+ },
+ {
+ /* RCE is related to tracing */
+ "name": "be00000.rce-fabric",
+ "affinity": [0]
+ },
+ {
+ "name": "b600000.sce-fabric",
+ "affinity": [0]
+ },
+ {
+ /* PIPE to UPHY */
+ "name": "tegra-p2u-intr",
+ "affinity": [0]
+ },
+ {
+ "name": "peak_pciefd",
+ "affinity": [5]
+ },
+ {
+ "name": "cana",
+ "affinity": [4]
+ },
+ {
+ "name": "uart-pl011",
+ "affinity": [0]
+ },
+ {
+ /* Hardware mailboxes for IPC */
+ "name": "3c00000.tegra-hsp",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "3210000.spi",
+ "affinity": [0, 1]
+ },
+ {
+ "name": "3230000.spi",
+ "affinity": [0, 1]
+ },
+ {
+ "name": "3160000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "c240000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "3180000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "3190000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "31b0000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "31c0000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "c250000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "31e0000.i2c",
+ "affinity": [2, 3]
+ },
+ {
+ /* General Purpose Central DMA controller */
+ "name": "gpcdma.0",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.1",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.2",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.3",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.4",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.5",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.6",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.7",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.8",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.9",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.10",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.11",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.12",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.13",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.14",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.15",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.16",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.17",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.18",
+ "affinity": [0]
+ },
+ {
+ "name": "gpcdma.19",
+ "affinity": [0]
+ },
+ {
+ "name": "bc00000.rtcpu",
+ "affinity": [0]
+ },
+ {
+ "name": "d230000.actmon",
+ "affinity": [0]
+ },
+ {
+ "name": "e860000.psc",
+ "affinity": [0]
+ },
+ {
+ "name": "host_syncpt",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "c150000.tegra-hsp",
+ "affinity": [3]
+ },
+ {
+ "name": "3c00000.tegra-hsp",
+ "affinity": [3]
+ },
+ {
+ "name": "3d00000.tegra-hsp",
+ "affinity": [3]
+ },
+ {
+ "name": "b950000.tegra-hsp",
+ "affinity": [3]
+ },
+ {
+ "name": "2190000.watchdog",
+ "affinity": [0]
+ },
+ {
+ "name": "3610000.xhci",
+ "affinity": [0]
+ },
+ {
+ "name": "3550000.xudc",
+ "affinity": [0]
+ },
+ {
+ "name": "xhci-hcd:usb1",
+ "affinity": [0]
+ },
+ {
+ "name": "3610000.xhci",
+ "affinity": [0]
+ },
+ {
+ "name": "gk20a_stall",
+ "affinity": [2, 3]
+ },
+ {
+ "name": "eth0",
+ "affinity": [1]
+ }
+ ],
+ "kthreads": [
+ {
+ "name": "irq/*nvme0q0",
+ "scheduler": "SCHEDULER_OTHER"
+ },
+ {
+ "name": "irq/*nvme0q1",
+ "scheduler": "SCHEDULER_OTHER"
+ },
+ {
+ "name": "irq/*nvme0q2",
+ "scheduler": "SCHEDULER_OTHER"
+ },
+ {
+ "name": "irq/*nvme0q3",
+ "scheduler": "SCHEDULER_OTHER"
+ },
+ {
+ "name": "irq/*nvme0q4",
+ "scheduler": "SCHEDULER_OTHER"
+ },
+ {
+ "name": "irq/*nvme0q5",
+ "scheduler": "SCHEDULER_OTHER"
+ },
+ {
+ "name": "irq/*nvme0q6",
+ "scheduler": "SCHEDULER_OTHER"
+ },
+ {
+ "name": "irq/*-uart-pl0",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": -20
+ },
+ {
+ "name": "irq/*-eth0",
+ "scheduler": "SCHEDULER_FIFO",
+ "priority": 51,
+ "affinity": [1]
+ },
+ {
+ "name": "irq/*-cana",
+ "scheduler": "SCHEDULER_FIFO",
+ "priority": 51,
+ "affinity": [4]
+ },
+ {
+ "name": "irq/*-peak_pcie",
+ "scheduler": "SCHEDULER_FIFO",
+ "priority": 51,
+ "affinity": [5]
+ },
+ {
+ "name": "irq/*-PCIe PME",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": -20
+ },
+ {
+ "name": "irq/*-tegra-pc",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": -20
+ },
+ {
+ "name": "irq/*-s-tegra-",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": -20
+ },
+ {
+ "name": "irq/*-s-aerdrv",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": -20
+ },
+ {
+ "name": "irq/*-aerdrv",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": -20
+ },
+ {
+ "name": "irq/*-tegra-p2",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": -20
+ },
+ {
+ "name": "irq/*-host_sy",
+ "scheduler": "SCHEDULER_FIFO",
+ "affinity": [2, 3],
+ "priority": 51
+ },
+ {
+ "name": "irq/*-b950000",
+ "scheduler": "SCHEDULER_FIFO",
+ "affinity": [2, 3],
+ "priority": 51
+ },
+ {
+ "name": "ivc/*.rtc",
+ "scheduler": "SCHEDULER_FIFO",
+ "nice": 49
+ },
+ {
+ "name": "nvgpu_nvs_ga10b",
+ "scheduler": "SCHEDULER_OTHER",
+ "affinity": [2, 3],
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.0",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.1",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.2",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.3",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.4",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.5",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.6",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.7",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.8",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gpcdma.9",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-s-gk20a",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "irq/*-gk20a_n",
+ "scheduler": "SCHEDULER_OTHER",
+ "nice": 0
+ },
+ {
+ "name": "nvmap-bz*",
+ "scheduler": "SCHEDULER_FIFO",
+ "priority": 50
+ },
+ {
+ "name": "nvgpu_channel_p",
+ "scheduler": "SCHEDULER_OTHER",
+ "affinity": [2, 3],
+ "nice": 0
+ }
+ ]
+}
diff --git a/frc971/orin/set_orin_clock.sh b/frc971/orin/set_orin_clock.sh
new file mode 100755
index 0000000..32edb86
--- /dev/null
+++ b/frc971/orin/set_orin_clock.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Helper script to set orins clock based on our local timestamp
+
+set -e
+
+ROBOT_PREFIX="9" #71 (Should be one of 79, 89, 99, or 9)
+
+ORIN_LIST="1"
+
+echo "Setting hwclock on Orins"
+
+for orin in $ORIN_LIST; do
+ echo "========================================================"
+ echo "Setting clock for ${ROBOT_PREFIX}71.1${orin}"
+ echo "========================================================"
+ current_time=`sudo hwclock`
+ IFS="."
+ read -ra split_time <<< "$current_time"
+ echo "Setting time to ${split_time[0]}"
+ ssh pi@10.${ROBOT_PREFIX}.71.10${orin} "sudo timedatectl set-timezone US/Pacific"
+ ssh pi@10.${ROBOT_PREFIX}.71.10${orin} "sudo hwclock --set --date \"${split_time[0]}\""
+ ssh pi@10.${ROBOT_PREFIX}.71.10${orin} "sudo hwclock -s"
+done
diff --git a/frc971/vision/BUILD b/frc971/vision/BUILD
index 6cee780..d96822b 100644
--- a/frc971/vision/BUILD
+++ b/frc971/vision/BUILD
@@ -328,6 +328,7 @@
"//frc971/control_loops/drivetrain:improved_down_estimator",
"//frc971/vision:charuco_lib",
"//frc971/vision:vision_fbs",
+ "//frc971/vision:vision_util_lib",
"//frc971/wpilib:imu_batch_fbs",
"//frc971/wpilib:imu_fbs",
"//third_party:opencv",
@@ -374,3 +375,25 @@
"@com_github_google_glog//:glog",
],
)
+
+cc_test(
+ name = "vision_util_lib_test",
+ srcs = ["vision_util_lib_test.cc"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos/testing:googletest",
+ "//frc971/vision:vision_util_lib",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
+cc_library(
+ name = "target_map_utils",
+ srcs = ["target_map_utils.cc"],
+ hdrs = ["target_map_utils.h"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/vision:target_map_fbs",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
diff --git a/frc971/vision/calibration.fbs b/frc971/vision/calibration.fbs
index df8b7b0..99b6228 100644
--- a/frc971/vision/calibration.fbs
+++ b/frc971/vision/calibration.fbs
@@ -8,7 +8,7 @@
// Calibration information for a given camera on a given robot.
table CameraCalibration {
- // The name of the camera node which this calibration data applies to.
+ // The name of the compute node which this calibration data applies to.
node_name:string (id: 0);
// The team number of the robot which this calibration data applies to.
team_number:int (id: 1);
@@ -48,6 +48,12 @@
// ID for the physical camera hardware (typically will be a string of the form
// YY-NN, with a two-digit year and an index).
camera_id:string (id: 7);
+
+ // The camera number. This may be empty, "0", or "1".
+ camera_number:int (id: 8);
+
+ // The reprojection error associated with the camera calibration.
+ reprojection_error:float (id: 9);
}
// Calibration information for all the cameras we know about.
diff --git a/frc971/vision/calibration_accumulator.cc b/frc971/vision/calibration_accumulator.cc
index 9c6222b..13481b4 100644
--- a/frc971/vision/calibration_accumulator.cc
+++ b/frc971/vision/calibration_accumulator.cc
@@ -114,12 +114,13 @@
}
CalibrationFoxgloveVisualizer::CalibrationFoxgloveVisualizer(
- aos::EventLoop *event_loop)
+ aos::EventLoop *event_loop, std::string_view camera_channel)
: event_loop_(event_loop),
- image_converter_(event_loop_, "/camera", "/camera",
+ image_converter_(event_loop_, camera_channel, camera_channel,
ImageCompression::kJpeg),
annotations_sender_(
- event_loop_->MakeSender<foxglove::ImageAnnotations>("/camera")) {}
+ event_loop_->MakeSender<foxglove::ImageAnnotations>(camera_channel)) {
+}
aos::FlatbufferDetachedBuffer<aos::Configuration>
CalibrationFoxgloveVisualizer::AddVisualizationChannels(
@@ -175,7 +176,7 @@
}),
data_(data),
visualizer_event_loop_(image_factory_->MakeEventLoop("visualization")),
- visualizer_(visualizer_event_loop_.get()) {
+ visualizer_(visualizer_event_loop_.get(), image_channel) {
imu_factory_->OnShutdown([]() { cv::destroyAllWindows(); });
// Check for IMUValuesBatch topic on both /localizer and /drivetrain channels,
diff --git a/frc971/vision/calibration_accumulator.h b/frc971/vision/calibration_accumulator.h
index 550b691..dbed9ae 100644
--- a/frc971/vision/calibration_accumulator.h
+++ b/frc971/vision/calibration_accumulator.h
@@ -79,7 +79,8 @@
class CalibrationFoxgloveVisualizer {
public:
- CalibrationFoxgloveVisualizer(aos::EventLoop *event_loop);
+ CalibrationFoxgloveVisualizer(aos::EventLoop *event_loop,
+ std::string_view camera_channel = "/camera");
static aos::FlatbufferDetachedBuffer<aos::Configuration>
AddVisualizationChannels(const aos::Configuration *config,
diff --git a/frc971/vision/intrinsics_calibration.cc b/frc971/vision/intrinsics_calibration.cc
index f98aadb..16a53a7 100644
--- a/frc971/vision/intrinsics_calibration.cc
+++ b/frc971/vision/intrinsics_calibration.cc
@@ -13,15 +13,17 @@
#include "aos/util/file.h"
#include "frc971/vision/intrinsics_calibration_lib.h"
-DEFINE_string(calibration_folder, ".", "Folder to place calibration files.");
-DEFINE_string(camera_id, "", "Camera ID in format YY-NN-- year and number.");
-DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
-DEFINE_bool(display_undistorted, false,
- "If true, display the undistorted image.");
-DEFINE_string(pi, "", "Pi name to calibrate.");
+// TODO: Would be nice to remove this, but it depends on year-by-year Constants
DEFINE_string(base_intrinsics, "",
"Intrinsics to use for estimating board pose prior to solving "
"for the new intrinsics.");
+DEFINE_string(calibration_folder, ".", "Folder to place calibration files.");
+DEFINE_string(camera_id, "", "Camera ID in format YY-NN-- year and number.");
+DEFINE_string(channel, "/camera", "Camera channel to use");
+DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
+DEFINE_string(cpu_name, "", "Pi/Orin name to calibrate.");
+DEFINE_bool(display_undistorted, false,
+ "If true, display the undistorted image.");
namespace frc971::vision {
namespace {
@@ -32,7 +34,7 @@
aos::ShmEventLoop event_loop(&config.message());
- std::string hostname = FLAGS_pi;
+ std::string hostname = FLAGS_cpu_name;
if (hostname == "") {
hostname = aos::network::GetHostname();
LOG(INFO) << "Using pi/orin name from hostname as " << hostname;
@@ -41,9 +43,10 @@
<< "Need a base intrinsics json to use to auto-capture images when the "
"camera moves.";
std::unique_ptr<aos::ExitHandle> exit_handle = event_loop.MakeExitHandle();
- IntrinsicsCalibration extractor(
- &event_loop, hostname, FLAGS_camera_id, FLAGS_base_intrinsics,
- FLAGS_display_undistorted, FLAGS_calibration_folder, exit_handle.get());
+ IntrinsicsCalibration extractor(&event_loop, hostname, FLAGS_channel,
+ FLAGS_camera_id, FLAGS_base_intrinsics,
+ FLAGS_display_undistorted,
+ FLAGS_calibration_folder, exit_handle.get());
event_loop.Run();
diff --git a/frc971/vision/intrinsics_calibration_lib.cc b/frc971/vision/intrinsics_calibration_lib.cc
index a5ffec7..c48c12d 100644
--- a/frc971/vision/intrinsics_calibration_lib.cc
+++ b/frc971/vision/intrinsics_calibration_lib.cc
@@ -1,15 +1,23 @@
#include "frc971/vision/intrinsics_calibration_lib.h"
+DECLARE_bool(visualize);
+
namespace frc971::vision {
+// Found that under 50 ms would fail image too often on intrinsics with
+// visualize on
+constexpr aos::monotonic_clock::duration kMaxImageAge =
+ aos::monotonic_clock::duration(std::chrono::milliseconds(50));
+
IntrinsicsCalibration::IntrinsicsCalibration(
aos::EventLoop *event_loop, std::string_view hostname,
- std::string_view camera_id, std::string_view base_intrinsics_file,
- bool display_undistorted, std::string_view calibration_folder,
- aos::ExitHandle *exit_handle)
+ std::string_view camera_channel, std::string_view camera_id,
+ std::string_view base_intrinsics_file, bool display_undistorted,
+ std::string_view calibration_folder, aos::ExitHandle *exit_handle)
: hostname_(hostname),
cpu_type_(aos::network::ParsePiOrOrin(hostname_)),
cpu_number_(aos::network::ParsePiOrOrinNumber(hostname_)),
+ camera_channel_(camera_channel),
camera_id_(camera_id),
prev_H_camera_board_(Eigen::Affine3d()),
prev_image_H_camera_board_(Eigen::Affine3d()),
@@ -18,7 +26,7 @@
base_intrinsics_file)),
charuco_extractor_(
event_loop, &base_intrinsics_.message(), TargetType::kCharuco,
- "/camera",
+ camera_channel_,
[this](cv::Mat rgb_image, const aos::monotonic_clock::time_point eof,
std::vector<cv::Vec4i> charuco_ids,
std::vector<std::vector<cv::Point2f>> charuco_corners,
@@ -30,17 +38,18 @@
image_callback_(
event_loop,
absl::StrCat("/", aos::network::ParsePiOrOrin(hostname_).value(),
- // TODO: Need to make this work with multiple cameras
- std::to_string(cpu_number_.value()), "/camera"),
+ std::to_string(cpu_number_.value()), camera_channel_),
[this](cv::Mat rgb_image,
const aos::monotonic_clock::time_point eof) {
charuco_extractor_.HandleImage(rgb_image, eof);
},
- std::chrono::milliseconds(5)),
+ kMaxImageAge),
display_undistorted_(display_undistorted),
calibration_folder_(calibration_folder),
exit_handle_(exit_handle) {
- LOG(INFO) << "Hostname is: " << hostname_;
+ LOG(INFO) << "Hostname is: " << hostname_ << " and camera channel is "
+ << camera_channel_;
+
CHECK(cpu_number_) << ": Invalid cpu number " << hostname_
<< ", failed to parse cpu number";
std::regex re{"^[0-9][0-9]-[0-9][0-9]"};
@@ -54,25 +63,28 @@
std::vector<std::vector<cv::Point2f>> charuco_corners, bool valid,
std::vector<Eigen::Vector3d> rvecs_eigen,
std::vector<Eigen::Vector3d> tvecs_eigen) {
- // Reduce resolution displayed on remote viewer to prevent lag
- cv::resize(rgb_image, rgb_image,
- cv::Size(rgb_image.cols / 2, rgb_image.rows / 2));
- cv::imshow("Display", rgb_image);
+ if (FLAGS_visualize) {
+ // Reduce resolution displayed on remote viewer to prevent lag
+ cv::resize(rgb_image, rgb_image,
+ cv::Size(rgb_image.cols / 2, rgb_image.rows / 2));
+ cv::imshow("Display", rgb_image);
- if (display_undistorted_) {
- const cv::Size image_size(rgb_image.cols, rgb_image.rows);
- cv::Mat undistorted_rgb_image(image_size, CV_8UC3);
- cv::undistort(rgb_image, undistorted_rgb_image,
- charuco_extractor_.camera_matrix(),
- charuco_extractor_.dist_coeffs());
+ if (display_undistorted_) {
+ const cv::Size image_size(rgb_image.cols, rgb_image.rows);
+ cv::Mat undistorted_rgb_image(image_size, CV_8UC3);
+ cv::undistort(rgb_image, undistorted_rgb_image,
+ charuco_extractor_.camera_matrix(),
+ charuco_extractor_.dist_coeffs());
- cv::imshow("Display undist", undistorted_rgb_image);
+ cv::imshow("Display undist", undistorted_rgb_image);
+ }
}
int keystroke = cv::waitKey(1);
// If we haven't got a valid pose estimate, don't use these points
if (!valid) {
+ LOG(INFO) << "Skipping because pose is not valid";
return;
}
CHECK(tvecs_eigen.size() == 1)
@@ -99,8 +111,8 @@
bool store_image = false;
double percent_motion =
std::max<double>(r_norm / kDeltaRThreshold, t_norm / kDeltaTThreshold);
- LOG(INFO) << "Captured: " << all_charuco_ids_.size() << " points; Moved "
- << percent_motion << "% of what's needed";
+ LOG(INFO) << "Captured: " << all_charuco_ids_.size() << " points; \nMoved "
+ << static_cast<int>(percent_motion * 100) << "% of what's needed";
// Verify that camera has moved enough from last stored image
if (r_norm > kDeltaRThreshold || t_norm > kDeltaTThreshold) {
// frame_ refers to deltas between current and last captured image
@@ -120,9 +132,11 @@
double percent_stop = std::max<double>(frame_r_norm / kFrameDeltaRLimit,
frame_t_norm / kFrameDeltaTLimit);
LOG(INFO) << "Captured: " << all_charuco_ids_.size()
- << "points; Moved enough (" << percent_motion
- << "%); Need to stop (last motion was " << percent_stop
- << "% of limit; needs to be < 1 to capture)";
+ << "points; \nMoved enough ("
+ << static_cast<int>(percent_motion * 100)
+ << "%); Need to stop (last motion was "
+ << static_cast<int>(percent_stop * 100)
+ << "% of limit; needs to be < 1% to capture)";
}
prev_image_H_camera_board_ = H_camera_board_;
@@ -157,7 +171,9 @@
IntrinsicsCalibration::BuildCalibration(
cv::Mat camera_matrix, cv::Mat dist_coeffs,
aos::realtime_clock::time_point realtime_now, std::string_view cpu_type,
- int cpu_number, std::string_view camera_id, uint16_t team_number) {
+ uint16_t cpu_number, std::string_view camera_channel,
+ std::string_view camera_id, uint16_t team_number,
+ double reprojection_error) {
flatbuffers::FlatBufferBuilder fbb;
flatbuffers::Offset<flatbuffers::String> name_offset =
fbb.CreateString(absl::StrFormat("%s%d", cpu_type, cpu_number));
@@ -169,6 +185,9 @@
reinterpret_cast<double *>(camera_matrix.data)[i]);
});
+ std::optional<uint16_t> camera_number =
+ frc971::vision::CameraNumberFromChannel(std::string(camera_channel));
+
flatbuffers::Offset<flatbuffers::Vector<float>>
distortion_coefficients_offset =
fbb.CreateVector<float>(5u, [&dist_coeffs](size_t i) {
@@ -180,7 +199,10 @@
camera_calibration_builder.add_node_name(name_offset);
camera_calibration_builder.add_team_number(team_number);
+ camera_calibration_builder.add_camera_number(camera_number.value());
camera_calibration_builder.add_camera_id(camera_id_offset);
+ camera_calibration_builder.add_reprojection_error(
+ static_cast<float>(reprojection_error));
camera_calibration_builder.add_calibration_timestamp(
realtime_now.time_since_epoch().count());
camera_calibration_builder.add_intrinsics(intrinsics_offset);
@@ -209,7 +231,7 @@
img_size, camera_matrix, dist_coeffs, rvecs, tvecs,
std_deviations_intrinsics, std_deviations_extrinsics, per_view_errors,
calibration_flags);
- CHECK_LE(reprojection_error, 1.0)
+ CHECK_LE(reprojection_error, 2.0)
<< ": Reproduction error is bad-- greater than 1 pixel.";
LOG(INFO) << "Reprojection Error is " << reprojection_error;
@@ -222,15 +244,23 @@
aos::FlatbufferDetachedBuffer<calibration::CameraCalibration>
camera_calibration = BuildCalibration(
camera_matrix, dist_coeffs, realtime_now, cpu_type_.value(),
- cpu_number_.value(), camera_id_, team_number.value());
+ cpu_number_.value(), camera_channel_, camera_id_,
+ team_number.value(), reprojection_error);
std::stringstream time_ss;
time_ss << realtime_now;
+ std::string camera_number_optional = "";
+ std::optional<uint16_t> camera_number =
+ frc971::vision::CameraNumberFromChannel(camera_channel_);
+ if (camera_number != std::nullopt) {
+ camera_number_optional = "-" + std::to_string(camera_number.value());
+ }
const std::string calibration_filename =
calibration_folder_ +
- absl::StrFormat("/calibration_%s-%d-%d_cam-%s_%s.json",
+ absl::StrFormat("/calibration_%s-%d-%d%s_cam-%s_%s.json",
cpu_type_.value(), team_number.value(),
- cpu_number_.value(), camera_id_, time_ss.str());
+ cpu_number_.value(), camera_number_optional, camera_id_,
+ time_ss.str());
LOG(INFO) << calibration_filename << " -> "
<< aos::FlatbufferToJson(camera_calibration,
diff --git a/frc971/vision/intrinsics_calibration_lib.h b/frc971/vision/intrinsics_calibration_lib.h
index 7f08138..605741f 100644
--- a/frc971/vision/intrinsics_calibration_lib.h
+++ b/frc971/vision/intrinsics_calibration_lib.h
@@ -15,12 +15,14 @@
#include "aos/time/time.h"
#include "aos/util/file.h"
#include "frc971/vision/charuco_lib.h"
+#include "frc971/vision/vision_util_lib.h"
namespace frc971::vision {
class IntrinsicsCalibration {
public:
IntrinsicsCalibration(aos::EventLoop *event_loop, std::string_view hostname,
+ std::string_view camera_channel,
std::string_view camera_id,
std::string_view base_intrinsics_file,
bool display_undistorted,
@@ -39,8 +41,9 @@
static aos::FlatbufferDetachedBuffer<calibration::CameraCalibration>
BuildCalibration(cv::Mat camera_matrix, cv::Mat dist_coeffs,
aos::realtime_clock::time_point realtime_now,
- std::string_view cpu_type, int cpu_number,
- std::string_view camera_id, uint16_t team_number);
+ std::string_view cpu_type, uint16_t cpu_number,
+ std::string_view camera_channel, std::string_view camera_id,
+ uint16_t team_number, double reprojection_error);
private:
static constexpr double kDeltaRThreshold = M_PI / 6.0;
@@ -52,6 +55,7 @@
std::string hostname_;
const std::optional<std::string_view> cpu_type_;
const std::optional<uint16_t> cpu_number_;
+ const std::string camera_channel_;
const std::string camera_id_;
std::vector<std::vector<int>> all_charuco_ids_;
diff --git a/y2023/localizer/utils.cc b/frc971/vision/target_map_utils.cc
similarity index 70%
rename from y2023/localizer/utils.cc
rename to frc971/vision/target_map_utils.cc
index 7faca1f..3800628 100644
--- a/y2023/localizer/utils.cc
+++ b/frc971/vision/target_map_utils.cc
@@ -1,6 +1,6 @@
-#include "y2023/localizer/utils.h"
+#include "frc971/vision/target_map_utils.h"
-namespace y2023::localizer {
+namespace frc971::vision {
Eigen::Matrix<double, 4, 4> PoseToTransform(
const frc971::vision::TargetPoseFbs *pose) {
const frc971::vision::Position *position = pose->position();
@@ -8,7 +8,8 @@
return (Eigen::Translation3d(
Eigen::Vector3d(position->x(), position->y(), position->z())) *
Eigen::Quaterniond(quaternion->w(), quaternion->x(), quaternion->y(),
- quaternion->z()))
+ quaternion->z())
+ .normalized())
.matrix();
}
-} // namespace y2023::localizer
+} // namespace frc971::vision
diff --git a/frc971/vision/target_map_utils.h b/frc971/vision/target_map_utils.h
new file mode 100644
index 0000000..36b4e30
--- /dev/null
+++ b/frc971/vision/target_map_utils.h
@@ -0,0 +1,14 @@
+#ifndef FRC971_VISION_TARGET_MAP_UTILS_H_
+#define FRC971_VISION_TARGET_MAP_UTILS_H_
+
+#include <Eigen/Dense>
+
+#include "frc971/vision/target_map_generated.h"
+
+namespace frc971::vision {
+// Converts a TargetPoseFbs into a transformation matrix.
+Eigen::Matrix<double, 4, 4> PoseToTransform(
+ const frc971::vision::TargetPoseFbs *pose);
+} // namespace frc971::vision
+
+#endif // FRC971_VISION_TARGET_MAP_UTILS_H_
diff --git a/frc971/vision/target_mapper.cc b/frc971/vision/target_mapper.cc
index 8077f16..c14b032 100644
--- a/frc971/vision/target_mapper.cc
+++ b/frc971/vision/target_mapper.cc
@@ -104,7 +104,8 @@
Eigen::Quaterniond(target_pose_fbs.orientation()->w(),
target_pose_fbs.orientation()->x(),
target_pose_fbs.orientation()->y(),
- target_pose_fbs.orientation()->z())}};
+ target_pose_fbs.orientation()->z())
+ .normalized()}};
}
ceres::examples::VectorOfConstraints DataAdapter::MatchTargetDetections(
diff --git a/frc971/vision/vision_util_lib.cc b/frc971/vision/vision_util_lib.cc
index b65e883..bfd6209 100644
--- a/frc971/vision/vision_util_lib.cc
+++ b/frc971/vision/vision_util_lib.cc
@@ -43,4 +43,19 @@
return result;
}
+std::optional<uint16_t> CameraNumberFromChannel(std::string camera_channel) {
+ if (camera_channel.find("/camera") == std::string::npos) {
+ return std::nullopt;
+ }
+ // If the string doesn't end in /camera#, return nullopt
+ uint16_t cam_len = std::string("/camera").length();
+ if (camera_channel.length() != camera_channel.find("/camera") + cam_len + 1) {
+ return std::nullopt;
+ }
+
+ uint16_t camera_number = std::stoi(
+ camera_channel.substr(camera_channel.find("/camera") + cam_len, 1));
+ return camera_number;
+}
+
} // namespace frc971::vision
diff --git a/frc971/vision/vision_util_lib.h b/frc971/vision/vision_util_lib.h
index 6fb32eb..8ce651c 100644
--- a/frc971/vision/vision_util_lib.h
+++ b/frc971/vision/vision_util_lib.h
@@ -7,16 +7,24 @@
#include "frc971/vision/calibration_generated.h"
+// Extract the CameraExtrinsics from a CameraCalibration struct
namespace frc971::vision {
std::optional<cv::Mat> CameraExtrinsics(
const frc971::vision::calibration::CameraCalibration *camera_calibration);
+// Extract the CameraIntrinsics from a CameraCalibration struct
cv::Mat CameraIntrinsics(
const frc971::vision::calibration::CameraCalibration *camera_calibration);
+// Extract the CameraDistCoeffs from a CameraCalibration struct
cv::Mat CameraDistCoeffs(
const frc971::vision::calibration::CameraCalibration *camera_calibration);
+// Get the camera number from a camera channel name, e.g., return 2 from
+// "/camera2". Returns nullopt if string doesn't start with "/camera" or does
+// not have a number
+std::optional<uint16_t> CameraNumberFromChannel(std::string camera_channel);
+
} // namespace frc971::vision
#endif // FRC971_VISION_VISION_UTIL_LIB_H_
diff --git a/frc971/vision/vision_util_lib_test.cc b/frc971/vision/vision_util_lib_test.cc
new file mode 100644
index 0000000..ff9c0a3
--- /dev/null
+++ b/frc971/vision/vision_util_lib_test.cc
@@ -0,0 +1,15 @@
+#include "frc971/vision/vision_util_lib.h"
+
+#include "gtest/gtest.h"
+
+namespace frc971::vision {
+// For now, just testing extracting camera number from channel name
+TEST(VisionUtilsTest, CameraNumberFromChannel) {
+ ASSERT_EQ(CameraNumberFromChannel("/camera0").value(), 0);
+ ASSERT_EQ(CameraNumberFromChannel("/camera1").value(), 1);
+ ASSERT_EQ(CameraNumberFromChannel("/camera"), std::nullopt);
+ ASSERT_EQ(CameraNumberFromChannel("/orin1/camera0").value(), 0);
+ ASSERT_EQ(CameraNumberFromChannel("/orin1/camera1").value(), 1);
+ ASSERT_EQ(CameraNumberFromChannel("/orin1"), std::nullopt);
+}
+} // namespace frc971::vision
diff --git a/frc971/wpilib/can_sensor_reader.cc b/frc971/wpilib/can_sensor_reader.cc
index 54be05d..1f56b17 100644
--- a/frc971/wpilib/can_sensor_reader.cc
+++ b/frc971/wpilib/can_sensor_reader.cc
@@ -7,8 +7,10 @@
aos::EventLoop *event_loop,
std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry,
std::vector<std::shared_ptr<TalonFX>> talonfxs,
- std::function<void(ctre::phoenix::StatusCode status)> flatbuffer_callback)
+ std::function<void(ctre::phoenix::StatusCode status)> flatbuffer_callback,
+ SignalSync sync)
: event_loop_(event_loop),
+ sync_(sync),
signals_(signals_registry.begin(), signals_registry.end()),
talonfxs_(talonfxs),
flatbuffer_callback_(flatbuffer_callback) {
@@ -28,8 +30,12 @@
}
void CANSensorReader::Loop() {
- ctre::phoenix::StatusCode status =
- ctre::phoenix6::BaseStatusSignal::WaitForAll(20_ms, signals_);
+ ctre::phoenix::StatusCode status;
+ if (sync_ == SignalSync::kDoSync) {
+ status = ctre::phoenix6::BaseStatusSignal::WaitForAll(20_ms, signals_);
+ } else {
+ status = ctre::phoenix6::BaseStatusSignal::RefreshAll(signals_);
+ }
if (!status.IsOK()) {
AOS_LOG(ERROR, "Failed to read signals from talonfx motors: %s: %s",
diff --git a/frc971/wpilib/can_sensor_reader.h b/frc971/wpilib/can_sensor_reader.h
index 8eddfca..c79d7f2 100644
--- a/frc971/wpilib/can_sensor_reader.h
+++ b/frc971/wpilib/can_sensor_reader.h
@@ -12,18 +12,23 @@
namespace frc971::wpilib {
class CANSensorReader {
public:
+ enum class SignalSync {
+ kDoSync,
+ kNoSync,
+ };
CANSensorReader(
aos::EventLoop *event_loop,
std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry,
std::vector<std::shared_ptr<TalonFX>> talonfxs,
- std::function<void(ctre::phoenix::StatusCode status)>
- flatbuffer_callback);
+ std::function<void(ctre::phoenix::StatusCode status)> flatbuffer_callback,
+ SignalSync sync = SignalSync::kDoSync);
private:
void Loop();
aos::EventLoop *event_loop_;
+ const SignalSync sync_;
const std::vector<ctre::phoenix6::BaseStatusSignal *> signals_;
// This is a vector of talonfxs becuase we don't need to care
diff --git a/frc971/wpilib/encoder_and_potentiometer.h b/frc971/wpilib/encoder_and_potentiometer.h
index a5eac24..84bfb64 100644
--- a/frc971/wpilib/encoder_and_potentiometer.h
+++ b/frc971/wpilib/encoder_and_potentiometer.h
@@ -9,6 +9,7 @@
#include "aos/time/time.h"
#include "frc971/wpilib/ahal/AnalogInput.h"
#include "frc971/wpilib/ahal/Counter.h"
+#include "frc971/wpilib/ahal/DigitalInput.h"
#include "frc971/wpilib/ahal/DigitalSource.h"
#include "frc971/wpilib/ahal/Encoder.h"
#include "frc971/wpilib/dma.h"
@@ -168,6 +169,38 @@
::std::unique_ptr<::frc::DigitalInput> input_;
};
+class DMAAbsoluteEncoderAndPotentiometer {
+ public:
+ void set_absolute_pwm(::std::unique_ptr<frc::DigitalInput> input) {
+ duty_cycle_input_ = ::std::move(input);
+ duty_cycle_reader_.set_input(duty_cycle_input_.get());
+ }
+
+ void set_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ encoder_ = ::std::move(encoder);
+ }
+
+ void set_potentiometer(::std::unique_ptr<frc::AnalogInput> potentiometer) {
+ potentiometer_ = ::std::move(potentiometer);
+ }
+
+ double ReadAbsoluteEncoder() const {
+ return duty_cycle_reader_.last_width() / duty_cycle_reader_.last_period();
+ }
+ int32_t ReadRelativeEncoder() const { return encoder_->GetRaw(); }
+ double ReadPotentiometerVoltage() const {
+ return potentiometer_->GetVoltage();
+ }
+
+ DMAPulseWidthReader &reader() { return duty_cycle_reader_; }
+
+ private:
+ DMAPulseWidthReader duty_cycle_reader_;
+ ::std::unique_ptr<::frc::DigitalInput> duty_cycle_input_;
+ ::std::unique_ptr<frc::Encoder> encoder_;
+ ::std::unique_ptr<frc::AnalogInput> potentiometer_;
+};
+
// Class to hold a CTRE encoder with absolute angle pwm and potentiometer pair.
class AbsoluteEncoderAndPotentiometer {
public:
diff --git a/frc971/wpilib/imu_batch.fbs b/frc971/wpilib/imu_batch.fbs
index 1483314..bead5df 100644
--- a/frc971/wpilib/imu_batch.fbs
+++ b/frc971/wpilib/imu_batch.fbs
@@ -2,8 +2,10 @@
namespace frc971;
+attribute "static_length";
+
table IMUValuesBatch {
- readings:[IMUValues] (id: 0);
+ readings:[IMUValues] (id: 0, static_length: 1);
}
root_type IMUValuesBatch;
diff --git a/frc971/wpilib/sensor_reader.h b/frc971/wpilib/sensor_reader.h
index 455ed0e..da650a5 100644
--- a/frc971/wpilib/sensor_reader.h
+++ b/frc971/wpilib/sensor_reader.h
@@ -102,6 +102,27 @@
encoder_ratio * (2.0 * M_PI));
}
+ void CopyPosition(
+ const ::frc971::wpilib::DMAAbsoluteEncoderAndPotentiometer &encoder,
+ ::frc971::PotAndAbsolutePositionStatic *position,
+ double encoder_counts_per_revolution, double encoder_ratio,
+ ::std::function<double(double)> potentiometer_translate, bool reverse,
+ double pot_offset) {
+ const double multiplier = reverse ? -1.0 : 1.0;
+ position->set_pot(multiplier * potentiometer_translate(
+ encoder.ReadPotentiometerVoltage()) +
+ pot_offset);
+ position->set_encoder(multiplier *
+ encoder_translate(encoder.ReadRelativeEncoder(),
+ encoder_counts_per_revolution,
+ encoder_ratio));
+
+ position->set_absolute_encoder((reverse
+ ? (1.0 - encoder.ReadAbsoluteEncoder())
+ : encoder.ReadAbsoluteEncoder()) *
+ encoder_ratio * (2.0 * M_PI));
+ }
+
// Copies an AbsoluteEncoderAndPotentiometer to an AbsoluteAndAbsolutePosition
// with the correct unit and direction changes.
void CopyPosition(const ::frc971::wpilib::AbsoluteAndAbsoluteEncoder &encoder,
diff --git a/frc971/wpilib/talonfx.cc b/frc971/wpilib/talonfx.cc
index 5e77618..7959f2f 100644
--- a/frc971/wpilib/talonfx.cc
+++ b/frc971/wpilib/talonfx.cc
@@ -8,6 +8,7 @@
double stator_current_limit, double supply_current_limit)
: talon_(device_id, canbus),
device_id_(device_id),
+ neutral_mode_(ctre::phoenix6::signals::NeutralModeValue::Brake),
inverted_(inverted),
device_temp_(talon_.GetDeviceTemp()),
supply_voltage_(talon_.GetSupplyVoltage()),
@@ -63,7 +64,7 @@
current_limits.SupplyCurrentLimitEnable = true;
ctre::phoenix6::configs::MotorOutputConfigs output_configs;
- output_configs.NeutralMode = ctre::phoenix6::signals::NeutralModeValue::Brake;
+ output_configs.NeutralMode = neutral_mode_;
output_configs.DutyCycleNeutralDeadband = 0;
output_configs.Inverted = inverted_;
diff --git a/frc971/wpilib/talonfx.h b/frc971/wpilib/talonfx.h
index a3e3066..1f64b5b 100644
--- a/frc971/wpilib/talonfx.h
+++ b/frc971/wpilib/talonfx.h
@@ -79,6 +79,10 @@
supply_current_limit_ = supply_current_limit;
}
+ void set_neutral_mode(ctre::phoenix6::signals::NeutralModeValue value) {
+ neutral_mode_ = value;
+ }
+
static double SafeSpeed(double voltage) {
return (::aos::Clip(voltage, -kMaxBringupPower, kMaxBringupPower) / 12.0);
}
@@ -87,6 +91,7 @@
ctre::phoenix6::hardware::TalonFX talon_;
int device_id_;
+ ctre::phoenix6::signals::NeutralModeValue neutral_mode_;
ctre::phoenix6::signals::InvertedValue inverted_;
ctre::phoenix6::StatusSignal<units::temperature::celsius_t> device_temp_;
diff --git a/package.json b/package.json
index 8456a91..df5df67 100644
--- a/package.json
+++ b/package.json
@@ -4,28 +4,30 @@
"type": "module",
"private": true,
"devDependencies": {
- "@angular/animations": "15.1.5",
- "@angular/common": "15.1.5",
- "@angular/compiler": "15.1.5",
- "@angular/compiler-cli": "15.1.5",
- "@angular/core": "15.1.5",
- "@angular/forms": "15.1.5",
- "@angular/platform-browser": "15.1.5",
- "@angular/cli": "15.1.5",
- "@babel/cli": "^7.6.0",
- "@babel/core": "^7.6.0",
+ "@angular/animations": "v16-lts",
+ "@angular/common": "v16-lts",
+ "@angular/compiler": "v16-lts",
+ "@angular/compiler-cli": "v16-lts",
+ "@angular/core": "v16-lts",
+ "@angular/forms": "v16-lts",
+ "@angular/platform-browser": "v16-lts",
+ "@angular/cli": "v16-lts",
+ "@babel/cli": "^7.16.0",
+ "@babel/core": "^7.16.0",
"@types/jasmine": "3.10.3",
+ "@types/babel__core": "^7.20.5",
+ "@types/babel__generator": "^7.6.8",
"html-insert-assets": "0.14.3",
"cypress": "12.3.0",
"prettier": "2.6.1",
"requirejs": "2.3.6",
- "rollup": "3.17.2",
+ "rollup": "4.12.0",
"rxjs": "7.5.7",
- "@rollup/plugin-node-resolve": "13.1.3",
+ "@rollup/plugin-node-resolve": "15.2.3",
"@types/flatbuffers": "1.10.0",
- "@types/node": "17.0.21",
- "typescript": "4.8.4",
+ "@types/node": "20.11.19",
+ "typescript": "5.1.6",
"terser": "5.16.4",
- "zone.js": "^0.11.4"
+ "zone.js": "^0.13.0"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d844678..8f37b3b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,115 +1,146 @@
-lockfileVersion: 5.4
+lockfileVersion: '6.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
importers:
.:
- specifiers:
- '@angular/animations': 15.1.5
- '@angular/cli': 15.1.5
- '@angular/common': 15.1.5
- '@angular/compiler': 15.1.5
- '@angular/compiler-cli': 15.1.5
- '@angular/core': 15.1.5
- '@angular/forms': 15.1.5
- '@angular/platform-browser': 15.1.5
- '@babel/cli': ^7.6.0
- '@babel/core': ^7.6.0
- '@rollup/plugin-node-resolve': 13.1.3
- '@types/flatbuffers': 1.10.0
- '@types/jasmine': 3.10.3
- '@types/node': 17.0.21
- cypress: 12.3.0
- html-insert-assets: 0.14.3
- prettier: 2.6.1
- requirejs: 2.3.6
- rollup: 3.17.2
- rxjs: 7.5.7
- terser: 5.16.4
- typescript: 4.8.4
- zone.js: ^0.11.4
devDependencies:
- '@angular/animations': 15.1.5_@angular+core@15.1.5
- '@angular/cli': 15.1.5
- '@angular/common': 15.1.5_w2a4ar2ssyezibn6c65i4snjzu
- '@angular/compiler': 15.1.5_@angular+core@15.1.5
- '@angular/compiler-cli': 15.1.5_4dhd3kzleoe6yecgeixihz776m
- '@angular/core': 15.1.5_rxjs@7.5.7+zone.js@0.11.8
- '@angular/forms': 15.1.5_kkteiffnjutwxhimrtvkwyrg3a
- '@angular/platform-browser': 15.1.5_s7kwnqxnlkypgp4vtemlnxkbmi
- '@babel/cli': 7.22.5_@babel+core@7.22.5
- '@babel/core': 7.22.5
- '@rollup/plugin-node-resolve': 13.1.3_rollup@3.17.2
- '@types/flatbuffers': 1.10.0
- '@types/jasmine': 3.10.3
- '@types/node': 17.0.21
- cypress: 12.3.0
- html-insert-assets: 0.14.3
- prettier: 2.6.1
- requirejs: 2.3.6
- rollup: 3.17.2
- rxjs: 7.5.7
- terser: 5.16.4
- typescript: 4.8.4
- zone.js: 0.11.8
+ '@angular/animations':
+ specifier: v16-lts
+ version: 16.2.12(@angular/core@16.2.12)
+ '@angular/cli':
+ specifier: v16-lts
+ version: 16.2.12
+ '@angular/common':
+ specifier: v16-lts
+ version: 16.2.12(@angular/core@16.2.12)(rxjs@7.5.7)
+ '@angular/compiler':
+ specifier: v16-lts
+ version: 16.2.12(@angular/core@16.2.12)
+ '@angular/compiler-cli':
+ specifier: v16-lts
+ version: 16.2.12(@angular/compiler@16.2.12)(typescript@5.1.6)
+ '@angular/core':
+ specifier: v16-lts
+ version: 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
+ '@angular/platform-browser':
+ specifier: v16-lts
+ version: 16.2.12(@angular/animations@16.2.12)(@angular/common@16.2.12)(@angular/core@16.2.12)
+ '@babel/cli':
+ specifier: ^7.16.0
+ version: 7.23.9(@babel/core@7.23.9)
+ '@babel/core':
+ specifier: ^7.16.0
+ version: 7.23.9
+ '@rollup/plugin-node-resolve':
+ specifier: 15.2.3
+ version: 15.2.3(rollup@4.12.0)
+ '@types/babel__core':
+ specifier: ^7.20.5
+ version: 7.20.5
+ '@types/babel__generator':
+ specifier: ^7.6.8
+ version: 7.6.8
+ '@types/flatbuffers':
+ specifier: 1.10.0
+ version: 1.10.0
+ '@types/jasmine':
+ specifier: 3.10.3
+ version: 3.10.3
+ '@types/node':
+ specifier: 20.11.19
+ version: 20.11.19
+ cypress:
+ specifier: 12.3.0
+ version: 12.3.0
+ html-insert-assets:
+ specifier: 0.14.3
+ version: 0.14.3
+ prettier:
+ specifier: 2.6.1
+ version: 2.6.1
+ requirejs:
+ specifier: 2.3.6
+ version: 2.3.6
+ rollup:
+ specifier: 4.12.0
+ version: 4.12.0
+ rxjs:
+ specifier: 7.5.7
+ version: 7.5.7
+ terser:
+ specifier: 5.16.4
+ version: 5.16.4
+ typescript:
+ specifier: 5.1.6
+ version: 5.1.6
+ zone.js:
+ specifier: ^0.13.0
+ version: 0.13.3
- scouting/www:
- specifiers: {}
+ scouting/www: {}
- scouting/www/counter_button:
- specifiers: {}
+ scouting/www/counter_button: {}
scouting/www/driver_ranking:
- specifiers:
- '@angular/forms': 15.1.5
dependencies:
- '@angular/forms': 15.1.5
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
scouting/www/entry:
- specifiers:
- '@angular/forms': 15.1.5
- '@org_frc971/scouting/www/counter_button': workspace:*
dependencies:
- '@angular/forms': 15.1.5
- '@org_frc971/scouting/www/counter_button': link:../counter_button
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
+ '@org_frc971/scouting/www/counter_button':
+ specifier: workspace:*
+ version: link:../counter_button
scouting/www/match_list:
- specifiers:
- '@angular/forms': 15.1.5
- '@org_frc971/scouting/www/rpc': workspace:*
dependencies:
- '@angular/forms': 15.1.5
- '@org_frc971/scouting/www/rpc': link:../rpc
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
+ '@org_frc971/scouting/www/rpc':
+ specifier: workspace:*
+ version: link:../rpc
scouting/www/notes:
- specifiers:
- '@angular/forms': 15.1.5
dependencies:
- '@angular/forms': 15.1.5
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
scouting/www/pit_scouting:
- specifiers:
- '@angular/forms': 15.1.5
dependencies:
- '@angular/forms': 15.1.5
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
- scouting/www/rpc:
- specifiers: {}
+ scouting/www/rpc: {}
scouting/www/shift_schedule:
- specifiers:
- '@angular/forms': 15.1.5
dependencies:
- '@angular/forms': 15.1.5
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
scouting/www/view:
- specifiers:
- '@angular/forms': 15.1.5
dependencies:
- '@angular/forms': 15.1.5
+ '@angular/forms':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7)
packages:
- /@ampproject/remapping/2.2.1:
+ /@ampproject/remapping@2.2.1:
resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
engines: {node: '>=6.0.0'}
dependencies:
@@ -117,19 +148,19 @@
'@jridgewell/trace-mapping': 0.3.18
dev: true
- /@angular-devkit/architect/0.1501.5:
- resolution: {integrity: sha512-T4zJMvJvCqZeeENdeHcFtdrISrZSe8MycQOWZwPYU9zBTGMmdYpa4GQKQmFRZGBwX2PKHFlkQ1HLLe366sySAQ==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
+ /@angular-devkit/architect@0.1602.12:
+ resolution: {integrity: sha512-19Fwwfx+KvJ01SyI6cstRgqT9+cwer8Ro1T27t1JqlGyOX8tY3pV78ulwxy2+wCzPjR18V6W7cb7Cv6fyK4xog==}
+ engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
dependencies:
- '@angular-devkit/core': 15.1.5
- rxjs: 6.6.7
+ '@angular-devkit/core': 16.2.12
+ rxjs: 7.8.1
transitivePeerDependencies:
- chokidar
dev: true
- /@angular-devkit/core/15.1.5:
- resolution: {integrity: sha512-SkGQFkruTwVM77WEOIQivfFBtnHW41tttsGrT6MTrti98hs8tvOTlzfYD/sDTyh0WKbZGeAtkRXx0raevb63YQ==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
+ /@angular-devkit/core@16.2.12:
+ resolution: {integrity: sha512-o6ziQs+EcEonFezrsA46jbZqkQrs4ckS1bAQj93g5ZjGtieUz8l/U3lclvKpL/iEzWkGVViSYuP2KyW2oqTDiQ==}
+ engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
peerDependencies:
chokidar: ^3.5.2
peerDependenciesMeta:
@@ -137,181 +168,163 @@
optional: true
dependencies:
ajv: 8.12.0
- ajv-formats: 2.1.1
+ ajv-formats: 2.1.1(ajv@8.12.0)
jsonc-parser: 3.2.0
- rxjs: 6.6.7
+ picomatch: 2.3.1
+ rxjs: 7.8.1
source-map: 0.7.4
dev: true
- /@angular-devkit/schematics/15.1.5:
- resolution: {integrity: sha512-9MPuy0BjJAlSJVMqPmt50lDq6nq6AL5XJwv6NVP1fLSLXABlLBZe7jjaHLg8XVHaKbzS7BSPnHaGfHJkUipP+A==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
+ /@angular-devkit/schematics@16.2.12:
+ resolution: {integrity: sha512-lf/Nz2o875pllxGNUcI2by4rctfRsOZOxvaLq2UaH6XG6Re9tqeNfn40a8qXrr9/IYntXnlvEid/pd9e8gFBIw==}
+ engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
dependencies:
- '@angular-devkit/core': 15.1.5
+ '@angular-devkit/core': 16.2.12
jsonc-parser: 3.2.0
- magic-string: 0.27.0
+ magic-string: 0.30.1
ora: 5.4.1
- rxjs: 6.6.7
+ rxjs: 7.8.1
transitivePeerDependencies:
- chokidar
dev: true
- /@angular/animations/15.1.5_@angular+core@15.1.5:
- resolution: {integrity: sha512-yac9PHy5Y72MtKQhaBSQFOdIxEJIacmJrYNRFoa82z0YCa3VrEYjvuG0x5JewBN4gQGC5IOpj2C7c9zdXZv5HA==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
+ /@angular/animations@16.2.12(@angular/core@16.2.12):
+ resolution: {integrity: sha512-MD0ElviEfAJY8qMOd6/jjSSvtqER2RDAi0lxe6EtUacC1DHCYkaPrKW4vLqY+tmZBg1yf+6n+uS77pXcHHcA3w==}
+ engines: {node: ^16.14.0 || >=18.10.0}
peerDependencies:
- '@angular/core': 15.1.5
+ '@angular/core': 16.2.12
dependencies:
- '@angular/core': 15.1.5_rxjs@7.5.7+zone.js@0.11.8
+ '@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
tslib: 2.6.0
- dev: true
- /@angular/cli/15.1.5:
- resolution: {integrity: sha512-R+mi0+IJyBFobinCI9nu7hdGR5tXW6mBa/nsN3fwoebV0Qc07rSf9qqYkvnPjLWMiJ5eQxdLJhPcmMjB9Xs0aA==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
+ /@angular/cli@16.2.12:
+ resolution: {integrity: sha512-Pcbiraoqdw4rR2Ey5Ooy0ESLS1Ffbjkb6sPfinKRkHmAvyqsmlvkfbB/qK8GrzDSFSWvAKMMXRw9l8nbjvQEXg==}
+ engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
hasBin: true
dependencies:
- '@angular-devkit/architect': 0.1501.5
- '@angular-devkit/core': 15.1.5
- '@angular-devkit/schematics': 15.1.5
- '@schematics/angular': 15.1.5
+ '@angular-devkit/architect': 0.1602.12
+ '@angular-devkit/core': 16.2.12
+ '@angular-devkit/schematics': 16.2.12
+ '@schematics/angular': 16.2.12
'@yarnpkg/lockfile': 1.1.0
ansi-colors: 4.1.3
- ini: 3.0.1
+ ini: 4.1.1
inquirer: 8.2.4
jsonc-parser: 3.2.0
npm-package-arg: 10.1.0
npm-pick-manifest: 8.0.1
- open: 8.4.0
+ open: 8.4.2
ora: 5.4.1
- pacote: 15.0.8
- resolve: 1.22.1
- semver: 7.3.8
+ pacote: 15.2.0
+ resolve: 1.22.2
+ semver: 7.5.4
symbol-observable: 4.0.0
- yargs: 17.6.2
+ yargs: 17.7.2
transitivePeerDependencies:
- bluebird
- chokidar
- supports-color
dev: true
- /@angular/common/15.1.5_w2a4ar2ssyezibn6c65i4snjzu:
- resolution: {integrity: sha512-52Ut/IeoM3avzV3Ts/ISkq7cc1FlA6dhLUq+L3ebY+Z8zZskCWjJWu4UgLGyVdtgSuAItyQm9CoZd+DrPLYtDA==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
+ /@angular/common@16.2.12(@angular/core@16.2.12)(rxjs@7.5.7):
+ resolution: {integrity: sha512-B+WY/cT2VgEaz9HfJitBmgdk4I333XG/ybC98CMC4Wz8E49T8yzivmmxXB3OD6qvjcOB6ftuicl6WBqLbZNg2w==}
+ engines: {node: ^16.14.0 || >=18.10.0}
peerDependencies:
- '@angular/core': 15.1.5
+ '@angular/core': 16.2.12
rxjs: ^6.5.3 || ^7.4.0
dependencies:
- '@angular/core': 15.1.5_rxjs@7.5.7+zone.js@0.11.8
+ '@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
rxjs: 7.5.7
tslib: 2.6.0
- dev: true
- /@angular/compiler-cli/15.1.5_4dhd3kzleoe6yecgeixihz776m:
- resolution: {integrity: sha512-gWg6MpMJOpfkwf2zxHJDp9EGwORga4MLTkvugL+1KbN+lvx4Ac9Y0GinlJ4+EGpttvQlTYHzn8GabWhcdzzUiQ==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
+ /@angular/compiler-cli@16.2.12(@angular/compiler@16.2.12)(typescript@5.1.6):
+ resolution: {integrity: sha512-pWSrr152562ujh6lsFZR8NfNc5Ljj+zSTQO44DsuB0tZjwEpnRcjJEgzuhGXr+CoiBf+jTSPZKemtSktDk5aaA==}
+ engines: {node: ^16.14.0 || >=18.10.0}
hasBin: true
peerDependencies:
- '@angular/compiler': 15.1.5
- typescript: '>=4.8.2 <5.0'
+ '@angular/compiler': 16.2.12
+ typescript: '>=4.9.3 <5.2'
dependencies:
- '@angular/compiler': 15.1.5_@angular+core@15.1.5
- '@babel/core': 7.19.3
+ '@angular/compiler': 16.2.12(@angular/core@16.2.12)
+ '@babel/core': 7.23.2
'@jridgewell/sourcemap-codec': 1.4.15
chokidar: 3.5.3
convert-source-map: 1.9.0
- dependency-graph: 0.11.0
- magic-string: 0.27.0
- reflect-metadata: 0.1.13
- semver: 7.5.3
+ reflect-metadata: 0.1.14
+ semver: 7.6.0
tslib: 2.6.0
- typescript: 4.8.4
+ typescript: 5.1.6
yargs: 17.7.2
transitivePeerDependencies:
- supports-color
dev: true
- /@angular/compiler/15.1.5_@angular+core@15.1.5:
- resolution: {integrity: sha512-4Ciswu3HKE+Pk+6Lhi6v3inZ01WkNBi9D33OKGC+7uEAjl8DCNF13rBXLyMF6tIFd+L98KYpzwUyQYk8FI/vgA==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
+ /@angular/compiler@16.2.12(@angular/core@16.2.12):
+ resolution: {integrity: sha512-6SMXUgSVekGM7R6l1Z9rCtUGtlg58GFmgbpMCsGf+VXxP468Njw8rjT2YZkf5aEPxEuRpSHhDYjqz7n14cwCXQ==}
+ engines: {node: ^16.14.0 || >=18.10.0}
peerDependencies:
- '@angular/core': 15.1.5
+ '@angular/core': 16.2.12
peerDependenciesMeta:
'@angular/core':
optional: true
dependencies:
- '@angular/core': 15.1.5_rxjs@7.5.7+zone.js@0.11.8
+ '@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
tslib: 2.6.0
dev: true
- /@angular/core/15.1.5_rxjs@7.5.7+zone.js@0.11.8:
- resolution: {integrity: sha512-JCbhGVaskqrstLB8CJoPtMQKH4gryhuLFUVL5cwbVy3UJGGNmc3Gzvk+9I7zDf/D08vKyXGGmBNBVx2J65SJgw==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
+ /@angular/core@16.2.12(rxjs@7.5.7)(zone.js@0.13.3):
+ resolution: {integrity: sha512-GLLlDeke/NjroaLYOks0uyzFVo6HyLl7VOm0K1QpLXnYvW63W9Ql/T3yguRZa7tRkOAeFZ3jw+1wnBD4O8MoUA==}
+ engines: {node: ^16.14.0 || >=18.10.0}
peerDependencies:
rxjs: ^6.5.3 || ^7.4.0
- zone.js: ~0.11.4 || ~0.12.0
+ zone.js: ~0.13.0
dependencies:
rxjs: 7.5.7
tslib: 2.6.0
- zone.js: 0.11.8
- dev: true
+ zone.js: 0.13.3
- /@angular/forms/15.1.5:
- resolution: {integrity: sha512-FnuEdyYs1o/DJepLpTsY2/GwKTEXJ7sZlQb+NKkRWOoGpA0E4nSbhn3aCUic++MTgbZyHO0rmFKnD8TI2yyJDA==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
+ /@angular/forms@16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)(rxjs@7.5.7):
+ resolution: {integrity: sha512-1Eao89hlBgLR3v8tU91vccn21BBKL06WWxl7zLpQmG6Hun+2jrThgOE4Pf3os4fkkbH4Apj0tWL2fNIWe/blbw==}
+ engines: {node: ^16.14.0 || >=18.10.0}
peerDependencies:
- '@angular/common': 15.1.5
- '@angular/core': 15.1.5
- '@angular/platform-browser': 15.1.5
+ '@angular/common': 16.2.12
+ '@angular/core': 16.2.12
+ '@angular/platform-browser': 16.2.12
rxjs: ^6.5.3 || ^7.4.0
dependencies:
- tslib: 2.6.0
- dev: false
-
- /@angular/forms/15.1.5_kkteiffnjutwxhimrtvkwyrg3a:
- resolution: {integrity: sha512-FnuEdyYs1o/DJepLpTsY2/GwKTEXJ7sZlQb+NKkRWOoGpA0E4nSbhn3aCUic++MTgbZyHO0rmFKnD8TI2yyJDA==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
- peerDependencies:
- '@angular/common': 15.1.5
- '@angular/core': 15.1.5
- '@angular/platform-browser': 15.1.5
- rxjs: ^6.5.3 || ^7.4.0
- dependencies:
- '@angular/common': 15.1.5_w2a4ar2ssyezibn6c65i4snjzu
- '@angular/core': 15.1.5_rxjs@7.5.7+zone.js@0.11.8
- '@angular/platform-browser': 15.1.5_s7kwnqxnlkypgp4vtemlnxkbmi
+ '@angular/common': 16.2.12(@angular/core@16.2.12)(rxjs@7.5.7)
+ '@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
+ '@angular/platform-browser': 16.2.12(@angular/animations@16.2.12)(@angular/common@16.2.12)(@angular/core@16.2.12)
rxjs: 7.5.7
tslib: 2.6.0
- dev: true
- /@angular/platform-browser/15.1.5_s7kwnqxnlkypgp4vtemlnxkbmi:
- resolution: {integrity: sha512-epeESrWEt41W6i2NqIbGKNE0Oa1JfeDtKfMXtcjUNCgT76qS3zmC0G6irO8BOVbrwpA/YI4yYx1B9vTDUXYbEg==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0}
+ /@angular/platform-browser@16.2.12(@angular/animations@16.2.12)(@angular/common@16.2.12)(@angular/core@16.2.12):
+ resolution: {integrity: sha512-NnH7ju1iirmVEsUq432DTm0nZBGQsBrU40M3ZeVHMQ2subnGiyUs3QyzDz8+VWLL/T5xTxWLt9BkDn65vgzlIQ==}
+ engines: {node: ^16.14.0 || >=18.10.0}
peerDependencies:
- '@angular/animations': 15.1.5
- '@angular/common': 15.1.5
- '@angular/core': 15.1.5
+ '@angular/animations': 16.2.12
+ '@angular/common': 16.2.12
+ '@angular/core': 16.2.12
peerDependenciesMeta:
'@angular/animations':
optional: true
dependencies:
- '@angular/animations': 15.1.5_@angular+core@15.1.5
- '@angular/common': 15.1.5_w2a4ar2ssyezibn6c65i4snjzu
- '@angular/core': 15.1.5_rxjs@7.5.7+zone.js@0.11.8
+ '@angular/animations': 16.2.12(@angular/core@16.2.12)
+ '@angular/common': 16.2.12(@angular/core@16.2.12)(rxjs@7.5.7)
+ '@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
tslib: 2.6.0
- dev: true
- /@babel/cli/7.22.5_@babel+core@7.22.5:
- resolution: {integrity: sha512-N5d7MjzwsQ2wppwjhrsicVDhJSqF9labEP/swYiHhio4Ca2XjEehpgPmerjnLQl7BPE59BLud0PTWGYwqFl/cQ==}
+ /@babel/cli@7.23.9(@babel/core@7.23.9):
+ resolution: {integrity: sha512-vB1UXmGDNEhcf1jNAHKT9IlYk1R+hehVTLFlCLHBi8gfuHQGP6uRjgXVYU0EVlI/qwAWpstqkBdf2aez3/z/5Q==}
engines: {node: '>=6.9.0'}
hasBin: true
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.22.5
+ '@babel/core': 7.23.9
'@jridgewell/trace-mapping': 0.3.18
commander: 4.1.1
- convert-source-map: 1.9.0
+ convert-source-map: 2.0.0
fs-readdir-recursive: 1.1.0
glob: 7.2.3
make-dir: 2.1.0
@@ -321,246 +334,242 @@
chokidar: 3.5.3
dev: true
- /@babel/code-frame/7.22.5:
- resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==}
+ /@babel/code-frame@7.23.5:
+ resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/highlight': 7.22.5
+ '@babel/highlight': 7.23.4
+ chalk: 2.4.2
dev: true
- /@babel/compat-data/7.22.5:
- resolution: {integrity: sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==}
+ /@babel/compat-data@7.23.5:
+ resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/core/7.19.3:
- resolution: {integrity: sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==}
+ /@babel/core@7.23.2:
+ resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.1
- '@babel/code-frame': 7.22.5
- '@babel/generator': 7.22.5
- '@babel/helper-compilation-targets': 7.22.5_@babel+core@7.19.3
- '@babel/helper-module-transforms': 7.22.5
- '@babel/helpers': 7.22.5
- '@babel/parser': 7.22.5
- '@babel/template': 7.22.5
- '@babel/traverse': 7.22.5
- '@babel/types': 7.22.5
- convert-source-map: 1.9.0
- debug: 4.3.4
+ '@babel/code-frame': 7.23.5
+ '@babel/generator': 7.23.6
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.2)
+ '@babel/helpers': 7.23.9
+ '@babel/parser': 7.23.9
+ '@babel/template': 7.23.9
+ '@babel/traverse': 7.23.9
+ '@babel/types': 7.23.9
+ convert-source-map: 2.0.0
+ debug: 4.3.4(supports-color@8.1.1)
gensync: 1.0.0-beta.2
json5: 2.2.3
- semver: 6.3.0
+ semver: 6.3.1
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/core/7.22.5:
- resolution: {integrity: sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==}
+ /@babel/core@7.23.9:
+ resolution: {integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.1
- '@babel/code-frame': 7.22.5
- '@babel/generator': 7.22.5
- '@babel/helper-compilation-targets': 7.22.5_@babel+core@7.22.5
- '@babel/helper-module-transforms': 7.22.5
- '@babel/helpers': 7.22.5
- '@babel/parser': 7.22.5
- '@babel/template': 7.22.5
- '@babel/traverse': 7.22.5
- '@babel/types': 7.22.5
- convert-source-map: 1.9.0
- debug: 4.3.4
+ '@babel/code-frame': 7.23.5
+ '@babel/generator': 7.23.6
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9)
+ '@babel/helpers': 7.23.9
+ '@babel/parser': 7.23.9
+ '@babel/template': 7.23.9
+ '@babel/traverse': 7.23.9
+ '@babel/types': 7.23.9
+ convert-source-map: 2.0.0
+ debug: 4.3.4(supports-color@8.1.1)
gensync: 1.0.0-beta.2
json5: 2.2.3
- semver: 6.3.0
+ semver: 6.3.1
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/generator/7.22.5:
- resolution: {integrity: sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==}
+ /@babel/generator@7.23.6:
+ resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.22.5
+ '@babel/types': 7.23.9
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.18
jsesc: 2.5.2
dev: true
- /@babel/helper-compilation-targets/7.22.5_@babel+core@7.19.3:
- resolution: {integrity: sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==}
+ /@babel/helper-compilation-targets@7.23.6:
+ resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
dependencies:
- '@babel/compat-data': 7.22.5
- '@babel/core': 7.19.3
- '@babel/helper-validator-option': 7.22.5
- browserslist: 4.21.9
+ '@babel/compat-data': 7.23.5
+ '@babel/helper-validator-option': 7.23.5
+ browserslist: 4.23.0
lru-cache: 5.1.1
- semver: 6.3.0
+ semver: 6.3.1
dev: true
- /@babel/helper-compilation-targets/7.22.5_@babel+core@7.22.5:
- resolution: {integrity: sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
- dependencies:
- '@babel/compat-data': 7.22.5
- '@babel/core': 7.22.5
- '@babel/helper-validator-option': 7.22.5
- browserslist: 4.21.9
- lru-cache: 5.1.1
- semver: 6.3.0
- dev: true
-
- /@babel/helper-environment-visitor/7.22.5:
- resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==}
+ /@babel/helper-environment-visitor@7.22.20:
+ resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helper-function-name/7.22.5:
- resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==}
+ /@babel/helper-function-name@7.23.0:
+ resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/template': 7.22.5
- '@babel/types': 7.22.5
+ '@babel/template': 7.23.9
+ '@babel/types': 7.23.9
dev: true
- /@babel/helper-hoist-variables/7.22.5:
+ /@babel/helper-hoist-variables@7.22.5:
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.22.5
+ '@babel/types': 7.23.9
dev: true
- /@babel/helper-module-imports/7.22.5:
- resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==}
+ /@babel/helper-module-imports@7.22.15:
+ resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.22.5
+ '@babel/types': 7.23.9
dev: true
- /@babel/helper-module-transforms/7.22.5:
- resolution: {integrity: sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==}
+ /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.2):
+ resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
dependencies:
- '@babel/helper-environment-visitor': 7.22.5
- '@babel/helper-module-imports': 7.22.5
+ '@babel/core': 7.23.2
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-module-imports': 7.22.15
'@babel/helper-simple-access': 7.22.5
- '@babel/helper-split-export-declaration': 7.22.5
- '@babel/helper-validator-identifier': 7.22.5
- '@babel/template': 7.22.5
- '@babel/traverse': 7.22.5
- '@babel/types': 7.22.5
- transitivePeerDependencies:
- - supports-color
+ '@babel/helper-split-export-declaration': 7.22.6
+ '@babel/helper-validator-identifier': 7.22.20
dev: true
- /@babel/helper-simple-access/7.22.5:
+ /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9):
+ resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.9
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-module-imports': 7.22.15
+ '@babel/helper-simple-access': 7.22.5
+ '@babel/helper-split-export-declaration': 7.22.6
+ '@babel/helper-validator-identifier': 7.22.20
+ dev: true
+
+ /@babel/helper-simple-access@7.22.5:
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.22.5
+ '@babel/types': 7.23.9
dev: true
- /@babel/helper-split-export-declaration/7.22.5:
- resolution: {integrity: sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==}
+ /@babel/helper-split-export-declaration@7.22.6:
+ resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.22.5
+ '@babel/types': 7.23.9
dev: true
- /@babel/helper-string-parser/7.22.5:
- resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
+ /@babel/helper-string-parser@7.23.4:
+ resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helper-validator-identifier/7.22.5:
- resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
+ /@babel/helper-validator-identifier@7.22.20:
+ resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helper-validator-option/7.22.5:
- resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==}
+ /@babel/helper-validator-option@7.23.5:
+ resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helpers/7.22.5:
- resolution: {integrity: sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==}
+ /@babel/helpers@7.23.9:
+ resolution: {integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/template': 7.22.5
- '@babel/traverse': 7.22.5
- '@babel/types': 7.22.5
+ '@babel/template': 7.23.9
+ '@babel/traverse': 7.23.9
+ '@babel/types': 7.23.9
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/highlight/7.22.5:
- resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==}
+ /@babel/highlight@7.23.4:
+ resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/helper-validator-identifier': 7.22.5
+ '@babel/helper-validator-identifier': 7.22.20
chalk: 2.4.2
js-tokens: 4.0.0
dev: true
- /@babel/parser/7.22.5:
- resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==}
+ /@babel/parser@7.23.9:
+ resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
- '@babel/types': 7.22.5
+ '@babel/types': 7.23.9
dev: true
- /@babel/template/7.22.5:
- resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==}
+ /@babel/template@7.23.9:
+ resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/code-frame': 7.22.5
- '@babel/parser': 7.22.5
- '@babel/types': 7.22.5
+ '@babel/code-frame': 7.23.5
+ '@babel/parser': 7.23.9
+ '@babel/types': 7.23.9
dev: true
- /@babel/traverse/7.22.5:
- resolution: {integrity: sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==}
+ /@babel/traverse@7.23.9:
+ resolution: {integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/code-frame': 7.22.5
- '@babel/generator': 7.22.5
- '@babel/helper-environment-visitor': 7.22.5
- '@babel/helper-function-name': 7.22.5
+ '@babel/code-frame': 7.23.5
+ '@babel/generator': 7.23.6
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-function-name': 7.23.0
'@babel/helper-hoist-variables': 7.22.5
- '@babel/helper-split-export-declaration': 7.22.5
- '@babel/parser': 7.22.5
- '@babel/types': 7.22.5
- debug: 4.3.4
+ '@babel/helper-split-export-declaration': 7.22.6
+ '@babel/parser': 7.23.9
+ '@babel/types': 7.23.9
+ debug: 4.3.4(supports-color@8.1.1)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/types/7.22.5:
- resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==}
+ /@babel/types@7.23.9:
+ resolution: {integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/helper-string-parser': 7.22.5
- '@babel/helper-validator-identifier': 7.22.5
+ '@babel/helper-string-parser': 7.23.4
+ '@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0
dev: true
- /@colors/colors/1.5.0:
+ /@colors/colors@1.5.0:
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
engines: {node: '>=0.1.90'}
requiresBuild: true
dev: true
optional: true
- /@cypress/request/2.88.11:
+ /@cypress/request@2.88.11:
resolution: {integrity: sha512-M83/wfQ1EkspjkE2lNWNV5ui2Cv7UCv1swW1DqljahbzLVWltcsexQh8jYtuS/vzFXP+HySntGM83ZXA9fn17w==}
engines: {node: '>= 6'}
dependencies:
@@ -584,28 +593,32 @@
uuid: 8.3.2
dev: true
- /@cypress/xvfb/1.2.4_supports-color@8.1.1:
+ /@cypress/xvfb@1.2.4(supports-color@8.1.1):
resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
dependencies:
- debug: 3.2.7_supports-color@8.1.1
+ debug: 3.2.7(supports-color@8.1.1)
lodash.once: 4.1.1
transitivePeerDependencies:
- supports-color
dev: true
- /@isaacs/cliui/8.0.2:
+ /@gar/promisify@1.1.3:
+ resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
+ dev: true
+
+ /@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
- string-width-cjs: /string-width/4.2.3
+ string-width-cjs: /string-width@4.2.3
strip-ansi: 7.1.0
- strip-ansi-cjs: /strip-ansi/6.0.1
+ strip-ansi-cjs: /strip-ansi@6.0.1
wrap-ansi: 8.1.0
- wrap-ansi-cjs: /wrap-ansi/7.0.0
+ wrap-ansi-cjs: /wrap-ansi@7.0.0
dev: true
- /@jridgewell/gen-mapping/0.3.3:
+ /@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
engines: {node: '>=6.0.0'}
dependencies:
@@ -614,49 +627,57 @@
'@jridgewell/trace-mapping': 0.3.18
dev: true
- /@jridgewell/resolve-uri/3.1.0:
+ /@jridgewell/resolve-uri@3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: true
- /@jridgewell/set-array/1.1.2:
+ /@jridgewell/set-array@1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'}
dev: true
- /@jridgewell/source-map/0.3.4:
+ /@jridgewell/source-map@0.3.4:
resolution: {integrity: sha512-KE/SxsDqNs3rrWwFHcRh15ZLVFrI0YoZtgAdIyIq9k5hUNmiWRXXThPomIxHuL20sLdgzbDFyvkUMna14bvtrw==}
dev: true
- /@jridgewell/sourcemap-codec/1.4.14:
+ /@jridgewell/sourcemap-codec@1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: true
- /@jridgewell/sourcemap-codec/1.4.15:
+ /@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
dev: true
- /@jridgewell/trace-mapping/0.3.18:
+ /@jridgewell/trace-mapping@0.3.18:
resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
- /@nicolo-ribaudo/chokidar-2/2.1.8-no-fsevents.3:
+ /@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3:
resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==}
requiresBuild: true
dev: true
optional: true
- /@npmcli/fs/3.1.0:
+ /@npmcli/fs@2.1.2:
+ resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ dependencies:
+ '@gar/promisify': 1.1.3
+ semver: 7.6.0
+ dev: true
+
+ /@npmcli/fs@3.1.0:
resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
- semver: 7.3.8
+ semver: 7.6.0
dev: true
- /@npmcli/git/4.1.0:
+ /@npmcli/git@4.1.0:
resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
@@ -666,13 +687,13 @@
proc-log: 3.0.0
promise-inflight: 1.0.1
promise-retry: 2.0.1
- semver: 7.3.8
+ semver: 7.6.0
which: 3.0.1
transitivePeerDependencies:
- bluebird
dev: true
- /@npmcli/installed-package-contents/2.0.2:
+ /@npmcli/installed-package-contents@2.0.2:
resolution: {integrity: sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
hasBin: true
@@ -681,158 +702,349 @@
npm-normalize-package-bin: 3.0.1
dev: true
- /@npmcli/node-gyp/3.0.0:
+ /@npmcli/move-file@2.0.1:
+ resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ deprecated: This functionality has been moved to @npmcli/fs
+ dependencies:
+ mkdirp: 1.0.4
+ rimraf: 3.0.2
+ dev: true
+
+ /@npmcli/node-gyp@3.0.0:
resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
- /@npmcli/promise-spawn/6.0.2:
+ /@npmcli/promise-spawn@6.0.2:
resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
which: 3.0.1
dev: true
- /@npmcli/run-script/6.0.2:
+ /@npmcli/run-script@6.0.2:
resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
'@npmcli/node-gyp': 3.0.0
'@npmcli/promise-spawn': 6.0.2
- node-gyp: 9.4.0
+ node-gyp: 9.4.1
read-package-json-fast: 3.0.2
which: 3.0.1
transitivePeerDependencies:
+ - bluebird
- supports-color
dev: true
- /@pkgjs/parseargs/0.11.0:
+ /@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
requiresBuild: true
dev: true
optional: true
- /@rollup/plugin-node-resolve/13.1.3_rollup@3.17.2:
- resolution: {integrity: sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ==}
- engines: {node: '>= 10.0.0'}
+ /@rollup/plugin-node-resolve@15.2.3(rollup@4.12.0):
+ resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==}
+ engines: {node: '>=14.0.0'}
peerDependencies:
- rollup: ^2.42.0
+ rollup: ^2.78.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
dependencies:
- '@rollup/pluginutils': 3.1.0_rollup@3.17.2
- '@types/resolve': 1.17.1
- builtin-modules: 3.3.0
+ '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
+ '@types/resolve': 1.20.2
deepmerge: 4.3.1
+ is-builtin-module: 3.2.1
is-module: 1.0.0
resolve: 1.22.2
- rollup: 3.17.2
+ rollup: 4.12.0
dev: true
- /@rollup/pluginutils/3.1.0_rollup@3.17.2:
- resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
- engines: {node: '>= 8.0.0'}
+ /@rollup/pluginutils@5.1.0(rollup@4.12.0):
+ resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
+ engines: {node: '>=14.0.0'}
peerDependencies:
- rollup: ^1.20.0||^2.0.0
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
dependencies:
- '@types/estree': 0.0.39
- estree-walker: 1.0.1
+ '@types/estree': 1.0.5
+ estree-walker: 2.0.2
picomatch: 2.3.1
- rollup: 3.17.2
+ rollup: 4.12.0
dev: true
- /@schematics/angular/15.1.5:
- resolution: {integrity: sha512-mw5adVNSLX8h6c8F0tNEe11LVOlj100c1PrPggZNVz9nd2fwb32SVFSx+FmOxLVfE1kfnPgsvLpDH23z8SF6bg==}
- engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
+ /@rollup/rollup-android-arm-eabi@4.12.0:
+ resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-android-arm64@4.12.0:
+ resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-arm64@4.12.0:
+ resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-x64@4.12.0:
+ resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
+ resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-gnu@4.12.0:
+ resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-musl@4.12.0:
+ resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-riscv64-gnu@4.12.0:
+ resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-gnu@4.12.0:
+ resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-musl@4.12.0:
+ resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-arm64-msvc@4.12.0:
+ resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-ia32-msvc@4.12.0:
+ resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-x64-msvc@4.12.0:
+ resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@schematics/angular@16.2.12:
+ resolution: {integrity: sha512-rc6Dxo7yLnNhECxZyvwv3qL40GvMHw/gMeme8DUGN7zgcUdBJ7LOCURp7EZqOBghMVeeJvLrohitEbs9NhRLBA==}
+ engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
dependencies:
- '@angular-devkit/core': 15.1.5
- '@angular-devkit/schematics': 15.1.5
+ '@angular-devkit/core': 16.2.12
+ '@angular-devkit/schematics': 16.2.12
jsonc-parser: 3.2.0
transitivePeerDependencies:
- chokidar
dev: true
- /@tootallnate/once/2.0.0:
+ /@sigstore/bundle@1.1.0:
+ resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dependencies:
+ '@sigstore/protobuf-specs': 0.2.1
+ dev: true
+
+ /@sigstore/protobuf-specs@0.2.1:
+ resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dev: true
+
+ /@sigstore/sign@1.0.0:
+ resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dependencies:
+ '@sigstore/bundle': 1.1.0
+ '@sigstore/protobuf-specs': 0.2.1
+ make-fetch-happen: 11.1.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@sigstore/tuf@1.0.3:
+ resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dependencies:
+ '@sigstore/protobuf-specs': 0.2.1
+ tuf-js: 1.1.7
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@tootallnate/once@2.0.0:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
engines: {node: '>= 10'}
dev: true
- /@types/estree/0.0.39:
- resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
+ /@tufjs/canonical-json@1.0.0:
+ resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
- /@types/flatbuffers/1.10.0:
+ /@tufjs/models@1.0.4:
+ resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dependencies:
+ '@tufjs/canonical-json': 1.0.0
+ minimatch: 9.0.3
+ dev: true
+
+ /@types/babel__core@7.20.5:
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+ dependencies:
+ '@babel/parser': 7.23.9
+ '@babel/types': 7.23.9
+ '@types/babel__generator': 7.6.8
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.20.5
+ dev: true
+
+ /@types/babel__generator@7.6.8:
+ resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
+ dependencies:
+ '@babel/types': 7.23.9
+ dev: true
+
+ /@types/babel__template@7.4.4:
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+ dependencies:
+ '@babel/parser': 7.23.9
+ '@babel/types': 7.23.9
+ dev: true
+
+ /@types/babel__traverse@7.20.5:
+ resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==}
+ dependencies:
+ '@babel/types': 7.23.9
+ dev: true
+
+ /@types/estree@1.0.5:
+ resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+ dev: true
+
+ /@types/flatbuffers@1.10.0:
resolution: {integrity: sha512-7btbphLrKvo5yl/5CC2OCxUSMx1wV1wvGT1qDXkSt7yi00/YW7E8k6qzXqJHsp+WU0eoG7r6MTQQXI9lIvd0qA==}
dev: true
- /@types/jasmine/3.10.3:
+ /@types/jasmine@3.10.3:
resolution: {integrity: sha512-SWyMrjgdAUHNQmutvDcKablrJhkDLy4wunTme8oYLjKp41GnHGxMRXr2MQMvy/qy8H3LdzwQk9gH4hZ6T++H8g==}
dev: true
- /@types/node/14.18.53:
+ /@types/node@14.18.53:
resolution: {integrity: sha512-soGmOpVBUq+gaBMwom1M+krC/NNbWlosh4AtGA03SyWNDiqSKtwp7OulO1M6+mg8YkHMvJ/y0AkCeO8d1hNb7A==}
dev: true
- /@types/node/17.0.21:
- resolution: {integrity: sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==}
- dev: true
-
- /@types/resolve/1.17.1:
- resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
+ /@types/node@20.11.19:
+ resolution: {integrity: sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==}
dependencies:
- '@types/node': 17.0.21
+ undici-types: 5.26.5
dev: true
- /@types/sinonjs__fake-timers/8.1.1:
+ /@types/resolve@1.20.2:
+ resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
+ dev: true
+
+ /@types/sinonjs__fake-timers@8.1.1:
resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
dev: true
- /@types/sizzle/2.3.3:
+ /@types/sizzle@2.3.3:
resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
dev: true
- /@types/yauzl/2.10.0:
+ /@types/yauzl@2.10.0:
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
requiresBuild: true
dependencies:
- '@types/node': 17.0.21
+ '@types/node': 20.11.19
dev: true
optional: true
- /@yarnpkg/lockfile/1.1.0:
+ /@yarnpkg/lockfile@1.1.0:
resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
dev: true
- /abbrev/1.1.1:
+ /abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
dev: true
- /acorn/8.9.0:
+ /acorn@8.9.0:
resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
- /agent-base/6.0.2:
+ /agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
dependencies:
- debug: 4.3.4
+ debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
dev: true
- /agentkeepalive/4.3.0:
- resolution: {integrity: sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==}
+ /agentkeepalive@4.5.0:
+ resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
engines: {node: '>= 8.0.0'}
dependencies:
- debug: 4.3.4
- depd: 2.0.0
humanize-ms: 1.2.1
- transitivePeerDependencies:
- - supports-color
dev: true
- /aggregate-error/3.1.0:
+ /aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
dependencies:
@@ -840,8 +1052,10 @@
indent-string: 4.0.0
dev: true
- /ajv-formats/2.1.1:
+ /ajv-formats@2.1.1(ajv@8.12.0):
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
+ peerDependencies:
+ ajv: ^8.0.0
peerDependenciesMeta:
ajv:
optional: true
@@ -849,7 +1063,7 @@
ajv: 8.12.0
dev: true
- /ajv/8.12.0:
+ /ajv@8.12.0:
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
dependencies:
fast-deep-equal: 3.1.3
@@ -858,48 +1072,48 @@
uri-js: 4.4.1
dev: true
- /ansi-colors/4.1.3:
+ /ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
dev: true
- /ansi-escapes/4.3.2:
+ /ansi-escapes@4.3.2:
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
engines: {node: '>=8'}
dependencies:
type-fest: 0.21.3
dev: true
- /ansi-regex/5.0.1:
+ /ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
dev: true
- /ansi-regex/6.0.1:
+ /ansi-regex@6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
dev: true
- /ansi-styles/3.2.1:
+ /ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: true
- /ansi-styles/4.3.0:
+ /ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
dependencies:
color-convert: 2.0.1
dev: true
- /ansi-styles/6.2.1:
+ /ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
dev: true
- /anymatch/3.1.3:
+ /anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
dependencies:
@@ -907,15 +1121,15 @@
picomatch: 2.3.1
dev: true
- /aproba/2.0.0:
+ /aproba@2.0.0:
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
dev: true
- /arch/2.2.0:
+ /arch@2.2.0:
resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
dev: true
- /are-we-there-yet/3.0.1:
+ /are-we-there-yet@3.0.1:
resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
dependencies:
@@ -923,63 +1137,63 @@
readable-stream: 3.6.2
dev: true
- /asn1/0.2.6:
+ /asn1@0.2.6:
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
dependencies:
safer-buffer: 2.1.2
dev: true
- /assert-plus/1.0.0:
+ /assert-plus@1.0.0:
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
engines: {node: '>=0.8'}
dev: true
- /astral-regex/2.0.0:
+ /astral-regex@2.0.0:
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
engines: {node: '>=8'}
dev: true
- /async/3.2.4:
+ /async@3.2.4:
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
dev: true
- /asynckit/0.4.0:
+ /asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: true
- /at-least-node/1.0.0:
+ /at-least-node@1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
dev: true
- /aws-sign2/0.7.0:
+ /aws-sign2@0.7.0:
resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
dev: true
- /aws4/1.12.0:
+ /aws4@1.12.0:
resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
dev: true
- /balanced-match/1.0.2:
+ /balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
- /base64-js/1.5.1:
+ /base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true
- /bcrypt-pbkdf/1.0.2:
+ /bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
dependencies:
tweetnacl: 0.14.5
dev: true
- /binary-extensions/2.2.0:
+ /binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
dev: true
- /bl/4.1.0:
+ /bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
dependencies:
buffer: 5.7.1
@@ -987,80 +1201,106 @@
readable-stream: 3.6.2
dev: true
- /blob-util/2.0.2:
+ /blob-util@2.0.2:
resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==}
dev: true
- /bluebird/3.7.2:
+ /bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
dev: true
- /brace-expansion/1.1.11:
+ /brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
dev: true
- /brace-expansion/2.0.1:
+ /brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: true
- /braces/3.0.2:
+ /braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
engines: {node: '>=8'}
dependencies:
fill-range: 7.0.1
dev: true
- /browserslist/4.21.9:
- resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==}
+ /browserslist@4.23.0:
+ resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
- caniuse-lite: 1.0.30001509
- electron-to-chromium: 1.4.447
- node-releases: 2.0.12
- update-browserslist-db: 1.0.11_browserslist@4.21.9
+ caniuse-lite: 1.0.30001588
+ electron-to-chromium: 1.4.679
+ node-releases: 2.0.14
+ update-browserslist-db: 1.0.13(browserslist@4.23.0)
dev: true
- /buffer-crc32/0.2.13:
+ /buffer-crc32@0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
dev: true
- /buffer-from/1.1.2:
+ /buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: true
- /buffer/5.7.1:
+ /buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
dev: true
- /builtin-modules/3.3.0:
+ /builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
engines: {node: '>=6'}
dev: true
- /builtins/5.0.1:
+ /builtins@5.0.1:
resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==}
dependencies:
- semver: 7.3.8
+ semver: 7.6.0
dev: true
- /cacache/17.1.3:
- resolution: {integrity: sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==}
+ /cacache@16.1.3:
+ resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ dependencies:
+ '@npmcli/fs': 2.1.2
+ '@npmcli/move-file': 2.0.1
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ glob: 8.1.0
+ infer-owner: 1.0.4
+ lru-cache: 7.18.3
+ minipass: 3.3.6
+ minipass-collect: 1.0.2
+ minipass-flush: 1.0.5
+ minipass-pipeline: 1.2.4
+ mkdirp: 1.0.4
+ p-map: 4.0.0
+ promise-inflight: 1.0.1
+ rimraf: 3.0.2
+ ssri: 9.0.1
+ tar: 6.1.15
+ unique-filename: 2.0.1
+ transitivePeerDependencies:
+ - bluebird
+ dev: true
+
+ /cacache@17.1.4:
+ resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
'@npmcli/fs': 3.1.0
fs-minipass: 3.0.2
- glob: 10.3.1
+ glob: 10.3.10
lru-cache: 7.18.3
- minipass: 5.0.0
+ minipass: 7.0.4
minipass-collect: 1.0.2
minipass-flush: 1.0.5
minipass-pipeline: 1.2.4
@@ -1070,27 +1310,27 @@
unique-filename: 3.0.0
dev: true
- /cachedir/2.3.0:
+ /cachedir@2.3.0:
resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==}
engines: {node: '>=6'}
dev: true
- /call-bind/1.0.2:
+ /call-bind@1.0.2:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
function-bind: 1.1.1
get-intrinsic: 1.2.1
dev: true
- /caniuse-lite/1.0.30001509:
- resolution: {integrity: sha512-2uDDk+TRiTX5hMcUYT/7CSyzMZxjfGu0vAUjS2g0LSD8UoXOv0LtpH4LxGMemsiPq6LCVIUjNwVM0erkOkGCDA==}
+ /caniuse-lite@1.0.30001588:
+ resolution: {integrity: sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==}
dev: true
- /caseless/0.12.0:
+ /caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
dev: true
- /chalk/2.4.2:
+ /chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
@@ -1099,7 +1339,7 @@
supports-color: 5.5.0
dev: true
- /chalk/4.1.2:
+ /chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
dependencies:
@@ -1107,16 +1347,16 @@
supports-color: 7.2.0
dev: true
- /chardet/0.7.0:
+ /chardet@0.7.0:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
dev: true
- /check-more-types/2.24.0:
+ /check-more-types@2.24.0:
resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==}
engines: {node: '>= 0.8.0'}
dev: true
- /chokidar/3.5.3:
+ /chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
dependencies:
@@ -1131,34 +1371,34 @@
fsevents: 2.3.2
dev: true
- /chownr/2.0.0:
+ /chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'}
dev: true
- /ci-info/3.8.0:
+ /ci-info@3.8.0:
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
engines: {node: '>=8'}
dev: true
- /clean-stack/2.2.0:
+ /clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
dev: true
- /cli-cursor/3.1.0:
+ /cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
dependencies:
restore-cursor: 3.1.0
dev: true
- /cli-spinners/2.9.0:
+ /cli-spinners@2.9.0:
resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==}
engines: {node: '>=6'}
dev: true
- /cli-table3/0.6.3:
+ /cli-table3@0.6.3:
resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==}
engines: {node: 10.* || >= 12.*}
dependencies:
@@ -1167,7 +1407,7 @@
'@colors/colors': 1.5.0
dev: true
- /cli-truncate/2.1.0:
+ /cli-truncate@2.1.0:
resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
engines: {node: '>=8'}
dependencies:
@@ -1175,12 +1415,12 @@
string-width: 4.2.3
dev: true
- /cli-width/3.0.0:
+ /cli-width@3.0.0:
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
engines: {node: '>= 10'}
dev: true
- /cliui/8.0.1:
+ /cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
dependencies:
@@ -1189,84 +1429,88 @@
wrap-ansi: 7.0.0
dev: true
- /clone/1.0.4:
+ /clone@1.0.4:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
engines: {node: '>=0.8'}
dev: true
- /color-convert/1.9.3:
+ /color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: true
- /color-convert/2.0.1:
+ /color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
dependencies:
color-name: 1.1.4
dev: true
- /color-name/1.1.3:
+ /color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
dev: true
- /color-name/1.1.4:
+ /color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
- /color-support/1.1.3:
+ /color-support@1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
hasBin: true
dev: true
- /colorette/2.0.20:
+ /colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
dev: true
- /combined-stream/1.0.8:
+ /combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: true
- /commander/2.20.3:
+ /commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
- /commander/4.1.1:
+ /commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
dev: true
- /commander/5.1.0:
+ /commander@5.1.0:
resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
engines: {node: '>= 6'}
dev: true
- /common-tags/1.8.2:
+ /common-tags@1.8.2:
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
engines: {node: '>=4.0.0'}
dev: true
- /concat-map/0.0.1:
+ /concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
- /console-control-strings/1.1.0:
+ /console-control-strings@1.1.0:
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
dev: true
- /convert-source-map/1.9.0:
+ /convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
dev: true
- /core-util-is/1.0.2:
+ /convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ dev: true
+
+ /core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
dev: true
- /cross-spawn/7.0.3:
+ /cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
dependencies:
@@ -1275,14 +1519,14 @@
which: 2.0.2
dev: true
- /cypress/12.3.0:
+ /cypress@12.3.0:
resolution: {integrity: sha512-ZQNebibi6NBt51TRxRMYKeFvIiQZ01t50HSy7z/JMgRVqBUey3cdjog5MYEbzG6Ktti5ckDt1tfcC47lmFwXkw==}
engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0}
hasBin: true
requiresBuild: true
dependencies:
'@cypress/request': 2.88.11
- '@cypress/xvfb': 1.2.4_supports-color@8.1.1
+ '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
'@types/node': 14.18.53
'@types/sinonjs__fake-timers': 8.1.1
'@types/sizzle': 2.3.3
@@ -1298,19 +1542,19 @@
commander: 5.1.0
common-tags: 1.8.2
dayjs: 1.11.9
- debug: 4.3.4_supports-color@8.1.1
+ debug: 4.3.4(supports-color@8.1.1)
enquirer: 2.3.6
eventemitter2: 6.4.7
execa: 4.1.0
executable: 4.1.1
- extract-zip: 2.0.1_supports-color@8.1.1
+ extract-zip: 2.0.1(supports-color@8.1.1)
figures: 3.2.0
fs-extra: 9.1.0
getos: 3.2.1
is-ci: 3.0.1
is-installed-globally: 0.4.0
lazy-ass: 1.6.0
- listr2: 3.14.0_enquirer@2.3.6
+ listr2: 3.14.0(enquirer@2.3.6)
lodash: 4.17.21
log-symbols: 4.1.0
minimist: 1.2.8
@@ -1325,18 +1569,18 @@
yauzl: 2.10.0
dev: true
- /dashdash/1.14.1:
+ /dashdash@1.14.1:
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
engines: {node: '>=0.10'}
dependencies:
assert-plus: 1.0.0
dev: true
- /dayjs/1.11.9:
+ /dayjs@1.11.9:
resolution: {integrity: sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==}
dev: true
- /debug/3.2.7_supports-color@8.1.1:
+ /debug@3.2.7(supports-color@8.1.1):
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
@@ -1348,19 +1592,7 @@
supports-color: 8.1.1
dev: true
- /debug/4.3.4:
- resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
- dependencies:
- ms: 2.1.2
- dev: true
-
- /debug/4.3.4_supports-color@8.1.1:
+ /debug@4.3.4(supports-color@8.1.1):
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
@@ -1373,65 +1605,55 @@
supports-color: 8.1.1
dev: true
- /deepmerge/4.3.1:
+ /deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
dev: true
- /defaults/1.0.4:
+ /defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
dependencies:
clone: 1.0.4
dev: true
- /define-lazy-prop/2.0.0:
+ /define-lazy-prop@2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
engines: {node: '>=8'}
dev: true
- /delayed-stream/1.0.0:
+ /delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: true
- /delegates/1.0.0:
+ /delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
dev: true
- /depd/2.0.0:
- resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
- engines: {node: '>= 0.8'}
- dev: true
-
- /dependency-graph/0.11.0:
- resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==}
- engines: {node: '>= 0.6.0'}
- dev: true
-
- /eastasianwidth/0.2.0:
+ /eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true
- /ecc-jsbn/0.1.2:
+ /ecc-jsbn@0.1.2:
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
dependencies:
jsbn: 0.1.1
safer-buffer: 2.1.2
dev: true
- /electron-to-chromium/1.4.447:
- resolution: {integrity: sha512-sxX0LXh+uL41hSJsujAN86PjhrV/6c79XmpY0TvjZStV6VxIgarf8SRkUoUTuYmFcZQTemsoqo8qXOGw5npWfw==}
+ /electron-to-chromium@1.4.679:
+ resolution: {integrity: sha512-NhQMsz5k0d6m9z3qAxnsOR/ebal4NAGsrNVRwcDo4Kc/zQ7KdsTKZUxZoygHcVRb0QDW3waEDIcE3isZ79RP6g==}
dev: true
- /emoji-regex/8.0.0:
+ /emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
- /emoji-regex/9.2.2:
+ /emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: true
- /encoding/0.1.13:
+ /encoding@0.1.13:
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
requiresBuild: true
dependencies:
@@ -1439,47 +1661,47 @@
dev: true
optional: true
- /end-of-stream/1.4.4:
+ /end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
dependencies:
once: 1.4.0
dev: true
- /enquirer/2.3.6:
+ /enquirer@2.3.6:
resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
engines: {node: '>=8.6'}
dependencies:
ansi-colors: 4.1.3
dev: true
- /env-paths/2.2.1:
+ /env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
dev: true
- /err-code/2.0.3:
+ /err-code@2.0.3:
resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
dev: true
- /escalade/3.1.1:
+ /escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
dev: true
- /escape-string-regexp/1.0.5:
+ /escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
dev: true
- /estree-walker/1.0.1:
- resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
+ /estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
- /eventemitter2/6.4.7:
+ /eventemitter2@6.4.7:
resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==}
dev: true
- /execa/4.1.0:
+ /execa@4.1.0:
resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
engines: {node: '>=10'}
dependencies:
@@ -1494,22 +1716,22 @@
strip-final-newline: 2.0.0
dev: true
- /executable/4.1.1:
+ /executable@4.1.1:
resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
engines: {node: '>=4'}
dependencies:
pify: 2.3.0
dev: true
- /exponential-backoff/3.1.1:
+ /exponential-backoff@3.1.1:
resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
dev: true
- /extend/3.0.2:
+ /extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
dev: true
- /external-editor/3.1.0:
+ /external-editor@3.1.0:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
dependencies:
@@ -1518,12 +1740,12 @@
tmp: 0.0.33
dev: true
- /extract-zip/2.0.1_supports-color@8.1.1:
+ /extract-zip@2.0.1(supports-color@8.1.1):
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
hasBin: true
dependencies:
- debug: 4.3.4_supports-color@8.1.1
+ debug: 4.3.4(supports-color@8.1.1)
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
@@ -1532,36 +1754,36 @@
- supports-color
dev: true
- /extsprintf/1.3.0:
+ /extsprintf@1.3.0:
resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
engines: {'0': node >=0.6.0}
dev: true
- /fast-deep-equal/3.1.3:
+ /fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
- /fd-slicer/1.1.0:
+ /fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
dependencies:
pend: 1.2.0
dev: true
- /figures/3.2.0:
+ /figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
dependencies:
escape-string-regexp: 1.0.5
dev: true
- /fill-range/7.0.1:
+ /fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
dev: true
- /foreground-child/3.1.1:
+ /foreground-child@3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
dependencies:
@@ -1569,11 +1791,11 @@
signal-exit: 4.0.2
dev: true
- /forever-agent/0.6.1:
+ /forever-agent@0.6.1:
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
dev: true
- /form-data/2.3.3:
+ /form-data@2.3.3:
resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
engines: {node: '>= 0.12'}
dependencies:
@@ -1582,7 +1804,7 @@
mime-types: 2.1.35
dev: true
- /fs-extra/9.1.0:
+ /fs-extra@9.1.0:
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
engines: {node: '>=10'}
dependencies:
@@ -1592,29 +1814,29 @@
universalify: 2.0.0
dev: true
- /fs-minipass/2.1.0:
+ /fs-minipass@2.1.0:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
dependencies:
minipass: 3.3.6
dev: true
- /fs-minipass/3.0.2:
+ /fs-minipass@3.0.2:
resolution: {integrity: sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
minipass: 5.0.0
dev: true
- /fs-readdir-recursive/1.1.0:
+ /fs-readdir-recursive@1.1.0:
resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==}
dev: true
- /fs.realpath/1.0.0:
+ /fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
- /fsevents/2.3.2:
+ /fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
@@ -1622,11 +1844,15 @@
dev: true
optional: true
- /function-bind/1.1.1:
+ /function-bind@1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
- /gauge/4.0.4:
+ /function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ dev: true
+
+ /gauge@4.0.4:
resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
dependencies:
@@ -1640,17 +1866,17 @@
wide-align: 1.1.5
dev: true
- /gensync/1.0.0-beta.2:
+ /gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
dev: true
- /get-caller-file/2.0.5:
+ /get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
dev: true
- /get-intrinsic/1.2.1:
+ /get-intrinsic@1.2.1:
resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
dependencies:
function-bind: 1.1.1
@@ -1659,45 +1885,45 @@
has-symbols: 1.0.3
dev: true
- /get-stream/5.2.0:
+ /get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
engines: {node: '>=8'}
dependencies:
pump: 3.0.0
dev: true
- /getos/3.2.1:
+ /getos@3.2.1:
resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
dependencies:
async: 3.2.4
dev: true
- /getpass/0.1.7:
+ /getpass@0.1.7:
resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
dependencies:
assert-plus: 1.0.0
dev: true
- /glob-parent/5.1.2:
+ /glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
dependencies:
is-glob: 4.0.3
dev: true
- /glob/10.3.1:
- resolution: {integrity: sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==}
+ /glob@10.3.10:
+ resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
- jackspeak: 2.2.1
- minimatch: 9.0.2
- minipass: 5.0.0
- path-scurry: 1.10.0
+ jackspeak: 2.3.6
+ minimatch: 9.0.3
+ minipass: 7.0.4
+ path-scurry: 1.10.1
dev: true
- /glob/7.2.3:
+ /glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
dependencies:
fs.realpath: 1.0.0
@@ -1708,61 +1934,79 @@
path-is-absolute: 1.0.1
dev: true
- /global-dirs/3.0.1:
+ /glob@8.1.0:
+ resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 5.1.6
+ once: 1.4.0
+ dev: true
+
+ /global-dirs@3.0.1:
resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
engines: {node: '>=10'}
dependencies:
ini: 2.0.0
dev: true
- /globals/11.12.0:
+ /globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
dev: true
- /graceful-fs/4.2.11:
+ /graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
dev: true
- /has-flag/3.0.0:
+ /has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: true
- /has-flag/4.0.0:
+ /has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
dev: true
- /has-proto/1.0.1:
+ /has-proto@1.0.1:
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
engines: {node: '>= 0.4'}
dev: true
- /has-symbols/1.0.3:
+ /has-symbols@1.0.3:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
dev: true
- /has-unicode/2.0.1:
+ /has-unicode@2.0.1:
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
dev: true
- /has/1.0.3:
+ /has@1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'}
dependencies:
function-bind: 1.1.1
dev: true
- /hosted-git-info/6.1.1:
+ /hasown@2.0.1:
+ resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ function-bind: 1.1.2
+ dev: true
+
+ /hosted-git-info@6.1.1:
resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
lru-cache: 7.18.3
dev: true
- /html-insert-assets/0.14.3:
+ /html-insert-assets@0.14.3:
resolution: {integrity: sha512-4st+C8j3KFwzo8nZE8g7lgzuF+8l6+0WxhXKszV0+siYtbP4WZCHO4U2DVnW/9PJ4PSQYUuz/u92pXByDzZdJg==}
hasBin: true
dependencies:
@@ -1770,22 +2014,22 @@
parse5: 6.0.1
dev: true
- /http-cache-semantics/4.1.1:
+ /http-cache-semantics@4.1.1:
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
dev: true
- /http-proxy-agent/5.0.0:
+ /http-proxy-agent@5.0.0:
resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
engines: {node: '>= 6'}
dependencies:
'@tootallnate/once': 2.0.0
agent-base: 6.0.2
- debug: 4.3.4
+ debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
dev: true
- /http-signature/1.3.6:
+ /http-signature@1.3.6:
resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==}
engines: {node: '>=0.10'}
dependencies:
@@ -1794,85 +2038,90 @@
sshpk: 1.17.0
dev: true
- /https-proxy-agent/5.0.1:
+ /https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
dependencies:
agent-base: 6.0.2
- debug: 4.3.4
+ debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
dev: true
- /human-signals/1.1.1:
+ /human-signals@1.1.1:
resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
engines: {node: '>=8.12.0'}
dev: true
- /humanize-ms/1.2.1:
+ /humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
dependencies:
ms: 2.1.3
dev: true
- /iconv-lite/0.4.24:
+ /iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: true
- /iconv-lite/0.6.3:
+ /iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
+ requiresBuild: true
dependencies:
safer-buffer: 2.1.2
dev: true
optional: true
- /ieee754/1.2.1:
+ /ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: true
- /ignore-walk/6.0.3:
- resolution: {integrity: sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==}
+ /ignore-walk@6.0.4:
+ resolution: {integrity: sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
- minimatch: 9.0.2
+ minimatch: 9.0.3
dev: true
- /imurmurhash/0.1.4:
+ /imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
dev: true
- /indent-string/4.0.0:
+ /indent-string@4.0.0:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'}
dev: true
- /inflight/1.0.6:
+ /infer-owner@1.0.4:
+ resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
+ dev: true
+
+ /inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
dependencies:
once: 1.4.0
wrappy: 1.0.2
dev: true
- /inherits/2.0.4:
+ /inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
- /ini/2.0.0:
+ /ini@2.0.0:
resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==}
engines: {node: '>=10'}
dev: true
- /ini/3.0.1:
- resolution: {integrity: sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==}
- engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ /ini@4.1.1:
+ resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
- /inquirer/8.2.4:
+ /inquirer@8.2.4:
resolution: {integrity: sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==}
engines: {node: '>=12.0.0'}
dependencies:
@@ -1893,54 +2142,67 @@
wrap-ansi: 7.0.0
dev: true
- /ip/2.0.0:
+ /ip@2.0.0:
resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
dev: true
- /is-binary-path/2.1.0:
+ /is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
dependencies:
binary-extensions: 2.2.0
dev: true
- /is-ci/3.0.1:
+ /is-builtin-module@3.2.1:
+ resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
+ engines: {node: '>=6'}
+ dependencies:
+ builtin-modules: 3.3.0
+ dev: true
+
+ /is-ci@3.0.1:
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
hasBin: true
dependencies:
ci-info: 3.8.0
dev: true
- /is-core-module/2.12.1:
+ /is-core-module@2.12.1:
resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==}
dependencies:
has: 1.0.3
dev: true
- /is-docker/2.2.1:
+ /is-core-module@2.13.1:
+ resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
+ dependencies:
+ hasown: 2.0.1
+ dev: true
+
+ /is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
hasBin: true
dev: true
- /is-extglob/2.1.1:
+ /is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
dev: true
- /is-fullwidth-code-point/3.0.0:
+ /is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: true
- /is-glob/4.0.3:
+ /is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
dependencies:
is-extglob: 2.1.1
dev: true
- /is-installed-globally/0.4.0:
+ /is-installed-globally@0.4.0:
resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
engines: {node: '>=10'}
dependencies:
@@ -1948,60 +2210,60 @@
is-path-inside: 3.0.3
dev: true
- /is-interactive/1.0.0:
+ /is-interactive@1.0.0:
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
engines: {node: '>=8'}
dev: true
- /is-lambda/1.0.1:
+ /is-lambda@1.0.1:
resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
dev: true
- /is-module/1.0.0:
+ /is-module@1.0.0:
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
dev: true
- /is-number/7.0.0:
+ /is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
dev: true
- /is-path-inside/3.0.3:
+ /is-path-inside@3.0.3:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
dev: true
- /is-stream/2.0.1:
+ /is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
dev: true
- /is-typedarray/1.0.0:
+ /is-typedarray@1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
dev: true
- /is-unicode-supported/0.1.0:
+ /is-unicode-supported@0.1.0:
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
engines: {node: '>=10'}
dev: true
- /is-wsl/2.2.0:
+ /is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
dependencies:
is-docker: 2.2.1
dev: true
- /isexe/2.0.0:
+ /isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
- /isstream/0.1.2:
+ /isstream@0.1.2:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
dev: true
- /jackspeak/2.2.1:
- resolution: {integrity: sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==}
+ /jackspeak@2.3.6:
+ resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
@@ -2009,48 +2271,48 @@
'@pkgjs/parseargs': 0.11.0
dev: true
- /js-tokens/4.0.0:
+ /js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true
- /jsbn/0.1.1:
+ /jsbn@0.1.1:
resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
dev: true
- /jsesc/2.5.2:
+ /jsesc@2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
hasBin: true
dev: true
- /json-parse-even-better-errors/3.0.0:
+ /json-parse-even-better-errors@3.0.0:
resolution: {integrity: sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
- /json-schema-traverse/1.0.0:
+ /json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
dev: true
- /json-schema/0.4.0:
+ /json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
dev: true
- /json-stringify-safe/5.0.1:
+ /json-stringify-safe@5.0.1:
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
dev: true
- /json5/2.2.3:
+ /json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
dev: true
- /jsonc-parser/3.2.0:
+ /jsonc-parser@3.2.0:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
dev: true
- /jsonfile/6.1.0:
+ /jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
dependencies:
universalify: 2.0.0
@@ -2058,12 +2320,12 @@
graceful-fs: 4.2.11
dev: true
- /jsonparse/1.3.1:
+ /jsonparse@1.3.1:
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
engines: {'0': node >= 0.2.0}
dev: true
- /jsprim/2.0.2:
+ /jsprim@2.0.2:
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
engines: {'0': node >=0.6.0}
dependencies:
@@ -2073,12 +2335,12 @@
verror: 1.10.0
dev: true
- /lazy-ass/1.6.0:
+ /lazy-ass@1.6.0:
resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==}
engines: {node: '> 0.8'}
dev: true
- /listr2/3.14.0_enquirer@2.3.6:
+ /listr2@3.14.0(enquirer@2.3.6):
resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==}
engines: {node: '>=10.0.0'}
peerDependencies:
@@ -2098,15 +2360,15 @@
wrap-ansi: 7.0.0
dev: true
- /lodash.once/4.1.1:
+ /lodash.once@4.1.1:
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
dev: true
- /lodash/4.17.21:
+ /lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: true
- /log-symbols/4.1.0:
+ /log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
engines: {node: '>=10'}
dependencies:
@@ -2114,7 +2376,7 @@
is-unicode-supported: 0.1.0
dev: true
- /log-update/4.0.0:
+ /log-update@4.0.0:
resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
engines: {node: '>=10'}
dependencies:
@@ -2124,37 +2386,37 @@
wrap-ansi: 6.2.0
dev: true
- /lru-cache/10.0.0:
- resolution: {integrity: sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==}
+ /lru-cache@10.2.0:
+ resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
engines: {node: 14 || >=16.14}
dev: true
- /lru-cache/5.1.1:
+ /lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
dependencies:
yallist: 3.1.1
dev: true
- /lru-cache/6.0.0:
+ /lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
dev: true
- /lru-cache/7.18.3:
+ /lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
dev: true
- /magic-string/0.27.0:
- resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
+ /magic-string@0.30.1:
+ resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
- /make-dir/2.1.0:
+ /make-dir@2.1.0:
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
engines: {node: '>=6'}
dependencies:
@@ -2162,12 +2424,37 @@
semver: 5.7.1
dev: true
- /make-fetch-happen/11.1.1:
+ /make-fetch-happen@10.2.1:
+ resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ dependencies:
+ agentkeepalive: 4.5.0
+ cacache: 16.1.3
+ http-cache-semantics: 4.1.1
+ http-proxy-agent: 5.0.0
+ https-proxy-agent: 5.0.1
+ is-lambda: 1.0.1
+ lru-cache: 7.18.3
+ minipass: 3.3.6
+ minipass-collect: 1.0.2
+ minipass-fetch: 2.1.2
+ minipass-flush: 1.0.5
+ minipass-pipeline: 1.2.4
+ negotiator: 0.6.3
+ promise-retry: 2.0.1
+ socks-proxy-agent: 7.0.0
+ ssri: 9.0.1
+ transitivePeerDependencies:
+ - bluebird
+ - supports-color
+ dev: true
+
+ /make-fetch-happen@11.1.1:
resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
- agentkeepalive: 4.3.0
- cacache: 17.1.3
+ agentkeepalive: 4.5.0
+ cacache: 17.1.4
http-cache-semantics: 4.1.1
http-proxy-agent: 5.0.0
https-proxy-agent: 5.0.1
@@ -2185,52 +2472,70 @@
- supports-color
dev: true
- /merge-stream/2.0.0:
+ /merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: true
- /mime-db/1.52.0:
+ /mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: true
- /mime-types/2.1.35:
+ /mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: true
- /mimic-fn/2.1.0:
+ /mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
dev: true
- /minimatch/3.1.2:
+ /minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
brace-expansion: 1.1.11
dev: true
- /minimatch/9.0.2:
- resolution: {integrity: sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==}
+ /minimatch@5.1.6:
+ resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+ engines: {node: '>=10'}
+ dependencies:
+ brace-expansion: 2.0.1
+ dev: true
+
+ /minimatch@9.0.3:
+ resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: true
- /minimist/1.2.8:
+ /minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: true
- /minipass-collect/1.0.2:
+ /minipass-collect@1.0.2:
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
engines: {node: '>= 8'}
dependencies:
minipass: 3.3.6
dev: true
- /minipass-fetch/3.0.3:
+ /minipass-fetch@2.1.2:
+ resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ dependencies:
+ minipass: 3.3.6
+ minipass-sized: 1.0.3
+ minizlib: 2.1.2
+ optionalDependencies:
+ encoding: 0.1.13
+ dev: true
+
+ /minipass-fetch@3.0.3:
resolution: {integrity: sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
@@ -2241,52 +2546,52 @@
encoding: 0.1.13
dev: true
- /minipass-flush/1.0.5:
+ /minipass-flush@1.0.5:
resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
engines: {node: '>= 8'}
dependencies:
minipass: 3.3.6
dev: true
- /minipass-json-stream/1.0.1:
+ /minipass-json-stream@1.0.1:
resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==}
dependencies:
jsonparse: 1.3.1
minipass: 3.3.6
dev: true
- /minipass-pipeline/1.2.4:
+ /minipass-pipeline@1.2.4:
resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
engines: {node: '>=8'}
dependencies:
minipass: 3.3.6
dev: true
- /minipass-sized/1.0.3:
+ /minipass-sized@1.0.3:
resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==}
engines: {node: '>=8'}
dependencies:
minipass: 3.3.6
dev: true
- /minipass/3.3.6:
+ /minipass@3.3.6:
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
engines: {node: '>=8'}
dependencies:
yallist: 4.0.0
dev: true
- /minipass/4.2.8:
- resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==}
- engines: {node: '>=8'}
- dev: true
-
- /minipass/5.0.0:
+ /minipass@5.0.0:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'}
dev: true
- /minizlib/2.1.2:
+ /minipass@7.0.4:
+ resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dev: true
+
+ /minizlib@2.1.2:
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
engines: {node: '>= 8'}
dependencies:
@@ -2294,31 +2599,31 @@
yallist: 4.0.0
dev: true
- /mkdirp/1.0.4:
+ /mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
dev: true
- /ms/2.1.2:
+ /ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
- /ms/2.1.3:
+ /ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
dev: true
- /mute-stream/0.0.8:
+ /mute-stream@0.0.8:
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
dev: true
- /negotiator/0.6.3:
+ /negotiator@0.6.3:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
dev: true
- /node-gyp/9.4.0:
- resolution: {integrity: sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==}
+ /node-gyp@9.4.1:
+ resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==}
engines: {node: ^12.13 || ^14.13 || >=16}
hasBin: true
dependencies:
@@ -2326,22 +2631,23 @@
exponential-backoff: 3.1.1
glob: 7.2.3
graceful-fs: 4.2.11
- make-fetch-happen: 11.1.1
+ make-fetch-happen: 10.2.1
nopt: 6.0.0
npmlog: 6.0.2
rimraf: 3.0.2
- semver: 7.3.8
+ semver: 7.6.0
tar: 6.1.15
which: 2.0.2
transitivePeerDependencies:
+ - bluebird
- supports-color
dev: true
- /node-releases/2.0.12:
- resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==}
+ /node-releases@2.0.14:
+ resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
dev: true
- /nopt/6.0.0:
+ /nopt@6.0.0:
resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
hasBin: true
@@ -2349,68 +2655,68 @@
abbrev: 1.1.1
dev: true
- /normalize-package-data/5.0.0:
+ /normalize-package-data@5.0.0:
resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
hosted-git-info: 6.1.1
- is-core-module: 2.12.1
- semver: 7.3.8
+ is-core-module: 2.13.1
+ semver: 7.6.0
validate-npm-package-license: 3.0.4
dev: true
- /normalize-path/3.0.0:
+ /normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
dev: true
- /npm-bundled/3.0.0:
+ /npm-bundled@3.0.0:
resolution: {integrity: sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
npm-normalize-package-bin: 3.0.1
dev: true
- /npm-install-checks/6.1.1:
+ /npm-install-checks@6.1.1:
resolution: {integrity: sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
- semver: 7.3.8
+ semver: 7.6.0
dev: true
- /npm-normalize-package-bin/3.0.1:
+ /npm-normalize-package-bin@3.0.1:
resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
- /npm-package-arg/10.1.0:
+ /npm-package-arg@10.1.0:
resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
hosted-git-info: 6.1.1
proc-log: 3.0.0
- semver: 7.3.8
+ semver: 7.6.0
validate-npm-package-name: 5.0.0
dev: true
- /npm-packlist/7.0.4:
+ /npm-packlist@7.0.4:
resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
- ignore-walk: 6.0.3
+ ignore-walk: 6.0.4
dev: true
- /npm-pick-manifest/8.0.1:
+ /npm-pick-manifest@8.0.1:
resolution: {integrity: sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
npm-install-checks: 6.1.1
npm-normalize-package-bin: 3.0.1
npm-package-arg: 10.1.0
- semver: 7.3.8
+ semver: 7.6.0
dev: true
- /npm-registry-fetch/14.0.5:
+ /npm-registry-fetch@14.0.5:
resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
@@ -2425,14 +2731,14 @@
- supports-color
dev: true
- /npm-run-path/4.0.1:
+ /npm-run-path@4.0.1:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'}
dependencies:
path-key: 3.1.1
dev: true
- /npmlog/6.0.2:
+ /npmlog@6.0.2:
resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
dependencies:
@@ -2442,25 +2748,25 @@
set-blocking: 2.0.0
dev: true
- /object-inspect/1.12.3:
+ /object-inspect@1.12.3:
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
dev: true
- /once/1.4.0:
+ /once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
wrappy: 1.0.2
dev: true
- /onetime/5.1.2:
+ /onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
dependencies:
mimic-fn: 2.1.0
dev: true
- /open/8.4.0:
- resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==}
+ /open@8.4.2:
+ resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
dependencies:
define-lazy-prop: 2.0.0
@@ -2468,7 +2774,7 @@
is-wsl: 2.2.0
dev: true
- /ora/5.4.1:
+ /ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
dependencies:
@@ -2483,24 +2789,24 @@
wcwidth: 1.0.1
dev: true
- /os-tmpdir/1.0.2:
+ /os-tmpdir@1.0.2:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'}
dev: true
- /ospath/1.2.2:
+ /ospath@1.2.2:
resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
dev: true
- /p-map/4.0.0:
+ /p-map@4.0.0:
resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
engines: {node: '>=10'}
dependencies:
aggregate-error: 3.1.0
dev: true
- /pacote/15.0.8:
- resolution: {integrity: sha512-UlcumB/XS6xyyIMwg/WwMAyUmga+RivB5KgkRwA1hZNtrx+0Bt41KxHCvg1kr0pZ/ZeD8qjhW4fph6VaYRCbLw==}
+ /pacote@15.2.0:
+ resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
hasBin: true
dependencies:
@@ -2508,9 +2814,9 @@
'@npmcli/installed-package-contents': 2.0.2
'@npmcli/promise-spawn': 6.0.2
'@npmcli/run-script': 6.0.2
- cacache: 17.1.3
+ cacache: 17.1.4
fs-minipass: 3.0.2
- minipass: 4.2.8
+ minipass: 5.0.0
npm-package-arg: 10.1.0
npm-packlist: 7.0.4
npm-pick-manifest: 8.0.1
@@ -2519,6 +2825,7 @@
promise-retry: 2.0.1
read-package-json: 6.0.4
read-package-json-fast: 3.0.2
+ sigstore: 1.9.0
ssri: 10.0.4
tar: 6.1.15
transitivePeerDependencies:
@@ -2526,76 +2833,76 @@
- supports-color
dev: true
- /parse5/6.0.1:
+ /parse5@6.0.1:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
dev: true
- /path-is-absolute/1.0.1:
+ /path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
dev: true
- /path-key/3.1.1:
+ /path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
dev: true
- /path-parse/1.0.7:
+ /path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
- /path-scurry/1.10.0:
- resolution: {integrity: sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==}
+ /path-scurry@1.10.1:
+ resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
- lru-cache: 10.0.0
- minipass: 5.0.0
+ lru-cache: 10.2.0
+ minipass: 7.0.4
dev: true
- /pend/1.2.0:
+ /pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
dev: true
- /performance-now/2.1.0:
+ /performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
dev: true
- /picocolors/1.0.0:
+ /picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
- /picomatch/2.3.1:
+ /picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: true
- /pify/2.3.0:
+ /pify@2.3.0:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
dev: true
- /pify/4.0.1:
+ /pify@4.0.1:
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
engines: {node: '>=6'}
dev: true
- /prettier/2.6.1:
+ /prettier@2.6.1:
resolution: {integrity: sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
- /pretty-bytes/5.6.0:
+ /pretty-bytes@5.6.0:
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
engines: {node: '>=6'}
dev: true
- /proc-log/3.0.0:
+ /proc-log@3.0.0:
resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
- /promise-inflight/1.0.1:
+ /promise-inflight@1.0.1:
resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
peerDependencies:
bluebird: '*'
@@ -2604,7 +2911,7 @@
optional: true
dev: true
- /promise-retry/2.0.1:
+ /promise-retry@2.0.1:
resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==}
engines: {node: '>=10'}
dependencies:
@@ -2612,34 +2919,34 @@
retry: 0.12.0
dev: true
- /proxy-from-env/1.0.0:
+ /proxy-from-env@1.0.0:
resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==}
dev: true
- /psl/1.9.0:
+ /psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
dev: true
- /pump/3.0.0:
+ /pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies:
end-of-stream: 1.4.4
once: 1.4.0
dev: true
- /punycode/2.3.0:
+ /punycode@2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
dev: true
- /qs/6.10.4:
+ /qs@6.10.4:
resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.4
dev: true
- /read-package-json-fast/3.0.2:
+ /read-package-json-fast@3.0.2:
resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
@@ -2647,17 +2954,17 @@
npm-normalize-package-bin: 3.0.1
dev: true
- /read-package-json/6.0.4:
+ /read-package-json@6.0.4:
resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
- glob: 10.3.1
+ glob: 10.3.10
json-parse-even-better-errors: 3.0.0
normalize-package-data: 5.0.0
npm-normalize-package-bin: 3.0.1
dev: true
- /readable-stream/3.6.2:
+ /readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
dependencies:
@@ -2666,49 +2973,40 @@
util-deprecate: 1.0.2
dev: true
- /readdirp/3.6.0:
+ /readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
dependencies:
picomatch: 2.3.1
dev: true
- /reflect-metadata/0.1.13:
- resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
+ /reflect-metadata@0.1.14:
+ resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
dev: true
- /request-progress/3.0.0:
+ /request-progress@3.0.0:
resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==}
dependencies:
throttleit: 1.0.0
dev: true
- /require-directory/2.1.1:
+ /require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
dev: true
- /require-from-string/2.0.2:
+ /require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
dev: true
- /requirejs/2.3.6:
+ /requirejs@2.3.6:
resolution: {integrity: sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
- /resolve/1.22.1:
- resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
- hasBin: true
- dependencies:
- is-core-module: 2.12.1
- path-parse: 1.0.7
- supports-preserve-symlinks-flag: 1.0.0
- dev: true
-
- /resolve/1.22.2:
+ /resolve@1.22.2:
resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==}
hasBin: true
dependencies:
@@ -2717,7 +3015,7 @@
supports-preserve-symlinks-flag: 1.0.0
dev: true
- /restore-cursor/3.1.0:
+ /restore-cursor@3.1.0:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'}
dependencies:
@@ -2725,75 +3023,80 @@
signal-exit: 3.0.7
dev: true
- /retry/0.12.0:
+ /retry@0.12.0:
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
engines: {node: '>= 4'}
dev: true
- /rfdc/1.3.0:
+ /rfdc@1.3.0:
resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
dev: true
- /rimraf/3.0.2:
+ /rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
hasBin: true
dependencies:
glob: 7.2.3
dev: true
- /rollup/3.17.2:
- resolution: {integrity: sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==}
- engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+ /rollup@4.12.0:
+ resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ dependencies:
+ '@types/estree': 1.0.5
optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.12.0
+ '@rollup/rollup-android-arm64': 4.12.0
+ '@rollup/rollup-darwin-arm64': 4.12.0
+ '@rollup/rollup-darwin-x64': 4.12.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.12.0
+ '@rollup/rollup-linux-arm64-gnu': 4.12.0
+ '@rollup/rollup-linux-arm64-musl': 4.12.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.12.0
+ '@rollup/rollup-linux-x64-gnu': 4.12.0
+ '@rollup/rollup-linux-x64-musl': 4.12.0
+ '@rollup/rollup-win32-arm64-msvc': 4.12.0
+ '@rollup/rollup-win32-ia32-msvc': 4.12.0
+ '@rollup/rollup-win32-x64-msvc': 4.12.0
fsevents: 2.3.2
dev: true
- /run-async/2.4.1:
+ /run-async@2.4.1:
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
engines: {node: '>=0.12.0'}
dev: true
- /rxjs/6.6.7:
- resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==}
- engines: {npm: '>=2.0.0'}
- dependencies:
- tslib: 1.14.1
- dev: true
-
- /rxjs/7.5.7:
+ /rxjs@7.5.7:
resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==}
dependencies:
tslib: 2.6.0
+
+ /rxjs@7.8.1:
+ resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
+ dependencies:
+ tslib: 2.6.0
dev: true
- /safe-buffer/5.2.1:
+ /safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: true
- /safer-buffer/2.1.2:
+ /safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: true
- /semver/5.7.1:
+ /semver@5.7.1:
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
hasBin: true
dev: true
- /semver/6.3.0:
- resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+ /semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
dev: true
- /semver/7.3.8:
- resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
- engines: {node: '>=10'}
- hasBin: true
- dependencies:
- lru-cache: 6.0.0
- dev: true
-
- /semver/7.5.3:
+ /semver@7.5.3:
resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==}
engines: {node: '>=10'}
hasBin: true
@@ -2801,23 +3104,39 @@
lru-cache: 6.0.0
dev: true
- /set-blocking/2.0.0:
+ /semver@7.5.4:
+ resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ lru-cache: 6.0.0
+ dev: true
+
+ /semver@7.6.0:
+ resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ lru-cache: 6.0.0
+ dev: true
+
+ /set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
dev: true
- /shebang-command/2.0.0:
+ /shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
dependencies:
shebang-regex: 3.0.0
dev: true
- /shebang-regex/3.0.0:
+ /shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
dev: true
- /side-channel/1.0.4:
+ /side-channel@1.0.4:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
dependencies:
call-bind: 1.0.2
@@ -2825,21 +3144,35 @@
object-inspect: 1.12.3
dev: true
- /signal-exit/3.0.7:
+ /signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: true
- /signal-exit/4.0.2:
+ /signal-exit@4.0.2:
resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==}
engines: {node: '>=14'}
dev: true
- /slash/2.0.0:
+ /sigstore@1.9.0:
+ resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ hasBin: true
+ dependencies:
+ '@sigstore/bundle': 1.1.0
+ '@sigstore/protobuf-specs': 0.2.1
+ '@sigstore/sign': 1.0.0
+ '@sigstore/tuf': 1.0.3
+ make-fetch-happen: 11.1.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /slash@2.0.0:
resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==}
engines: {node: '>=6'}
dev: true
- /slice-ansi/3.0.0:
+ /slice-ansi@3.0.0:
resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
engines: {node: '>=8'}
dependencies:
@@ -2848,7 +3181,7 @@
is-fullwidth-code-point: 3.0.0
dev: true
- /slice-ansi/4.0.0:
+ /slice-ansi@4.0.0:
resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
engines: {node: '>=10'}
dependencies:
@@ -2857,23 +3190,23 @@
is-fullwidth-code-point: 3.0.0
dev: true
- /smart-buffer/4.2.0:
+ /smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
dev: true
- /socks-proxy-agent/7.0.0:
+ /socks-proxy-agent@7.0.0:
resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==}
engines: {node: '>= 10'}
dependencies:
agent-base: 6.0.2
- debug: 4.3.4
+ debug: 4.3.4(supports-color@8.1.1)
socks: 2.7.1
transitivePeerDependencies:
- supports-color
dev: true
- /socks/2.7.1:
+ /socks@2.7.1:
resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==}
engines: {node: '>= 10.13.0', npm: '>= 3.0.0'}
dependencies:
@@ -2881,46 +3214,46 @@
smart-buffer: 4.2.0
dev: true
- /source-map-support/0.5.21:
+ /source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
dev: true
- /source-map/0.6.1:
+ /source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
dev: true
- /source-map/0.7.4:
+ /source-map@0.7.4:
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
engines: {node: '>= 8'}
dev: true
- /spdx-correct/3.2.0:
+ /spdx-correct@3.2.0:
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
dependencies:
spdx-expression-parse: 3.0.1
spdx-license-ids: 3.0.13
dev: true
- /spdx-exceptions/2.3.0:
+ /spdx-exceptions@2.3.0:
resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
dev: true
- /spdx-expression-parse/3.0.1:
+ /spdx-expression-parse@3.0.1:
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
dependencies:
spdx-exceptions: 2.3.0
spdx-license-ids: 3.0.13
dev: true
- /spdx-license-ids/3.0.13:
+ /spdx-license-ids@3.0.13:
resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==}
dev: true
- /sshpk/1.17.0:
+ /sshpk@1.17.0:
resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==}
engines: {node: '>=0.10.0'}
hasBin: true
@@ -2936,14 +3269,21 @@
tweetnacl: 0.14.5
dev: true
- /ssri/10.0.4:
+ /ssri@10.0.4:
resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
minipass: 5.0.0
dev: true
- /string-width/4.2.3:
+ /ssri@9.0.1:
+ resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ dependencies:
+ minipass: 3.3.6
+ dev: true
+
+ /string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
dependencies:
@@ -2952,7 +3292,7 @@
strip-ansi: 6.0.1
dev: true
- /string-width/5.1.2:
+ /string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
dependencies:
@@ -2961,63 +3301,63 @@
strip-ansi: 7.1.0
dev: true
- /string_decoder/1.3.0:
+ /string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
safe-buffer: 5.2.1
dev: true
- /strip-ansi/6.0.1:
+ /strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
dependencies:
ansi-regex: 5.0.1
dev: true
- /strip-ansi/7.1.0:
+ /strip-ansi@7.1.0:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
dependencies:
ansi-regex: 6.0.1
dev: true
- /strip-final-newline/2.0.0:
+ /strip-final-newline@2.0.0:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
dev: true
- /supports-color/5.5.0:
+ /supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: true
- /supports-color/7.2.0:
+ /supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
dependencies:
has-flag: 4.0.0
dev: true
- /supports-color/8.1.1:
+ /supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
dependencies:
has-flag: 4.0.0
dev: true
- /supports-preserve-symlinks-flag/1.0.0:
+ /supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
dev: true
- /symbol-observable/4.0.0:
+ /symbol-observable@4.0.0:
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
engines: {node: '>=0.10'}
dev: true
- /tar/6.1.15:
+ /tar@6.1.15:
resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==}
engines: {node: '>=10'}
dependencies:
@@ -3029,7 +3369,7 @@
yallist: 4.0.0
dev: true
- /terser/5.16.4:
+ /terser@5.16.4:
resolution: {integrity: sha512-5yEGuZ3DZradbogeYQ1NaGz7rXVBDWujWlx1PT8efXO6Txn+eWbfKqB2bTDVmFXmePFkoLU6XI8UektMIEA0ug==}
engines: {node: '>=10'}
hasBin: true
@@ -3040,41 +3380,41 @@
source-map-support: 0.5.21
dev: true
- /throttleit/1.0.0:
+ /throttleit@1.0.0:
resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==}
dev: true
- /through/2.3.8:
+ /through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: true
- /tmp/0.0.33:
+ /tmp@0.0.33:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
dependencies:
os-tmpdir: 1.0.2
dev: true
- /tmp/0.2.1:
+ /tmp@0.2.1:
resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
engines: {node: '>=8.17.0'}
dependencies:
rimraf: 3.0.2
dev: true
- /to-fast-properties/2.0.0:
+ /to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
dev: true
- /to-regex-range/5.0.1:
+ /to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
dependencies:
is-number: 7.0.0
dev: true
- /tough-cookie/2.5.0:
+ /tough-cookie@2.5.0:
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
engines: {node: '>=0.8'}
dependencies:
@@ -3082,99 +3422,124 @@
punycode: 2.3.0
dev: true
- /tslib/1.14.1:
- resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
- dev: true
-
- /tslib/2.6.0:
+ /tslib@2.6.0:
resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==}
- /tunnel-agent/0.6.0:
+ /tuf-js@1.1.7:
+ resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dependencies:
+ '@tufjs/models': 1.0.4
+ debug: 4.3.4(supports-color@8.1.1)
+ make-fetch-happen: 11.1.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
safe-buffer: 5.2.1
dev: true
- /tweetnacl/0.14.5:
+ /tweetnacl@0.14.5:
resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
dev: true
- /type-fest/0.21.3:
+ /type-fest@0.21.3:
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
engines: {node: '>=10'}
dev: true
- /typescript/4.8.4:
- resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==}
- engines: {node: '>=4.2.0'}
+ /typescript@5.1.6:
+ resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
+ engines: {node: '>=14.17'}
hasBin: true
dev: true
- /unique-filename/3.0.0:
+ /undici-types@5.26.5:
+ resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+ dev: true
+
+ /unique-filename@2.0.1:
+ resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ dependencies:
+ unique-slug: 3.0.0
+ dev: true
+
+ /unique-filename@3.0.0:
resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
unique-slug: 4.0.0
dev: true
- /unique-slug/4.0.0:
+ /unique-slug@3.0.0:
+ resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ dependencies:
+ imurmurhash: 0.1.4
+ dev: true
+
+ /unique-slug@4.0.0:
resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
imurmurhash: 0.1.4
dev: true
- /universalify/2.0.0:
+ /universalify@2.0.0:
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
engines: {node: '>= 10.0.0'}
dev: true
- /untildify/4.0.0:
+ /untildify@4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
engines: {node: '>=8'}
dev: true
- /update-browserslist-db/1.0.11_browserslist@4.21.9:
- resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
+ /update-browserslist-db@1.0.13(browserslist@4.23.0):
+ resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
- browserslist: 4.21.9
+ browserslist: 4.23.0
escalade: 3.1.1
picocolors: 1.0.0
dev: true
- /uri-js/4.4.1:
+ /uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
punycode: 2.3.0
dev: true
- /util-deprecate/1.0.2:
+ /util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: true
- /uuid/8.3.2:
+ /uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
dev: true
- /validate-npm-package-license/3.0.4:
+ /validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
dev: true
- /validate-npm-package-name/5.0.0:
+ /validate-npm-package-name@5.0.0:
resolution: {integrity: sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
builtins: 5.0.1
dev: true
- /verror/1.10.0:
+ /verror@1.10.0:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0}
dependencies:
@@ -3183,13 +3548,13 @@
extsprintf: 1.3.0
dev: true
- /wcwidth/1.0.1:
+ /wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
dependencies:
defaults: 1.0.4
dev: true
- /which/2.0.2:
+ /which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
@@ -3197,7 +3562,7 @@
isexe: 2.0.0
dev: true
- /which/3.0.1:
+ /which@3.0.1:
resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
hasBin: true
@@ -3205,13 +3570,13 @@
isexe: 2.0.0
dev: true
- /wide-align/1.1.5:
+ /wide-align@1.1.5:
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
dependencies:
string-width: 4.2.3
dev: true
- /wrap-ansi/6.2.0:
+ /wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'}
dependencies:
@@ -3220,7 +3585,7 @@
strip-ansi: 6.0.1
dev: true
- /wrap-ansi/7.0.0:
+ /wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
dependencies:
@@ -3229,7 +3594,7 @@
strip-ansi: 6.0.1
dev: true
- /wrap-ansi/8.1.0:
+ /wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
dependencies:
@@ -3238,42 +3603,29 @@
strip-ansi: 7.1.0
dev: true
- /wrappy/1.0.2:
+ /wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true
- /y18n/5.0.8:
+ /y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
dev: true
- /yallist/3.1.1:
+ /yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true
- /yallist/4.0.0:
+ /yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
- /yargs-parser/21.1.1:
+ /yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
dev: true
- /yargs/17.6.2:
- resolution: {integrity: sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==}
- engines: {node: '>=12'}
- dependencies:
- cliui: 8.0.1
- escalade: 3.1.1
- get-caller-file: 2.0.5
- require-directory: 2.1.1
- string-width: 4.2.3
- y18n: 5.0.8
- yargs-parser: 21.1.1
- dev: true
-
- /yargs/17.7.2:
+ /yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
dependencies:
@@ -3286,15 +3638,14 @@
yargs-parser: 21.1.1
dev: true
- /yauzl/2.10.0:
+ /yauzl@2.10.0:
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
dependencies:
buffer-crc32: 0.2.13
fd-slicer: 1.1.0
dev: true
- /zone.js/0.11.8:
- resolution: {integrity: sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==}
+ /zone.js@0.13.3:
+ resolution: {integrity: sha512-MKPbmZie6fASC/ps4dkmIhaT5eonHkEt6eAy80K42tAm0G2W+AahLJjbfi6X9NPdciOE9GRFTTM8u2IiF6O3ww==}
dependencies:
tslib: 2.6.0
- dev: true
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 3cd14a7..7d6e98e 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -1124,7 +1124,7 @@
return
}
- log.Println("Got actions for match", request.MatchNumber(), "team", request.TeamNumber(), "from", username)
+ log.Println("Got actions for match", request.MatchNumber(), "team", string(request.TeamNumber()), "from", username)
for i := 0; i < request.ActionsListLength(); i++ {
@@ -1195,7 +1195,7 @@
return
}
- log.Println("Got actions for match", request.MatchNumber(), "team", request.TeamNumber(), "from", username)
+ log.Println("Got actions for match", request.MatchNumber(), "team", string(request.TeamNumber()), "from", username)
for i := 0; i < request.ActionsListLength(); i++ {
diff --git a/scouting/www/app/app.ts b/scouting/www/app/app.ts
index 011c94f..597e5c5 100644
--- a/scouting/www/app/app.ts
+++ b/scouting/www/app/app.ts
@@ -10,7 +10,7 @@
| 'Pit';
// Ignore the guard for tabs that don't require the user to enter any data.
-const unguardedTabs: Tab[] = ['MatchList'];
+const unguardedTabs: Tab[] = ['MatchList', 'View'];
type TeamInMatch = {
teamNumber: string;
diff --git a/scouting/www/driver_ranking/package.json b/scouting/www/driver_ranking/package.json
index 83dacf4..38d9358 100644
--- a/scouting/www/driver_ranking/package.json
+++ b/scouting/www/driver_ranking/package.json
@@ -2,6 +2,6 @@
"name": "@org_frc971/scouting/www/driver_ranking",
"private": true,
"dependencies": {
- "@angular/forms": "15.1.5"
+ "@angular/forms": "v16-lts"
}
}
diff --git a/scouting/www/entry/entry.component.ts b/scouting/www/entry/entry.component.ts
index 5b461f8..6da8be8 100644
--- a/scouting/www/entry/entry.component.ts
+++ b/scouting/www/entry/entry.component.ts
@@ -25,7 +25,7 @@
Action,
} from '../../webserver/requests/messages/submit_2024_actions_generated';
import {Match} from '../../webserver/requests/messages/request_all_matches_response_generated';
-import {MatchListRequestor} from '@org_frc971/scouting/www/rpc';
+import {MatchListRequestor} from '../rpc';
type Section =
| 'Team Selection'
diff --git a/scouting/www/entry/package.json b/scouting/www/entry/package.json
index 4c05778..d37ce10 100644
--- a/scouting/www/entry/package.json
+++ b/scouting/www/entry/package.json
@@ -3,6 +3,6 @@
"private": true,
"dependencies": {
"@org_frc971/scouting/www/counter_button": "workspace:*",
- "@angular/forms": "15.1.5"
+ "@angular/forms": "v16-lts"
}
}
diff --git a/scouting/www/match_list/match_list.component.ts b/scouting/www/match_list/match_list.component.ts
index 0deeb11..8fafdce 100644
--- a/scouting/www/match_list/match_list.component.ts
+++ b/scouting/www/match_list/match_list.component.ts
@@ -7,7 +7,7 @@
RequestAllMatchesResponse,
} from '../../webserver/requests/messages/request_all_matches_response_generated';
-import {MatchListRequestor} from '@org_frc971/scouting/www/rpc';
+import {MatchListRequestor} from '../rpc';
type TeamInMatch = {
teamNumber: string;
diff --git a/scouting/www/match_list/package.json b/scouting/www/match_list/package.json
index 284e77b..00977c5 100644
--- a/scouting/www/match_list/package.json
+++ b/scouting/www/match_list/package.json
@@ -3,6 +3,6 @@
"private": true,
"dependencies": {
"@org_frc971/scouting/www/rpc": "workspace:*",
- "@angular/forms": "15.1.5"
+ "@angular/forms": "v16-lts"
}
}
diff --git a/scouting/www/notes/package.json b/scouting/www/notes/package.json
index c5c6afe..f1ad3ae 100644
--- a/scouting/www/notes/package.json
+++ b/scouting/www/notes/package.json
@@ -2,6 +2,6 @@
"name": "@org_frc971/scouting/www/notes",
"private": true,
"dependencies": {
- "@angular/forms": "15.1.5"
+ "@angular/forms": "v16-lts"
}
}
diff --git a/scouting/www/pit_scouting/package.json b/scouting/www/pit_scouting/package.json
index e58fc51..f41150f 100644
--- a/scouting/www/pit_scouting/package.json
+++ b/scouting/www/pit_scouting/package.json
@@ -2,6 +2,6 @@
"name": "@org_frc971/scouting/www/pit_scouting",
"private": true,
"dependencies": {
- "@angular/forms": "15.1.5"
+ "@angular/forms": "v16-lts"
}
}
diff --git a/scouting/www/shift_schedule/package.json b/scouting/www/shift_schedule/package.json
index 1270235..f2d6d7e 100644
--- a/scouting/www/shift_schedule/package.json
+++ b/scouting/www/shift_schedule/package.json
@@ -2,6 +2,6 @@
"name": "@org_frc971/scouting/www/shift_schedule",
"private": true,
"dependencies": {
- "@angular/forms": "15.1.5"
+ "@angular/forms": "v16-lts"
}
}
diff --git a/scouting/www/view/package.json b/scouting/www/view/package.json
index ef94a11..d214f6f 100644
--- a/scouting/www/view/package.json
+++ b/scouting/www/view/package.json
@@ -2,6 +2,6 @@
"name": "@org_frc971/scouting/www/view",
"private": true,
"dependencies": {
- "@angular/forms": "15.1.5"
+ "@angular/forms": "v16-lts"
}
}
diff --git a/third_party/autocxx/Cargo.lock b/third_party/autocxx/Cargo.lock
index a7872e3..4587dcb 100644
--- a/third_party/autocxx/Cargo.lock
+++ b/third_party/autocxx/Cargo.lock
@@ -1392,9 +1392,9 @@
[[package]]
name = "rustix"
-version = "0.37.23"
+version = "0.37.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
+checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035"
dependencies = [
"bitflags 1.3.2",
"errno",
@@ -1561,7 +1561,7 @@
"cfg-if",
"fastrand",
"redox_syscall",
- "rustix 0.37.23",
+ "rustix 0.37.25",
"windows-sys",
]
@@ -1590,7 +1590,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
- "rustix 0.37.23",
+ "rustix 0.37.25",
"windows-sys",
]
diff --git a/third_party/autocxx/engine/src/ast_discoverer.rs b/third_party/autocxx/engine/src/ast_discoverer.rs
index f555419..6be9d8f 100644
--- a/third_party/autocxx/engine/src/ast_discoverer.rs
+++ b/third_party/autocxx/engine/src/ast_discoverer.rs
@@ -672,7 +672,7 @@
}
};
discoveries.search_item(&itm, None).unwrap();
- assert!(discoveries.extern_rust_funs.get(0).unwrap().sig.ident == "bar");
+ assert!(discoveries.extern_rust_funs.first().unwrap().sig.ident == "bar");
}
#[test]
@@ -686,7 +686,7 @@
}
};
discoveries.search_item(&itm, None).unwrap();
- assert!(discoveries.extern_rust_funs.get(0).unwrap().sig.ident == "bar");
+ assert!(discoveries.extern_rust_funs.first().unwrap().sig.ident == "bar");
}
#[test]
@@ -702,7 +702,7 @@
assert!(
discoveries
.extern_rust_types
- .get(0)
+ .first()
.unwrap()
.get_final_ident()
== "Bar"
diff --git a/third_party/autocxx/engine/src/conversion/analysis/allocators.rs b/third_party/autocxx/engine/src/conversion/analysis/allocators.rs
index da25a77..aab637c 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/allocators.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/allocators.rs
@@ -101,11 +101,11 @@
}
pub(crate) fn get_alloc_name(ty_name: &QualifiedName) -> QualifiedName {
- get_name(ty_name, "alloc")
+ get_name(ty_name, "autocxx_alloc")
}
pub(crate) fn get_free_name(ty_name: &QualifiedName) -> QualifiedName {
- get_name(ty_name, "free")
+ get_name(ty_name, "autocxx_free")
}
fn get_name(ty_name: &QualifiedName, label: &str) -> QualifiedName {
diff --git a/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs b/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs
index baadbd7..3c6241b 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/depth_first.rs
@@ -44,7 +44,7 @@
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
- let first_candidate = self.queue.get(0).map(|api| api.name());
+ let first_candidate = self.queue.front().map(|api| api.name());
while let Some(candidate) = self.queue.pop_front() {
if !candidate
.field_and_base_deps()
@@ -54,7 +54,7 @@
return Some(candidate);
}
self.queue.push_back(candidate);
- if self.queue.get(0).map(|api| api.name()) == first_candidate {
+ if self.queue.front().map(|api| api.name()) == first_candidate {
panic!(
"Failed to find a candidate; there must be a circular dependency. Queue is {}",
self.queue
diff --git a/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs b/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs
index 415e40a..dc953ad 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/fun/mod.rs
@@ -80,7 +80,7 @@
#[derive(Clone, Debug)]
pub(crate) enum MethodKind {
- Normal(ReceiverMutability),
+ Normal,
Constructor { is_default: bool },
Static,
Virtual(ReceiverMutability),
@@ -964,7 +964,7 @@
let receiver_mutability =
receiver_mutability.expect("Failed to find receiver details");
match fun.virtualness {
- Virtualness::None => MethodKind::Normal(receiver_mutability),
+ Virtualness::None => MethodKind::Normal,
Virtualness::Virtual => MethodKind::Virtual(receiver_mutability),
Virtualness::PureVirtual => MethodKind::PureVirtual(receiver_mutability),
}
@@ -1152,7 +1152,7 @@
ref impl_for,
method_kind:
MethodKind::Constructor { .. }
- | MethodKind::Normal(..)
+ | MethodKind::Normal
| MethodKind::PureVirtual(..)
| MethodKind::Virtual(..),
..
diff --git a/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs b/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs
index 6e2e9b9..d0c828e 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/pod/byvalue_checker.rs
@@ -158,6 +158,12 @@
));
break;
}
+ None if ty_id.get_final_item() == "__BindgenBitfieldUnit" => {
+ field_safety_problem = PodState::UnsafeToBePod(format!(
+ "Type {tyname} could not be POD because it is a bitfield"
+ ));
+ break;
+ }
None => {
field_safety_problem = PodState::UnsafeToBePod(format!(
"Type {tyname} could not be POD because its dependent type {ty_id} isn't known"
diff --git a/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs b/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs
index 63f7ae9..99cd61d 100644
--- a/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs
+++ b/third_party/autocxx/engine/src/conversion/analysis/type_converter.rs
@@ -251,7 +251,7 @@
let i = make_ident(s);
parse_quote! { #i }
})
- .chain(typ.path.segments.into_iter())
+ .chain(typ.path.segments)
.collect();
}
}
diff --git a/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs b/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs
index abab612..ee56169 100644
--- a/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/codegen_rs/mod.rs
@@ -1130,6 +1130,7 @@
let segs =
Self::find_output_mod_root(name.get_namespace()).chain(name.get_bindgen_path_idents());
Item::Use(parse_quote! {
+ #[allow(unused_imports)]
pub use #(#segs)::*;
})
}
diff --git a/third_party/autocxx/engine/src/conversion/mod.rs b/third_party/autocxx/engine/src/conversion/mod.rs
index 61d7d6d..fa24d4d 100644
--- a/third_party/autocxx/engine/src/conversion/mod.rs
+++ b/third_party/autocxx/engine/src/conversion/mod.rs
@@ -114,7 +114,7 @@
None => Err(ConvertError::NoContent),
Some((_, items)) => {
// Parse the bindgen mod.
- let items_to_process = items.drain(..).collect();
+ let items_to_process = std::mem::take(items);
let parser = ParseBindgen::new(self.config);
let apis = parser.parse_items(items_to_process, source_file_contents)?;
Self::dump_apis("parsing", &apis);
diff --git a/third_party/autocxx/engine/src/known_types.rs b/third_party/autocxx/engine/src/known_types.rs
index 10199fb..573be4e 100644
--- a/third_party/autocxx/engine/src/known_types.rs
+++ b/third_party/autocxx/engine/src/known_types.rs
@@ -12,7 +12,7 @@
use once_cell::sync::OnceCell;
use syn::{parse_quote, TypePath};
-//// The behavior of the type.
+/// The behavior of the type.
#[derive(Debug)]
enum Behavior {
CxxContainerPtr,
diff --git a/third_party/autocxx/engine/src/parse_file.rs b/third_party/autocxx/engine/src/parse_file.rs
index 16ea625..9dd620c 100644
--- a/third_party/autocxx/engine/src/parse_file.rs
+++ b/third_party/autocxx/engine/src/parse_file.rs
@@ -21,7 +21,7 @@
use std::{io::Read, path::PathBuf};
use std::{panic::UnwindSafe, path::Path, rc::Rc};
use syn::spanned::Spanned;
-use syn::{token::Brace, Item, ItemMod};
+use syn::Item;
use thiserror::Error;
/// Errors which may occur when parsing a Rust source file to discover
@@ -119,7 +119,7 @@
Segment::Cxx(CxxBridge::from(itm))
}
Item::Mod(itm) => {
- if let Some((brace, items)) = itm.content {
+ if let Some((_, items)) = itm.content {
let mut mod_state = State {
auto_allowlist: self.auto_allowlist,
..Default::default()
@@ -137,18 +137,9 @@
}
self.extra_superclasses.extend(mod_state.extra_superclasses);
self.discoveries.extend(mod_state.discoveries);
- Segment::Mod(
- mod_state.results,
- (
- brace,
- ItemMod {
- content: None,
- ..itm
- },
- ),
- )
+ Segment::Mod(mod_state.results)
} else {
- Segment::Other(Item::Mod(itm))
+ Segment::Other
}
}
Item::Struct(ref its) => {
@@ -189,13 +180,13 @@
self.discoveries
.search_item(&item, mod_path)
.map_err(ParseError::Discovery)?;
- Segment::Other(item)
+ Segment::Other
}
_ => {
self.discoveries
.search_item(&item, mod_path)
.map_err(ParseError::Discovery)?;
- Segment::Other(item)
+ Segment::Other
}
};
self.results.push(result);
@@ -283,8 +274,8 @@
enum Segment {
Autocxx(IncludeCppEngine),
Cxx(CxxBridge),
- Mod(Vec<Segment>, (Brace, ItemMod)),
- Other(Item),
+ Mod(Vec<Segment>),
+ Other,
}
pub trait CppBuildable {
@@ -303,7 +294,7 @@
.flat_map(|s| -> Box<dyn Iterator<Item = &IncludeCppEngine>> {
match s {
Segment::Autocxx(includecpp) => Box::new(std::iter::once(includecpp)),
- Segment::Mod(segments, _) => Box::new(do_get_autocxxes(segments)),
+ Segment::Mod(segments) => Box::new(do_get_autocxxes(segments)),
_ => Box::new(std::iter::empty()),
}
})
@@ -331,7 +322,7 @@
Segment::Cxx(cxxbridge) => {
Box::new(std::iter::once(cxxbridge as &dyn CppBuildable))
}
- Segment::Mod(segments, _) => Box::new(do_get_cpp_buildables(segments)),
+ Segment::Mod(segments) => Box::new(do_get_cpp_buildables(segments)),
_ => Box::new(std::iter::empty()),
}
})
@@ -349,7 +340,7 @@
.flat_map(|s| -> Box<dyn Iterator<Item = &mut IncludeCppEngine>> {
match s {
Segment::Autocxx(includecpp) => Box::new(std::iter::once(includecpp)),
- Segment::Mod(segments, _) => Box::new(do_get_autocxxes_mut(segments)),
+ Segment::Mod(segments) => Box::new(do_get_autocxxes_mut(segments)),
_ => Box::new(std::iter::empty()),
}
})
@@ -368,7 +359,7 @@
.flat_map(|s| -> Box<dyn Iterator<Item = &PathBuf>> {
match s {
Segment::Autocxx(includecpp) => Box::new(includecpp.include_dirs()),
- Segment::Mod(segments, _) => Box::new(do_get_include_dirs(segments)),
+ Segment::Mod(segments) => Box::new(do_get_include_dirs(segments)),
_ => Box::new(std::iter::empty()),
}
})
diff --git a/third_party/autocxx/engine/src/types.rs b/third_party/autocxx/engine/src/types.rs
index f7eaae0..3afbf9d 100644
--- a/third_party/autocxx/engine/src/types.rs
+++ b/third_party/autocxx/engine/src/types.rs
@@ -236,6 +236,10 @@
/// cxx.
#[derive(Error, Clone, Debug)]
pub enum InvalidIdentError {
+ #[error("Union are not supported by autocxx (and their bindgen names have __ so are not acceptable to cxx)")]
+ Union,
+ #[error("Bitfields are not supported by autocxx (and their bindgen names have __ so are not acceptable to cxx)")]
+ Bitfield,
#[error("Names containing __ are reserved by C++ so not acceptable to cxx")]
TooManyUnderscores,
#[error("bindgen decided to call this type _bindgen_ty_N because it couldn't deduce the correct name for it. That means we can't generate C++ bindings to it.")]
@@ -251,7 +255,12 @@
/// where code will be output as part of the `#[cxx::bridge]` mod.
pub fn validate_ident_ok_for_cxx(id: &str) -> Result<(), InvalidIdentError> {
validate_ident_ok_for_rust(id)?;
- if id.contains("__") {
+ // Provide a couple of more specific diagnostics if we can.
+ if id.starts_with("__BindgenBitfieldUnit") {
+ Err(InvalidIdentError::Bitfield)
+ } else if id.starts_with("__BindgenUnionField") {
+ Err(InvalidIdentError::Union)
+ } else if id.contains("__") {
Err(InvalidIdentError::TooManyUnderscores)
} else if id.starts_with("_bindgen_ty_") {
Err(InvalidIdentError::BindgenTy)
diff --git a/third_party/autocxx/gen/cmd/BUILD b/third_party/autocxx/gen/cmd/BUILD
index 60d011c..c05e24d 100644
--- a/third_party/autocxx/gen/cmd/BUILD
+++ b/third_party/autocxx/gen/cmd/BUILD
@@ -22,7 +22,7 @@
version = "0.16.0",
deps = [
"//third_party/autocxx/engine:autocxx_engine",
- "@crate_index//:clap",
+ "@crate_index//:clap3",
"@crate_index//:env_logger",
"@crate_index//:indexmap",
"@crate_index//:miette",
diff --git a/third_party/autocxx/gen/cmd/Cargo.toml b/third_party/autocxx/gen/cmd/Cargo.toml
index 7a78ba3..55242ff 100644
--- a/third_party/autocxx/gen/cmd/Cargo.toml
+++ b/third_party/autocxx/gen/cmd/Cargo.toml
@@ -23,7 +23,7 @@
[dependencies]
autocxx-engine = { version = "=0.26.0", path = "../../engine" }
-clap = { version = "3.1.2", features = ["cargo"] }
+clap3 = { package = "clap", version = "3.2.25", features = ["cargo"] }
proc-macro2 = "1.0"
env_logger = "0.9.0"
miette = { version = "5", features = ["fancy"] }
diff --git a/third_party/autocxx/integration-tests/src/lib.rs b/third_party/autocxx/integration-tests/src/lib.rs
index 2335ee5..02d672f 100644
--- a/third_party/autocxx/integration-tests/src/lib.rs
+++ b/third_party/autocxx/integration-tests/src/lib.rs
@@ -459,7 +459,7 @@
let generated_rs_files = build_results.1;
if let Some(code_checker) = &rust_code_checker {
- let mut file = File::open(generated_rs_files.get(0).ok_or(TestError::NoRs)?)
+ let mut file = File::open(generated_rs_files.first().ok_or(TestError::NoRs)?)
.map_err(TestError::RsFileOpen)?;
let mut content = String::new();
file.read_to_string(&mut content)
diff --git a/third_party/autocxx/integration-tests/tests/cpprefs_test.rs b/third_party/autocxx/integration-tests/tests/cpprefs_test.rs
index 9cc6d39..820e67a 100644
--- a/third_party/autocxx/integration-tests/tests/cpprefs_test.rs
+++ b/third_party/autocxx/integration-tests/tests/cpprefs_test.rs
@@ -52,6 +52,7 @@
indoc! {"
#include <string>
#include <sstream>
+ #include <cstdint>
class Goat {
public:
@@ -80,6 +81,7 @@
indoc! {"
#include <string>
#include <sstream>
+ #include <cstdint>
class Goat {
public:
diff --git a/third_party/autocxx/integration-tests/tests/integration_test.rs b/third_party/autocxx/integration-tests/tests/integration_test.rs
index ec2b1e7..0939cae 100644
--- a/third_party/autocxx/integration-tests/tests/integration_test.rs
+++ b/third_party/autocxx/integration-tests/tests/integration_test.rs
@@ -4490,6 +4490,7 @@
fn test_typedef_to_std() {
let hdr = indoc! {"
#include <string>
+ #include <cstdint>
typedef std::string my_string;
inline uint32_t take_str(my_string a) {
return a.size();
@@ -4523,6 +4524,7 @@
fn test_typedef_in_pod_struct() {
let hdr = indoc! {"
#include <string>
+ #include <cstdint>
typedef uint32_t my_int;
struct A {
my_int a;
@@ -4544,6 +4546,7 @@
fn test_cint_in_pod_struct() {
let hdr = indoc! {"
#include <string>
+ #include <cstdint>
struct A {
int a;
};
@@ -4613,6 +4616,7 @@
fn test_typedef_to_std_in_struct() {
let hdr = indoc! {"
#include <string>
+ #include <cstdint>
typedef std::string my_string;
struct A {
my_string a;
@@ -4998,6 +5002,43 @@
}
#[test]
+fn test_take_struct_built_array_in_function() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ struct data {
+ char a[4];
+ };
+ uint32_t take_array(char a[4]) {
+ return a[0] + a[2];
+ }
+ "};
+ let rs = quote! {
+ let mut c = ffi::data { a: [ 10, 20, 30, 40 ] };
+ unsafe {
+ assert_eq!(ffi::take_array(c.a.as_mut_ptr()), 40);
+ }
+ };
+ run_test("", hdr, rs, &["take_array"], &["data"]);
+}
+
+#[test]
+fn test_take_array_in_function() {
+ let hdr = indoc! {"
+ #include <cstdint>
+ uint32_t take_array(char a[4]) {
+ return a[0] + a[2];
+ }
+ "};
+ let rs = quote! {
+ let mut a: [i8; 4] = [ 10, 20, 30, 40 ];
+ unsafe {
+ assert_eq!(ffi::take_array(a.as_mut_ptr()), 40);
+ }
+ };
+ run_test("", hdr, rs, &["take_array"], &[]);
+}
+
+#[test]
fn test_union_ignored() {
let hdr = indoc! {"
#include <cstdint>
@@ -7772,6 +7813,58 @@
}
#[test]
+fn test_pv_subclass_opaque_param() {
+ let hdr = indoc! {"
+ #include <cstdint>
+
+ typedef uint32_t MyUnsupportedType[4];
+
+ struct MySupportedType {
+ uint32_t a;
+ };
+
+ class MySuperType {
+ public:
+ virtual void foo(const MyUnsupportedType* foo, const MySupportedType* bar) const = 0;
+ virtual ~MySuperType() = default;
+ };
+ "};
+ run_test_ex(
+ "",
+ hdr,
+ quote! {
+ MySubType::new_rust_owned(MySubType { a: 3, cpp_peer: Default::default() });
+ },
+ quote! {
+ subclass!("MySuperType",MySubType)
+ extern_cpp_opaque_type!("MyUnsupportedType", crate::ffi2::MyUnsupportedType)
+ },
+ None,
+ None,
+ Some(quote! {
+
+ #[cxx::bridge]
+ pub mod ffi2 {
+ unsafe extern "C++" {
+ include!("input.h");
+ type MyUnsupportedType;
+ }
+ }
+ use autocxx::subclass::CppSubclass;
+ use ffi::MySuperType_methods;
+ #[autocxx::subclass::subclass]
+ pub struct MySubType {
+ a: u32
+ }
+ impl MySuperType_methods for MySubType {
+ unsafe fn foo(&self, _foo: *const ffi2::MyUnsupportedType, _bar: *const ffi::MySupportedType) {
+ }
+ }
+ }),
+ );
+}
+
+#[test]
fn test_pv_subclass_return() {
let hdr = indoc! {"
#include <cstdint>
@@ -12241,6 +12334,58 @@
run_test("", hdr, rs, &["A"], &[]);
}
+#[test]
+fn test_badly_named_alloc() {
+ let hdr = indoc! {"
+ #include <stdarg.h>
+ class A {
+ public:
+ void alloc();
+ };
+ "};
+ let rs = quote! {};
+ run_test("", hdr, rs, &["A"], &[]);
+}
+
+#[test]
+fn test_cpp_union_pod() {
+ let hdr = indoc! {"
+ typedef unsigned long long UInt64_t;
+ struct ManagedPtr_t_;
+ typedef struct ManagedPtr_t_ ManagedPtr_t;
+
+ typedef int (*ManagedPtr_ManagerFunction_t)(
+ ManagedPtr_t *managedPtr,
+ const ManagedPtr_t *srcPtr,
+ int operation);
+
+ typedef union {
+ int intValue;
+ void *ptr;
+ } ManagedPtr_t_data_;
+
+ struct ManagedPtr_t_ {
+ void *pointer;
+ ManagedPtr_t_data_ userData[4];
+ ManagedPtr_ManagerFunction_t manager;
+ };
+
+ typedef struct CorrelationId_t_ {
+ unsigned int size : 8;
+ unsigned int valueType : 4;
+ unsigned int classId : 16;
+ unsigned int reserved : 4;
+
+ union {
+ UInt64_t intValue;
+ ManagedPtr_t ptrValue;
+ } value;
+ } CorrelationId_t;
+ "};
+ run_test("", hdr, quote! {}, &["CorrelationId_t_"], &[]);
+ run_test_expect_fail("", hdr, quote! {}, &[], &["CorrelationId_t_"]);
+}
+
// Yet to test:
// - Ifdef
// - Out param pointers
diff --git a/third_party/flatbuffers/build_defs.bzl b/third_party/flatbuffers/build_defs.bzl
index 5f6d71b..da26cec 100644
--- a/third_party/flatbuffers/build_defs.bzl
+++ b/third_party/flatbuffers/build_defs.bzl
@@ -96,7 +96,7 @@
mnemonic = "Flatc",
progress_message = "Generating flatbuffer files for %{input}:",
)
- return [DefaultInfo(files = depset(outs), runfiles = ctx.runfiles(files = outs)), FlatbufferLibraryInfo(srcs = ctx.files.srcs)]
+ return [DefaultInfo(files = depset(outs)), FlatbufferLibraryInfo(srcs = ctx.files.srcs)]
_flatbuffer_library_compile = rule(
implementation = _flatbuffer_library_compile_impl,
diff --git a/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h b/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h
index efa4d89..6f9d7c8 100644
--- a/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h
+++ b/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h
@@ -787,7 +787,7 @@
/// where the vector is stored.
template<typename T>
Offset<Vector<const T *>> CreateVectorOfStructs(const T *v, size_t len) {
- StartVector(len * sizeof(T) / AlignOf<T>(), sizeof(T), AlignOf<T>());
+ StartVector(len, sizeof(T), AlignOf<T>());
if (len > 0) {
PushBytes(reinterpret_cast<const uint8_t *>(v), sizeof(T) * len);
}
@@ -1211,7 +1211,7 @@
// Allocates space for a vector of structures.
// Must be completed with EndVectorOfStructs().
template<typename T> T *StartVectorOfStructs(size_t vector_size) {
- StartVector(vector_size * sizeof(T) / AlignOf<T>(), sizeof(T), AlignOf<T>());
+ StartVector(vector_size, sizeof(T), AlignOf<T>());
return reinterpret_cast<T *>(buf_.make_space(vector_size * sizeof(T)));
}
diff --git a/third_party/flatbuffers/include/flatbuffers/reflection.h b/third_party/flatbuffers/include/flatbuffers/reflection.h
index 1aa0863..84213c7 100644
--- a/third_party/flatbuffers/include/flatbuffers/reflection.h
+++ b/third_party/flatbuffers/include/flatbuffers/reflection.h
@@ -508,14 +508,19 @@
// root should point to the root type for this flatbuffer.
// buf should point to the start of flatbuffer data.
// length specifies the size of the flatbuffer data.
+// Returns true if the flatbuffer is valid. Returns false if either:
+// * The flatbuffer is incorrectly constructed (e.g., it points to memory
+// locations outside of the current memory buffer).
+// * The flatbuffer is too complex, and the flatbuffer verifier chosen to bail
+// when attempting to traverse the tree of tables.
bool Verify(const reflection::Schema &schema, const reflection::Object &root,
const uint8_t *buf, size_t length, uoffset_t max_depth = 64,
- uoffset_t max_tables = 1000000);
+ uoffset_t max_tables = 3000000);
bool VerifySizePrefixed(const reflection::Schema &schema,
const reflection::Object &root, const uint8_t *buf,
size_t length, uoffset_t max_depth = 64,
- uoffset_t max_tables = 1000000);
+ uoffset_t max_tables = 3000000);
} // namespace flatbuffers
diff --git a/third_party/flatbuffers/include/flatbuffers/reflection_generated.h b/third_party/flatbuffers/include/flatbuffers/reflection_generated.h
index 6a99e66..b340086 100644
--- a/third_party/flatbuffers/include/flatbuffers/reflection_generated.h
+++ b/third_party/flatbuffers/include/flatbuffers/reflection_generated.h
@@ -523,6 +523,7 @@
int64_t value = 0;
flatbuffers::unique_ptr<reflection::TypeT> union_type{};
std::vector<std::string> documentation{};
+ std::vector<flatbuffers::unique_ptr<reflection::KeyValueT>> attributes{};
EnumValT() = default;
EnumValT(const EnumValT &o);
EnumValT(EnumValT&&) FLATBUFFERS_NOEXCEPT = default;
@@ -603,6 +604,9 @@
const flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *attributes() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *>(VT_ATTRIBUTES);
}
+ flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *mutable_attributes() {
+ return GetPointer<flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *>(VT_ATTRIBUTES);
+ }
void clear_attributes() {
ClearField(VT_ATTRIBUTES);
}
@@ -2462,7 +2466,8 @@
(lhs.name == rhs.name) &&
(lhs.value == rhs.value) &&
((lhs.union_type == rhs.union_type) || (lhs.union_type && rhs.union_type && *lhs.union_type == *rhs.union_type)) &&
- (lhs.documentation == rhs.documentation);
+ (lhs.documentation == rhs.documentation) &&
+ (lhs.attributes.size() == rhs.attributes.size() && std::equal(lhs.attributes.cbegin(), lhs.attributes.cend(), rhs.attributes.cbegin(), [](flatbuffers::unique_ptr<reflection::KeyValueT> const &a, flatbuffers::unique_ptr<reflection::KeyValueT> const &b) { return (a == b) || (a && b && *a == *b); }));
}
inline bool operator!=(const EnumValT &lhs, const EnumValT &rhs) {
@@ -2475,6 +2480,8 @@
value(o.value),
union_type((o.union_type) ? new reflection::TypeT(*o.union_type) : nullptr),
documentation(o.documentation) {
+ attributes.reserve(o.attributes.size());
+ for (const auto &attributes_ : o.attributes) { attributes.emplace_back((attributes_) ? new reflection::KeyValueT(*attributes_) : nullptr); }
}
inline EnumValT &EnumValT::operator=(EnumValT o) FLATBUFFERS_NOEXCEPT {
@@ -2482,6 +2489,7 @@
std::swap(value, o.value);
std::swap(union_type, o.union_type);
std::swap(documentation, o.documentation);
+ std::swap(attributes, o.attributes);
return *this;
}
@@ -2498,6 +2506,7 @@
{ auto _e = value(); _o->value = _e; }
{ auto _e = union_type(); if (_e) { if(_o->union_type) { _e->UnPackTo(_o->union_type.get(), _resolver); } else { _o->union_type = flatbuffers::unique_ptr<reflection::TypeT>(_e->UnPack(_resolver)); } } else if (_o->union_type) { _o->union_type.reset(); } }
{ auto _e = documentation(); if (_e) { _o->documentation.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->documentation[_i] = _e->Get(_i)->str(); } } else { _o->documentation.resize(0); } }
+ { auto _e = attributes(); if (_e) { _o->attributes.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { if(_o->attributes[_i]) { _e->Get(_i)->UnPackTo(_o->attributes[_i].get(), _resolver); } else { _o->attributes[_i] = flatbuffers::unique_ptr<reflection::KeyValueT>(_e->Get(_i)->UnPack(_resolver)); }; } } else { _o->attributes.resize(0); } }
}
inline flatbuffers::Offset<EnumVal> EnumVal::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EnumValT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
@@ -2512,12 +2521,14 @@
auto _value = _o->value;
auto _union_type = _o->union_type ? CreateType(_fbb, _o->union_type.get(), _rehasher) : 0;
auto _documentation = _fbb.CreateVectorOfStrings(_o->documentation);
+ auto _attributes = _fbb.CreateVector<flatbuffers::Offset<reflection::KeyValue>> (_o->attributes.size(), [](size_t i, _VectorArgs *__va) { return CreateKeyValue(*__va->__fbb, __va->__o->attributes[i].get(), __va->__rehasher); }, &_va );
return reflection::CreateEnumVal(
_fbb,
_name,
_value,
_union_type,
- _documentation);
+ _documentation,
+ _attributes);
}
@@ -3211,21 +3222,24 @@
{ flatbuffers::ET_LONG, 0, -1 },
{ flatbuffers::ET_SEQUENCE, 0, 0 },
{ flatbuffers::ET_SEQUENCE, 0, 1 },
- { flatbuffers::ET_STRING, 1, -1 }
+ { flatbuffers::ET_STRING, 1, -1 },
+ { flatbuffers::ET_SEQUENCE, 1, 2 }
};
static const flatbuffers::TypeFunction type_refs[] = {
reflection::ObjectTypeTable,
- reflection::TypeTypeTable
+ reflection::TypeTypeTable,
+ reflection::KeyValueTypeTable
};
static const char * const names[] = {
"name",
"value",
"object",
"union_type",
- "documentation"
+ "documentation",
+ "attributes"
};
static const flatbuffers::TypeTable tt = {
- flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names
+ flatbuffers::ST_TABLE, 6, type_codes, type_refs, nullptr, nullptr, names
};
return &tt;
}
diff --git a/third_party/flatbuffers/rust/BUILD.bazel b/third_party/flatbuffers/rust/BUILD.bazel
index d2191dc..64a0dd8 100644
--- a/third_party/flatbuffers/rust/BUILD.bazel
+++ b/third_party/flatbuffers/rust/BUILD.bazel
@@ -9,6 +9,7 @@
version = "2.1.1",
visibility = ["//visibility:public"],
deps = [
+ "@crate_index//:arrayvec",
"@crate_index//:bitflags",
"@crate_index//:smallvec",
"@crate_index//:thiserror",
diff --git a/third_party/flatbuffers/rust/flatbuffers/Cargo.toml b/third_party/flatbuffers/rust/flatbuffers/Cargo.toml
index 2cba5b7..bacbcb3 100644
--- a/third_party/flatbuffers/rust/flatbuffers/Cargo.toml
+++ b/third_party/flatbuffers/rust/flatbuffers/Cargo.toml
@@ -19,6 +19,7 @@
[dependencies]
bitflags = "1.2.1"
serde = { version = "1.0", optional = true }
+arrayvec = "0.7.4"
[build-dependencies]
rustc_version = "0.4.0"
diff --git a/third_party/flatbuffers/rust/flatbuffers/src/builder.rs b/third_party/flatbuffers/rust/flatbuffers/src/builder.rs
index 7d0f408..d5c3e94 100644
--- a/third_party/flatbuffers/rust/flatbuffers/src/builder.rs
+++ b/third_party/flatbuffers/rust/flatbuffers/src/builder.rs
@@ -16,9 +16,13 @@
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
+use arrayvec::ArrayVec;
use core::cmp::max;
+use core::convert::Infallible;
+use core::fmt::{Debug, Display};
use core::iter::{DoubleEndedIterator, ExactSizeIterator};
use core::marker::PhantomData;
+use core::ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut, Sub, SubAssign};
use core::ptr::write_bytes;
use crate::endian_scalar::emplace_scalar;
@@ -30,6 +34,90 @@
use crate::vtable::{field_index_to_field_offset, VTable};
use crate::vtable_writer::VTableWriter;
+/// Trait to implement custom allocation strategies for [`FlatBufferBuilder`].
+///
+/// An implementation can be used with [`FlatBufferBuilder::new_in`], enabling a custom allocation
+/// strategy for the [`FlatBufferBuilder`].
+///
+/// # Safety
+///
+/// The implementation of the allocator must match the defined behavior as described by the
+/// comments.
+pub unsafe trait Allocator: DerefMut<Target = [u8]> {
+ /// A type describing allocation failures
+ type Error: Display + Debug;
+ /// Grows the buffer, with the old contents being moved to the end.
+ ///
+ /// NOTE: While not unsound, an implementation that doesn't grow the
+ /// internal buffer will get stuck in an infinite loop.
+ fn grow_downwards(&mut self) -> Result<(), Self::Error>;
+
+ /// Returns the size of the internal buffer in bytes.
+ fn len(&self) -> usize;
+}
+
+/// Default [`FlatBufferBuilder`] allocator backed by a [`Vec<u8>`].
+#[derive(Default)]
+pub struct DefaultAllocator(Vec<u8>);
+
+impl DefaultAllocator {
+ /// Builds the allocator from an existing buffer.
+ pub fn from_vec(buffer: Vec<u8>) -> Self {
+ Self(buffer)
+ }
+}
+
+impl Deref for DefaultAllocator {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for DefaultAllocator {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+// SAFETY: The methods are implemented as described by the documentation.
+unsafe impl Allocator for DefaultAllocator {
+ type Error = Infallible;
+ fn grow_downwards(&mut self) -> Result<(), Self::Error> {
+ let old_len = self.0.len();
+ let new_len = max(1, old_len * 2);
+
+ self.0.resize(new_len, 0);
+
+ if new_len == 1 {
+ return Ok(());
+ }
+
+ // calculate the midpoint, and safely copy the old end data to the new
+ // end position:
+ let middle = new_len / 2;
+ {
+ let (left, right) = &mut self.0[..].split_at_mut(middle);
+ right.copy_from_slice(left);
+ }
+ // finally, zero out the old end data.
+ {
+ let ptr = self.0[..middle].as_mut_ptr();
+ // Safety:
+ // ptr is byte aligned and of length middle
+ unsafe {
+ write_bytes(ptr, 0, middle);
+ }
+ }
+ Ok(())
+ }
+
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct FieldLoc {
off: UOffsetT,
@@ -40,24 +128,25 @@
/// state. It has an owned `Vec<u8>` that grows as needed (up to the hardcoded
/// limit of 2GiB, which is set by the FlatBuffers format).
#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct FlatBufferBuilder<'fbb> {
- owned_buf: Vec<u8>,
- head: usize,
+pub struct FlatBufferBuilder<'fbb, A: Allocator = DefaultAllocator> {
+ allocator: A,
+ head: ReverseIndex,
- field_locs: Vec<FieldLoc>,
- written_vtable_revpos: Vec<UOffsetT>,
+ // TODO(Adam): Make vectors generic.
+ field_locs: ArrayVec<FieldLoc, 100>,
+ written_vtable_revpos: ArrayVec<UOffsetT, 20>,
nested: bool,
finished: bool,
min_align: usize,
force_defaults: bool,
- strings_pool: Vec<WIPOffset<&'fbb str>>,
+ strings_pool: ArrayVec<WIPOffset<&'fbb str>, 20>,
_phantom: PhantomData<&'fbb ()>,
}
-impl<'fbb> FlatBufferBuilder<'fbb> {
+impl<'fbb> FlatBufferBuilder<'fbb, DefaultAllocator> {
/// Create a FlatBufferBuilder that is ready for writing.
pub fn new() -> Self {
Self::with_capacity(0)
@@ -77,30 +166,52 @@
/// an existing vector.
pub fn from_vec(buffer: Vec<u8>) -> Self {
// we need to check the size here because we create the backing buffer
- // directly, bypassing the typical way of using grow_owned_buf:
+ // directly, bypassing the typical way of using grow_allocator:
assert!(
buffer.len() <= FLATBUFFERS_MAX_BUFFER_SIZE,
"cannot initialize buffer bigger than 2 gigabytes"
);
- let head = buffer.len();
+ let allocator = DefaultAllocator::from_vec(buffer);
+ Self::new_in(allocator)
+ }
+
+ /// Destroy the FlatBufferBuilder, returning its internal byte vector
+ /// and the index into it that represents the start of valid data.
+ pub fn collapse(self) -> (Vec<u8>, usize) {
+ let index = self.head.to_forward_index(&self.allocator);
+ (self.allocator.0, index)
+ }
+}
+
+impl<'fbb, A: Allocator> FlatBufferBuilder<'fbb, A> {
+ /// Create a [`FlatBufferBuilder`] that is ready for writing with a custom [`Allocator`].
+ pub fn new_in(allocator: A) -> Self {
+ let head = ReverseIndex::end();
FlatBufferBuilder {
- owned_buf: buffer,
+ allocator,
head,
- field_locs: Vec::new(),
- written_vtable_revpos: Vec::new(),
+ field_locs: ArrayVec::new(),
+ written_vtable_revpos: ArrayVec::new(),
nested: false,
finished: false,
min_align: 0,
force_defaults: false,
- strings_pool: Vec::new(),
+ strings_pool: ArrayVec::new(),
_phantom: PhantomData,
}
}
+ /// Destroy the [`FlatBufferBuilder`], returning its [`Allocator`] and the index
+ /// into it that represents the start of valid data.
+ pub fn collapse_in(self) -> (A, usize) {
+ let index = self.head.to_forward_index(&self.allocator);
+ (self.allocator, index)
+ }
+
/// Reset the FlatBufferBuilder internal state. Use this method after a
/// call to a `finish` function in order to re-use a FlatBufferBuilder.
///
@@ -114,17 +225,11 @@
/// new object.
pub fn reset(&mut self) {
// memset only the part of the buffer that could be dirty:
- {
- let to_clear = self.owned_buf.len() - self.head;
- let ptr = self.owned_buf[self.head..].as_mut_ptr();
- // Safety:
- // Verified ptr is valid for `to_clear` above
- unsafe {
- write_bytes(ptr, 0, to_clear);
- }
- }
+ self.allocator[self.head.range_to_end()]
+ .iter_mut()
+ .for_each(|x| *x = 0);
- self.head = self.owned_buf.len();
+ self.head = ReverseIndex::end();
self.written_vtable_revpos.clear();
self.nested = false;
@@ -134,12 +239,6 @@
self.strings_pool.clear();
}
- /// Destroy the FlatBufferBuilder, returning its internal byte vector
- /// and the index into it that represents the start of valid data.
- pub fn collapse(self) -> (Vec<u8>, usize) {
- (self.owned_buf, self.head)
- }
-
/// Push a Push'able value onto the front of the in-progress data.
///
/// This function uses traits to provide a unified API for writing
@@ -150,7 +249,7 @@
self.align(sz, P::alignment());
self.make_space(sz);
{
- let (dst, rest) = self.owned_buf[self.head..].split_at_mut(sz);
+ let (dst, rest) = self.allocator[self.head.range_to_end()].split_at_mut(sz);
// Safety:
// Called make_space above
unsafe { x.push(dst, rest.len()) };
@@ -254,9 +353,9 @@
"create_shared_string can not be called when a table or vector is under construction",
);
- // Saves a ref to owned_buf since rust doesnt like us refrencing it
+ // Saves a ref to allocator since rust doesnt like us refrencing it
// in the binary_search_by code.
- let buf = &self.owned_buf;
+ let buf = &self.allocator;
let found = self.strings_pool.binary_search_by(|offset| {
let ptr = offset.value() as usize;
@@ -324,9 +423,9 @@
self.ensure_capacity(slice_size + UOffsetT::size());
self.head -= slice_size;
- let mut written_len = self.owned_buf.len() - self.head;
+ let mut written_len = self.head.distance_to_end();
- let buf = &mut self.owned_buf[self.head..self.head + slice_size];
+ let buf = &mut self.allocator[self.head.range_to(self.head + slice_size)];
for (item, out) in items.iter().zip(buf.chunks_exact_mut(elem_size)) {
written_len -= elem_size;
@@ -373,7 +472,7 @@
/// whether it has been finished.
#[inline]
pub fn unfinished_data(&self) -> &[u8] {
- &self.owned_buf[self.head..]
+ &self.allocator[self.head.range_to_end()]
}
/// Get the byte slice for the data that has been written after a call to
/// one of the `finish` functions.
@@ -382,7 +481,7 @@
#[inline]
pub fn finished_data(&self) -> &[u8] {
self.assert_finished("finished_bytes cannot be called when the buffer is not yet finished");
- &self.owned_buf[self.head..]
+ &self.allocator[self.head.range_to_end()]
}
/// Returns a mutable view of a finished buffer and location of where the flatbuffer starts.
/// Note that modifying the flatbuffer data may corrupt it.
@@ -390,7 +489,8 @@
/// Panics if the flatbuffer is not finished.
#[inline]
pub fn mut_finished_buffer(&mut self) -> (&mut [u8], usize) {
- (&mut self.owned_buf, self.head)
+ let index = self.head.to_forward_index(&self.allocator);
+ (&mut self.allocator[..], index)
}
/// Assert that a field is present in the just-finished Table.
///
@@ -405,13 +505,13 @@
let idx = self.used_space() - tab_revloc.value() as usize;
// Safety:
- // The value of TableFinishedWIPOffset is the offset from the end of owned_buf
+ // The value of TableFinishedWIPOffset is the offset from the end of the allocator
// to an SOffsetT pointing to a valid VTable
//
- // `self.owned_buf.len() = self.used_space() + self.head`
- // `self.owned_buf.len() - tab_revloc = self.used_space() - tab_revloc + self.head`
- // `self.owned_buf.len() - tab_revloc = idx + self.head`
- let tab = unsafe { Table::new(&self.owned_buf[self.head..], idx) };
+ // `self.allocator.len() = self.used_space() + self.head`
+ // `self.allocator.len() - tab_revloc = self.used_space() - tab_revloc + self.head`
+ // `self.allocator.len() - tab_revloc = idx + self.head`
+ let tab = unsafe { Table::new(&self.allocator[self.head.range_to_end()], idx) };
let o = tab.vtable().get(slot_byte_loc) as usize;
assert!(o != 0, "missing required field {}", assert_msg_name);
}
@@ -444,7 +544,7 @@
#[inline]
fn used_space(&self) -> usize {
- self.owned_buf.len() - self.head as usize
+ self.head.distance_to_end()
}
#[inline]
@@ -517,7 +617,8 @@
let vt_end_pos = self.head + vtable_byte_len;
{
// write the vtable header:
- let vtfw = &mut VTableWriter::init(&mut self.owned_buf[vt_start_pos..vt_end_pos]);
+ let vtfw =
+ &mut VTableWriter::init(&mut self.allocator[vt_start_pos.range_to(vt_end_pos)]);
vtfw.write_vtable_byte_length(vtable_byte_len as VOffsetT);
vtfw.write_object_inline_size(table_object_size as VOffsetT);
@@ -527,20 +628,20 @@
vtfw.write_field_offset(fl.id, pos);
}
}
- let new_vt_bytes = &self.owned_buf[vt_start_pos..vt_end_pos];
+ let new_vt_bytes = &self.allocator[vt_start_pos.range_to(vt_end_pos)];
let found = self
.written_vtable_revpos
.binary_search_by(|old_vtable_revpos: &UOffsetT| {
- let old_vtable_pos = self.owned_buf.len() - *old_vtable_revpos as usize;
+ let old_vtable_pos = self.allocator.len() - *old_vtable_revpos as usize;
// Safety:
// Already written vtables are valid by construction
- let old_vtable = unsafe { VTable::init(&self.owned_buf, old_vtable_pos) };
+ let old_vtable = unsafe { VTable::init(&self.allocator, old_vtable_pos) };
new_vt_bytes.cmp(old_vtable.as_bytes())
});
let final_vtable_revpos = match found {
Ok(i) => {
// The new vtable is a duplicate so clear it.
- VTableWriter::init(&mut self.owned_buf[vt_start_pos..vt_end_pos]).clear();
+ VTableWriter::init(&mut self.allocator[vt_start_pos.range_to(vt_end_pos)]).clear();
self.head += vtable_byte_len;
self.written_vtable_revpos[i]
}
@@ -552,17 +653,17 @@
}
};
// Write signed offset from table to its vtable.
- let table_pos = self.owned_buf.len() - object_revloc_to_vtable.value() as usize;
+ let table_pos = self.allocator.len() - object_revloc_to_vtable.value() as usize;
if cfg!(debug_assertions) {
// Safety:
// Verified slice length
let tmp_soffset_to_vt = unsafe {
- read_scalar::<UOffsetT>(&self.owned_buf[table_pos..table_pos + SIZE_UOFFSET])
+ read_scalar::<UOffsetT>(&self.allocator[table_pos..table_pos + SIZE_UOFFSET])
};
assert_eq!(tmp_soffset_to_vt, 0xF0F0_F0F0);
}
- let buf = &mut self.owned_buf[table_pos..table_pos + SIZE_SOFFSET];
+ let buf = &mut self.allocator[table_pos..table_pos + SIZE_SOFFSET];
// Safety:
// Verified length of buf above
unsafe {
@@ -579,39 +680,14 @@
// Only call this when you know it is safe to double the size of the buffer.
#[inline]
- fn grow_owned_buf(&mut self) {
- let old_len = self.owned_buf.len();
- let new_len = max(1, old_len * 2);
-
+ fn grow_allocator(&mut self) {
let starting_active_size = self.used_space();
-
- let diff = new_len - old_len;
- self.owned_buf.resize(new_len, 0);
- self.head += diff;
+ self.allocator
+ .grow_downwards()
+ .expect("Flatbuffer allocation failure");
let ending_active_size = self.used_space();
debug_assert_eq!(starting_active_size, ending_active_size);
-
- if new_len == 1 {
- return;
- }
-
- // calculate the midpoint, and safely copy the old end data to the new
- // end position:
- let middle = new_len / 2;
- {
- let (left, right) = &mut self.owned_buf[..].split_at_mut(middle);
- right.copy_from_slice(left);
- }
- // finally, zero out the old end data.
- {
- let ptr = self.owned_buf[..middle].as_mut_ptr();
- // Safety:
- // ptr is byte aligned and of length middle
- unsafe {
- write_bytes(ptr, 0, middle);
- }
- }
}
// with or without a size prefix changes how we load the data, so finish*
@@ -676,13 +752,13 @@
#[inline]
fn push_bytes_unprefixed(&mut self, x: &[u8]) -> UOffsetT {
let n = self.make_space(x.len());
- self.owned_buf[n..n + x.len()].copy_from_slice(x);
+ self.allocator[n.range_to(n + x.len())].copy_from_slice(x);
- n as UOffsetT
+ n.to_forward_index(&self.allocator) as UOffsetT
}
#[inline]
- fn make_space(&mut self, want: usize) -> usize {
+ fn make_space(&mut self, want: usize) -> ReverseIndex {
self.ensure_capacity(want);
self.head -= want;
self.head
@@ -699,13 +775,13 @@
);
while self.unused_ready_space() < want {
- self.grow_owned_buf();
+ self.grow_allocator();
}
want
}
#[inline]
fn unused_ready_space(&self) -> usize {
- self.head
+ self.allocator.len() - self.head.distance_to_end()
}
#[inline]
fn assert_nested(&self, fn_name: &'static str) {
@@ -754,3 +830,127 @@
Self::with_capacity(0)
}
}
+
+/// An index that indexes from the reverse of a slice.
+///
+/// Note that while the internal representation is an index
+/// from the end of a buffer, operations like `Add` and `Sub`
+/// behave like a regular index:
+///
+/// # Examples
+///
+/// ```ignore
+/// let buf = [0, 1, 2, 3, 4, 5];
+/// let idx = ReverseIndex::end() - 2;
+/// assert_eq!(&buf[idx.range_to_end()], &[4, 5]);
+/// assert_eq!(idx.to_forward_index(&buf), 4);
+/// ```
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+struct ReverseIndex(usize);
+
+impl ReverseIndex {
+ /// Returns an index set to the end.
+ ///
+ /// Note: Indexing this will result in an out of bounds error.
+ pub fn end() -> Self {
+ Self(0)
+ }
+
+ /// Returns a struct equivalent to the range `self..`
+ pub fn range_to_end(self) -> ReverseIndexRange {
+ ReverseIndexRange(self, ReverseIndex::end())
+ }
+
+ /// Returns a struct equivalent to the range `self..end`
+ pub fn range_to(self, end: ReverseIndex) -> ReverseIndexRange {
+ ReverseIndexRange(self, end)
+ }
+
+ /// Transforms this reverse index into a regular index for the given buffer.
+ pub fn to_forward_index<T>(self, buf: &[T]) -> usize {
+ buf.len() - self.0
+ }
+
+ /// Returns the number of elements until the end of the range.
+ pub fn distance_to_end(&self) -> usize {
+ self.0
+ }
+}
+
+impl Sub<usize> for ReverseIndex {
+ type Output = Self;
+
+ fn sub(self, rhs: usize) -> Self::Output {
+ Self(self.0 + rhs)
+ }
+}
+
+impl SubAssign<usize> for ReverseIndex {
+ fn sub_assign(&mut self, rhs: usize) {
+ *self = *self - rhs;
+ }
+}
+
+impl Add<usize> for ReverseIndex {
+ type Output = Self;
+
+ fn add(self, rhs: usize) -> Self::Output {
+ Self(self.0 - rhs)
+ }
+}
+
+impl AddAssign<usize> for ReverseIndex {
+ fn add_assign(&mut self, rhs: usize) {
+ *self = *self + rhs;
+ }
+}
+impl<T> Index<ReverseIndex> for [T] {
+ type Output = T;
+
+ fn index(&self, index: ReverseIndex) -> &Self::Output {
+ let index = index.to_forward_index(self);
+ &self[index]
+ }
+}
+
+impl<T> IndexMut<ReverseIndex> for [T] {
+ fn index_mut(&mut self, index: ReverseIndex) -> &mut Self::Output {
+ let index = index.to_forward_index(self);
+ &mut self[index]
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+struct ReverseIndexRange(ReverseIndex, ReverseIndex);
+
+impl<T> Index<ReverseIndexRange> for [T] {
+ type Output = [T];
+
+ fn index(&self, index: ReverseIndexRange) -> &Self::Output {
+ let start = index.0.to_forward_index(self);
+ let end = index.1.to_forward_index(self);
+ &self[start..end]
+ }
+}
+
+impl<T> IndexMut<ReverseIndexRange> for [T] {
+ fn index_mut(&mut self, index: ReverseIndexRange) -> &mut Self::Output {
+ let start = index.0.to_forward_index(self);
+ let end = index.1.to_forward_index(self);
+ &mut self[start..end]
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn reverse_index_test() {
+ let buf = [0, 1, 2, 3, 4, 5];
+ let idx = ReverseIndex::end() - 2;
+ assert_eq!(&buf[idx.range_to_end()], &[4, 5]);
+ assert_eq!(&buf[idx.range_to(idx + 1)], &[4]);
+ assert_eq!(idx.to_forward_index(&buf), 4);
+ }
+}
diff --git a/third_party/flatbuffers/rust/flatbuffers/src/lib.rs b/third_party/flatbuffers/rust/flatbuffers/src/lib.rs
index 2741811..324dc1a 100644
--- a/third_party/flatbuffers/rust/flatbuffers/src/lib.rs
+++ b/third_party/flatbuffers/rust/flatbuffers/src/lib.rs
@@ -48,7 +48,7 @@
mod vtable_writer;
pub use crate::array::{array_init, emplace_scalar_array, Array};
-pub use crate::builder::FlatBufferBuilder;
+pub use crate::builder::{Allocator, DefaultAllocator, FlatBufferBuilder};
pub use crate::endian_scalar::{emplace_scalar, read_scalar, read_scalar_at, EndianScalar};
pub use crate::follow::{Follow, FollowStart, FollowWith};
pub use crate::primitives::*;
diff --git a/third_party/flatbuffers/src/idl_gen_rust.cpp b/third_party/flatbuffers/src/idl_gen_rust.cpp
index 43237b2..824f33e 100644
--- a/third_party/flatbuffers/src/idl_gen_rust.cpp
+++ b/third_party/flatbuffers/src/idl_gen_rust.cpp
@@ -975,7 +975,8 @@
code_ += " }";
// Pack flatbuffers union value
code_ +=
- " pub fn pack(&self, fbb: &mut flatbuffers::FlatBufferBuilder)"
+ " pub fn pack<'b, A: flatbuffers::Allocator + 'b>(&self, fbb: &mut "
+ "flatbuffers::FlatBufferBuilder<'b, A>)"
" -> Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>"
" {";
code_ += " match self {";
@@ -1704,8 +1705,11 @@
code_.SetValue("MAYBE_LT",
TableBuilderArgsNeedsLifetime(struct_def) ? "<'args>" : "");
code_ += " #[allow(unused_mut)]";
- code_ += " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(";
- code_ += " _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,";
+ code_ +=
+ " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: "
+ "flatbuffers::Allocator + 'bldr>(";
+ code_ +=
+ " _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,";
code_ += " {{MAYBE_US}}args: &'args {{STRUCT_TY}}Args{{MAYBE_LT}}";
code_ += " ) -> flatbuffers::WIPOffset<{{STRUCT_TY}}<'bldr>> {";
@@ -2097,15 +2101,20 @@
}
// Generate a builder struct:
- code_ += "{{ACCESS_TYPE}} struct {{STRUCT_TY}}Builder<'a: 'b, 'b> {";
- code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ +=
+ "{{ACCESS_TYPE}} struct {{STRUCT_TY}}Builder<'a: 'b, 'b, A: "
+ "flatbuffers::Allocator + 'a> {";
+ code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,";
code_ +=
" start_: flatbuffers::WIPOffset<"
"flatbuffers::TableUnfinishedWIPOffset>,";
code_ += "}";
// Generate builder functions:
- code_ += "impl<'a: 'b, 'b> {{STRUCT_TY}}Builder<'a, 'b> {";
+ code_ +=
+ "impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> "
+ "{{STRUCT_TY}}Builder<'a, "
+ "'b, A> {";
ForAllTableFields(struct_def, [&](const FieldDef &field) {
const bool is_scalar = IsScalar(field.value.type.base_type);
std::string offset = namer_.LegacyRustFieldOffsetName(field);
@@ -2140,8 +2149,8 @@
// Struct initializer (all fields required);
code_ += " #[inline]";
code_ +=
- " pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> "
- "{{STRUCT_TY}}Builder<'a, 'b> {";
+ " pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> "
+ "{{STRUCT_TY}}Builder<'a, 'b, A> {";
code_.SetValue("NUM_FIELDS", NumToString(struct_def.fields.vec.size()));
code_ += " let start = _fbb.start_table();";
code_ += " {{STRUCT_TY}}Builder {";
@@ -2244,9 +2253,9 @@
// Generate pack function.
code_ += "impl {{STRUCT_OTY}} {";
- code_ += " pub fn pack<'b>(";
+ code_ += " pub fn pack<'b, A: flatbuffers::Allocator + 'b>(";
code_ += " &self,";
- code_ += " _fbb: &mut flatbuffers::FlatBufferBuilder<'b>";
+ code_ += " _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>";
code_ += " ) -> flatbuffers::WIPOffset<{{STRUCT_TY}}<'b>> {";
// First we generate variables for each field and then later assemble them
// using "StructArgs" to more easily manage ownership of the builder.
@@ -2529,8 +2538,10 @@
// Finish a buffer with a given root object:
code_ += "#[inline]";
- code_ += "pub fn finish_{{STRUCT_FN}}_buffer<'a, 'b>(";
- code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ +=
+ "pub fn finish_{{STRUCT_FN}}_buffer<'a, 'b, A: "
+ "flatbuffers::Allocator + 'a>(";
+ code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,";
code_ += " root: flatbuffers::WIPOffset<{{STRUCT_TY}}<'a>>) {";
if (parser_.file_identifier_.length()) {
code_ += " fbb.finish(root, Some({{STRUCT_CONST}}_IDENTIFIER));";
@@ -2542,8 +2553,8 @@
code_ += "#[inline]";
code_ +=
"pub fn finish_size_prefixed_{{STRUCT_FN}}_buffer"
- "<'a, 'b>("
- "fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, "
+ "<'a, 'b, A: flatbuffers::Allocator + 'a>("
+ "fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, "
"root: flatbuffers::WIPOffset<{{STRUCT_TY}}<'a>>) {";
if (parser_.file_identifier_.length()) {
code_ +=
diff --git a/third_party/flatbuffers/typescript.bzl b/third_party/flatbuffers/typescript.bzl
index f00c550..ccfd2d6 100644
--- a/third_party/flatbuffers/typescript.bzl
+++ b/third_party/flatbuffers/typescript.bzl
@@ -93,7 +93,7 @@
compatible_with = compatible_with,
restricted_to = restricted_to,
target_compatible_with = target_compatible_with,
- supports_workers = False,
+ supports_workers = 0,
tsconfig = {
"compilerOptions": {
"declaration": True,
diff --git a/third_party/rules_rollup/0001-Fix-resolving-files.patch b/third_party/rules_rollup/0001-Fix-resolving-files.patch
index a81a656..a57c4bc 100644
--- a/third_party/rules_rollup/0001-Fix-resolving-files.patch
+++ b/third_party/rules_rollup/0001-Fix-resolving-files.patch
@@ -1,19 +1,19 @@
-From fd6dd080ea58fd71c70ce2303873feab1abda760 Mon Sep 17 00:00:00 2001
+From 5c0b6653d0fa500b5ad3a65f8e58c97517f2d6bd Mon Sep 17 00:00:00 2001
From: Philipp Schrader <philipp.schrader@gmail.com>
-Date: Sun, 19 Feb 2023 14:18:11 -0800
+Date: Wed, 21 Feb 2024 17:26:27 -0800
Subject: [PATCH] Fix resolving files
I don't really know what the underlying problem is, but returning a
File instead of a path is causing us grief.
---
- rollup/private/rollup_bundle.bzl | 2 +-
+ rollup/private/rollup.bzl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
-diff --git a/rollup/private/rollup_bundle.bzl b/rollup/private/rollup_bundle.bzl
-index 32aaad4..a2061dd 100644
---- a/rollup/private/rollup_bundle.bzl
-+++ b/rollup/private/rollup_bundle.bzl
-@@ -186,7 +186,7 @@ def _resolve_js_input(f, inputs):
+diff --git a/rollup/private/rollup.bzl b/rollup/private/rollup.bzl
+index c1634cf..8cd5407 100644
+--- a/rollup/private/rollup.bzl
++++ b/rollup/private/rollup.bzl
+@@ -96,7 +96,7 @@ def _resolve_js_input(f, inputs):
for i in inputs:
if i.extension == "js" or i.extension == "mjs":
if _no_ext(i) == no_ext:
diff --git a/tools/build_rules/autocxx.bzl b/tools/build_rules/autocxx.bzl
index 6cb74de..5cadf2b 100644
--- a/tools/build_rules/autocxx.bzl
+++ b/tools/build_rules/autocxx.bzl
@@ -1,4 +1,4 @@
-load("@rules_rust//rust:defs.bzl", "rust_library")
+load("@org_frc971//tools/rust:defs.bzl", "rust_library")
load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
@@ -241,7 +241,7 @@
def autocxx_library(
name,
visibility = None,
- target_compatible_with = None,
+ target_compatible_with = ["//tools/platforms/rust:has_support"],
libs = [],
srcs = [],
cxxbridge_srcs = [],
@@ -251,7 +251,8 @@
testonly = None,
crate_features = None,
crate_name = None,
- gen_debug = None):
+ gen_debug = None,
+ **kwargs):
"""A macro to generate Rust <-> C++ interop code with autocxx.
Creates the following rules:
@@ -343,4 +344,5 @@
],
compile_data = [gen_compile_data_name],
rustc_env_files = [gen_env_files_name],
+ **kwargs
)
diff --git a/tools/build_rules/js.bzl b/tools/build_rules/js.bzl
index 4b11ea7..51fe987 100644
--- a/tools/build_rules/js.bzl
+++ b/tools/build_rules/js.bzl
@@ -8,8 +8,8 @@
load("@npm//:html-insert-assets/package_json.bzl", html_insert_assets_bin = "bin")
load("//tools/build_rules/js:ng.bzl", "ng_esbuild", "ng_project")
load("//tools/build_rules/js:ts.bzl", _ts_project = "ts_project")
-load("@aspect_rules_rollup//rollup:defs.bzl", upstream_rollup_bundle = "rollup_bundle")
-load("@aspect_rules_terser//terser:defs.bzl", "terser_minified")
+load("@aspect_rules_rollup//rollup:defs.bzl", upstream_rollup_bundle = "rollup")
+load("@aspect_rules_terser//terser:defs.bzl", terser_minified = "terser")
load("@aspect_rules_cypress//cypress:defs.bzl", "cypress_module_test")
ts_project = _ts_project
@@ -283,7 +283,7 @@
**kwargs
)
-def rollup_bundle(name, entry_point, deps = [], visibility = None, **kwargs):
+def rollup_bundle(name, entry_point, node_modules = "//:node_modules", deps = [], visibility = None, **kwargs):
"""Calls the upstream rollup_bundle() and exposes a .min.js file.
Legacy version of rollup_bundle() used to provide the .min.js file. This
@@ -301,6 +301,7 @@
deps = deps + [
"//:node_modules/@rollup/plugin-node-resolve",
],
+ node_modules = node_modules,
sourcemap = "false",
config_file = ":%s__rollup_config.js" % name,
entry_point = entry_point,
@@ -310,6 +311,7 @@
terser_minified(
name = name + "__min",
srcs = [name + ".js"],
+ node_modules = node_modules,
tags = [
"no-remote-cache",
],
@@ -354,7 +356,7 @@
},
)
-def cypress_test(runner, data = None, **kwargs):
+def cypress_test(name, runner, data = None, **kwargs):
"""Runs a cypress test with the specified runner.
Args:
@@ -373,16 +375,24 @@
# Chrome is located at the runfiles root. So we need to go up one more
# directory than the workspace root.
chrome_location = "../" * (package_depth + 1) + "chrome_linux/chrome"
- config_location = "../" * package_depth + "tools/build_rules/js/cypress.config.js"
+
+ copy_file(
+ name = name + "_config",
+ out = "cypress.config.js",
+ src = "//tools/build_rules/js:cypress.config.js",
+ visibility = ["//visibility:private"],
+ )
data = data or []
- data.append("//tools/build_rules/js:cypress.config.js")
+ data.append(":%s_config" % name)
data.append("@xvfb_amd64//:wrapped_bin/Xvfb")
+ data.append("//:node_modules")
cypress_module_test(
+ name = name,
args = [
"run",
- "--config-file=" + config_location,
+ "--config-file=cypress.config.js",
"--browser=" + chrome_location,
],
browsers = ["@chrome_linux//:all"],
diff --git a/tools/build_rules/js/cypress.config.js b/tools/build_rules/js/cypress.config.js
index c8d6988..e991016 100644
--- a/tools/build_rules/js/cypress.config.js
+++ b/tools/build_rules/js/cypress.config.js
@@ -1,6 +1,6 @@
-const {defineConfig} = require('cypress');
+import {defineConfig} from 'cypress';
-module.exports = defineConfig({
+export default defineConfig({
e2e: {
specPattern: ['*.cy.js'],
supportFile: false,
diff --git a/tools/build_rules/js/ts.bzl b/tools/build_rules/js/ts.bzl
index a5b0770..01b8bfe 100644
--- a/tools/build_rules/js/ts.bzl
+++ b/tools/build_rules/js/ts.bzl
@@ -15,6 +15,6 @@
# TODO(phil): Is this a good idea? I don't _actually_ know what this
# does.
- supports_workers = False,
+ supports_workers = 0,
**kwargs
)
diff --git a/tools/dependency_rewrite b/tools/dependency_rewrite
index 2d2e610..1cd117a 100644
--- a/tools/dependency_rewrite
+++ b/tools/dependency_rewrite
@@ -17,6 +17,7 @@
rewrite downloads.sourceforge.net/(.*) software.frc971.org/Build-Dependencies/downloads.sourceforge.net/$1
rewrite cdn.cypress.io/(.*) software.frc971.org/Build-Dependencies/cdn.cypress.io/$1
rewrite www.googleapis.com/(.*) software.frc971.org/Build-Dependencies/www.googleapis.com/$1
+rewrite www.johnvansickle.com/(.*) software.frc971.org/Build-Dependencies/www.johnvansickle.com/$1
allow crates.io
allow golang.org
allow go.dev
diff --git a/tools/rust/defs.bzl b/tools/rust/defs.bzl
new file mode 100644
index 0000000..88caca5
--- /dev/null
+++ b/tools/rust/defs.bzl
@@ -0,0 +1,106 @@
+load(
+ "@rules_rust//rust:defs.bzl",
+ _rust_binary = "rust_binary",
+ _rust_doc = "rust_doc",
+ _rust_doc_test = "rust_doc_test",
+ _rust_library = "rust_library",
+ _rust_test = "rust_test",
+)
+load("@com_github_google_flatbuffers//:build_defs.bzl", _flatbuffer_rust_library = "flatbuffer_rust_library")
+
+def rust_doc_test(target_compatible_with = ["//tools/platforms/rust:has_support"], tags = [], **kwargs):
+ # TODO(james): Attempting to execute this remotely results
+ # in complaints about overly large files.
+ _rust_doc_test(
+ tags = tags + ["no-remote-exec"],
+ target_compatible_with = target_compatible_with,
+ **kwargs
+ )
+
+def rust_doc(target_compatible_with = ["//tools/platforms/rust:has_support"], rustdoc_flags = ["-Dwarnings"], **kwargs):
+ _rust_doc(
+ target_compatible_with = target_compatible_with,
+ rustdoc_flags = rustdoc_flags,
+ **kwargs
+ )
+
+def rust_binary(target_compatible_with = ["//tools/platforms/rust:has_support"], rustc_flags = [], **kwargs):
+ _rust_binary(
+ target_compatible_with = select({
+ Label("//conditions:default"): target_compatible_with,
+ Label("//tools:has_msan"): ["@platforms//:incompatible"],
+ }),
+ # TODO: Make Rust play happy with pic vs nopic. Details at:
+ # https://github.com/bazelbuild/rules_rust/issues/118
+ rustc_flags = rustc_flags + ["-Crelocation-model=static"],
+ **kwargs
+ )
+
+def rust_test(target_compatible_with = ["//tools/platforms/rust:has_support"], rustc_flags = [], **kwargs):
+ _rust_test(
+ target_compatible_with = select({
+ Label("//conditions:default"): target_compatible_with,
+ Label("//tools:has_msan"): ["@platforms//:incompatible"],
+ }),
+ rustc_flags = rustc_flags + ["-Crelocation-model=static"],
+ **kwargs
+ )
+
+def rust_library(
+ name,
+ target_compatible_with = ["//tools/platforms/rust:has_support"],
+ gen_docs = True,
+ gen_tests = True,
+ gen_doctests = True,
+ **kwargs):
+ test_params = {}
+ doctest_params = {}
+ params = {}
+
+ for (param, value) in kwargs.items():
+ if param.startswith("test_"):
+ test_params[param[5:]] = value
+ elif param.startswith("doctest_"):
+ doctest_params[param[8:]] = value
+ else:
+ params[param] = value
+
+ _rust_library(
+ name = name,
+ target_compatible_with = select({
+ Label("//conditions:default"): target_compatible_with,
+ Label("//tools:has_msan"): ["@platforms//:incompatible"],
+ }),
+ **params
+ )
+
+ if gen_tests:
+ rust_test(
+ name = name + "_test",
+ crate = name,
+ **test_params
+ )
+
+ if gen_docs:
+ rust_doc(
+ name = name + "_doc",
+ crate = name,
+ target_compatible_with = ["//tools/platforms/rust:has_support"],
+ rustdoc_flags = ["--document-private-items", "-Dwarnings"],
+ )
+
+ if gen_doctests:
+ rust_doc_test(
+ name = name + "_doctest",
+ crate = name,
+ **doctest_params
+ )
+
+def flatbuffer_rust_library(target_compatible_with = ["//tools/platforms/rust:has_support"], **kwargs):
+ _flatbuffer_rust_library(
+ target_compatible_with = select({
+ Label("//conditions:default"): target_compatible_with,
+ Label("//tools:has_msan"): ["@platforms//:incompatible"],
+ }),
+ **kwargs
+ )
diff --git a/y2016/control_loops/superstructure/superstructure_lib_test.cc b/y2016/control_loops/superstructure/superstructure_lib_test.cc
index ed2f210..eeea9b5 100644
--- a/y2016/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2016/control_loops/superstructure/superstructure_lib_test.cc
@@ -208,26 +208,26 @@
if (superstructure_status_fetcher_->state() == Superstructure::RUNNING ||
superstructure_status_fetcher_->state() ==
Superstructure::LANDING_RUNNING) {
- AOS_CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_intake()),
- Superstructure::kOperatingVoltage);
- AOS_CHECK_LE(
- ::std::abs(superstructure_output_fetcher_->voltage_shoulder()),
- Superstructure::kOperatingVoltage);
- AOS_CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_wrist()),
- Superstructure::kOperatingVoltage);
+ CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_intake()),
+ Superstructure::kOperatingVoltage);
+ CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_shoulder()),
+ Superstructure::kOperatingVoltage);
+ CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_wrist()),
+ Superstructure::kOperatingVoltage);
} else {
- AOS_CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_intake()),
- Superstructure::kZeroingVoltage);
- AOS_CHECK_LE(
- ::std::abs(superstructure_output_fetcher_->voltage_shoulder()),
- Superstructure::kZeroingVoltage);
- AOS_CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_wrist()),
- Superstructure::kZeroingVoltage);
+ CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_intake()),
+ Superstructure::kZeroingVoltage);
+ CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_shoulder()),
+ Superstructure::kZeroingVoltage);
+ CHECK_LE(::std::abs(superstructure_output_fetcher_->voltage_wrist()),
+ Superstructure::kZeroingVoltage);
}
if (arm_plant_->X(0, 0) <=
- Superstructure::kShoulderTransitionToLanded + 1e-4) {
- AOS_CHECK_GE(superstructure_output_fetcher_->voltage_shoulder(),
- Superstructure::kLandingShoulderDownVoltage - 0.00001);
+ Superstructure::kShoulderTransitionToLanded - 1e-4) {
+ CHECK_GE(superstructure_output_fetcher_->voltage_shoulder(),
+ Superstructure::kLandingShoulderDownVoltage - 0.00001)
+ << ": Arm at " << arm_plant_->X(0, 0) << " and transition at "
+ << Superstructure::kShoulderTransitionToLanded;
}
// Use the plant to generate the next physical state given the voltages to
diff --git a/y2019/control_loops/drivetrain/BUILD b/y2019/control_loops/drivetrain/BUILD
index 65857c6..53cb777 100644
--- a/y2019/control_loops/drivetrain/BUILD
+++ b/y2019/control_loops/drivetrain/BUILD
@@ -133,6 +133,7 @@
"//frc971/control_loops:pose",
"//frc971/control_loops/drivetrain:camera",
"//frc971/control_loops/drivetrain:hybrid_ekf",
+ "//frc971/control_loops/drivetrain/localization:utils",
],
)
diff --git a/y2019/control_loops/drivetrain/localizer.h b/y2019/control_loops/drivetrain/localizer.h
index f4c7a8a..3494536 100644
--- a/y2019/control_loops/drivetrain/localizer.h
+++ b/y2019/control_loops/drivetrain/localizer.h
@@ -6,6 +6,7 @@
#include "frc971/control_loops/drivetrain/camera.h"
#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
+#include "frc971/control_loops/drivetrain/localization/utils.h"
#include "frc971/control_loops/pose.h"
#if !defined(__clang__) && defined(__GNUC__)
@@ -437,37 +438,8 @@
Eigen::Matrix<Scalar, kNOutputs, kNStates> HMatrix(const Target &target,
const Pose &camera_pose) {
- // To calculate dheading/d{x,y,theta}:
- // heading = arctan2(target_pos - camera_pos) - camera_theta
- Eigen::Matrix<Scalar, 3, 1> target_pos = target.pose().abs_pos();
- Eigen::Matrix<Scalar, 3, 1> camera_pos = camera_pose.abs_pos();
- Scalar diffx = target_pos.x() - camera_pos.x();
- Scalar diffy = target_pos.y() - camera_pos.y();
- Scalar norm2 = diffx * diffx + diffy * diffy;
- Scalar dheadingdx = diffy / norm2;
- Scalar dheadingdy = -diffx / norm2;
- Scalar dheadingdtheta = -1.0;
-
- // To calculate ddistance/d{x,y}:
- // distance = sqrt(diffx^2 + diffy^2)
- Scalar distance = ::std::sqrt(norm2);
- Scalar ddistdx = -diffx / distance;
- Scalar ddistdy = -diffy / distance;
-
- // Skew = target.theta - camera.theta - heading
- // = target.theta - arctan2(target_pos - camera_pos)
- Scalar dskewdx = -dheadingdx;
- Scalar dskewdy = -dheadingdy;
- Eigen::Matrix<Scalar, kNOutputs, kNStates> H;
- H.setZero();
- H(0, 0) = dheadingdx;
- H(0, 1) = dheadingdy;
- H(0, 2) = dheadingdtheta;
- H(1, 0) = ddistdx;
- H(1, 1) = ddistdy;
- H(2, 0) = dskewdx;
- H(2, 1) = dskewdy;
- return H;
+ return frc971::control_loops::drivetrain::
+ HMatrixForCameraHeadingDistanceSkew(target.pose(), camera_pose);
}
// A helper function for the fuller version of MatchFrames; this just
diff --git a/y2023/localizer/BUILD b/y2023/localizer/BUILD
index eb0f886..9b35fcf 100644
--- a/y2023/localizer/BUILD
+++ b/y2023/localizer/BUILD
@@ -81,26 +81,15 @@
)
cc_library(
- name = "utils",
- srcs = ["utils.cc"],
- hdrs = ["utils.h"],
- visibility = ["//visibility:public"],
- deps = [
- "//frc971/vision:target_map_fbs",
- "@org_tuxfamily_eigen//:eigen",
- ],
-)
-
-cc_library(
name = "map_expander_lib",
srcs = ["map_expander_lib.cc"],
hdrs = ["map_expander_lib.h"],
deps = [
":relative_scoring_map_fbs",
":scoring_map_fbs",
- ":utils",
"//aos:flatbuffers",
"//aos:json_to_flatbuffer",
+ "//frc971/vision:target_map_utils",
],
)
@@ -138,7 +127,6 @@
visibility = ["//visibility:public"],
deps = [
":status_fbs",
- ":utils",
":visualization_fbs",
"//aos/containers:sized_array",
"//aos/events:event_loop",
@@ -152,6 +140,7 @@
"//frc971/control_loops/drivetrain/localization:utils",
"//frc971/imu_reader:imu_watcher",
"//frc971/vision:target_map_fbs",
+ "//frc971/vision:target_map_utils",
"//y2023:constants",
"//y2023/constants:constants_fbs",
],
@@ -164,12 +153,12 @@
deps = [
":localizer",
":status_fbs",
- ":utils",
"//aos/events:simulated_event_loop",
"//aos/events/logging:log_writer",
"//aos/testing:googletest",
"//frc971/control_loops/drivetrain:drivetrain_test_lib",
"//frc971/control_loops/drivetrain:localizer_fbs",
+ "//frc971/vision:target_map_utils",
"//y2023/constants:simulated_constants_sender",
"//y2023/control_loops/drivetrain:drivetrain_base",
],
diff --git a/y2023/localizer/localizer.cc b/y2023/localizer/localizer.cc
index 76784fb..6f9a16b 100644
--- a/y2023/localizer/localizer.cc
+++ b/y2023/localizer/localizer.cc
@@ -5,8 +5,8 @@
#include "aos/containers/sized_array.h"
#include "frc971/control_loops/drivetrain/localizer_generated.h"
#include "frc971/control_loops/pose.h"
+#include "frc971/vision/target_map_utils.h"
#include "y2023/constants.h"
-#include "y2023/localizer/utils.h"
DEFINE_double(max_pose_error, 1e-6,
"Throw out target poses with a higher pose error than this");
diff --git a/y2023/localizer/localizer_test.cc b/y2023/localizer/localizer_test.cc
index fb0efa9..c47e46e 100644
--- a/y2023/localizer/localizer_test.cc
+++ b/y2023/localizer/localizer_test.cc
@@ -8,10 +8,10 @@
#include "frc971/control_loops/drivetrain/localizer_generated.h"
#include "frc971/control_loops/pose.h"
#include "frc971/vision/target_map_generated.h"
+#include "frc971/vision/target_map_utils.h"
#include "y2023/constants/simulated_constants_sender.h"
#include "y2023/control_loops/drivetrain/drivetrain_base.h"
#include "y2023/localizer/status_generated.h"
-#include "y2023/localizer/utils.h"
DEFINE_string(output_folder, "",
"If set, logs all channels to the provided logfile.");
diff --git a/y2023/localizer/map_expander_lib.cc b/y2023/localizer/map_expander_lib.cc
index 5a94985..884b184 100644
--- a/y2023/localizer/map_expander_lib.cc
+++ b/y2023/localizer/map_expander_lib.cc
@@ -1,6 +1,6 @@
#include "y2023/localizer/map_expander_lib.h"
-#include "y2023/localizer/utils.h"
+#include "frc971/vision/target_map_utils.h"
namespace y2023::localizer {
namespace {
diff --git a/y2023/localizer/utils.h b/y2023/localizer/utils.h
deleted file mode 100644
index 8241cf8..0000000
--- a/y2023/localizer/utils.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef Y2023_LOCALIZER_UTILS_H_
-#define Y2023_LOCALIZER_UTILS_H_
-
-#include <Eigen/Dense>
-
-#include "frc971/vision/target_map_generated.h"
-
-namespace y2023::localizer {
-// Converts a TargetPoseFbs into a transformation matrix.
-Eigen::Matrix<double, 4, 4> PoseToTransform(
- const frc971::vision::TargetPoseFbs *pose);
-} // namespace y2023::localizer
-
-#endif // Y2023_LOCALIZER_UTILS_H_
diff --git a/y2023/vision/BUILD b/y2023/vision/BUILD
index f583abd..97ce003 100644
--- a/y2023/vision/BUILD
+++ b/y2023/vision/BUILD
@@ -78,9 +78,9 @@
"//aos/events:shm_event_loop",
"//frc971/constants:constants_sender_lib",
"//frc971/control_loops/drivetrain/localization:localizer_output_fbs",
+ "//frc971/vision:target_map_utils",
"//frc971/vision:vision_fbs",
"//y2023/localizer",
- "//y2023/localizer:utils",
"//y2023/vision:vision_util",
"@com_google_absl//absl/strings",
],
@@ -410,3 +410,32 @@
"//aos/events:shm_event_loop",
],
)
+
+cc_binary(
+ name = "video_ripper",
+ srcs = [
+ "video_ripper.cc",
+ ],
+ data = [
+ "@ffmpeg",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos:init",
+ "//aos/events:simulated_event_loop",
+ "//aos/events/logging:log_reader",
+ "//frc971/vision:vision_fbs",
+ "@com_github_gflags_gflags//:gflags",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
+sh_binary(
+ name = "video_tiler",
+ srcs = ["video_tiler.sh"],
+ data = [
+ "@bazel_tools//tools/bash/runfiles",
+ "@ffmpeg",
+ ],
+)
diff --git a/y2023/vision/localization_verifier.cc b/y2023/vision/localization_verifier.cc
index c16f874..01ef9fe 100644
--- a/y2023/vision/localization_verifier.cc
+++ b/y2023/vision/localization_verifier.cc
@@ -3,9 +3,9 @@
#include "aos/init.h"
#include "frc971/constants/constants_sender_lib.h"
#include "frc971/control_loops/drivetrain/localization/localizer_output_generated.h"
+#include "frc971/vision/target_map_utils.h"
#include "frc971/vision/vision_generated.h"
#include "y2023/localizer/localizer.h"
-#include "y2023/localizer/utils.h"
DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
@@ -49,7 +49,7 @@
}
const Localizer::Transform H_camera_target =
- localizer::PoseToTransform(target_pose);
+ frc971::vision::PoseToTransform(target_pose);
const Localizer::Transform H_field_robot =
LocalizerOutputToTransform(*localizer_fetcher->get());
diff --git a/y2023/vision/video_ripper.cc b/y2023/vision/video_ripper.cc
new file mode 100644
index 0000000..3c93139
--- /dev/null
+++ b/y2023/vision/video_ripper.cc
@@ -0,0 +1,80 @@
+#include <unistd.h>
+
+#include <cstdlib>
+
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+#include "aos/events/logging/log_reader.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/init.h"
+#include "aos/scoped/scoped_fd.h"
+#include "frc971/vision/vision_generated.h"
+
+DEFINE_string(channel, "/camera", "Channel name for the image.");
+DEFINE_int32(width, 1280, "Width of the image");
+DEFINE_int32(height, 720, "Height of the image");
+DEFINE_string(ffmpeg_binary, "external/ffmpeg/ffmpeg",
+ "The path to the ffmpeg binary");
+DEFINE_string(output_path, "video_ripper_output.mp4",
+ "The path to output the mp4 video");
+DEFINE_bool(flip, true, "If true, rotate the video 180 deg.");
+
+// Replays a log and dumps the contents of /camera frc971.vision.CameraImage
+// directly to stdout.
+int main(int argc, char *argv[]) {
+ aos::InitGoogle(&argc, &argv);
+ const std::vector<aos::logger::LogFile> logfiles =
+ aos::logger::SortParts(aos::logger::FindLogs(argc, argv));
+ CHECK(!logfiles.empty());
+
+ // Start ffmpeg
+ std::stringstream command;
+ command << FLAGS_ffmpeg_binary;
+ command << " -framerate 30 -f rawvideo -pix_fmt yuyv422";
+ command << " -s " << FLAGS_width << "x" << FLAGS_height;
+ command << " -i pipe:";
+ command << " -c:v libx264 -f mp4";
+ if (FLAGS_flip) {
+ command << " -vf rotate=PI";
+ }
+ command << " \"" << FLAGS_output_path << "\"";
+
+ FILE *stream = popen(command.str().c_str(), "w");
+
+ const std::string replay_node = logfiles.at(0).logger_node;
+ LOG(INFO) << "Replaying as \"" << replay_node << "\"";
+
+ aos::logger::LogReader reader(logfiles, nullptr);
+ aos::SimulatedEventLoopFactory factory(reader.configuration());
+ reader.RegisterWithoutStarting(&factory);
+
+ const aos::Node *node =
+ (replay_node.empty() ||
+ !aos::configuration::MultiNode(reader.configuration()))
+ ? nullptr
+ : aos::configuration::GetNode(reader.configuration(), replay_node);
+
+ std::unique_ptr<aos::EventLoop> event_loop;
+ factory.GetNodeEventLoopFactory(node)->OnStartup([&stream, &event_loop,
+ &reader, node]() {
+ event_loop =
+ reader.event_loop_factory()->MakeEventLoop("video_ripper", node);
+ event_loop->MakeWatcher(
+ FLAGS_channel, [&stream](const frc971::vision::CameraImage &image) {
+ CHECK_EQ(FLAGS_width, image.cols())
+ << "Image width needs to match the images in the logfile";
+ CHECK_EQ(FLAGS_height, image.rows())
+ << "Image width needs to match the images in the logfile";
+
+ const size_t bytes_written =
+ write(fileno(stream), image.data()->data(), image.data()->size());
+ PCHECK(bytes_written == image.data()->size());
+ });
+ });
+
+ reader.event_loop_factory()->Run();
+ reader.Deregister();
+
+ pclose(stream);
+}
diff --git a/y2023/vision/video_tiler.sh b/y2023/vision/video_tiler.sh
new file mode 100755
index 0000000..5ad3e59
--- /dev/null
+++ b/y2023/vision/video_tiler.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# --- begin runfiles.bash initialization ---
+# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
+set -euo pipefail
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ if [[ -f "$0.runfiles_manifest" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+ elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+ elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ export RUNFILES_DIR="$0.runfiles"
+ fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+ echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+ exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+
+
+# Copied from http://trac.ffmpeg.org/wiki/Create%20a%20mosaic%20out%20of%20several%20input%20videos
+
+$(rlocation ffmpeg/ffmpeg)\
+ -i $1 -i $2 -i $3 -i $4\
+ -filter_complex "
+ nullsrc=size=2560x1440 [base];
+ [0:v] setpts=PTS-STARTPTS, scale=1280x720 [upperleft];
+ [1:v] setpts=PTS-STARTPTS, scale=1280x720 [upperright];
+ [2:v] setpts=PTS-STARTPTS, scale=1280x720 [lowerleft];
+ [3:v] setpts=PTS-STARTPTS, scale=1280x720 [lowerright];
+ [base][upperleft] overlay=shortest=1 [tmp1];
+ [tmp1][upperright] overlay=shortest=1:x=1280 [tmp2];
+ [tmp2][lowerleft] overlay=shortest=1:y=720 [tmp3];
+ [tmp3][lowerright] overlay=shortest=1:x=1280:y=720
+ " \
+ -c:v libx264 ~/video_tiler_out.mp4
diff --git a/y2024/BUILD b/y2024/BUILD
index 875bf84..212f70e 100644
--- a/y2024/BUILD
+++ b/y2024/BUILD
@@ -1,6 +1,5 @@
load("//frc971:downloader.bzl", "robot_downloader")
load("//aos:config.bzl", "aos_config")
-load("//tools/build_rules:template.bzl", "jinja2_template")
load("//aos/util:config_validator_macro.bzl", "config_validator_test")
config_validator_test(
@@ -19,7 +18,9 @@
":aos_config",
"//aos/starter:roborio_irq_config.json",
"//y2024/constants:constants.json",
+ "@ctre_phoenix6_api_cpp_athena//:shared_libraries",
"@ctre_phoenix6_tools_athena//:shared_libraries",
+ "@ctre_phoenix_api_cpp_athena//:shared_libraries",
"@ctre_phoenix_cci_athena//:shared_libraries",
],
dirs = [
@@ -56,6 +57,8 @@
"//frc971/vision:intrinsics_calibration",
"//y2024/vision:viewer",
"//y2024/constants:constants_sender",
+ "//y2024/localizer:localizer_main",
+ "//y2024/localizer:localizer_logger",
"//y2024/vision:foxglove_image_converter",
],
data = [
@@ -71,6 +74,8 @@
],
start_binaries = [
"//aos/events/logging:logger_main",
+ "//frc971/imu_fdcan:can_translator",
+ "//frc971/imu_fdcan:dual_imu_blender",
"//aos/network:message_bridge_client",
"//aos/network:message_bridge_server",
"//aos/network:web_proxy_main",
@@ -100,7 +105,6 @@
deps = [
":config_imu",
":config_orin1",
- ":config_orin2",
":config_roborio",
],
)
@@ -111,10 +115,20 @@
flatbuffers = [
"//aos/network:message_bridge_client_fbs",
"//aos/network:message_bridge_server_fbs",
+ "//frc971/imu_fdcan:dual_imu_fbs",
+ "//frc971/imu_fdcan:can_translator_status_fbs",
+ "//frc971/imu_fdcan:dual_imu_blender_status_fbs",
"//y2024/constants:constants_fbs",
+ "//frc971/control_loops/drivetrain/localization:localizer_output_fbs",
"//frc971/can_logger:can_logging_fbs",
+ "//y2024/localizer:status_fbs",
+ "//y2024/localizer:visualization_fbs",
"//aos/network:timestamp_fbs",
"//aos/network:remote_message_fbs",
+ "//frc971/vision:calibration_fbs",
+ "//frc971/vision:target_map_fbs",
+ "//frc971/vision:vision_fbs",
+ "@com_github_foxglove_schemas//:schemas",
],
target_compatible_with = ["@platforms//os:linux"],
visibility = ["//visibility:public"],
@@ -132,6 +146,7 @@
"//aos/network:remote_message_fbs",
"//aos/network:message_bridge_client_fbs",
"//aos/network:message_bridge_server_fbs",
+ "//frc971/wpilib:pdp_values_fbs",
#y2019 stuff shouldn't be here (e.g. target selector)
"//y2024/constants:constants_fbs",
"//aos/network:timestamp_fbs",
@@ -139,7 +154,9 @@
"//frc971/control_loops/drivetrain:drivetrain_can_position_fbs",
"//y2024/control_loops/superstructure:superstructure_can_position_fbs",
"//y2024/control_loops/superstructure:superstructure_output_fbs",
+ "//frc971/control_loops/drivetrain:rio_localizer_inputs_fbs",
"//y2024/control_loops/superstructure:superstructure_position_fbs",
+ "//frc971/control_loops/drivetrain/localization:localizer_output_fbs",
"//y2024/control_loops/superstructure:superstructure_status_fbs",
"//frc971/can_logger:can_logging_fbs",
],
@@ -152,45 +169,30 @@
],
)
-[
- aos_config(
- name = "config_" + orin,
- src = "y2024_" + orin + ".json",
- flatbuffers = [
- "//aos/network:message_bridge_client_fbs",
- "//aos/network:message_bridge_server_fbs",
- "//aos/network:timestamp_fbs",
- "//aos/network:remote_message_fbs",
- "//y2024/constants:constants_fbs",
- "//frc971/control_loops/drivetrain/localization:localizer_output_fbs",
- "//frc971/vision:calibration_fbs",
- "//frc971/vision:target_map_fbs",
- "//frc971/vision:vision_fbs",
- "@com_github_foxglove_schemas//:schemas",
- ],
- target_compatible_with = ["@platforms//os:linux"],
- visibility = ["//visibility:public"],
- deps = [
- "//aos/events:aos_config",
- "//frc971/control_loops/drivetrain:aos_config",
- "//frc971/input:aos_config",
- ],
- )
- for orin in [
- "orin1",
- "orin2",
- ]
-]
-
-[
- jinja2_template(
- name = "y2024_orin" + str(num) + ".json",
- src = "y2024_orin_template.json",
- parameters = {"NUM": str(num)},
- target_compatible_with = ["@platforms//os:linux"],
- )
- for num in range(1, 3)
-]
+aos_config(
+ name = "config_orin1",
+ src = "y2024_orin1.json",
+ flatbuffers = [
+ "//aos/network:message_bridge_client_fbs",
+ "//aos/network:message_bridge_server_fbs",
+ "//aos/network:timestamp_fbs",
+ "//aos/network:remote_message_fbs",
+ "//y2024/constants:constants_fbs",
+ "//frc971/control_loops/drivetrain/localization:localizer_output_fbs",
+ "//frc971/vision:calibration_fbs",
+ "//y2024/localizer:visualization_fbs",
+ "//frc971/vision:target_map_fbs",
+ "//frc971/vision:vision_fbs",
+ "@com_github_foxglove_schemas//:schemas",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos/events:aos_config",
+ "//frc971/control_loops/drivetrain:aos_config",
+ "//frc971/input:aos_config",
+ ],
+)
cc_library(
name = "constants",
@@ -210,9 +212,14 @@
"//frc971/shooter_interpolation:interpolation",
"//frc971/zeroing:absolute_encoder",
"//frc971/zeroing:pot_and_absolute_encoder",
+ "//y2024/constants:constants_fbs",
"//y2024/control_loops/drivetrain:polydrivetrain_plants",
+ "//y2024/control_loops/superstructure/altitude:altitude_plants",
+ "//y2024/control_loops/superstructure/catapult:catapult_plants",
"//y2024/control_loops/superstructure/climber:climber_plants",
+ "//y2024/control_loops/superstructure/extend:extend_plants",
"//y2024/control_loops/superstructure/intake_pivot:intake_pivot_plants",
+ "//y2024/control_loops/superstructure/turret:turret_plants",
"@com_github_google_glog//:glog",
"@com_google_absl//absl/base",
],
@@ -282,6 +289,7 @@
"//frc971/autonomous:auto_fbs",
"//frc971/autonomous:base_autonomous_actor",
"//frc971/control_loops:profiled_subsystem_fbs",
+ "//frc971/control_loops:static_zeroing_single_dof_profiled_subsystem",
"//frc971/input:action_joystick_input",
"//frc971/input:drivetrain_input",
"//frc971/input:joystick_input",
diff --git a/y2024/autonomous/BUILD b/y2024/autonomous/BUILD
index 611f3cd..f69d9fc 100644
--- a/y2024/autonomous/BUILD
+++ b/y2024/autonomous/BUILD
@@ -49,6 +49,7 @@
"//aos/logging",
"//aos/util:phased_loop",
"//frc971/autonomous:base_autonomous_actor",
+ "//frc971/autonomous:user_button_localized_autonomous_actor",
"//frc971/control_loops:control_loops_fbs",
"//frc971/control_loops:profiled_subsystem_fbs",
"//frc971/control_loops/drivetrain:drivetrain_config",
diff --git a/y2024/autonomous/autonomous_actor.cc b/y2024/autonomous/autonomous_actor.cc
index 6b1d9a5..3c7f2fa 100644
--- a/y2024/autonomous/autonomous_actor.cc
+++ b/y2024/autonomous/autonomous_actor.cc
@@ -34,74 +34,24 @@
namespace chrono = ::std::chrono;
AutonomousActor::AutonomousActor(::aos::EventLoop *event_loop)
- : frc971::autonomous::BaseAutonomousActor(
+ : frc971::autonomous::UserButtonLocalizedAutonomousActor(
event_loop,
control_loops::drivetrain::GetDrivetrainConfig(event_loop)),
localizer_control_sender_(
event_loop->MakeSender<
::frc971::control_loops::drivetrain::LocalizerControl>(
"/drivetrain")),
- joystick_state_fetcher_(
- event_loop->MakeFetcher<aos::JoystickState>("/aos")),
- robot_state_fetcher_(event_loop->MakeFetcher<aos::RobotState>("/aos")),
- auto_splines_(),
superstructure_goal_sender_(
- event_loop->MakeSender<::y2024::control_loops::superstructure::Goal>(
- "/superstructure")),
+ event_loop
+ ->MakeSender<::y2024::control_loops::superstructure::GoalStatic>(
+ "/superstructure")),
superstructure_status_fetcher_(
event_loop
->MakeFetcher<::y2024::control_loops::superstructure::Status>(
- "/superstructure")) {
- drivetrain_status_fetcher_.Fetch();
- replan_timer_ = event_loop->AddTimer([this]() { Replan(); });
-
- event_loop->OnRun([this, event_loop]() {
- replan_timer_->Schedule(event_loop->monotonic_now());
- button_poll_->Schedule(event_loop->monotonic_now(),
- chrono::milliseconds(50));
- });
-
- // TODO(james): Really need to refactor this code since we keep using it.
- button_poll_ = event_loop->AddTimer([this]() {
- const aos::monotonic_clock::time_point now =
- this->event_loop()->context().monotonic_event_time;
- if (robot_state_fetcher_.Fetch()) {
- if (robot_state_fetcher_->user_button()) {
- user_indicated_safe_to_reset_ = true;
- MaybeSendStartingPosition();
- }
- }
- if (joystick_state_fetcher_.Fetch()) {
- if (joystick_state_fetcher_->has_alliance() &&
- (joystick_state_fetcher_->alliance() != alliance_)) {
- alliance_ = joystick_state_fetcher_->alliance();
- is_planned_ = false;
- // Only kick the planning out by 2 seconds. If we end up enabled in
- // that second, then we will kick it out further based on the code
- // below.
- replan_timer_->Schedule(now + std::chrono::seconds(2));
- }
- if (joystick_state_fetcher_->enabled()) {
- if (!is_planned_) {
- // Only replan once we've been disabled for 5 seconds.
- replan_timer_->Schedule(now + std::chrono::seconds(5));
- }
- }
- }
- });
-}
+ "/superstructure")),
+ auto_splines_() {}
void AutonomousActor::Replan() {
- if (!drivetrain_status_fetcher_.Fetch()) {
- replan_timer_->Schedule(event_loop()->monotonic_now() + chrono::seconds(1));
- AOS_LOG(INFO, "Drivetrain not up, replanning in 1 second");
- return;
- }
-
- if (alliance_ == aos::Alliance::kInvalid) {
- return;
- }
- sent_starting_position_ = false;
if (FLAGS_spline_auto) {
test_spline_ =
PlanSpline(std::bind(&AutonomousSplines::TestSpline, &auto_splines_,
@@ -116,42 +66,8 @@
MaybeSendStartingPosition();
}
-void AutonomousActor::MaybeSendStartingPosition() {
- if (is_planned_ && user_indicated_safe_to_reset_ &&
- !sent_starting_position_) {
- CHECK(starting_position_);
- SendStartingPosition(starting_position_.value());
- }
-}
-
-void AutonomousActor::Reset() {
- InitializeEncoders();
- ResetDrivetrain();
-
- joystick_state_fetcher_.Fetch();
- CHECK(joystick_state_fetcher_.get() != nullptr)
- << "Expect at least one JoystickState message before running auto...";
- alliance_ = joystick_state_fetcher_->alliance();
- preloaded_ = false;
-}
-
-bool AutonomousActor::RunAction(
+bool AutonomousActor::Run(
const ::frc971::autonomous::AutonomousActionParams *params) {
- Reset();
-
- AOS_LOG(INFO, "Params are %d\n", params->mode());
-
- if (!user_indicated_safe_to_reset_) {
- AOS_LOG(WARNING, "Didn't send starting position prior to starting auto.");
- CHECK(starting_position_);
- SendStartingPosition(starting_position_.value());
- }
- // Clear this so that we don't accidentally resend things as soon as we
- // replan later.
- user_indicated_safe_to_reset_ = false;
- is_planned_ = false;
- starting_position_.reset();
-
AOS_LOG(INFO, "Params are %d\n", params->mode());
if (alliance_ == aos::Alliance::kInvalid) {
AOS_LOG(INFO, "Aborting autonomous due to invalid alliance selection.");
@@ -165,15 +81,6 @@
return true;
}
-void AutonomousActor::SplineAuto() {
- CHECK(test_spline_);
-
- if (!test_spline_->WaitForPlan()) return;
- test_spline_->Start();
-
- if (!test_spline_->WaitForSplineDistanceRemaining(0.02)) return;
-}
-
void AutonomousActor::SendStartingPosition(const Eigen::Vector3d &start) {
// Set up the starting position for the blue alliance.
@@ -192,4 +99,84 @@
AOS_LOG(ERROR, "Failed to reset localizer.\n");
}
}
+
+void AutonomousActor::Reset() {
+ set_intake_goal(control_loops::superstructure::IntakeGoal::NONE);
+ set_note_goal(control_loops::superstructure::NoteGoal::NONE);
+ set_auto_aim(false);
+ set_fire(false);
+ set_preloaded(false);
+ SendSuperstructureGoal();
+}
+
+void AutonomousActor::SplineAuto() {
+ CHECK(test_spline_);
+
+ if (!test_spline_->WaitForPlan()) return;
+ test_spline_->Start();
+
+ if (!test_spline_->WaitForSplineDistanceRemaining(0.02)) return;
+}
+
+void AutonomousActor::SendSuperstructureGoal() {
+ aos::Sender<control_loops::superstructure::GoalStatic>::StaticBuilder
+ goal_builder = superstructure_goal_sender_.MakeStaticBuilder();
+
+ goal_builder->set_intake_goal(intake_goal_);
+ goal_builder->set_note_goal(note_goal_);
+ goal_builder->set_fire(fire_);
+
+ control_loops::superstructure::ShooterGoalStatic *shooter_goal =
+ goal_builder->add_shooter_goal();
+
+ shooter_goal->set_auto_aim(auto_aim_);
+ shooter_goal->set_preloaded(preloaded_);
+
+ goal_builder.CheckOk(goal_builder.Send());
+}
+
+void AutonomousActor::Intake() {
+ set_intake_goal(control_loops::superstructure::IntakeGoal::INTAKE);
+ set_note_goal(control_loops::superstructure::NoteGoal::CATAPULT);
+ SendSuperstructureGoal();
+}
+
+void AutonomousActor::Aim() {
+ set_auto_aim(true);
+ SendSuperstructureGoal();
+}
+
+void AutonomousActor::Shoot() {
+ set_fire(true);
+ SendSuperstructureGoal();
+}
+
+[[nodiscard]] bool AutonomousActor::WaitForPreloaded() {
+ set_preloaded(true);
+ SendSuperstructureGoal();
+
+ ::aos::time::PhasedLoop phased_loop(frc971::controls::kLoopFrequency,
+ event_loop()->monotonic_now(),
+ aos::common::actions::kLoopOffset);
+
+ bool loaded = false;
+ while (!loaded) {
+ if (ShouldCancel()) {
+ return false;
+ }
+
+ phased_loop.SleepUntilNext();
+ superstructure_status_fetcher_.Fetch();
+ CHECK(superstructure_status_fetcher_.get() != nullptr);
+
+ loaded = (superstructure_status_fetcher_->shooter()->catapult_state() ==
+ control_loops::superstructure::CatapultState::LOADED);
+ }
+
+ set_preloaded(false);
+ SendSuperstructureGoal();
+
+ return true;
+}
+
} // namespace y2024::autonomous
diff --git a/y2024/autonomous/autonomous_actor.h b/y2024/autonomous/autonomous_actor.h
index c3d7d79..6796c94 100644
--- a/y2024/autonomous/autonomous_actor.h
+++ b/y2024/autonomous/autonomous_actor.h
@@ -3,62 +3,70 @@
#include "aos/actions/actions.h"
#include "aos/actions/actor.h"
-#include "frc971/autonomous/base_autonomous_actor.h"
+#include "frc971/autonomous/user_button_localized_autonomous_actor.h"
#include "frc971/control_loops/control_loops_generated.h"
#include "frc971/control_loops/drivetrain/drivetrain_config.h"
#include "frc971/control_loops/drivetrain/localizer_generated.h"
#include "y2024/autonomous/auto_splines.h"
-#include "y2024/control_loops/superstructure/superstructure_goal_generated.h"
-#include "y2024/control_loops/superstructure/superstructure_status_generated.h"
+#include "y2024/control_loops/superstructure/superstructure_goal_static.h"
+#include "y2024/control_loops/superstructure/superstructure_status_static.h"
namespace y2024::autonomous {
-class AutonomousActor : public ::frc971::autonomous::BaseAutonomousActor {
+class AutonomousActor
+ : public ::frc971::autonomous::UserButtonLocalizedAutonomousActor {
public:
explicit AutonomousActor(::aos::EventLoop *event_loop);
- bool RunAction(
- const ::frc971::autonomous::AutonomousActionParams *params) override;
-
private:
+ void set_intake_goal(control_loops::superstructure::IntakeGoal intake_goal) {
+ intake_goal_ = intake_goal;
+ }
+ void set_note_goal(control_loops::superstructure::NoteGoal note_goal) {
+ note_goal_ = note_goal;
+ }
+ void set_auto_aim(bool auto_aim) { auto_aim_ = auto_aim; }
+ void set_fire(bool fire) { fire_ = fire; }
+ void set_preloaded(bool preloaded) { preloaded_ = preloaded; }
+
+ bool Run(const ::frc971::autonomous::AutonomousActionParams *params) override;
+ void Replan() override;
+ void SendStartingPosition(const Eigen::Vector3d &start) override;
+ void Reset() override;
+
+ void SplineAuto();
void SendSuperstructureGoal();
- void Reset();
+ void Intake();
+ void Aim();
+ void Shoot();
- void SendStartingPosition(const Eigen::Vector3d &start);
- void MaybeSendStartingPosition();
- void SplineAuto();
- void Replan();
+ [[nodiscard]] bool WaitForPreloaded();
aos::Sender<frc971::control_loops::drivetrain::LocalizerControl>
localizer_control_sender_;
- aos::Fetcher<aos::JoystickState> joystick_state_fetcher_;
- aos::Fetcher<aos::RobotState> robot_state_fetcher_;
- aos::TimerHandler *replan_timer_;
- aos::TimerHandler *button_poll_;
+ aos::Sender<control_loops::superstructure::GoalStatic>
+ superstructure_goal_sender_;
- aos::Alliance alliance_ = aos::Alliance::kInvalid;
- AutonomousSplines auto_splines_;
- bool user_indicated_safe_to_reset_ = false;
- bool sent_starting_position_ = false;
-
- bool is_planned_ = false;
-
- std::optional<Eigen::Vector3d> starting_position_;
-
- bool preloaded_ = false;
-
- aos::Sender<control_loops::superstructure::Goal> superstructure_goal_sender_;
aos::Fetcher<y2024::control_loops::superstructure::Status>
superstructure_status_fetcher_;
+ AutonomousSplines auto_splines_;
+
std::optional<SplineHandle> test_spline_;
- // List of arm angles from arm::PointsList
- const ::std::vector<::Eigen::Matrix<double, 3, 1>> points_;
+ control_loops::superstructure::IntakeGoal intake_goal_ =
+ control_loops::superstructure::IntakeGoal::NONE;
+
+ control_loops::superstructure::NoteGoal note_goal_ =
+ control_loops::superstructure::NoteGoal::NONE;
+
+ bool auto_aim_ = false;
+ bool fire_ = false;
+ bool preloaded_ = false;
};
} // namespace y2024::autonomous
-#endif // Y2024_AUTONOMOUS_AUTONOMOUS_ACTOR_H_
+#endif // Y2024_AUTONOMOUS_AUTONOMOUS_ACTOR_H_
\ No newline at end of file
diff --git a/y2024/constants.h b/y2024/constants.h
index 8eac0c9..9923660 100644
--- a/y2024/constants.h
+++ b/y2024/constants.h
@@ -8,11 +8,17 @@
#include "frc971/constants.h"
#include "frc971/control_loops/pose.h"
#include "frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h"
+#include "frc971/shooter_interpolation/interpolation.h"
#include "frc971/zeroing/absolute_encoder.h"
#include "frc971/zeroing/pot_and_absolute_encoder.h"
+#include "y2024/constants/constants_generated.h"
#include "y2024/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+#include "y2024/control_loops/superstructure/altitude/altitude_plant.h"
+#include "y2024/control_loops/superstructure/catapult/catapult_plant.h"
#include "y2024/control_loops/superstructure/climber/climber_plant.h"
+#include "y2024/control_loops/superstructure/extend/extend_plant.h"
#include "y2024/control_loops/superstructure/intake_pivot/intake_pivot_plant.h"
+#include "y2024/control_loops/superstructure/turret/turret_plant.h"
namespace y2024::constants {
@@ -55,16 +61,7 @@
return 4096.0;
}
- static constexpr double kIntakePivotEncoderRatio() {
- return (16.0 / 64.0) * (18.0 / 62.0);
- }
-
- static constexpr double kIntakePivotPotRatio() { return 16.0 / 64.0; }
-
- static constexpr double kIntakePivotPotRadiansPerVolt() {
- return kIntakePivotPotRatio() * (3.0 /*turns*/ / 5.0 /*volts*/) *
- (2 * M_PI /*radians*/);
- }
+ static constexpr double kIntakePivotEncoderRatio() { return (15.0 / 24.0); }
static constexpr double kMaxIntakePivotEncoderPulsesPerSecond() {
return control_loops::superstructure::intake_pivot::kFreeSpeed /
@@ -74,20 +71,23 @@
kIntakePivotEncoderCountsPerRevolution();
}
- // TODO(Filip): Update climber values once we have them.
static constexpr double kClimberEncoderCountsPerRevolution() {
return 4096.0;
}
- static constexpr double kClimberEncoderRatio() {
- return (16.0 / 64.0) * (18.0 / 62.0);
+ static constexpr double kClimberEncoderRatio() { return (16.0 / 60.0); }
+
+ static constexpr double kClimberPotMetersPerRevolution() {
+ return 16 * 0.25 * 0.0254;
}
- static constexpr double kClimberPotRatio() { return 16.0 / 64.0; }
+ static constexpr double kClimberEncoderMetersPerRadian() {
+ return kClimberEncoderRatio() * kClimberPotMetersPerRevolution() / 2.0 /
+ M_PI;
+ }
- static constexpr double kClimberPotRadiansPerVolt() {
- return kClimberPotRatio() * (3.0 /*turns*/ / 5.0 /*volts*/) *
- (2 * M_PI /*radians*/);
+ static constexpr double kClimberPotMetersPerVolt() {
+ return kClimberPotMetersPerRevolution() * (10.0 /*turns*/ / 5.0 /*volts*/);
}
static constexpr double kMaxClimberEncoderPulsesPerSecond() {
@@ -96,12 +96,151 @@
kClimberEncoderRatio() * kClimberEncoderCountsPerRevolution();
}
+ static constexpr double kExtendEncoderCountsPerRevolution() { return 4096.0; }
+ // TODO: (niko) add the gear ratios for the intake once we have them
+ static constexpr double kCatapultEncoderCountsPerRevolution() {
+ return 4096.0;
+ }
+
+ static constexpr double kCatapultEncoderRatio() { return 12.0 / 24.0; }
+
+ static constexpr double kCatapultPotRatio() { return 12.0 / 24.0; }
+
+ static constexpr double kCatapultPotRadiansPerVolt() {
+ return kCatapultPotRatio() * (3.0 /*turns*/ / 5.0 /*volts*/) *
+ (2 * M_PI /*radians*/);
+ }
+
+ static constexpr double kMaxCatapultEncoderPulsesPerSecond() {
+ return control_loops::superstructure::catapult::kFreeSpeed / (2.0 * M_PI) *
+ control_loops::superstructure::catapult::kOutputRatio /
+ kCatapultEncoderRatio() * kCatapultEncoderCountsPerRevolution();
+ }
+
+ static constexpr double kExtendEncoderRatio() { return 1.0; }
+
+ static constexpr double kExtendPotMetersPerRevolution() {
+ return 36 * 0.005 * kExtendEncoderRatio();
+ }
+ static constexpr double kExtendEncoderMetersPerRadian() {
+ return kExtendPotMetersPerRevolution() / 2.0 / M_PI;
+ }
+ static constexpr double kExtendPotMetersPerVolt() {
+ return kExtendPotMetersPerRevolution() * (5.0 /*turns*/ / 5.0 /*volts*/);
+ }
+ static constexpr double kMaxExtendEncoderPulsesPerSecond() {
+ return control_loops::superstructure::extend::kFreeSpeed / (2.0 * M_PI) *
+ control_loops::superstructure::extend::kOutputRatio /
+ kExtendEncoderRatio() * kExtendEncoderCountsPerRevolution();
+ }
+
+ static constexpr double kTurretEncoderCountsPerRevolution() { return 4096.0; }
+
+ static constexpr double kTurretPotRatio() {
+ return (22.0 / 100.0) * (28.0 / 48.0) * (36.0 / 24.0);
+ }
+
+ static constexpr double kTurretEncoderRatio() { return 22.0 / 100.0; }
+
+ static constexpr double kTurretPotRadiansPerVolt() {
+ return kTurretPotRatio() * (10.0 /*turns*/ / 5.0 /*volts*/) *
+ (2 * M_PI /*radians*/);
+ }
+ static constexpr double kMaxTurretEncoderPulsesPerSecond() {
+ return control_loops::superstructure::turret::kFreeSpeed / (2.0 * M_PI) *
+ control_loops::superstructure::turret::kOutputRatio /
+ kTurretEncoderRatio() * kTurretEncoderCountsPerRevolution();
+ }
+
+ static constexpr double kAltitudeEncoderCountsPerRevolution() {
+ return 4096.0;
+ }
+
+ static constexpr double kAltitudeEncoderRatio() { return 16.0 / 162.0; }
+
+ static constexpr double kAltitudePotRatio() { return 16.0 / 162.0; }
+
+ static constexpr double kAltitudePotRadiansPerVolt() {
+ return kAltitudePotRatio() * (10.0 /*turns*/ / 5.0 /*volts*/) *
+ (2 * M_PI /*radians*/);
+ }
+ static constexpr double kMaxAltitudeEncoderPulsesPerSecond() {
+ return control_loops::superstructure::altitude::kFreeSpeed / (2.0 * M_PI) *
+ control_loops::superstructure::altitude::kOutputRatio /
+ kAltitudeEncoderRatio() * kAltitudeEncoderCountsPerRevolution();
+ }
+
+ // 20 -> 28 reduction to a 0.5" radius roller
+ static constexpr double kTransferRollerOutputRatio = (20.0 / 28.0) * 0.0127;
+ // 20 -> 34 reduction, and the 34 is on a 0.625" radius roller
+ static constexpr double kIntakeRollerOutputRatio = (20.0 / 34.0) * 0.015875;
+ // 20 -> 28 reduction to a 0.5" radius roller
+ static constexpr double kExtendRollerOutputRatio = (20.0 / 28.0) * 0.0127;
+
+ struct ShotParams {
+ // Measured in radians
+ double shot_altitude_angle = 0.0;
+ double shot_catapult_angle = 0.0;
+
+ // Muzzle velocity (m/s) of the game piece as it is released from the
+ // catapult.
+ double shot_velocity = 0.0;
+
+ // Speed over ground to use for shooting on the fly
+ double shot_speed_over_ground = 0.0;
+
+ static ShotParams BlendY(double coefficient, ShotParams a1, ShotParams a2) {
+ using ::frc971::shooter_interpolation::Blend;
+ return ShotParams{
+ .shot_altitude_angle = Blend(coefficient, a1.shot_altitude_angle,
+ a2.shot_altitude_angle),
+ .shot_catapult_angle = Blend(coefficient, a1.shot_catapult_angle,
+ a2.shot_catapult_angle),
+ .shot_velocity =
+ Blend(coefficient, a1.shot_velocity, a2.shot_velocity),
+ .shot_speed_over_ground =
+ Blend(coefficient, a1.shot_speed_over_ground,
+ a2.shot_speed_over_ground),
+ };
+ }
+
+ static ShotParams FromFlatbuffer(const y2024::ShotParams *shot_params) {
+ return ShotParams{
+ .shot_altitude_angle = shot_params->shot_altitude_angle(),
+ .shot_catapult_angle = shot_params->shot_catapult_angle(),
+ .shot_velocity = shot_params->shot_velocity(),
+ .shot_speed_over_ground = shot_params->shot_speed_over_ground()};
+ }
+ };
+
+ static frc971::shooter_interpolation::InterpolationTable<ShotParams>
+ InterpolationTableFromFlatbuffer(
+ const flatbuffers::Vector<
+ flatbuffers::Offset<y2024::InterpolationTablePoint>> *table) {
+ std::vector<std::pair<double, ShotParams>> interpolation_table;
+
+ for (const InterpolationTablePoint *point : *table) {
+ interpolation_table.emplace_back(
+ point->distance_from_goal(),
+ ShotParams::FromFlatbuffer(point->shot_params()));
+ }
+
+ return frc971::shooter_interpolation::InterpolationTable<ShotParams>(
+ interpolation_table);
+ }
+
struct PotAndAbsEncoderConstants {
::frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemParams<
::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator>
subsystem_params;
double potentiometer_offset;
};
+
+ struct AbsoluteEncoderConstants {
+ ::frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemParams<
+ ::frc971::zeroing::AbsoluteEncoderZeroingEstimator>
+ subsystem_params;
+ };
};
// Creates and returns a Values instance for the constants.
diff --git a/y2024/constants/7971.json b/y2024/constants/7971.json
index 9e6bafb..be70754 100644
--- a/y2024/constants/7971.json
+++ b/y2024/constants/7971.json
@@ -1,17 +1,32 @@
{% from 'y2024/constants/common.jinja2' import intake_pivot_zero %}
{% from 'y2024/constants/common.jinja2' import climber_zero %}
+{% from 'y2024/constants/common.jinja2' import catapult_zero %}
+{% from 'y2024/constants/common.jinja2' import altitude_zero %}
+{% from 'y2024/constants/common.jinja2' import turret_zero %}
+{% from 'y2024/constants/common.jinja2' import extend_zero %}
{
- "robot": {
- "intake_constants": {
- {% set _ = intake_pivot_zero.update(
- {
- "measured_absolute_position" : 0.0
- }
- ) %}
- "zeroing_constants": {{ intake_pivot_zero | tojson(indent=2)}},
- "potentiometer_offset": 0.0
+ "cameras": [
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_imu-7971-0_cam-24-01_2024-03-02_19-44-12.098903651.json' %}
},
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_orin-7971-1-1_cam-24-02_2024-03-02_20-09-18.022901293.json' %}
+ },
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_orin-7971-c-1_cam-24-03_2024-03-02_20-09-18.016217514.json' %}
+ },
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_orin-7971-1-0_cam-24-04_2024-03-02_20-09-18.016860291.json' %}
+ }
+ ],
+ "robot": {
+ {% set _ = intake_pivot_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "intake_constants": {{ intake_pivot_zero | tojson(indent=2)}},
"climber_constants": {
{% set _ = climber_zero.update(
{
@@ -20,6 +35,42 @@
) %}
"zeroing_constants": {{ climber_zero | tojson(indent=2)}},
"potentiometer_offset": 0.0
+ },
+ "catapult_constants": {
+ {% set _ = catapult_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ catapult_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "altitude_constants": {
+ {% set _ = altitude_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ altitude_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "turret_constants": {
+ {% set _ = turret_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ turret_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "extend_constants": {
+ {% set _ = extend_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ extend_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
}
},
{% include 'y2024/constants/common.json' %}
diff --git a/y2024/constants/971.json b/y2024/constants/971.json
index 641783f..5cee276 100644
--- a/y2024/constants/971.json
+++ b/y2024/constants/971.json
@@ -1,30 +1,76 @@
{% from 'y2024/constants/common.jinja2' import intake_pivot_zero %}
{% from 'y2024/constants/common.jinja2' import climber_zero %}
+{% from 'y2024/constants/common.jinja2' import catapult_zero %}
+{% from 'y2024/constants/common.jinja2' import altitude_zero %}
+{% from 'y2024/constants/common.jinja2' import turret_zero %}
+{% from 'y2024/constants/common.jinja2' import extend_zero %}
{
"cameras": [
{
- "calibration": {% include 'y2024/constants/calib_files/calibration_orin-971-1_cam-24-00.json' %}
+ "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-0_cam-24-05_2024-03-01_11-01-05.102438041.json' %}
+ },
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-1_cam-24-06_2024-03-01_11-01-20.409861949.json' %}
+ },
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-0_cam-24-07_2024-03-01_11-01-32.895328333.json' %}
+ },
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-1_cam-24-08_2024-03-01_11-02-11.982641320.json' %}
}
],
"robot": {
- "intake_constants": {
- {% set _ = intake_pivot_zero.update(
- {
- "measured_absolute_position" : 0.0
- }
- ) %}
- "zeroing_constants": {{ intake_pivot_zero | tojson(indent=2)}},
- "potentiometer_offset": 0.0
- },
+ {% set _ = intake_pivot_zero.update(
+ {
+ "measured_absolute_position" : 3.2990161941868
+ }
+ ) %}
+ "intake_constants": {{ intake_pivot_zero | tojson(indent=2)}},
"climber_constants": {
{% set _ = climber_zero.update(
{
- "measured_absolute_position" : 0.0
+ "measured_absolute_position" : 0.00260967415741875
}
) %}
"zeroing_constants": {{ climber_zero | tojson(indent=2)}},
- "potentiometer_offset": 0.0
+ "potentiometer_offset": {{ -0.935529777248618 + 1.83632555414775 + 0.0431080619919798 - 0.493015437796464 }}
+ },
+ "catapult_constants": {
+ {% set _ = catapult_zero.update(
+ {
+ "measured_absolute_position" : 0.741253220327565
+ }
+ ) %}
+ "zeroing_constants": {{ catapult_zero | tojson(indent=2)}},
+ "potentiometer_offset": {{ 9.41595277209342 }}
+ },
+ "altitude_constants": {
+ {% set _ = altitude_zero.update(
+ {
+ "measured_absolute_position" : 0.130841088837793
+ }
+ ) %}
+ "zeroing_constants": {{ altitude_zero | tojson(indent=2)}},
+ "potentiometer_offset": -0.15316323147786
+ },
+ "turret_constants": {
+ {% set _ = turret_zero.update(
+ {
+ "measured_absolute_position" : 0.138686395993591
+ }
+ ) %}
+ "zeroing_constants": {{ turret_zero | tojson(indent=2)}},
+ "potentiometer_offset": {{ -6.47164779835404 }}
+ },
+ "extend_constants": {
+ {% set _ = extend_zero.update(
+ {
+ "measured_absolute_position" : 0.0314256815130559
+ }
+ ) %}
+ "zeroing_constants": {{ extend_zero | tojson(indent=2)}},
+ "potentiometer_offset": {{ -0.2574404033256 + 0.0170793439542 - 0.177097393974999 }}
}
},
{% include 'y2024/constants/common.json' %}
diff --git a/y2024/constants/9971.json b/y2024/constants/9971.json
index 9e6bafb..ec59033 100644
--- a/y2024/constants/9971.json
+++ b/y2024/constants/9971.json
@@ -1,17 +1,23 @@
{% from 'y2024/constants/common.jinja2' import intake_pivot_zero %}
{% from 'y2024/constants/common.jinja2' import climber_zero %}
+{% from 'y2024/constants/common.jinja2' import catapult_zero %}
+{% from 'y2024/constants/common.jinja2' import altitude_zero %}
+{% from 'y2024/constants/common.jinja2' import turret_zero %}
+{% from 'y2024/constants/common.jinja2' import extend_zero %}
{
+ "cameras": [
+ {
+ "calibration": {% include 'y2024/constants/calib_files/calibration_orin-971-1_cam-24-00.json' %}
+ }
+ ],
"robot": {
- "intake_constants": {
- {% set _ = intake_pivot_zero.update(
- {
- "measured_absolute_position" : 0.0
- }
- ) %}
- "zeroing_constants": {{ intake_pivot_zero | tojson(indent=2)}},
- "potentiometer_offset": 0.0
- },
+ {% set _ = intake_pivot_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "intake_constants": {{ intake_pivot_zero | tojson(indent=2)}},
"climber_constants": {
{% set _ = climber_zero.update(
{
@@ -20,6 +26,42 @@
) %}
"zeroing_constants": {{ climber_zero | tojson(indent=2)}},
"potentiometer_offset": 0.0
+ },
+ "catapult_constants": {
+ {% set _ = catapult_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ catapult_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "altitude_constants": {
+ {% set _ = altitude_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ altitude_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "turret_constants": {
+ {% set _ = turret_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ turret_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "extend_constants": {
+ {% set _ = extend_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ extend_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
}
},
{% include 'y2024/constants/common.json' %}
diff --git a/y2024/constants/BUILD b/y2024/constants/BUILD
index 1f25bcb..b07851e 100644
--- a/y2024/constants/BUILD
+++ b/y2024/constants/BUILD
@@ -1,5 +1,6 @@
load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
load("//tools/build_rules:template.bzl", "jinja2_template")
+load("//y2024/constants:validator.bzl", "constants_json")
cc_library(
name = "simulated_constants_sender",
@@ -17,14 +18,19 @@
)
jinja2_template(
- name = "test_constants.json",
+ name = "test_constants_unvalidated.json",
src = "test_constants.jinja2.json",
includes = glob([
"test_data/*.json",
]) + [
"//y2024/control_loops/superstructure/intake_pivot:intake_pivot_json",
"//y2024/control_loops/superstructure/climber:climber_json",
+ "//y2024/control_loops/superstructure/catapult:catapult_json",
+ "//y2024/control_loops/superstructure/altitude:altitude_json",
+ "//y2024/control_loops/superstructure/extend:extend_json",
+ "//y2024/control_loops/superstructure/turret:turret_json",
"//y2024/control_loops/drivetrain:drivetrain_config.json",
+ "//y2024/constants/calib_files",
"common.json",
"common.jinja2",
"//y2024/vision/maps",
@@ -34,7 +40,7 @@
)
jinja2_template(
- name = "constants.json",
+ name = "constants_unvalidated.json",
src = "constants.jinja2.json",
includes = [
"7971.json",
@@ -44,8 +50,12 @@
"common.json",
"//y2024/constants/calib_files",
"//y2024/control_loops/drivetrain:drivetrain_config.json",
+ "//y2024/control_loops/superstructure/altitude:altitude_json",
+ "//y2024/control_loops/superstructure/catapult:catapult_json",
"//y2024/control_loops/superstructure/climber:climber_json",
+ "//y2024/control_loops/superstructure/extend:extend_json",
"//y2024/control_loops/superstructure/intake_pivot:intake_pivot_json",
+ "//y2024/control_loops/superstructure/turret:turret_json",
"//y2024/vision/maps",
],
parameters = {},
@@ -86,15 +96,27 @@
],
)
-cc_test(
- name = "constants_validator_test",
- srcs = ["constants_validator_test.cc"],
- data = [":constants.json"],
+cc_binary(
+ name = "constants_formatter",
+ srcs = ["constants_formatter.cc"],
+ data = [":constants_unvalidated.json"],
visibility = ["//visibility:public"],
deps = [
":constants_list_fbs",
+ "//aos:init",
"//aos:json_to_flatbuffer",
- "//aos/testing:googletest",
"@com_github_google_glog//:glog",
],
)
+
+constants_json(
+ name = "constants_json",
+ src = ":constants_unvalidated.json",
+ out = "constants.json",
+)
+
+constants_json(
+ name = "test_constants_json",
+ src = ":test_constants_unvalidated.json",
+ out = "test_constants.json",
+)
diff --git a/y2024/constants/calib_files/calibration_imu-7971-0_cam-24-01_2024-03-02_19-44-12.098903651.json b/y2024/constants/calib_files/calibration_imu-7971-0_cam-24-01_2024-03-02_19-44-12.098903651.json
new file mode 100755
index 0000000..13301a0
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_imu-7971-0_cam-24-01_2024-03-02_19-44-12.098903651.json
@@ -0,0 +1,45 @@
+{
+ "node_name": "imu",
+ "team_number": 7971,
+ "intrinsics": [
+ 646.870789,
+ 0.0,
+ 731.468811,
+ 0.0,
+ 646.616333,
+ 570.003723,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 1.0,
+ -0.0,
+ 0.0,
+ -0.131843,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.061756,
+ -0.0,
+ -1.0,
+ 0.0,
+ -0.05456,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.249077,
+ 0.063132,
+ 0.000082,
+ 0.00026,
+ -0.006916
+ ],
+ "calibration_timestamp": 1707365495566609408,
+ "camera_id": "24-01",
+ "camera_number": 0
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_imu-971-0_cam-24-07_2024-03-01_11-01-32.895328333.json b/y2024/constants/calib_files/calibration_imu-971-0_cam-24-07_2024-03-01_11-01-32.895328333.json
new file mode 100755
index 0000000..d013e2a
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_imu-971-0_cam-24-07_2024-03-01_11-01-32.895328333.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "imu",
+ "team_number": 971,
+ "intrinsics": [
+ 647.822815,
+ 0.0,
+ 715.37616,
+ 0.0,
+ 647.799316,
+ 494.638641,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 1.0,
+ -0.0,
+ 0.0,
+ 0.111049,
+ 0.0,
+ 0.258819,
+ 0.965926,
+ 0.263806,
+ -0.0,
+ -0.965926,
+ 0.258819,
+ 0.347685,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.2423,
+ 0.057169,
+ 0.000302,
+ 0.000016,
+ -0.005638
+ ],
+ "calibration_timestamp": 1708833147338466592,
+ "camera_id": "24-07",
+ "camera_number": 0,
+ "reprojection_error": 1.362672
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_imu-971-1_cam-24-08_2024-03-01_11-02-11.982641320.json b/y2024/constants/calib_files/calibration_imu-971-1_cam-24-08_2024-03-01_11-02-11.982641320.json
new file mode 100755
index 0000000..fbe79d5
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_imu-971-1_cam-24-08_2024-03-01_11-02-11.982641320.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "imu",
+ "team_number": 971,
+ "intrinsics": [
+ 645.963562,
+ 0.0,
+ 751.21698,
+ 0.0,
+ 645.34906,
+ 605.204102,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 0.0,
+ -0.258819,
+ -0.965926,
+ -0.323293,
+ 1.0,
+ 0.0,
+ -0.0,
+ 0.268249,
+ 0.0,
+ -0.965926,
+ 0.258819,
+ 0.471129,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.248733,
+ 0.06221,
+ -0.000901,
+ 0.000128,
+ -0.006595
+ ],
+ "calibration_timestamp": 1708820514420797344,
+ "camera_id": "24-08",
+ "camera_number": 1,
+ "reprojection_error": 1.591953
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-7971-1-0_cam-24-04_2024-03-02_20-09-18.016860291.json b/y2024/constants/calib_files/calibration_orin-7971-1-0_cam-24-04_2024-03-02_20-09-18.016860291.json
new file mode 100755
index 0000000..d60cc2a
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-7971-1-0_cam-24-04_2024-03-02_20-09-18.016860291.json
@@ -0,0 +1,45 @@
+{
+ "node_name": "orin1",
+ "team_number": 7971,
+ "intrinsics": [
+ 642.80365,
+ 0.0,
+ 718.017517,
+ 0.0,
+ 642.83667,
+ 555.022461,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ -0.845268,
+ 0.031126,
+ 0.533435,
+ 0.494822,
+ -0.525295,
+ 0.134521,
+ -0.84022,
+ -1.212857,
+ -0.097911,
+ -0.990422,
+ -0.097356,
+ -0.319412,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.239969,
+ 0.055889,
+ 0.000086,
+ 0.000099,
+ -0.005468
+ ],
+ "calibration_timestamp": 1709438958016860291,
+ "camera_id": "24-04",
+ "camera_number": 0
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-7971-1-1_cam-24-02_2024-03-02_20-09-18.022901293.json b/y2024/constants/calib_files/calibration_orin-7971-1-1_cam-24-02_2024-03-02_20-09-18.022901293.json
new file mode 100755
index 0000000..ed7b2f3
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-7971-1-1_cam-24-02_2024-03-02_20-09-18.022901293.json
@@ -0,0 +1,45 @@
+{
+ "node_name": "orin1",
+ "team_number": 7971,
+ "intrinsics": [
+ 644.604858,
+ 0.0,
+ 752.152954,
+ 0.0,
+ 644.477173,
+ 558.911682,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ -0.07336,
+ 0.085699,
+ 0.993617,
+ 0.608349,
+ -0.989433,
+ 0.118683,
+ -0.083288,
+ -0.939084,
+ -0.125063,
+ -0.989227,
+ 0.076087,
+ -0.35071,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.242432,
+ 0.057303,
+ -0.000057,
+ 0.000015,
+ -0.005636
+ ],
+ "calibration_timestamp": 1709438958022901293,
+ "camera_id": "24-02",
+ "camera_number": 1
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-7971-c-1_cam-24-03_2024-03-02_20-09-18.016217514.json b/y2024/constants/calib_files/calibration_orin-7971-c-1_cam-24-03_2024-03-02_20-09-18.016217514.json
new file mode 100755
index 0000000..c0847a8
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-7971-c-1_cam-24-03_2024-03-02_20-09-18.016217514.json
@@ -0,0 +1,45 @@
+{
+ "node_name": "imu",
+ "team_number": 7971,
+ "intrinsics": [
+ 648.13446,
+ 0.0,
+ 759.733093,
+ 0.0,
+ 648.40332,
+ 557.951538,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 0.646787,
+ 0.04307,
+ 0.761453,
+ 0.280984,
+ -0.762291,
+ 0.067995,
+ 0.643653,
+ -0.238549,
+ -0.024053,
+ -0.996756,
+ 0.07681,
+ -0.200534,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.258663,
+ 0.071646,
+ 0.000113,
+ -0.000061,
+ -0.00879
+ ],
+ "calibration_timestamp": 1709438958016217514,
+ "camera_id": "24-03",
+ "camera_number": 1
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-10_2024-02-24_16-44-05.975708672.json b/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-10_2024-02-24_16-44-05.975708672.json
new file mode 100755
index 0000000..0912038
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-10_2024-02-24_16-44-05.975708672.json
@@ -0,0 +1,26 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 646.04834,
+ 0.0,
+ 703.327576,
+ 0.0,
+ 645.444458,
+ 527.86261,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.251594,
+ 0.064935,
+ 0.000479,
+ 0.000036,
+ -0.007207
+ ],
+ "calibration_timestamp": 1708821845975708672,
+ "camera_id": "24-10",
+ "camera_number": 0,
+ "reprojection_error": 1.523209
+}
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-09_2024-02-24_16-10-16.872521280.json b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-09_2024-02-24_16-10-16.872521280.json
new file mode 100755
index 0000000..9eed9bf
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-09_2024-02-24_16-10-16.872521280.json
@@ -0,0 +1,26 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 648.187805,
+ 0.0,
+ 736.903137,
+ 0.0,
+ 648.028687,
+ 557.169861,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.265564,
+ 0.078084,
+ -0.000231,
+ 0.000386,
+ -0.010425
+ ],
+ "calibration_timestamp": 1708819816872521280,
+ "camera_id": "24-09",
+ "camera_number": 1,
+ "reprojection_error": 1.881098
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-11_2024-02-24_16-44-06.986729504.json b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-11_2024-02-24_16-44-06.986729504.json
new file mode 100755
index 0000000..4957b75
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-11_2024-02-24_16-44-06.986729504.json
@@ -0,0 +1,26 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 649.866699,
+ 0.0,
+ 709.355713,
+ 0.0,
+ 648.893066,
+ 576.101868,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.248092,
+ 0.060938,
+ 0.000313,
+ 0.00009,
+ -0.006163
+ ],
+ "calibration_timestamp": 1708821846986729504,
+ "camera_id": "24-11",
+ "camera_number": 1,
+ "reprojection_error": 1.450069
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-12_2024-02-24_19-52-39.488095264.json b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-12_2024-02-24_19-52-39.488095264.json
new file mode 100755
index 0000000..0fda16d
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-12_2024-02-24_19-52-39.488095264.json
@@ -0,0 +1,26 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 647.19928,
+ 0.0,
+ 690.698181,
+ 0.0,
+ 646.449158,
+ 530.162842,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.249799,
+ 0.062593,
+ 0.00003,
+ 0.000366,
+ -0.006532
+ ],
+ "calibration_timestamp": 1708833159488095264,
+ "camera_id": "24-12",
+ "camera_number": 1,
+ "reprojection_error": 1.23409
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin1-971-0_cam-24-05_2024-03-01_11-01-05.102438041.json b/y2024/constants/calib_files/calibration_orin1-971-0_cam-24-05_2024-03-01_11-01-05.102438041.json
new file mode 100755
index 0000000..317e453
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin1-971-0_cam-24-05_2024-03-01_11-01-05.102438041.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 648.360168,
+ 0.0,
+ 729.818665,
+ 0.0,
+ 648.210327,
+ 641.988037,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.284397,
+ -1.0,
+ 0.0,
+ 0.0,
+ 0.226771,
+ 0.0,
+ -1.0,
+ 0.0,
+ 0.442951,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.255473,
+ 0.068444,
+ 0.000028,
+ -0.000078,
+ -0.008004
+ ],
+ "calibration_timestamp": 409227793683328,
+ "camera_id": "24-05",
+ "camera_number": 0,
+ "reprojection_error": 1.058851
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin1-971-1_cam-24-06_2024-03-01_11-01-20.409861949.json b/y2024/constants/calib_files/calibration_orin1-971-1_cam-24-06_2024-03-01_11-01-20.409861949.json
new file mode 100755
index 0000000..0eb10db
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin1-971-1_cam-24-06_2024-03-01_11-01-20.409861949.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 648.644104,
+ 0.0,
+ 755.677979,
+ 0.0,
+ 648.522644,
+ 597.744812,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ -1.0,
+ 0.0,
+ 0.0,
+ 0.111049,
+ -0.0,
+ -0.258819,
+ -0.965926,
+ -0.263806,
+ 0.0,
+ -0.965926,
+ 0.258819,
+ 0.347685,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.25182,
+ 0.063137,
+ 0.000118,
+ 0.000005,
+ -0.006342
+ ],
+ "calibration_timestamp": 409229245444672,
+ "camera_id": "24-06",
+ "camera_number": 1,
+ "reprojection_error": 1.344104
+}
\ No newline at end of file
diff --git a/y2024/constants/common.jinja2 b/y2024/constants/common.jinja2
index d6db5e5..27f28ac 100644
--- a/y2024/constants/common.jinja2
+++ b/y2024/constants/common.jinja2
@@ -1,27 +1,82 @@
{% set pi = 3.14159265 %}
-{# we do this here so we keep the encoder ratio in plaintext and also keep the math we're using. #}
-{% set intake_pivot_encoder_ratio = (24.0 / 15.0) %}
-
{%set zeroing_sample_size = 200 %}
+{# Intake #}
+{# we do this here so we keep the encoder ratio in plaintext and also keep the math we're using. #}
+{% set intake_pivot_encoder_ratio = (15.0 / 24.0) %}
+
+{% set intake_upper = 1.75 %}
+{% set intake_lower = -0.195 %}
+
{%
set intake_pivot_zero = {
"average_filter_size": zeroing_sample_size,
"one_revolution_distance": pi * 2.0 * intake_pivot_encoder_ratio,
+ "middle_position": (intake_upper + intake_lower) / 2,
"zeroing_threshold": 0.0005,
"moving_buffer_size": 20,
"allowable_encoder_error": 0.9
}
%}
-{# TODO(Filip): Update climber values #}
-{% set climber_encoder_ratio = (1.0 / 1.0) %}
-{% set climber_radius = 0.436496 %}
+{% set extend_encoder_ratio = (1.0 / 1.0) %}
+{% set extend_radius = 36.0 * 0.005 / 2.0 / pi %}
+{%
+set extend_zero = {
+ "average_filter_size": zeroing_sample_size,
+ "one_revolution_distance": pi * 2.0 * extend_encoder_ratio * extend_radius,
+ "zeroing_threshold": 0.0005,
+ "moving_buffer_size": 20,
+ "allowable_encoder_error": 0.9
+}
+%}
+
+{% set climber_encoder_ratio = (16.0 / 60.0) %}
+{% set climber_circumference = 16.0 * 0.25 * 0.0254 %}
{%
set climber_zero = {
"average_filter_size": zeroing_sample_size,
- "one_revolution_distance": pi * 2.0 * climber_encoder_ratio * climber_radius,
+ "one_revolution_distance": climber_encoder_ratio * climber_circumference,
+ "zeroing_threshold": 0.0005,
+ "moving_buffer_size": 20,
+ "allowable_encoder_error": 0.9
+}
+%}
+
+{# Catapult #}
+{% set catapult_encoder_ratio = (12.0 / 24.0) %}
+
+{%
+set catapult_zero = {
+ "average_filter_size": zeroing_sample_size,
+ "one_revolution_distance": pi * 2.0 * catapult_encoder_ratio,
+ "zeroing_threshold": 0.0005,
+ "moving_buffer_size": 20,
+ "allowable_encoder_error": 0.9
+}
+%}
+
+{# Altitude #}
+{% set altitude_encoder_ratio = (16.0 / 162.0) %}
+
+{%
+set altitude_zero = {
+ "average_filter_size": zeroing_sample_size,
+ "one_revolution_distance": pi * 2.0 * altitude_encoder_ratio,
+ "zeroing_threshold": 0.0005,
+ "moving_buffer_size": 20,
+ "allowable_encoder_error": 0.9
+}
+%}
+
+{# Turret #}
+{% set turret_encoder_ratio = (22.0 / 100.0) %}
+
+{%
+set turret_zero = {
+ "average_filter_size": zeroing_sample_size,
+ "one_revolution_distance": pi * 2.0 * turret_encoder_ratio,
"zeroing_threshold": 0.0005,
"moving_buffer_size": 20,
"allowable_encoder_error": 0.9
diff --git a/y2024/constants/common.json b/y2024/constants/common.json
index 554f1e8..1050815 100644
--- a/y2024/constants/common.json
+++ b/y2024/constants/common.json
@@ -1,21 +1,34 @@
+{% from 'y2024/constants/common.jinja2' import intake_upper, intake_lower %}
+
"common": {
"target_map": {% include 'y2024/vision/maps/target_map.json' %},
"shooter_interpolation_table": [
{
- "distance_from_goal": 0.0,
+ "distance_from_goal": 5.0,
"shot_params": {
"shot_velocity": 0.0,
- "shot_angle": 0.0
+ "shot_altitude_angle": 0.0,
+ "shot_catapult_angle": 0.0,
+ "shot_speed_over_ground": 2.0
}
+ },
+ {
+ "distance_from_goal": 10.0,
+ "shot_params": {
+ "shot_velocity": 0.0,
+ "shot_altitude_angle": 0.0,
+ "shot_catapult_angle": 0.0,
+ "shot_speed_over_ground": 4.0
+ }
}
],
"intake_roller_voltages": {
- "spitting": -12.0,
+ "spitting": -4.0,
"intaking": 12.0
},
"intake_pivot_set_points": {
- "extended": 1.5,
- "retracted": 0.5
+ "extended": -0.03,
+ "retracted": 1.73
},
"intake_pivot": {
"zeroing_voltage": 3.0,
@@ -25,14 +38,14 @@
"max_acceleration": 3.0
},
"default_profile_params":{
- "max_velocity": 6.0,
- "max_acceleration": 30.0
+ "max_velocity": 4.0,
+ "max_acceleration": 10.0
},
"range": {
- "lower_hard": -0.85,
- "upper_hard": 1.85,
- "lower": -0.400,
- "upper": 1.57
+ "lower_hard": -0.2,
+ "upper_hard": 1.80,
+ "lower": {{ intake_lower }},
+ "upper": {{ intake_upper }}
},
"loop": {% include 'y2024/control_loops/superstructure/intake_pivot/integral_intake_pivot_plant.json' %}
},
@@ -40,25 +53,44 @@
// TODO: (niko) update the stator and supply current limits for the intake
"current_limits": {
// Values in amps
- "intake_pivot_supply_current_limit": 35,
- "intake_pivot_stator_current_limit": 60,
- "intake_roller_supply_current_limit": 35,
- "intake_roller_stator_current_limit": 60,
- "transfer_roller_supply_current_limit": 35,
- "transfer_roller_stator_current_limit": 60,
+ "intake_pivot_supply_current_limit": 40,
+ "intake_pivot_stator_current_limit": 100,
+ "intake_roller_supply_current_limit": 20,
+ "intake_roller_stator_current_limit": 50,
+ "transfer_roller_supply_current_limit": 20,
+ "transfer_roller_stator_current_limit": 50,
"drivetrain_supply_current_limit": 35,
"drivetrain_stator_current_limit": 60,
- "climber_supply_current_limit": 35,
- "climber_stator_current_limit": 60
+ "climber_supply_current_limit": 30,
+ "climber_stator_current_limit": 100,
+ "extend_supply_current_limit": 20,
+ "extend_stator_current_limit": 100,
+ "extend_roller_supply_current_limit": 60,
+ "extend_roller_stator_current_limit": 200,
+ "turret_supply_current_limit": 20,
+ "turret_stator_current_limit": 40,
+ "altitude_supply_current_limit": 10,
+ "altitude_stator_current_limit": 60,
+ "catapult_supply_current_limit": 60,
+ "catapult_stator_current_limit": 250,
+ "retention_roller_stator_current_limit": 20,
+ "slower_retention_roller_stator_current_limit": 2,
+ "shooting_retention_roller_stator_current_limit": -20,
+ "retention_roller_supply_current_limit": 10
},
"transfer_roller_voltages": {
"transfer_in": 12.0,
- "transfer_out": -12.0
+ "transfer_out": -4.0,
+ "extend_moving": 4.0
+ },
+ "extend_roller_voltages": {
+ "scoring": 12.0,
+ "reversing": -4.0
},
"climber_set_points": {
- "full_extend": 0.8,
- "half_extend": 0.6,
- "retract": 0.2
+ "full_extend": -0.005,
+ "stowed": -0.35,
+ "retract": -0.478
},
"climber": {
"zeroing_voltage": 3.0,
@@ -68,15 +100,143 @@
"max_acceleration": 3.0
},
"default_profile_params":{
- "max_velocity": 6.0,
- "max_acceleration": 30.0
+ "max_velocity": 0.05,
+ "max_acceleration": 3.0
},
"range": {
- "lower_hard": 0.1,
- "upper_hard": 2.01,
- "lower": 0.2,
- "upper": 2.0
+ "lower_hard": -0.488,
+ "upper_hard": 0.005,
+ "lower": -0.478,
+ "upper": -0.005
},
"loop": {% include 'y2024/control_loops/superstructure/climber/integral_climber_plant.json' %}
- }
+ },
+ "catapult": {
+ "zeroing_voltage": 3.0,
+ "operating_voltage": 12.0,
+ "zeroing_profile_params": {
+ "max_velocity": 1.0,
+ "max_acceleration": 6.0
+ },
+ "default_profile_params":{
+ "max_velocity": 0.025,
+ "max_acceleration": 0.05
+ },
+ "range": {
+ "lower_hard": -0.05,
+ "upper_hard": 4.2,
+ "lower": 0.0,
+ "upper": 2.38
+ },
+ "loop": {% include 'y2024/control_loops/superstructure/catapult/integral_catapult_plant.json' %}
+ },
+ // TODO: (max) update the constants for the shooter subsystems
+ "altitude": {
+ "zeroing_voltage": 3.0,
+ "operating_voltage": 12.0,
+ "zeroing_profile_params": {
+ "max_velocity": 0.5,
+ "max_acceleration": 3.0
+ },
+ "default_profile_params":{
+ "max_velocity": 3.0,
+ "max_acceleration": 5.0
+ },
+ "range": {
+ "lower_hard": -0.01,
+ "upper_hard": 1.66,
+ "lower": 0.0135,
+ "upper": 1.57
+ },
+ "loop": {% include 'y2024/control_loops/superstructure/altitude/integral_altitude_plant.json' %}
+ },
+ "turret": {
+ "zeroing_voltage": 3.0,
+ "operating_voltage": 12.0,
+ "zeroing_profile_params": {
+ "max_velocity": 0.5,
+ "max_acceleration": 3.0
+ },
+ "default_profile_params":{
+ "max_velocity": 2.0,
+ "max_acceleration": 5.0
+ },
+ "range": {
+ "lower_hard": -4.8,
+ "upper_hard": 4.8,
+ "lower": -4.7,
+ "upper": 4.7
+ },
+ "loop": {% include 'y2024/control_loops/superstructure/turret/integral_turret_plant.json' %}
+ },
+ "extend": {
+ "zeroing_voltage": 3.0,
+ "operating_voltage": 12.0,
+ "zeroing_profile_params": {
+ "max_velocity": 0.5,
+ "max_acceleration": 3.0
+ },
+ "default_profile_params":{
+ "max_velocity": 0.1,
+ "max_acceleration": 0.3
+ },
+ "range": {
+ "lower_hard": -0.005,
+ "upper_hard": 0.47,
+ "lower": 0.005,
+ "upper": 0.46
+ },
+ "loop": {% include 'y2024/control_loops/superstructure/extend/integral_extend_plant.json' %}
+ },
+ "shooter_targets": {
+ "red_alliance": {
+ "pos": {
+ "rows": 3,
+ "cols": 1,
+ "storage_order": "ColMajor",
+ // The data field contains the x, y and z
+ // coordinates of the speaker on the red alliance
+ "data": [8.0645, 1.4435, 2.0705]
+ },
+ "theta": 0.0
+ },
+ "blue_alliance": {
+ "pos": {
+ "rows": 3,
+ "cols": 1,
+ "storage_order": "ColMajor",
+ // The data field contains the x, y and z
+ // coordinates of the speaker on the blue alliance
+ "data": [-8.0645, 1.4435, 2.0705]
+ },
+ "theta": 0.0
+ }
+ },
+ "altitude_loading_position": 0.02,
+ "turret_loading_position": 0.58,
+ "catapult_return_position": 0.0,
+ "min_altitude_shooting_angle": 0.55,
+ "max_altitude_shooting_angle": 0.89,
+ "retention_roller_voltages": {
+ "retaining": 1.5,
+ "spitting": 6.0
+ },
+ // TODO(Filip): Update the speaker and amp shooter setpoints
+ "shooter_speaker_set_point": {
+ "turret_position": 0.0,
+ "altitude_position": 0.75,
+ "shot_velocity": 0.0
+ },
+ "shooter_podium_set_point":{
+ "turret_position": 0.0,
+ "altitude_position": 0.0,
+ "shot_velocity": 0.0
+ },
+ "extend_set_points": {
+ "trap": 0.46,
+ "amp": 0.2,
+ "catapult": 0.017,
+ "retracted": 0.017
+ },
+ "turret_avoid_extend_collision_position": 0.0
}
diff --git a/y2024/constants/constants.fbs b/y2024/constants/constants.fbs
index 40d609f..31e2057 100644
--- a/y2024/constants/constants.fbs
+++ b/y2024/constants/constants.fbs
@@ -2,6 +2,7 @@
include "frc971/vision/target_map.fbs";
include "frc971/control_loops/profiled_subsystem.fbs";
include "frc971/zeroing/constants.fbs";
+include "frc971/math/matrix.fbs";
include "frc971/control_loops/drivetrain/drivetrain_config.fbs";
namespace y2024;
@@ -12,7 +13,15 @@
table ShotParams {
shot_velocity: double (id: 0);
- shot_angle: double (id: 1);
+
+ // Angle of the altitude
+ shot_altitude_angle: double (id: 1);
+
+ // Angle of the catapult
+ shot_catapult_angle: double (id: 2);
+
+ // Speed over ground to use for shooting on the fly
+ shot_speed_over_ground: double (id: 3);
}
table InterpolationTablePoint {
@@ -42,7 +51,7 @@
// and retracted, which represents meters for when ClimberGoal is RETRACT
table ClimberSetPoints {
full_extend:double (id: 0);
- half_extend:double (id: 1);
+ stowed:double (id: 1);
retract:double (id: 2);
}
@@ -63,17 +72,79 @@
drivetrain_stator_current_limit:double (id: 7);
climber_supply_current_limit:double (id: 8);
climber_stator_current_limit:double (id: 9);
+ extend_supply_current_limit:double (id: 10);
+ extend_stator_current_limit:double (id: 11);
+ extend_roller_supply_current_limit:double (id: 12);
+ extend_roller_stator_current_limit:double (id: 13);
+ turret_supply_current_limit:double (id: 14);
+ turret_stator_current_limit:double (id: 15);
+ altitude_supply_current_limit:double (id: 16);
+ altitude_stator_current_limit:double (id: 17);
+ retention_roller_supply_current_limit:double (id: 18);
+ retention_roller_stator_current_limit:double (id: 19);
+ slower_retention_roller_stator_current_limit:double (id: 20);
+ shooting_retention_roller_stator_current_limit:double (id: 23);
+ catapult_supply_current_limit:double (id: 21);
+ catapult_stator_current_limit:double (id: 22);
}
table TransferRollerVoltages {
+ // Voltage to apply while intaking the game piece.
transfer_in:double (id: 0);
+ // Voltage to apply while spitting the game piece.
transfer_out:double (id: 1);
+ // Voltage to apply while the extend is moving and dragging the
+ // game piece out of the rollers.
+ extend_moving:double (id: 2);
+}
+
+table ExtendRollerVoltages {
+ scoring:double (id: 0);
+ reversing:double (id: 1);
+}
+
+// Extend positions
+table ExtendSetPoints {
+ // The position which lets us score on the trap.
+ trap:double (id: 0);
+ // The position which lets us score on the amp.
+ amp:double (id: 1);
+ // The position which lets us transfer into the catapult.
+ catapult:double (id: 2);
+ // The position near 0 where we are ready to intake a note.
+ retracted:double (id: 3);
}
table RobotConstants {
- intake_constants:PotAndAbsEncoderConstants (id: 0);
+ intake_constants:frc971.zeroing.AbsoluteEncoderZeroingConstants (id: 0);
climber_constants:PotAndAbsEncoderConstants (id: 1);
+ catapult_constants:PotAndAbsEncoderConstants (id: 2);
+ altitude_constants:PotAndAbsEncoderConstants (id: 3);
+ turret_constants:PotAndAbsEncoderConstants (id: 4);
+ extend_constants:PotAndAbsEncoderConstants (id: 5);
+}
+table ShooterSetPoint {
+ turret_position: double (id: 0);
+ altitude_position: double (id: 1);
+ shot_velocity:double (id: 2);
+}
+
+table Pose {
+ // Pos is a 3x1 matrix which contains the (x, y, z) component of the Pose.
+ pos: frc971.fbs.Matrix (id: 0);
+ theta: double (id: 1);
+}
+
+table ShooterTargets {
+ // The Pose of the red and blue alliance speakers we are aiming at.
+ red_alliance: Pose (id: 0);
+ blue_alliance: Pose (id: 1);
+}
+
+table RetentionRollerVoltages {
+ retaining:double (id: 0);
+ spitting:double (id: 1);
}
// Common table for constants unrelated to the robot
@@ -88,12 +159,30 @@
transfer_roller_voltages:TransferRollerVoltages (id: 7);
climber:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 8);
climber_set_points:ClimberSetPoints (id: 9);
+ turret_loading_position: double (id: 10);
+ catapult_return_position: double (id: 11);
+ catapult:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 12);
+ altitude:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 13);
+ turret:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 14);
+ extend:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 15);
+ extend_roller_voltages:ExtendRollerVoltages (id: 16);
+ shooter_targets:ShooterTargets (id: 17);
+ altitude_loading_position: double (id: 18);
+ retention_roller_voltages:RetentionRollerVoltages (id: 19);
+ min_altitude_shooting_angle: double (id: 20);
+ max_altitude_shooting_angle: double (id: 25);
+ shooter_speaker_set_point: ShooterSetPoint (id: 21);
+ shooter_podium_set_point: ShooterSetPoint (id: 22);
+ extend_set_points:ExtendSetPoints (id: 23);
+ // The position to move the turret to when avoiding collision
+ // with the extend when the extend is moving to amp/trap position.
+ turret_avoid_extend_collision_position: double (id: 24);
}
table Constants {
- robot:RobotConstants (id: 0);
- common:Common (id: 1);
- cameras:[CameraConfiguration] (id: 2);
+ cameras:[CameraConfiguration] (id: 0);
+ robot:RobotConstants (id: 1);
+ common:Common (id: 2);
}
root_type Constants;
diff --git a/y2024/constants/constants_formatter.cc b/y2024/constants/constants_formatter.cc
new file mode 100644
index 0000000..d857407
--- /dev/null
+++ b/y2024/constants/constants_formatter.cc
@@ -0,0 +1,26 @@
+#include "glog/logging.h"
+
+#include "aos/flatbuffers.h"
+#include "aos/init.h"
+#include "aos/json_to_flatbuffer.h"
+#include "aos/util/file.h"
+#include "y2024/constants/constants_list_generated.h"
+
+int main(int argc, char **argv) {
+ ::aos::InitGoogle(&argc, &argv);
+
+ CHECK(argc == 3) << ": Expected input and output json files to be passed in.";
+
+ aos::FlatbufferDetachedBuffer<y2024::ConstantsList> constants =
+ aos::JsonFileToFlatbuffer<y2024::ConstantsList>(argv[1]);
+
+ // Make sure the file is valid json before we output a formatted version.
+ CHECK(constants.message().constants() != nullptr)
+ << ": Failed to parse " << std::string(argv[2]);
+
+ aos::util::WriteStringToFileOrDie(
+ std::string(argv[2]),
+ aos::FlatbufferToJson(constants, {.multi_line = true}));
+
+ return 0;
+}
diff --git a/y2024/constants/constants_validator_test.cc b/y2024/constants/constants_validator_test.cc
deleted file mode 100644
index cd478a8..0000000
--- a/y2024/constants/constants_validator_test.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "glog/logging.h"
-#include "gtest/gtest.h"
-
-#include "aos/json_to_flatbuffer.h"
-#include "y2024/constants/constants_list_generated.h"
-
-namespace y2024::constants::testing {
-class ConstantsValidatorTest : public ::testing::Test {};
-
-TEST_F(ConstantsValidatorTest, CheckConstants) {
- CHECK_NOTNULL(aos::JsonFileToFlatbuffer<y2024::ConstantsList>(
- "y2024/constants/constants.json")
- .message()
- .constants());
-}
-
-} // namespace y2024::constants::testing
diff --git a/y2024/constants/test_data/calibration_cam-1.json b/y2024/constants/test_data/calibration_cam-1.json
new file mode 100644
index 0000000..2d81347
--- /dev/null
+++ b/y2024/constants/test_data/calibration_cam-1.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "orin1",
+ "camera_number": 0,
+ "team_number": 7971,
+ "intrinsics": [
+ 893.759521,
+ 0,
+ 645.470764,
+ 0,
+ 893.222351,
+ 388.150269,
+ 0,
+ 0,
+ 1
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 0.0,
+ 0.0,
+ 1.0,
+ 1.0,
+
+ -1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+
+ 0.0,
+ -1.0,
+ 0.0,
+ 0.0,
+
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.44902,
+ 0.248409,
+ -0.000537,
+ -0.000112,
+ -0.076989
+ ]
+}
diff --git a/y2024/constants/test_data/calibration_cam-2.json b/y2024/constants/test_data/calibration_cam-2.json
new file mode 100644
index 0000000..1128799
--- /dev/null
+++ b/y2024/constants/test_data/calibration_cam-2.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "orin1",
+ "camera_number": 1,
+ "team_number": 7971,
+ "intrinsics": [
+ 893.759521,
+ 0,
+ 645.470764,
+ 0,
+ 893.222351,
+ 388.150269,
+ 0,
+ 0,
+ 1
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 1.0,
+ 0.0,
+ 0.0,
+ 1.0,
+
+ 0.0,
+ 0.0,
+ -1.0,
+ 0.0,
+
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.44902,
+ 0.248409,
+ -0.000537,
+ -0.000112,
+ -0.076989
+ ]
+}
diff --git a/y2024/constants/test_data/calibration_cam-3.json b/y2024/constants/test_data/calibration_cam-3.json
new file mode 100644
index 0000000..16e67ec
--- /dev/null
+++ b/y2024/constants/test_data/calibration_cam-3.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "imu",
+ "camera_number": 0,
+ "team_number": 7971,
+ "intrinsics": [
+ 893.759521,
+ 0,
+ 645.470764,
+ 0,
+ 893.222351,
+ 388.150269,
+ 0,
+ 0,
+ 1
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ 0.0,
+ 1.0,
+ 0.0,
+ 1.0,
+
+ 0.0,
+ 0.0,
+ -1.0,
+ 0.0,
+
+ -1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.44902,
+ 0.248409,
+ -0.000537,
+ -0.000112,
+ -0.076989
+ ]
+}
diff --git a/y2024/constants/test_data/calibration_cam-4.json b/y2024/constants/test_data/calibration_cam-4.json
new file mode 100644
index 0000000..1e5b623
--- /dev/null
+++ b/y2024/constants/test_data/calibration_cam-4.json
@@ -0,0 +1,46 @@
+{
+ "node_name": "imu",
+ "camera_number": 1,
+ "team_number": 7971,
+ "intrinsics": [
+ 893.759521,
+ 0,
+ 645.470764,
+ 0,
+ 893.222351,
+ 388.150269,
+ 0,
+ 0,
+ 1
+ ],
+ "fixed_extrinsics": {
+ "data": [
+ -1.0,
+ 0.0,
+ 0.0,
+ 1.0,
+
+ 0.0,
+ 0.0,
+ -1.0,
+ 0.0,
+
+ 0.0,
+ -1.0,
+ 0.0,
+ 0.0,
+
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ },
+ "dist_coeffs": [
+ -0.44902,
+ 0.248409,
+ -0.000537,
+ -0.000112,
+ -0.076989
+ ]
+}
diff --git a/y2024/constants/test_data/test_team.json b/y2024/constants/test_data/test_team.json
index 9e6bafb..b717224 100644
--- a/y2024/constants/test_data/test_team.json
+++ b/y2024/constants/test_data/test_team.json
@@ -1,17 +1,32 @@
{% from 'y2024/constants/common.jinja2' import intake_pivot_zero %}
{% from 'y2024/constants/common.jinja2' import climber_zero %}
+{% from 'y2024/constants/common.jinja2' import catapult_zero %}
+{% from 'y2024/constants/common.jinja2' import altitude_zero %}
+{% from 'y2024/constants/common.jinja2' import turret_zero %}
+{% from 'y2024/constants/common.jinja2' import extend_zero %}
{
- "robot": {
- "intake_constants": {
- {% set _ = intake_pivot_zero.update(
- {
- "measured_absolute_position" : 0.0
- }
- ) %}
- "zeroing_constants": {{ intake_pivot_zero | tojson(indent=2)}},
- "potentiometer_offset": 0.0
+ "cameras": [
+ {
+ "calibration": {% include 'y2024/constants/test_data/calibration_cam-1.json' %}
},
+ {
+ "calibration": {% include 'y2024/constants/test_data/calibration_cam-2.json' %}
+ },
+ {
+ "calibration": {% include 'y2024/constants/test_data/calibration_cam-3.json' %}
+ },
+ {
+ "calibration": {% include 'y2024/constants/test_data/calibration_cam-4.json' %}
+ }
+ ],
+ "robot": {
+ {% set _ = intake_pivot_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "intake_constants": {{ intake_pivot_zero | tojson(indent=2)}},
"climber_constants": {
{% set _ = climber_zero.update(
{
@@ -20,6 +35,42 @@
) %}
"zeroing_constants": {{ climber_zero | tojson(indent=2)}},
"potentiometer_offset": 0.0
+ },
+ "catapult_constants": {
+ {% set _ = catapult_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ catapult_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "altitude_constants": {
+ {% set _ = altitude_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ altitude_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "turret_constants": {
+ {% set _ = turret_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ turret_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
+ },
+ "extend_constants": {
+ {% set _ = extend_zero.update(
+ {
+ "measured_absolute_position" : 0.0
+ }
+ ) %}
+ "zeroing_constants": {{ extend_zero | tojson(indent=2)}},
+ "potentiometer_offset": 0.0
}
},
{% include 'y2024/constants/common.json' %}
diff --git a/y2024/constants/validator.bzl b/y2024/constants/validator.bzl
new file mode 100644
index 0000000..6121f58
--- /dev/null
+++ b/y2024/constants/validator.bzl
@@ -0,0 +1,13 @@
+load("@aspect_bazel_lib//lib:run_binary.bzl", "run_binary")
+
+# Validates the constants.json file and outputs a formatted version.
+# TODO(max): Make this generic/template it out into frc971
+def constants_json(name, src, out):
+ run_binary(
+ name = name,
+ tool = "//y2024/constants:constants_formatter",
+ srcs = [src],
+ outs = [out],
+ args = ["$(location %s)" % (src)] + ["$(location %s)" % (out)],
+ visibility = ["//visibility:public"],
+ )
diff --git a/y2024/control_loops/drivetrain/drivetrain_main.cc b/y2024/control_loops/drivetrain/drivetrain_main.cc
index e0b2eb7..82629de 100644
--- a/y2024/control_loops/drivetrain/drivetrain_main.cc
+++ b/y2024/control_loops/drivetrain/drivetrain_main.cc
@@ -18,14 +18,15 @@
frc971::constants::WaitForConstants<y2024::Constants>(&config.message());
aos::ShmEventLoop event_loop(&config.message());
+ const auto drivetrain_config =
+ ::y2024::control_loops::drivetrain::GetDrivetrainConfig(&event_loop);
+
std::unique_ptr<::frc971::control_loops::drivetrain::PuppetLocalizer>
localizer = std::make_unique<
::frc971::control_loops::drivetrain::PuppetLocalizer>(
- &event_loop,
- ::y2024::control_loops::drivetrain::GetDrivetrainConfig(&event_loop));
+ &event_loop, drivetrain_config);
std::unique_ptr<DrivetrainLoop> drivetrain = std::make_unique<DrivetrainLoop>(
- y2024::control_loops::drivetrain::GetDrivetrainConfig(&event_loop),
- &event_loop, localizer.get());
+ drivetrain_config, &event_loop, localizer.get());
event_loop.Run();
diff --git a/y2024/control_loops/python/BUILD b/y2024/control_loops/python/BUILD
index da80d87..9cbfcf8 100644
--- a/y2024/control_loops/python/BUILD
+++ b/y2024/control_loops/python/BUILD
@@ -77,6 +77,67 @@
],
)
+py_binary(
+ name = "catapult",
+ srcs = [
+ "catapult.py",
+ ],
+ legacy_create_init = False,
+ target_compatible_with = ["@platforms//cpu:x86_64"],
+ deps = [
+ "//frc971/control_loops/python:angular_system",
+ "//frc971/control_loops/python:controls",
+ ],
+)
+
+py_binary(
+ name = "turret",
+ srcs = [
+ "turret.py",
+ ],
+ legacy_create_init = False,
+ target_compatible_with = ["@platforms//cpu:x86_64"],
+ deps = [
+ ":python_init",
+ "//frc971/control_loops/python:angular_system",
+ "//frc971/control_loops/python:controls",
+ "@pip//glog",
+ "@pip//python_gflags",
+ ],
+)
+
+py_binary(
+ name = "altitude",
+ srcs = [
+ "altitude.py",
+ ],
+ legacy_create_init = False,
+ target_compatible_with = ["@platforms//cpu:x86_64"],
+ deps = [
+ ":python_init",
+ "//frc971/control_loops/python:angular_system",
+ "//frc971/control_loops/python:controls",
+ "@pip//glog",
+ "@pip//python_gflags",
+ ],
+)
+
+py_binary(
+ name = "extend",
+ srcs = [
+ "extend.py",
+ ],
+ legacy_create_init = False,
+ target_compatible_with = ["@platforms//cpu:x86_64"],
+ deps = [
+ ":python_init",
+ "//frc971/control_loops/python:controls",
+ "//frc971/control_loops/python:linear_system",
+ "@pip//glog",
+ "@pip//python_gflags",
+ ],
+)
+
py_library(
name = "python_init",
srcs = ["__init__.py"],
diff --git a/y2024/control_loops/python/altitude.py b/y2024/control_loops/python/altitude.py
new file mode 100644
index 0000000..d302b79
--- /dev/null
+++ b/y2024/control_loops/python/altitude.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python3
+
+from aos.util.trapezoid_profile import TrapezoidProfile
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import angular_system
+from frc971.control_loops.python import controls
+import numpy
+import sys
+from matplotlib import pylab
+import gflags
+import glog
+
+FLAGS = gflags.FLAGS
+
+try:
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+except gflags.DuplicateFlagError:
+ pass
+
+gflags.DEFINE_bool('hybrid', False, 'If true, make it hybrid.')
+
+kAltitude = angular_system.AngularSystemParams(
+ name='Altitude',
+ motor=control_loop.KrakenFOC(),
+ G=(16.0 / 60.0) * (16.0 / 162.0),
+ # 4340 in^ lb
+ J=1.27,
+ q_pos=0.60,
+ q_vel=8.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=2.0,
+ kalman_r_position=0.05,
+ radius=10.5 * 0.0254)
+
+
+def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[numpy.pi / 2.0], [0.0]])
+ angular_system.PlotKick(kAltitude, R)
+ angular_system.PlotMotion(kAltitude, R)
+ return
+
+ # Write the generated constants out to a file.
+ if len(argv) != 7:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the intake and integral intake.'
+ )
+ else:
+ namespaces = ['y2024', 'control_loops', 'superstructure', 'altitude']
+ angular_system.WriteAngularSystem(kAltitude, argv[1:4], argv[4:7],
+ namespaces)
+
+
+if __name__ == '__main__':
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2024/control_loops/python/catapult.py b/y2024/control_loops/python/catapult.py
new file mode 100644
index 0000000..49c6305
--- /dev/null
+++ b/y2024/control_loops/python/catapult.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python3
+
+from aos.util.trapezoid_profile import TrapezoidProfile
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import angular_system
+from frc971.control_loops.python import controls
+import numpy
+import sys
+from matplotlib import pylab
+import gflags
+import glog
+
+FLAGS = gflags.FLAGS
+
+try:
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+except gflags.DuplicateFlagError:
+ pass
+
+gflags.DEFINE_bool('hybrid', False, 'If true, make it hybrid.')
+
+
+def AddResistance(motor, resistance):
+ motor.resistance += resistance
+ return motor
+
+
+kCatapultWithGamePiece = angular_system.AngularSystemParams(
+ name='Catapult',
+ # Add the battery series resistance to make it better match.
+ motor=AddResistance(control_loop.NMotor(control_loop.KrakenFOC(), 2),
+ 0.00),
+ G=(14.0 / 60.0) * (12.0 / 24.0),
+ # 208.7328 in^2 lb
+ J=0.065 + 0.04,
+ q_pos=0.80,
+ q_vel=15.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=0.7,
+ kalman_r_position=0.05,
+ radius=12 * 0.0254,
+ delayed_u=1)
+
+kCatapultWithoutGamePiece = angular_system.AngularSystemParams(
+ name='Catapult',
+ # Add the battery series resistance to make it better match.
+ motor=AddResistance(control_loop.NMotor(control_loop.KrakenFOC(), 2),
+ 0.00),
+ G=(14.0 / 60.0) * (12.0 / 24.0),
+ # 135.2928 in^2 lb
+ J=0.06,
+ q_pos=0.80,
+ q_vel=15.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=0.7,
+ kalman_r_position=0.05,
+ radius=12 * 0.0254,
+ delayed_u=1)
+
+
+def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[numpy.pi / 2.0], [0.0]])
+ angular_system.PlotKick(kCatapultWithGamePiece, R)
+ angular_system.PlotMotion(kCatapultWithGamePiece, R)
+ return
+
+ # Write the generated constants out to a file.
+ if len(argv) != 7:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the intake and integral intake.'
+ )
+ else:
+ namespaces = ['y2024', 'control_loops', 'superstructure', 'catapult']
+ angular_system.WriteAngularSystem(
+ [kCatapultWithoutGamePiece, kCatapultWithGamePiece], argv[1:4],
+ argv[4:7], namespaces)
+
+
+if __name__ == '__main__':
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2024/control_loops/python/climber.py b/y2024/control_loops/python/climber.py
index 10aeff9..997cf28 100644
--- a/y2024/control_loops/python/climber.py
+++ b/y2024/control_loops/python/climber.py
@@ -14,29 +14,30 @@
except gflags.DuplicateFlagError:
pass
-# TODO(Filip): Update information the climber when design is finalized.
kClimber = linear_system.LinearSystemParams(
name='Climber',
- motor=control_loop.Falcon(),
- G=(1.0 / 4.0) * (1.0 / 3.0) * (1.0 / 3.0),
- radius=22 * 0.25 / numpy.pi / 2.0 * 0.0254,
+ motor=control_loop.KrakenFOC(),
+ G=(8. / 60.) * (16. / 60.),
+ radius=16 * 0.25 / numpy.pi / 2.0 * 0.0254,
mass=2.0,
- q_pos=0.10,
- q_vel=1.35,
+ q_pos=0.03,
+ q_vel=2.,
kalman_q_pos=0.12,
kalman_q_vel=2.00,
kalman_q_voltage=35.0,
- kalman_r_position=0.05)
+ kalman_r_position=0.05,
+)
def main(argv):
if FLAGS.plot:
- R = numpy.matrix([[0.2], [0.0]])
- linear_system.PlotKick(kClimber, R, plant_params=kClimber)
+ R = numpy.matrix([[0.4], [0.0]])
linear_system.PlotMotion(kClimber,
R,
max_velocity=5.0,
plant_params=kClimber)
+ linear_system.PlotKick(kClimber, R, plant_params=kClimber)
+ return
# Write the generated constants out to a file.
if len(argv) != 7:
@@ -45,7 +46,8 @@
)
else:
namespaces = ['y2024', 'control_loops', 'superstructure', 'climber']
- linear_system.WriteLinearSystem(kClimber, argv[1:4], argv[4:7], namespaces)
+ linear_system.WriteLinearSystem(kClimber, argv[1:4], argv[4:7],
+ namespaces)
if __name__ == '__main__':
diff --git a/y2024/control_loops/python/extend.py b/y2024/control_loops/python/extend.py
new file mode 100644
index 0000000..f95c995
--- /dev/null
+++ b/y2024/control_loops/python/extend.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python3
+
+from aos.util.trapezoid_profile import TrapezoidProfile
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import linear_system
+from frc971.control_loops.python import controls
+import numpy
+import sys
+from matplotlib import pylab
+import gflags
+import glog
+
+FLAGS = gflags.FLAGS
+
+try:
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+except gflags.DuplicateFlagError:
+ pass
+
+gflags.DEFINE_bool('hybrid', False, 'If true, make it hybrid.')
+
+kExtend = linear_system.LinearSystemParams(
+ name='Extend',
+ motor=control_loop.KrakenFOC(),
+ G=(14. / 60.) * (32. / 48.),
+ radius=36 * 0.005 / numpy.pi / 2.0,
+ mass=5.0,
+ q_pos=0.20,
+ q_vel=80.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=8.0,
+ kalman_r_position=0.05,
+)
+
+
+def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[0.4], [0.0]])
+ linear_system.PlotKick(kExtend, R)
+ linear_system.PlotMotion(kExtend,
+ R,
+ max_velocity=2.0,
+ max_acceleration=15.0)
+ return
+
+ # Write the generated constants out to a file.
+ if len(argv) != 7:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the extend pivot and integral extend pivot.'
+ )
+ else:
+ namespaces = ['y2024', 'control_loops', 'superstructure', 'extend']
+ linear_system.WriteLinearSystem(kExtend, argv[1:4], argv[4:7],
+ namespaces)
+
+
+if __name__ == '__main__':
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2024/control_loops/python/intake_pivot.py b/y2024/control_loops/python/intake_pivot.py
index f32aa8b..5ea5c3f 100644
--- a/y2024/control_loops/python/intake_pivot.py
+++ b/y2024/control_loops/python/intake_pivot.py
@@ -20,15 +20,16 @@
kIntakePivot = angular_system.AngularSystemParams(
name='IntakePivot',
motor=control_loop.KrakenFOC(),
- G=(16.0 / 60.0) * (18.0 / 62.0) * (18.0 / 62.0) * (15.0 / 24.0),
- J=0.34, # Borrowed from 2022, 0.035 seems too low
- q_pos=0.40,
- q_vel=20.0,
+ G=(16. / 60.) * (18. / 62.) * (18. / 62.) * (15. / 24.),
+ J=0.4,
+ q_pos=1.0,
+ q_vel=800.0,
kalman_q_pos=0.12,
kalman_q_vel=2.0,
- kalman_q_voltage=4.0,
+ kalman_q_voltage=1.0,
kalman_r_position=0.05,
- radius=6.85 * 0.0254)
+ radius=6.85 * 0.0254,
+ enable_voltage_error=False)
def main(argv):
@@ -52,4 +53,4 @@
if __name__ == '__main__':
argv = FLAGS(sys.argv)
glog.init()
- sys.exit(main(argv))
\ No newline at end of file
+ sys.exit(main(argv))
diff --git a/y2024/control_loops/python/turret.py b/y2024/control_loops/python/turret.py
new file mode 100644
index 0000000..41cc48a
--- /dev/null
+++ b/y2024/control_loops/python/turret.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python3
+
+from aos.util.trapezoid_profile import TrapezoidProfile
+from frc971.control_loops.python import control_loop
+from frc971.control_loops.python import angular_system
+from frc971.control_loops.python import controls
+import numpy
+import sys
+from matplotlib import pylab
+import gflags
+import glog
+
+FLAGS = gflags.FLAGS
+
+try:
+ gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+except gflags.DuplicateFlagError:
+ pass
+
+kTurret = angular_system.AngularSystemParams(
+ name='Turret',
+ motor=control_loop.KrakenFOC(),
+ G=(14.0 / 60.0) * (28.0 / 48.0) * (22.0 / 100.0),
+ # 1305 in^2 lb
+ J=0.4,
+ q_pos=0.60,
+ q_vel=10.0,
+ kalman_q_pos=0.12,
+ kalman_q_vel=2.0,
+ kalman_q_voltage=2.0,
+ kalman_r_position=0.05,
+ radius=24 * 0.0254)
+
+
+def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[numpy.pi], [0.0]])
+ angular_system.PlotKick(kTurret, R)
+ angular_system.PlotMotion(kTurret, R)
+
+ # Write the generated constants out to a file.
+ if len(argv) != 7:
+ glog.fatal(
+ 'Expected .h file name and .cc file name for the turret and integral turret.'
+ )
+ else:
+ namespaces = ['y2024', 'control_loops', 'superstructure', 'turret']
+ angular_system.WriteAngularSystem(kTurret, argv[1:4], argv[4:7],
+ namespaces)
+
+
+if __name__ == '__main__':
+ argv = FLAGS(sys.argv)
+ glog.init()
+ sys.exit(main(argv))
diff --git a/y2024/control_loops/superstructure/BUILD b/y2024/control_loops/superstructure/BUILD
index 28e4d00..6150caa 100644
--- a/y2024/control_loops/superstructure/BUILD
+++ b/y2024/control_loops/superstructure/BUILD
@@ -81,6 +81,8 @@
data = [
],
deps = [
+ ":collision_avoidance_lib",
+ ":shooter",
":superstructure_goal_fbs",
":superstructure_output_fbs",
":superstructure_position_fbs",
@@ -112,6 +114,36 @@
],
)
+cc_library(
+ name = "collision_avoidance_lib",
+ srcs = ["collision_avoidance.cc"],
+ hdrs = ["collision_avoidance.h"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":superstructure_goal_fbs",
+ ":superstructure_status_fbs",
+ "//frc971/control_loops:control_loops_fbs",
+ "//frc971/control_loops:profiled_subsystem_fbs",
+ "//frc971/control_loops:static_zeroing_single_dof_profiled_subsystem",
+ "@com_github_google_glog//:glog",
+ "@com_google_absl//absl/functional:bind_front",
+ ],
+)
+
+cc_test(
+ name = "collision_avoidance_test",
+ srcs = ["collision_avoidance_test.cc"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":collision_avoidance_lib",
+ ":superstructure_goal_fbs",
+ ":superstructure_status_fbs",
+ "//aos:flatbuffers",
+ "//aos:math",
+ "//aos/testing:googletest",
+ ],
+)
+
cc_test(
name = "superstructure_lib_test",
srcs = [
@@ -140,6 +172,53 @@
],
)
+cc_library(
+ name = "shooter",
+ srcs = [
+ "shooter.cc",
+ ],
+ hdrs = [
+ "shooter.h",
+ ],
+ deps = [
+ ":aiming",
+ ":collision_avoidance_lib",
+ ":superstructure_can_position_fbs",
+ ":superstructure_goal_fbs",
+ ":superstructure_position_fbs",
+ ":superstructure_status_fbs",
+ "//frc971/control_loops:static_zeroing_single_dof_profiled_subsystem",
+ "//frc971/control_loops/catapult",
+ "//frc971/control_loops/catapult:catapult_goal_fbs",
+ "//frc971/shooter_interpolation:interpolation",
+ "//frc971/zeroing:pot_and_absolute_encoder",
+ "//y2024:constants",
+ "//y2024/constants:constants_fbs",
+ "//y2024/control_loops/superstructure/altitude:altitude_plants",
+ "//y2024/control_loops/superstructure/catapult:catapult_plants",
+ "//y2024/control_loops/superstructure/turret:turret_plants",
+ ],
+)
+
+cc_library(
+ name = "aiming",
+ srcs = [
+ "aiming.cc",
+ ],
+ hdrs = [
+ "aiming.h",
+ ],
+ deps = [
+ ":superstructure_status_fbs",
+ "//frc971/control_loops:static_zeroing_single_dof_profiled_subsystem",
+ "//frc971/control_loops/aiming",
+ "//frc971/control_loops/drivetrain:drivetrain_status_fbs",
+ "//y2024:constants",
+ "//y2024/constants:constants_fbs",
+ "//y2024/control_loops/drivetrain:drivetrain_base",
+ ],
+)
+
cc_binary(
name = "superstructure_replay",
srcs = ["superstructure_replay.cc"],
diff --git a/y2024/control_loops/superstructure/aiming.cc b/y2024/control_loops/superstructure/aiming.cc
new file mode 100644
index 0000000..5bb1d19
--- /dev/null
+++ b/y2024/control_loops/superstructure/aiming.cc
@@ -0,0 +1,87 @@
+#include "y2024/control_loops/superstructure/aiming.h"
+
+#include "frc971/control_loops/aiming/aiming.h"
+#include "frc971/control_loops/pose.h"
+
+using frc971::control_loops::aiming::RobotState;
+using frc971::control_loops::aiming::ShotConfig;
+using frc971::control_loops::aiming::ShotMode;
+using y2024::control_loops::superstructure::Aimer;
+
+// When the turret is at 0 the note will be leaving the robot at PI.
+static constexpr double kTurretZeroOffset = M_PI;
+
+Aimer::Aimer(aos::EventLoop *event_loop,
+ const y2024::Constants *robot_constants)
+ : event_loop_(event_loop),
+ robot_constants_(CHECK_NOTNULL(robot_constants)),
+ drivetrain_config_(
+ frc971::control_loops::drivetrain::DrivetrainConfig<double>::
+ FromFlatbuffer(*robot_constants_->common()->drivetrain())),
+ interpolation_table_(
+ y2024::constants::Values::InterpolationTableFromFlatbuffer(
+ robot_constants_->common()->shooter_interpolation_table())),
+ joystick_state_fetcher_(
+ event_loop_->MakeFetcher<aos::JoystickState>("/aos")) {}
+
+void Aimer::Update(
+ const frc971::control_loops::drivetrain::Status *status,
+ frc971::control_loops::aiming::ShotMode shot_mode,
+ frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoalStatic
+ *turret_goal) {
+ const frc971::control_loops::Pose robot_pose({status->x(), status->y(), 0},
+ status->theta());
+ joystick_state_fetcher_.Fetch();
+ CHECK_NOTNULL(joystick_state_fetcher_.get());
+
+ aos::Alliance alliance = joystick_state_fetcher_->alliance();
+
+ const frc971::control_loops::Pose red_alliance_goal(
+ frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
+ ->shooter_targets()
+ ->red_alliance()
+ ->pos()),
+ robot_constants_->common()->shooter_targets()->red_alliance()->theta());
+
+ const frc971::control_loops::Pose blue_alliance_goal(
+ frc971::ToEigenOrDie<3, 1>(*robot_constants_->common()
+ ->shooter_targets()
+ ->blue_alliance()
+ ->pos()),
+ robot_constants_->common()->shooter_targets()->blue_alliance()->theta());
+
+ const frc971::control_loops::Pose goal =
+ alliance == aos::Alliance::kRed ? red_alliance_goal : blue_alliance_goal;
+
+ const Eigen::Vector2d linear_angular =
+ drivetrain_config_.Tlr_to_la() *
+ Eigen::Vector2d(status->estimated_left_velocity(),
+ status->estimated_right_velocity());
+ const double xdot = linear_angular(0) * std::cos(status->theta());
+ const double ydot = linear_angular(0) * std::sin(status->theta());
+
+ // Use the previous shot distance to estimate the speed-over-ground of the
+ // note.
+ current_goal_ = frc971::control_loops::aiming::AimerGoal(
+ ShotConfig{goal, shot_mode,
+ frc971::constants::Range::FromFlatbuffer(
+ robot_constants_->common()->turret()->range()),
+ interpolation_table_.Get(current_goal_.target_distance)
+ .shot_speed_over_ground,
+ /*wrap_mode=*/0.0, kTurretZeroOffset},
+ RobotState{
+ robot_pose, {xdot, ydot}, linear_angular(1), current_goal_.position});
+
+ turret_goal->set_unsafe_goal(current_goal_.position);
+ turret_goal->set_goal_velocity(current_goal_.velocity);
+}
+
+flatbuffers::Offset<AimerStatus> Aimer::PopulateStatus(
+ flatbuffers::FlatBufferBuilder *fbb) const {
+ AimerStatus::Builder builder(*fbb);
+ builder.add_turret_position(current_goal_.position);
+ builder.add_turret_velocity(current_goal_.velocity);
+ builder.add_target_distance(current_goal_.target_distance);
+ builder.add_shot_distance(DistanceToGoal());
+ return builder.Finish();
+}
diff --git a/y2024/control_loops/superstructure/aiming.h b/y2024/control_loops/superstructure/aiming.h
new file mode 100644
index 0000000..21d427a
--- /dev/null
+++ b/y2024/control_loops/superstructure/aiming.h
@@ -0,0 +1,51 @@
+#ifndef Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_AIMING_H_
+#define Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_AIMING_H_
+
+#include "frc971/control_loops/aiming/aiming.h"
+#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
+#include "frc971/control_loops/pose.h"
+#include "frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h"
+#include "frc971/shooter_interpolation/interpolation.h"
+#include "y2024/constants.h"
+#include "y2024/constants/constants_generated.h"
+#include "y2024/control_loops/drivetrain/drivetrain_base.h"
+#include "y2024/control_loops/superstructure/superstructure_status_generated.h"
+
+using y2024::control_loops::superstructure::AimerStatus;
+
+namespace y2024::control_loops::superstructure {
+
+class Aimer {
+ public:
+ Aimer(aos::EventLoop *event_loop, const Constants *robot_constants);
+
+ void Update(
+ const frc971::control_loops::drivetrain::Status *status,
+ frc971::control_loops::aiming::ShotMode shot_mode,
+ frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoalStatic
+ *turret_goal);
+
+ double DistanceToGoal() const { return current_goal_.virtual_shot_distance; }
+
+ flatbuffers::Offset<AimerStatus> PopulateStatus(
+ flatbuffers::FlatBufferBuilder *fbb) const;
+
+ private:
+ aos::EventLoop *event_loop_;
+
+ const Constants *robot_constants_;
+
+ frc971::control_loops::drivetrain::DrivetrainConfig<double>
+ drivetrain_config_;
+
+ frc971::shooter_interpolation::InterpolationTable<
+ y2024::constants::Values::ShotParams>
+ interpolation_table_;
+
+ aos::Fetcher<aos::JoystickState> joystick_state_fetcher_;
+
+ frc971::control_loops::aiming::TurretGoal current_goal_;
+};
+
+} // namespace y2024::control_loops::superstructure
+#endif // Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_TURRET_AIMING_H_
diff --git a/y2024/control_loops/superstructure/altitude/BUILD b/y2024/control_loops/superstructure/altitude/BUILD
new file mode 100644
index 0000000..71e2e3b
--- /dev/null
+++ b/y2024/control_loops/superstructure/altitude/BUILD
@@ -0,0 +1,42 @@
+package(default_visibility = ["//y2024:__subpackages__"])
+
+genrule(
+ name = "genrule_altitude",
+ outs = [
+ "altitude_plant.h",
+ "altitude_plant.cc",
+ "altitude_plant.json",
+ "integral_altitude_plant.h",
+ "integral_altitude_plant.cc",
+ "integral_altitude_plant.json",
+ ],
+ cmd = "$(location //y2024/control_loops/python:altitude) $(OUTS)",
+ target_compatible_with = ["@platforms//os:linux"],
+ tools = [
+ "//y2024/control_loops/python:altitude",
+ ],
+)
+
+cc_library(
+ name = "altitude_plants",
+ srcs = [
+ "altitude_plant.cc",
+ "integral_altitude_plant.cc",
+ ],
+ hdrs = [
+ "altitude_plant.h",
+ "integral_altitude_plant.h",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops:hybrid_state_feedback_loop",
+ "//frc971/control_loops:state_feedback_loop",
+ ],
+)
+
+filegroup(
+ name = "altitude_json",
+ srcs = ["integral_altitude_plant.json"],
+ visibility = ["//visibility:public"],
+)
diff --git a/y2024/control_loops/superstructure/catapult/BUILD b/y2024/control_loops/superstructure/catapult/BUILD
new file mode 100644
index 0000000..a6025d8
--- /dev/null
+++ b/y2024/control_loops/superstructure/catapult/BUILD
@@ -0,0 +1,42 @@
+package(default_visibility = ["//y2024:__subpackages__"])
+
+genrule(
+ name = "genrule_catapult",
+ outs = [
+ "catapult_plant.h",
+ "catapult_plant.cc",
+ "catapult_plant.json",
+ "integral_catapult_plant.h",
+ "integral_catapult_plant.cc",
+ "integral_catapult_plant.json",
+ ],
+ cmd = "$(location //y2024/control_loops/python:catapult) $(OUTS)",
+ target_compatible_with = ["@platforms//os:linux"],
+ tools = [
+ "//y2024/control_loops/python:catapult",
+ ],
+)
+
+cc_library(
+ name = "catapult_plants",
+ srcs = [
+ "catapult_plant.cc",
+ "integral_catapult_plant.cc",
+ ],
+ hdrs = [
+ "catapult_plant.h",
+ "integral_catapult_plant.h",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops:hybrid_state_feedback_loop",
+ "//frc971/control_loops:state_feedback_loop",
+ ],
+)
+
+filegroup(
+ name = "catapult_json",
+ srcs = ["integral_catapult_plant.json"],
+ visibility = ["//visibility:public"],
+)
diff --git a/y2024/control_loops/superstructure/collision_avoidance.cc b/y2024/control_loops/superstructure/collision_avoidance.cc
new file mode 100644
index 0000000..bd82fe0
--- /dev/null
+++ b/y2024/control_loops/superstructure/collision_avoidance.cc
@@ -0,0 +1,156 @@
+#include "y2024/control_loops/superstructure/collision_avoidance.h"
+
+#include <cmath>
+
+#include "absl/functional/bind_front.h"
+#include "glog/logging.h"
+
+namespace y2024::control_loops::superstructure {
+
+CollisionAvoidance::CollisionAvoidance() {
+ clear_min_intake_pivot_goal();
+ clear_max_intake_pivot_goal();
+ clear_min_turret_goal();
+ clear_max_turret_goal();
+ clear_min_extend_goal();
+ clear_max_extend_goal();
+}
+
+bool CollisionAvoidance::IsCollided(const CollisionAvoidance::Status &status) {
+ // Checks if intake front is collided.
+ if (TurretCollided(status.intake_pivot_position, status.turret_position,
+ status.extend_position)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool AngleInRange(double theta, double theta_min, double theta_max) {
+ return (
+ (theta >= theta_min && theta <= theta_max) ||
+ (theta_min > theta_max && (theta >= theta_min || theta <= theta_max)));
+}
+
+bool CollisionAvoidance::TurretCollided(double intake_position,
+ double turret_position,
+ double extend_position) {
+ // Checks if turret is in the collision area.
+ if (AngleInRange(turret_position, kMinCollisionZoneTurret,
+ kMaxCollisionZoneTurret)) {
+ // Returns true if the intake is raised.
+ if (intake_position > kCollisionZoneIntake) {
+ return true;
+ }
+ }
+ return ExtendCollided(intake_position, turret_position, extend_position);
+}
+
+bool CollisionAvoidance::ExtendCollided(double /*intake_position*/,
+ double turret_position,
+ double extend_position) {
+ // Checks if turret is in the collision area.
+ if (!AngleInRange(turret_position, kSafeTurretExtendedPosition - kEpsTurret,
+ kSafeTurretExtendedPosition + kEpsTurret)) {
+ // Returns true if the extend is raised.
+ if (extend_position > kMinCollisionZoneExtend) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CollisionAvoidance::UpdateGoal(const CollisionAvoidance::Status &status,
+ const double turret_goal_position,
+ const double extend_goal_position) {
+ // Start with our constraints being wide open.
+ clear_min_extend_goal();
+ clear_max_extend_goal();
+ clear_max_turret_goal();
+ clear_min_turret_goal();
+ clear_max_intake_pivot_goal();
+ clear_min_intake_pivot_goal();
+
+ const double intake_pivot_position = status.intake_pivot_position;
+ const double turret_position = status.turret_position;
+ const double extend_position = status.extend_position;
+
+ const double turret_goal = turret_goal_position;
+ const double extend_goal = extend_goal_position;
+
+ // Calculating the avoidance with the intake
+
+ CalculateAvoidance(intake_pivot_position, turret_position, extend_position,
+ turret_goal, extend_goal);
+}
+
+void CollisionAvoidance::CalculateAvoidance(double intake_position,
+ double turret_position,
+ double extend_position,
+ double turret_goal,
+ double extend_goal) {
+ // If the turret goal is in a collison zone or moving through one, limit
+ // intake.
+ const bool turret_intake_pos_unsafe = AngleInRange(
+ turret_position, kMinCollisionZoneTurret, kMaxCollisionZoneTurret);
+ const bool turret_extend_pos_unsafe =
+ turret_position > kEpsTurret + kSafeTurretExtendedPosition ||
+ turret_position < -kEpsTurret + kSafeTurretExtendedPosition;
+
+ const bool extend_goal_unsafe =
+ extend_goal > kMinCollisionZoneExtend - kEpsExtend;
+ const bool extend_position_unsafe =
+ extend_position > kMinCollisionZoneExtend - kEpsExtend;
+
+ // OK, we are trying to move the extend, and need the turret to be at 0.
+ // Pretend that's the goal.
+ if (extend_goal_unsafe || extend_position_unsafe) {
+ turret_goal = kSafeTurretExtendedPosition;
+ }
+
+ const bool turret_moving_forward = (turret_goal > turret_position);
+
+ // Check if the closest angles are going to be passed
+ const bool turret_moving_past_intake =
+ ((turret_moving_forward && (turret_position <= kMaxCollisionZoneTurret &&
+ turret_goal >= kMinCollisionZoneTurret)) ||
+ (!turret_moving_forward && (turret_position >= kMinCollisionZoneTurret &&
+ turret_goal <= kMaxCollisionZoneTurret)));
+
+ if (turret_intake_pos_unsafe || turret_moving_past_intake) {
+ // If the turret is unsafe, limit the intake
+ update_max_intake_pivot_goal(kCollisionZoneIntake - kEpsIntake);
+
+ // If the intake is in the way, limit the turret until moved. Otherwise,
+ // let'errip!
+ if (!turret_intake_pos_unsafe && (intake_position > kCollisionZoneIntake)) {
+ if (turret_position <
+ (kMinCollisionZoneTurret + kMaxCollisionZoneTurret) / 2.) {
+ update_max_turret_goal(kMinCollisionZoneTurret - kEpsTurret);
+ } else {
+ update_min_turret_goal(kMaxCollisionZoneTurret + kEpsTurret);
+ }
+ }
+ }
+
+ // OK, the logic is pretty simple. The turret needs to be at
+ // kSafeTurretExtendedPosition any time extend is > kMinCollisionZoneExtend.
+ //
+ // Extend can't go up if the turret isn't near 0.
+ if (turret_extend_pos_unsafe) {
+ update_max_extend_goal(kMinCollisionZoneExtend - kEpsExtend);
+ }
+
+ // Turret is bound to the safe position if extend wants to be, or is unsafe.
+ if (extend_goal_unsafe || extend_position_unsafe) {
+ // If the turret isn't allowed to go to 0, don't drive it there.
+ if (min_turret_goal() < kSafeTurretExtendedPosition &&
+ max_turret_goal() > kSafeTurretExtendedPosition) {
+ update_min_turret_goal(kSafeTurretExtendedPosition);
+ update_max_turret_goal(kSafeTurretExtendedPosition);
+ }
+ }
+}
+
+} // namespace y2024::control_loops::superstructure
diff --git a/y2024/control_loops/superstructure/collision_avoidance.h b/y2024/control_loops/superstructure/collision_avoidance.h
new file mode 100644
index 0000000..be58b35
--- /dev/null
+++ b/y2024/control_loops/superstructure/collision_avoidance.h
@@ -0,0 +1,130 @@
+#ifndef Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_COLLISION_AVOIDENCE_H_
+#define Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_COLLISION_AVOIDENCE_H_
+
+#include <cmath>
+
+#include "frc971/control_loops/control_loops_generated.h"
+#include "frc971/control_loops/profiled_subsystem_generated.h"
+
+namespace y2024::control_loops::superstructure {
+
+// Checks if theta is between theta_min and theta_max. Expects all angles to be
+// wrapped from 0 to 2pi
+bool AngleInRange(double theta, double theta_min, double theta_max);
+
+// 1. Prevent the turret from moving if the intake is up
+// and prevent the back of the turret from colliding with the intake when it's
+// up.
+// 2. If the intake is up, drop it so it is not in the way
+// 3. Move the turret to the desired position.
+// 4. When the turret moves away, if the intake is down, move it back up.
+class CollisionAvoidance {
+ public:
+ struct Status {
+ double intake_pivot_position;
+ double turret_position;
+ double extend_position;
+
+ bool operator==(const Status &s) const {
+ return (intake_pivot_position == s.intake_pivot_position &&
+ turret_position == s.turret_position &&
+ extend_position == s.extend_position);
+ }
+ bool operator!=(const Status &s) const { return !(*this == s); }
+ };
+
+ // For the turret, 0 rad is pointing straight forwards
+ static constexpr double kMinCollisionZoneTurret = 0.15;
+ static constexpr double kMaxCollisionZoneTurret = 1.15;
+
+ static constexpr double kSafeTurretExtendedPosition = 0.0;
+
+ // Maximum position of the intake to avoid collisions
+ static constexpr double kCollisionZoneIntake = 1.6;
+
+ static constexpr double kMinCollisionZoneExtend = 0.03;
+
+ // Tolerances for the subsystems
+ static constexpr double kEpsTurret = 0.05;
+ static constexpr double kEpsIntake = 0.05;
+ static constexpr double kEpsExtend = 0.01;
+
+ CollisionAvoidance();
+
+ // Reports if the superstructure is collided.
+ bool IsCollided(const Status &status);
+ // Checks if there is a collision with the intake.
+ bool TurretCollided(double intake_position, double turret_position,
+ double extend_position);
+ // Checks if there is a collision with the extend.
+ bool ExtendCollided(double intake_position, double turret_position,
+ double extend_position);
+ // Checks and alters goals to make sure they're safe.
+ void UpdateGoal(const CollisionAvoidance::Status &status,
+ double turret_goal_position, double extend_goal_position);
+ // Limits if goal is in collision spots.
+ void CalculateAvoidance(double intake_position, double turret_position,
+ double extend_position, double turret_goal,
+ double extend_goal);
+
+ // Returns the goals to give to the respective control loops in
+ // superstructure.
+ double min_extend_goal() const { return min_extend_goal_; }
+ double max_extend_goal() const { return max_extend_goal_; }
+ double min_turret_goal() const { return min_turret_goal_; }
+ double max_turret_goal() const { return max_turret_goal_; }
+ double min_intake_pivot_goal() const { return min_intake_pivot_goal_; }
+ double max_intake_pivot_goal() const { return max_intake_pivot_goal_; }
+
+ void update_max_turret_goal(double max_turret_goal) {
+ max_turret_goal_ = ::std::min(max_turret_goal, max_turret_goal_);
+ }
+ void update_min_turret_goal(double min_turret_goal) {
+ min_turret_goal_ = ::std::max(min_turret_goal, min_turret_goal_);
+ }
+ void update_max_intake_pivot_goal(double max_intake_pivot_goal) {
+ max_intake_pivot_goal_ =
+ ::std::min(max_intake_pivot_goal, max_intake_pivot_goal_);
+ }
+ void update_min_intake_pivot_goal(double min_intake_pivot_goal) {
+ min_intake_pivot_goal_ =
+ ::std::max(min_intake_pivot_goal, min_intake_pivot_goal_);
+ }
+ void update_min_extend_goal(double min_extend_goal) {
+ min_extend_goal_ = ::std::max(min_extend_goal, min_extend_goal_);
+ }
+ void update_max_extend_goal(double max_extend_goal) {
+ max_extend_goal_ = ::std::min(max_extend_goal, max_extend_goal_);
+ }
+
+ private:
+ void clear_min_intake_pivot_goal() {
+ min_intake_pivot_goal_ = -::std::numeric_limits<double>::infinity();
+ }
+ void clear_max_intake_pivot_goal() {
+ max_intake_pivot_goal_ = ::std::numeric_limits<double>::infinity();
+ }
+ void clear_min_turret_goal() {
+ min_turret_goal_ = -::std::numeric_limits<double>::infinity();
+ }
+ void clear_max_turret_goal() {
+ max_turret_goal_ = ::std::numeric_limits<double>::infinity();
+ }
+ void clear_min_extend_goal() {
+ min_extend_goal_ = -::std::numeric_limits<double>::infinity();
+ }
+ void clear_max_extend_goal() {
+ max_extend_goal_ = ::std::numeric_limits<double>::infinity();
+ }
+
+ double min_intake_pivot_goal_;
+ double max_intake_pivot_goal_;
+ double min_turret_goal_;
+ double max_turret_goal_;
+ double min_extend_goal_;
+ double max_extend_goal_;
+};
+
+} // namespace y2024::control_loops::superstructure
+
+#endif
diff --git a/y2024/control_loops/superstructure/collision_avoidance_test.cc b/y2024/control_loops/superstructure/collision_avoidance_test.cc
new file mode 100644
index 0000000..c3a8e3a
--- /dev/null
+++ b/y2024/control_loops/superstructure/collision_avoidance_test.cc
@@ -0,0 +1,241 @@
+#include "y2024/control_loops/superstructure/collision_avoidance.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "aos/commonmath.h"
+#include "aos/flatbuffers.h"
+#include "y2024/control_loops/superstructure/superstructure_goal_generated.h"
+#include "y2024/control_loops/superstructure/superstructure_status_generated.h"
+
+namespace y2024::control_loops::superstructure::testing {
+
+using aos::FlatbufferDetachedBuffer;
+using frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus;
+using frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal;
+
+// Enums for the different classes of intake and turret states
+enum class IntakeState { kSafe, kUnsafe };
+enum class TurretState {
+ kSafe,
+ kUnsafe,
+};
+enum class ExtendState {
+ kSafe,
+ kUnsafe,
+};
+
+class CollisionAvoidanceTest : public ::testing::Test {
+ public:
+ CollisionAvoidanceTest()
+ : intake_goal_(0),
+ turret_goal_(0),
+ extend_goal_(0),
+ status_({0.0, 0.0, 0.0}),
+ prev_status_({0.0, 0.0, 0.0}) {}
+
+ void Simulate() {
+ double safe_intake_goal = 0;
+ double safe_turret_goal = 0;
+ double safe_extend_goal = 0;
+ bool was_collided = avoidance_.IsCollided(status_);
+
+ VLOG(1) << "Simulation of intake " << status_.intake_pivot_position
+ << ", intake_goal " << intake_goal_ << ", turret "
+ << status_.turret_position << ", turret_goal " << turret_goal_
+ << ", extend " << status_.extend_position << ", extend goal "
+ << extend_goal_;
+
+ bool moving = true;
+ while (moving) {
+ // Compute the safe goal
+ avoidance_.UpdateGoal(status_, turret_goal_, extend_goal_);
+
+ if (!was_collided) {
+ // The system should never be collided if it didn't start off collided
+ EXPECT_FALSE(avoidance_.IsCollided(status_))
+ << ": Now collided at intake " << status_.intake_pivot_position
+ << ", intake_goal " << intake_goal_ << ", turret "
+ << status_.turret_position << ", turret_goal " << turret_goal_
+ << ", extend " << status_.extend_position << ", extend goal "
+ << extend_goal_;
+ } else {
+ was_collided = avoidance_.IsCollided(status_);
+ }
+
+ safe_intake_goal =
+ ::aos::Clip(intake_goal_, avoidance_.min_intake_pivot_goal(),
+ avoidance_.max_intake_pivot_goal());
+
+ safe_turret_goal = ::aos::Clip(turret_goal_, avoidance_.min_turret_goal(),
+ avoidance_.max_turret_goal());
+
+ safe_extend_goal = ::aos::Clip(extend_goal_, avoidance_.min_extend_goal(),
+ avoidance_.max_extend_goal());
+
+ // Move each subsystem towards their goals a bit
+ status_.intake_pivot_position =
+ LimitedMove(status_.intake_pivot_position, safe_intake_goal);
+ status_.turret_position =
+ LimitedMove(status_.turret_position, safe_turret_goal);
+ status_.extend_position =
+ LimitedMove(status_.extend_position, safe_extend_goal);
+
+ // If it stopped moving, we're done
+ if (!IsMoving()) {
+ moving = false;
+ } else {
+ prev_status_ = status_;
+ }
+ }
+
+ EXPECT_FALSE(avoidance_.IsCollided(status_));
+
+ CheckGoals();
+ }
+
+ bool IsMoving() { return (status_ != prev_status_); }
+
+ double ComputeIntakeAngle(IntakeState intake_state) {
+ double intake_angle = 0.0;
+
+ switch (intake_state) {
+ case IntakeState::kSafe:
+ intake_angle = CollisionAvoidance::kCollisionZoneIntake -
+ CollisionAvoidance::kEpsIntake;
+ break;
+ case IntakeState::kUnsafe:
+ intake_angle = CollisionAvoidance::kCollisionZoneIntake +
+ CollisionAvoidance::kEpsIntake;
+ break;
+ }
+
+ return intake_angle;
+ }
+
+ double ComputeTurretAngle(TurretState turret_state) {
+ double turret_angle = 0.0;
+
+ switch (turret_state) {
+ case TurretState::kSafe:
+ turret_angle = CollisionAvoidance::kMaxCollisionZoneTurret +
+ CollisionAvoidance::kEpsTurret;
+ break;
+ case TurretState::kUnsafe:
+ turret_angle = CollisionAvoidance::kMaxCollisionZoneTurret -
+ CollisionAvoidance::kEpsTurret;
+ break;
+ }
+
+ return turret_angle;
+ }
+
+ double ComputeExtendPosition(ExtendState extend_state) {
+ double extend_position = 0.0;
+
+ switch (extend_state) {
+ case ExtendState::kSafe:
+ extend_position = CollisionAvoidance::kMinCollisionZoneExtend -
+ CollisionAvoidance::kEpsExtend;
+ break;
+ case ExtendState::kUnsafe:
+ extend_position = CollisionAvoidance::kMinCollisionZoneExtend +
+ CollisionAvoidance::kEpsTurret;
+ break;
+ }
+
+ return extend_position;
+ }
+
+ void Test(IntakeState intake_front_pos_state, TurretState turret_pos_state,
+ ExtendState extend_pos_state, IntakeState intake_front_goal_state,
+ TurretState turret_goal_state, ExtendState extend_goal_state) {
+ status_ = {ComputeIntakeAngle(intake_front_pos_state),
+ ComputeTurretAngle(turret_pos_state),
+ ComputeExtendPosition(extend_pos_state)};
+
+ intake_goal_ = ComputeIntakeAngle(intake_front_goal_state);
+
+ turret_goal_ = ComputeTurretAngle(turret_goal_state);
+
+ extend_goal_ = ComputeExtendPosition(extend_goal_state);
+
+ Simulate();
+ }
+
+ // Provide goals and status messages
+ double intake_goal_;
+ double turret_goal_;
+ double extend_goal_;
+ CollisionAvoidance::Status status_;
+
+ private:
+ static constexpr double kIterationMove = 0.001;
+
+ void CheckGoals() {
+ // Check to see if we reached the goals
+ // Turret is highest priority and should always reach the unsafe goal
+ EXPECT_NEAR(extend_goal_, status_.extend_position, kIterationMove);
+
+ if (avoidance_.ExtendCollided(intake_goal_, turret_goal_, extend_goal_)) {
+ EXPECT_EQ(status_.turret_position,
+ CollisionAvoidance::kSafeTurretExtendedPosition);
+ // If the unsafe goal had an intake colliding with the turret the intake
+ // position should be at least the collision zone angle. Otherwise, the
+ // intake should be at the unsafe goal
+ if (avoidance_.TurretCollided(intake_goal_, status_.turret_position,
+ extend_goal_)) {
+ EXPECT_LE(status_.intake_pivot_position,
+ CollisionAvoidance::kCollisionZoneIntake);
+ } else {
+ EXPECT_NEAR(intake_goal_, status_.intake_pivot_position,
+ kIterationMove);
+ }
+ } else {
+ EXPECT_NEAR(turret_goal_, status_.turret_position, kIterationMove);
+ }
+ }
+
+ double LimitedMove(double position, double goal) {
+ if (position + kIterationMove < goal) {
+ return position + kIterationMove;
+ } else if (position - kIterationMove > goal) {
+ return position - kIterationMove;
+ } else {
+ return goal;
+ }
+ }
+
+ CollisionAvoidance avoidance_;
+ CollisionAvoidance::Status prev_status_;
+}; // namespace y2024::control_loops::superstructure::testing
+
+// Just to be safe, brute force ALL the possible position-goal combinations
+// and make sure we never collide and the correct goals are reached
+TEST_F(CollisionAvoidanceTest, BruteForce) {
+ // Intake front position
+ for (IntakeState intake_front_pos :
+ {IntakeState::kSafe, IntakeState::kUnsafe}) {
+ // Turret back position
+ for (TurretState turret_pos : {TurretState::kSafe, TurretState::kUnsafe}) {
+ for (ExtendState extend_pos :
+ {ExtendState::kSafe, ExtendState::kUnsafe}) {
+ // Intake front goal
+ for (IntakeState intake_front_goal :
+ {IntakeState::kSafe, IntakeState::kUnsafe}) {
+ // Turret goal
+ for (TurretState turret_goal :
+ {TurretState::kSafe, TurretState::kUnsafe}) {
+ for (ExtendState extend_goal :
+ {ExtendState::kSafe, ExtendState::kUnsafe}) {
+ Test(intake_front_pos, turret_pos, extend_pos, intake_front_goal,
+ turret_goal, extend_goal);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace y2024::control_loops::superstructure::testing
diff --git a/y2024/control_loops/superstructure/extend/BUILD b/y2024/control_loops/superstructure/extend/BUILD
new file mode 100644
index 0000000..7a28bde
--- /dev/null
+++ b/y2024/control_loops/superstructure/extend/BUILD
@@ -0,0 +1,42 @@
+package(default_visibility = ["//y2024:__subpackages__"])
+
+genrule(
+ name = "genrule_extend",
+ outs = [
+ "extend_plant.h",
+ "extend_plant.cc",
+ "extend_plant.json",
+ "integral_extend_plant.h",
+ "integral_extend_plant.cc",
+ "integral_extend_plant.json",
+ ],
+ cmd = "$(location //y2024/control_loops/python:extend) $(OUTS)",
+ target_compatible_with = ["@platforms//os:linux"],
+ tools = [
+ "//y2024/control_loops/python:extend",
+ ],
+)
+
+cc_library(
+ name = "extend_plants",
+ srcs = [
+ "extend_plant.cc",
+ "integral_extend_plant.cc",
+ ],
+ hdrs = [
+ "extend_plant.h",
+ "integral_extend_plant.h",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops:hybrid_state_feedback_loop",
+ "//frc971/control_loops:state_feedback_loop",
+ ],
+)
+
+filegroup(
+ name = "extend_json",
+ srcs = ["integral_extend_plant.json"],
+ visibility = ["//visibility:public"],
+)
diff --git a/y2024/control_loops/superstructure/shooter.cc b/y2024/control_loops/superstructure/shooter.cc
new file mode 100644
index 0000000..cc00454
--- /dev/null
+++ b/y2024/control_loops/superstructure/shooter.cc
@@ -0,0 +1,311 @@
+#include "y2024/control_loops/superstructure/shooter.h"
+
+#include "aos/flatbuffers.h"
+#include "aos/flatbuffers/base.h"
+#include "frc971/control_loops/aiming/aiming.h"
+#include "y2024/control_loops/superstructure/catapult/catapult_plant.h"
+#include "y2024/control_loops/superstructure/collision_avoidance.h"
+
+namespace y2024::control_loops::superstructure {
+
+using frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus;
+
+constexpr double kCatapultActivationThreshold = 0.01;
+
+Shooter::Shooter(aos::EventLoop *event_loop, const Constants *robot_constants)
+ : drivetrain_status_fetcher_(
+ event_loop->MakeFetcher<frc971::control_loops::drivetrain::Status>(
+ "/drivetrain")),
+ robot_constants_(robot_constants),
+ catapult_(
+ robot_constants->common()->catapult(),
+ robot_constants->robot()->catapult_constants()->zeroing_constants()),
+ turret_(
+ robot_constants_->common()->turret(),
+ robot_constants_->robot()->turret_constants()->zeroing_constants()),
+ altitude_(
+ robot_constants_->common()->altitude(),
+ robot_constants_->robot()->altitude_constants()->zeroing_constants()),
+ aimer_(event_loop, robot_constants_),
+ interpolation_table_(
+ y2024::constants::Values::InterpolationTableFromFlatbuffer(
+ robot_constants_->common()->shooter_interpolation_table())),
+ debouncer_(std::chrono::milliseconds(100), std::chrono::milliseconds(8)) {
+}
+
+flatbuffers::Offset<y2024::control_loops::superstructure::ShooterStatus>
+Shooter::Iterate(
+ const y2024::control_loops::superstructure::Position *position,
+ const y2024::control_loops::superstructure::ShooterGoal *shooter_goal,
+ bool fire, double *catapult_output, double *altitude_output,
+ double *turret_output, double *retention_roller_output,
+ double *retention_roller_stator_current_limit, double /*battery_voltage*/,
+ CollisionAvoidance *collision_avoidance, const double extend_position,
+ const double extend_goal, double *max_extend_position,
+ double *min_extend_position, const double intake_pivot_position,
+ double *max_intake_pivot_position, double *min_intake_pivot_position,
+ flatbuffers::FlatBufferBuilder *fbb,
+ aos::monotonic_clock::time_point monotonic_now) {
+ drivetrain_status_fetcher_.Fetch();
+
+ // If our current is over the minimum current and our velocity is under our
+ // maximum velocity, then set loaded to true. If we are preloaded set it to
+ // true as well.
+ debouncer_.Update(position->catapult_beambreak() ||
+ (shooter_goal != nullptr && shooter_goal->preloaded()),
+ monotonic_now);
+ const bool piece_loaded = debouncer_.state();
+
+ aos::fbs::FixedStackAllocator<aos::fbs::Builder<
+ frc971::control_loops::
+ StaticZeroingSingleDOFProfiledSubsystemGoalStatic>::kBufferSize>
+ turret_allocator;
+
+ aos::fbs::Builder<
+ frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoalStatic>
+ turret_goal_builder(&turret_allocator);
+
+ aos::fbs::FixedStackAllocator<aos::fbs::Builder<
+ frc971::control_loops::
+ StaticZeroingSingleDOFProfiledSubsystemGoalStatic>::kBufferSize>
+ altitude_allocator;
+
+ aos::fbs::Builder<
+ frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoalStatic>
+ altitude_goal_builder(&altitude_allocator);
+
+ const double distance_to_goal = aimer_.DistanceToGoal();
+
+ // Always retain the game piece if we are enabled.
+ if (retention_roller_output != nullptr) {
+ *retention_roller_output =
+ robot_constants_->common()->retention_roller_voltages()->retaining();
+
+ if (piece_loaded) {
+ *retention_roller_stator_current_limit =
+ robot_constants_->common()
+ ->current_limits()
+ ->slower_retention_roller_stator_current_limit();
+ } else {
+ *retention_roller_stator_current_limit =
+ robot_constants_->common()
+ ->current_limits()
+ ->retention_roller_stator_current_limit();
+ }
+ }
+
+ bool aiming = false;
+
+ if (shooter_goal == nullptr || !shooter_goal->auto_aim() ||
+ (!piece_loaded && state_ == CatapultState::READY)) {
+ // We don't have the note so we should be ready to intake it.
+ PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ turret_goal_builder.get(),
+ robot_constants_->common()->turret_loading_position());
+
+ PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ altitude_goal_builder.get(),
+ robot_constants_->common()->altitude_loading_position());
+
+ } else {
+ // We have a game piece, lets start aiming.
+ if (drivetrain_status_fetcher_.get() != nullptr) {
+ aiming = true;
+ aimer_.Update(drivetrain_status_fetcher_.get(),
+ frc971::control_loops::aiming::ShotMode::kShootOnTheFly,
+ turret_goal_builder.get());
+ }
+ }
+
+ // We have a game piece and are being asked to aim.
+ constants::Values::ShotParams shot_params;
+ if (piece_loaded && shooter_goal != nullptr && shooter_goal->auto_aim() &&
+ interpolation_table_.GetInRange(distance_to_goal, &shot_params)) {
+ PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ altitude_goal_builder.get(), shot_params.shot_altitude_angle);
+ }
+
+ // The builder will contain either the auto-aim goal, or the loading goal. Use
+ // it if we have no goal, or no subsystem goal, or if we are auto-aiming.
+
+ const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
+ *turret_goal = (shooter_goal != nullptr && !shooter_goal->auto_aim() &&
+ shooter_goal->has_turret_position())
+ ? shooter_goal->turret_position()
+ : &turret_goal_builder->AsFlatbuffer();
+
+ const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
+ *altitude_goal = (shooter_goal != nullptr && !shooter_goal->auto_aim() &&
+ shooter_goal->has_altitude_position())
+ ? shooter_goal->altitude_position()
+ : &altitude_goal_builder->AsFlatbuffer();
+
+ bool subsystems_in_range =
+ (std::abs(turret_.estimated_position() - turret_goal->unsafe_goal()) <
+ kCatapultActivationThreshold &&
+ std::abs(altitude_.estimated_position() - altitude_goal->unsafe_goal()) <
+ kCatapultActivationThreshold &&
+ altitude_.estimated_position() >
+ robot_constants_->common()->min_altitude_shooting_angle());
+
+ const bool disabled = turret_.Correct(turret_goal, position->turret(),
+ turret_output == nullptr);
+
+ collision_avoidance->UpdateGoal(
+ {.intake_pivot_position = intake_pivot_position,
+ .turret_position = turret_.estimated_position(),
+ .extend_position = extend_position},
+ turret_goal->unsafe_goal(), extend_goal);
+
+ turret_.set_min_position(collision_avoidance->min_turret_goal());
+ turret_.set_max_position(collision_avoidance->max_turret_goal());
+
+ *max_intake_pivot_position = collision_avoidance->max_intake_pivot_goal();
+ *min_intake_pivot_position = collision_avoidance->min_intake_pivot_goal();
+
+ *max_extend_position = collision_avoidance->max_extend_goal();
+ *min_extend_position = collision_avoidance->min_extend_goal();
+
+ // Calculate the loops for a cycle.
+ const double voltage = turret_.UpdateController(disabled);
+
+ turret_.UpdateObserver(voltage);
+
+ // Write out all the voltages.
+ if (turret_output) {
+ *turret_output = voltage;
+ }
+
+ const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
+ turret_status_offset = turret_.MakeStatus(fbb);
+
+ const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
+ altitude_status_offset = altitude_.Iterate(
+ altitude_goal, position->altitude(), altitude_output, fbb);
+
+ flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
+ catapult_status_offset;
+ {
+ // The catapult will never use a provided goal. We'll always fabricate one
+ // for it.
+ //
+ // Correct handles resetting our state when disabled.
+ const bool disabled = catapult_.Correct(nullptr, position->catapult(),
+ catapult_output == nullptr);
+
+ catapult_.set_enable_profile(true);
+ // We want a trajectory which accelerates up over the first portion of the
+ // range of motion, holds top speed for a little bit, then decelerates
+ // before it swings too far.
+ //
+ // We can solve for these 3 parameters through the range of motion. Top
+ // speed is goverened by the voltage headroom we want to have for the
+ // controller.
+ //
+ // Accel can be tuned given the distance to accelerate over, and decel can
+ // be solved similarly.
+ //
+ // accel = v^2 / (2 * x)
+ catapult_.mutable_profile()->set_maximum_velocity(
+ catapult::kFreeSpeed * catapult::kOutputRatio * 4.0 / 12.0);
+
+ if (disabled) {
+ state_ = CatapultState::RETRACTING;
+ }
+
+ constexpr double kLoadingAcceleration = 20.0;
+ constexpr double kLoadingDeceleration = 10.0;
+
+ switch (state_) {
+ case CatapultState::READY:
+ [[fallthrough]];
+ case CatapultState::LOADED: {
+ if (piece_loaded) {
+ state_ = CatapultState::LOADED;
+ } else {
+ state_ = CatapultState::READY;
+ }
+
+ const bool catapult_close = CatapultClose();
+
+ if (subsystems_in_range && shooter_goal != nullptr && fire &&
+ catapult_close && piece_loaded) {
+ state_ = CatapultState::FIRING;
+ } else {
+ catapult_.set_controller_index(0);
+ catapult_.mutable_profile()->set_maximum_acceleration(
+ kLoadingAcceleration);
+ catapult_.mutable_profile()->set_maximum_deceleration(
+ kLoadingDeceleration);
+ catapult_.set_unprofiled_goal(0.0, 0.0);
+
+ if (!catapult_close) {
+ state_ = CatapultState::RETRACTING;
+ }
+ break;
+ }
+ [[fallthrough]];
+ }
+ case CatapultState::FIRING:
+ *retention_roller_output =
+ robot_constants_->common()->retention_roller_voltages()->spitting();
+ *retention_roller_stator_current_limit =
+ robot_constants_->common()
+ ->current_limits()
+ ->shooting_retention_roller_stator_current_limit();
+ catapult_.set_controller_index(1);
+ catapult_.mutable_profile()->set_maximum_acceleration(400.0);
+ catapult_.mutable_profile()->set_maximum_deceleration(500.0);
+ catapult_.set_unprofiled_goal(2.0, 0.0);
+ if (CatapultClose()) {
+ state_ = CatapultState::RETRACTING;
+ } else {
+ break;
+ }
+ [[fallthrough]];
+ case CatapultState::RETRACTING:
+ catapult_.set_controller_index(0);
+ catapult_.mutable_profile()->set_maximum_acceleration(
+ kLoadingAcceleration);
+ catapult_.mutable_profile()->set_maximum_deceleration(
+ kLoadingDeceleration);
+ // TODO: catapult_return_position
+ catapult_.set_unprofiled_goal(0.0, 0.0);
+
+ if (CatapultClose()) {
+ if (piece_loaded) {
+ state_ = CatapultState::LOADED;
+ } else {
+ state_ = CatapultState::READY;
+ }
+ }
+ break;
+ }
+
+ const double voltage = catapult_.UpdateController(disabled);
+ catapult_.UpdateObserver(voltage);
+ if (catapult_output != nullptr) {
+ *catapult_output = voltage;
+ }
+ catapult_status_offset = catapult_.MakeStatus(fbb);
+ }
+
+ flatbuffers::Offset<AimerStatus> aimer_offset;
+ if (aiming) {
+ aimer_offset = aimer_.PopulateStatus(fbb);
+ }
+
+ y2024::control_loops::superstructure::ShooterStatus::Builder status_builder(
+ *fbb);
+ status_builder.add_turret(turret_status_offset);
+ status_builder.add_altitude(altitude_status_offset);
+ status_builder.add_catapult(catapult_status_offset);
+ status_builder.add_catapult_state(state_);
+ if (aiming) {
+ status_builder.add_aimer(aimer_offset);
+ }
+
+ return status_builder.Finish();
+}
+
+} // namespace y2024::control_loops::superstructure
diff --git a/y2024/control_loops/superstructure/shooter.h b/y2024/control_loops/superstructure/shooter.h
new file mode 100644
index 0000000..2571cab
--- /dev/null
+++ b/y2024/control_loops/superstructure/shooter.h
@@ -0,0 +1,145 @@
+#ifndef Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_SHOOTER_H_
+#define Y2024_CONTROL_LOOPS_SUPERSTRUCTURE_SHOOTER_H_
+
+#include "frc971/control_loops/catapult/catapult.h"
+#include "frc971/control_loops/catapult/catapult_goal_static.h"
+#include "frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h"
+#include "frc971/shooter_interpolation/interpolation.h"
+#include "frc971/zeroing/pot_and_absolute_encoder.h"
+#include "y2024/constants.h"
+#include "y2024/constants/constants_generated.h"
+#include "y2024/control_loops/superstructure/aiming.h"
+#include "y2024/control_loops/superstructure/collision_avoidance.h"
+#include "y2024/control_loops/superstructure/superstructure_can_position_generated.h"
+#include "y2024/control_loops/superstructure/superstructure_goal_generated.h"
+#include "y2024/control_loops/superstructure/superstructure_position_generated.h"
+#include "y2024/control_loops/superstructure/superstructure_status_generated.h"
+
+namespace y2024::control_loops::superstructure {
+
+class Debouncer {
+ public:
+ Debouncer(std::chrono::nanoseconds rising_delay,
+ std::chrono::nanoseconds falling_delay)
+ : rising_delay_(rising_delay), falling_delay_(falling_delay) {}
+
+ void Update(bool state, aos::monotonic_clock::time_point now) {
+ if (state_transition_ != state) {
+ transition_time_ = now;
+ state_transition_ = state;
+ }
+
+ if (state != output_state_) {
+ if (state) {
+ output_state_ = now > transition_time_ + rising_delay_;
+ } else {
+ output_state_ = !(now > transition_time_ + falling_delay_);
+ }
+ }
+ }
+
+ bool state() const { return output_state_; }
+
+ private:
+ const std::chrono::nanoseconds rising_delay_;
+ const std::chrono::nanoseconds falling_delay_;
+
+ bool state_transition_ = false;
+ bool output_state_ = false;
+ aos::monotonic_clock::time_point transition_time_ =
+ aos::monotonic_clock::min_time;
+};
+
+// The shooter class will control the various subsystems involved in the
+// shooter- the turret, altitude, and catapult.
+class Shooter {
+ public:
+ using PotAndAbsoluteEncoderSubsystem =
+ ::frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystem<
+ ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator,
+ ::frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus>;
+
+ using CatapultSubsystem =
+ ::frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystem<
+ ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator,
+ ::frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus,
+ ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator,
+ aos::util::AsymmetricTrapezoidProfile>;
+
+ Shooter(aos::EventLoop *event_loop, const Constants *robot_constants);
+
+ void Reset() {
+ catapult_.Reset();
+ turret_.Reset();
+ altitude_.Reset();
+ }
+
+ void Estop() {
+ catapult_.Estop();
+ turret_.Estop();
+ altitude_.Estop();
+ }
+
+ bool zeroed() {
+ return catapult_.zeroed() && turret_.zeroed() && altitude_.zeroed();
+ }
+
+ bool estopped() {
+ return catapult_.estopped() && turret_.estopped() && altitude_.estopped();
+ }
+
+ inline const PotAndAbsoluteEncoderSubsystem &turret() const {
+ return turret_;
+ }
+
+ inline const PotAndAbsoluteEncoderSubsystem &altitude() const {
+ return altitude_;
+ }
+
+ flatbuffers::Offset<ShooterStatus> Iterate(
+ const Position *position, const ShooterGoal *shooter_goal, bool fire,
+ double *catapult_output, double *altitude_output, double *turret_output,
+ double *retention_roller_output,
+ double *retention_roller_stator_current_limit, double battery_voltage,
+ /* Hacky way to use collision avoidance in this class */
+ CollisionAvoidance *collision_avoidance, const double extend_position,
+ const double extend_goal, double *max_extend_position,
+ double *min_extend_position, const double intake_pivot_position,
+ double *max_turret_intake_position, double *min_intake_pivot_position,
+ flatbuffers::FlatBufferBuilder *fbb,
+ aos::monotonic_clock::time_point monotonic_now);
+
+ private:
+ CatapultState state_ = CatapultState::RETRACTING;
+
+ bool CatapultClose() const {
+ return (std::abs(catapult_.estimated_position() -
+ catapult_.unprofiled_goal(0, 0)) < 0.05 &&
+ std::abs(catapult_.estimated_velocity()) < 0.5);
+ }
+
+ aos::Fetcher<frc971::control_loops::drivetrain::Status>
+ drivetrain_status_fetcher_;
+
+ aos::Fetcher<y2024::control_loops::superstructure::CANPosition>
+ superstructure_can_position_fetcher_;
+
+ const Constants *robot_constants_;
+
+ CatapultSubsystem catapult_;
+
+ PotAndAbsoluteEncoderSubsystem turret_;
+ PotAndAbsoluteEncoderSubsystem altitude_;
+
+ Aimer aimer_;
+
+ frc971::shooter_interpolation::InterpolationTable<
+ y2024::constants::Values::ShotParams>
+ interpolation_table_;
+
+ Debouncer debouncer_;
+};
+
+} // namespace y2024::control_loops::superstructure
+
+#endif
diff --git a/y2024/control_loops/superstructure/superstructure.cc b/y2024/control_loops/superstructure/superstructure.cc
index 18db6d6..dc9369e 100644
--- a/y2024/control_loops/superstructure/superstructure.cc
+++ b/y2024/control_loops/superstructure/superstructure.cc
@@ -1,13 +1,31 @@
#include "y2024/control_loops/superstructure/superstructure.h"
+#include <chrono>
+
#include "aos/events/event_loop.h"
#include "aos/flatbuffer_merge.h"
#include "aos/network/team_number.h"
+#include "aos/time/time.h"
#include "frc971/shooter_interpolation/interpolation.h"
#include "frc971/zeroing/wrap.h"
DEFINE_bool(ignore_distance, false,
- "If true, ignore distance when shooting and obay joystick_reader");
+ "If true, ignore distance when shooting and obey joystick_reader");
+
+// The threshold used when decided if the extend is close enough to a goal to
+// continue.
+constexpr double kExtendThreshold = 0.01;
+
+constexpr double kTurretLoadingThreshold = 0.01;
+constexpr double kAltitudeLoadingThreshold = 0.01;
+
+constexpr std::chrono::milliseconds kExtraIntakingTime =
+ std::chrono::milliseconds(500);
+
+// Exit catapult loading state after this much time if we never
+// trigger any beambreaks.
+constexpr std::chrono::milliseconds kMaxCatapultLoadingTime =
+ std::chrono::milliseconds(3000);
namespace y2024::control_loops::superstructure {
@@ -18,11 +36,9 @@
using frc971::control_loops::RelativeEncoderProfiledJointStatus;
Superstructure::Superstructure(::aos::EventLoop *event_loop,
- std::shared_ptr<const constants::Values> values,
const ::std::string &name)
: frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
name),
- values_(values),
constants_fetcher_(event_loop),
robot_constants_(CHECK_NOTNULL(&constants_fetcher_.constants())),
drivetrain_status_fetcher_(
@@ -30,16 +46,22 @@
"/drivetrain")),
joystick_state_fetcher_(
event_loop->MakeFetcher<aos::JoystickState>("/aos")),
- transfer_goal_(TransferRollerGoal::NONE),
- intake_pivot_(
- robot_constants_->common()->intake_pivot(),
- robot_constants_->robot()->intake_constants()->zeroing_constants()),
+ intake_pivot_(robot_constants_->common()->intake_pivot(),
+ robot_constants_->robot()->intake_constants()),
climber_(
robot_constants_->common()->climber(),
- robot_constants_->robot()->climber_constants()->zeroing_constants()) {
+ robot_constants_->robot()->climber_constants()->zeroing_constants()),
+ shooter_(event_loop, robot_constants_),
+ extend_(
+ robot_constants_->common()->extend(),
+ robot_constants_->robot()->extend_constants()->zeroing_constants()) {
event_loop->SetRuntimeRealtimePriority(30);
}
+bool PositionNear(double position, double goal, double threshold) {
+ return std::abs(position - goal) < threshold;
+}
+
void Superstructure::RunIteration(const Goal *unsafe_goal,
const Position *position,
aos::Sender<Output>::Builder *output,
@@ -47,72 +69,17 @@
const monotonic_clock::time_point timestamp =
event_loop()->context().monotonic_event_time;
- (void)timestamp;
- (void)position;
-
if (WasReset()) {
AOS_LOG(ERROR, "WPILib reset, restarting\n");
intake_pivot_.Reset();
climber_.Reset();
+ shooter_.Reset();
+ extend_.Reset();
}
OutputT output_struct;
- double intake_pivot_position =
- robot_constants_->common()->intake_pivot_set_points()->retracted();
-
- if (unsafe_goal != nullptr &&
- unsafe_goal->intake_pivot_goal() == IntakePivotGoal::EXTENDED) {
- intake_pivot_position =
- robot_constants_->common()->intake_pivot_set_points()->extended();
- }
-
- IntakeRollerState intake_roller_state = IntakeRollerState::NONE;
-
- switch (unsafe_goal != nullptr ? unsafe_goal->intake_roller_goal()
- : IntakeRollerGoal::NONE) {
- case IntakeRollerGoal::NONE:
- output_struct.intake_roller_voltage = 0.0;
- break;
- case IntakeRollerGoal::SPIT:
- transfer_goal_ = TransferRollerGoal::TRANSFER_OUT;
- intake_roller_state = IntakeRollerState::SPITTING;
- output_struct.intake_roller_voltage =
- robot_constants_->common()->intake_roller_voltages()->spitting();
- break;
- case IntakeRollerGoal::INTAKE:
- transfer_goal_ = TransferRollerGoal::TRANSFER_IN;
- intake_roller_state = IntakeRollerState::INTAKING;
- output_struct.intake_roller_voltage =
- robot_constants_->common()->intake_roller_voltages()->intaking();
- break;
- }
-
- TransferRollerState transfer_roller_state = TransferRollerState::NONE;
-
- switch (unsafe_goal != nullptr ? transfer_goal_ : TransferRollerGoal::NONE) {
- case TransferRollerGoal::NONE:
- output_struct.transfer_roller_voltage = 0.0;
- break;
- case TransferRollerGoal::TRANSFER_IN:
- if (position->transfer_beambreak()) {
- transfer_goal_ = TransferRollerGoal::NONE;
- transfer_roller_state = TransferRollerState::NONE;
- output_struct.transfer_roller_voltage = 0.0;
- break;
- }
- transfer_roller_state = TransferRollerState::TRANSFERING_IN;
- output_struct.transfer_roller_voltage =
- robot_constants_->common()->transfer_roller_voltages()->transfer_in();
- break;
- case TransferRollerGoal::TRANSFER_OUT:
- transfer_roller_state = TransferRollerState::TRANSFERING_OUT;
- output_struct.transfer_roller_voltage = robot_constants_->common()
- ->transfer_roller_voltages()
- ->transfer_out();
- break;
- }
-
+ // Handle Climber Goal separately from main superstructure state machine
double climber_position =
robot_constants_->common()->climber_set_points()->retract();
@@ -122,19 +89,399 @@
climber_position =
robot_constants_->common()->climber_set_points()->full_extend();
break;
- case ClimberGoal::HALF_EXTEND:
- climber_position =
- robot_constants_->common()->climber_set_points()->half_extend();
- break;
case ClimberGoal::RETRACT:
climber_position =
robot_constants_->common()->climber_set_points()->retract();
break;
- default:
+ case ClimberGoal::STOWED:
+ climber_position =
+ robot_constants_->common()->climber_set_points()->stowed();
+ }
+ }
+
+ // If we started off preloaded, skip to the ready state.
+ if (unsafe_goal != nullptr && unsafe_goal->shooter_goal() &&
+ unsafe_goal->shooter_goal()->preloaded()) {
+ if (state_ != SuperstructureState::READY &&
+ state_ != SuperstructureState::FIRING) {
+ state_ = SuperstructureState::READY;
+ requested_note_goal_ = NoteGoal::CATAPULT;
+ }
+ }
+
+ // Handle the intake pivot goal separately from the main superstructure state
+ IntakeRollerStatus intake_roller_state = IntakeRollerStatus::NONE;
+ double intake_pivot_position =
+ robot_constants_->common()->intake_pivot_set_points()->retracted();
+
+ if (unsafe_goal != nullptr) {
+ switch (unsafe_goal->intake_goal()) {
+ case IntakeGoal::INTAKE:
+ intake_pivot_position =
+ robot_constants_->common()->intake_pivot_set_points()->extended();
+ intake_end_time_ = timestamp;
+ break;
+ case IntakeGoal::SPIT:
+ intake_pivot_position =
+ robot_constants_->common()->intake_pivot_set_points()->retracted();
+ break;
+ case IntakeGoal::NONE:
+ intake_pivot_position =
+ robot_constants_->common()->intake_pivot_set_points()->retracted();
break;
}
}
+ ExtendRollerStatus extend_roller_status = ExtendRollerStatus::IDLE;
+ ExtendStatus extend_goal_location = ExtendStatus::RETRACTED;
+
+ // True if the extend is moving towards a goal
+ bool extend_moving = false;
+
+ TransferRollerStatus transfer_roller_status = TransferRollerStatus::NONE;
+
+ const ExtendSetPoints *extend_set_points =
+ robot_constants_->common()->extend_set_points();
+
+ // Checks if the extend is close enough to the retracted position to be
+ // considered ready to accept note from the transfer rollers.
+ const bool extend_at_retracted = PositionNear(
+ extend_.position(), extend_set_points->retracted(), kExtendThreshold);
+
+ // Check if the turret is at the position to accept the note from extend
+ const bool turret_ready_for_load =
+ PositionNear(shooter_.turret().estimated_position(),
+ robot_constants_->common()->turret_loading_position(),
+ kTurretLoadingThreshold);
+
+ // Check if the altitude is at the position to accept the note from
+ // extend
+ const bool altitude_ready_for_load =
+ PositionNear(shooter_.altitude().estimated_position(),
+ robot_constants_->common()->altitude_loading_position(),
+ kAltitudeLoadingThreshold);
+
+ // Check if the extend is at the position to load the catapult
+ const bool extend_ready_for_catapult_transfer = PositionNear(
+ extend_.position(), extend_set_points->catapult(), kExtendThreshold);
+
+ // Only update the reuested note goal to the first goal that is requested by
+ // the manipulator
+ if (unsafe_goal != nullptr && unsafe_goal->note_goal() != NoteGoal::NONE &&
+ requested_note_goal_ == NoteGoal::NONE) {
+ requested_note_goal_ = unsafe_goal->note_goal();
+ }
+
+ // Superstructure state machine:
+ // 1. IDLE. The intake is retracted and there is no note in the robot.
+ // Wait for a intake goal to switch state to INTAKING if the extend is ready
+ // 2. INTAKING. Intake the note and transfer it towards the extend.
+ // Give intake, transfer, and extend rollers positive voltage to intake and
+ // transfer. Switch to LOADED when the extend beambreak is triggered.
+ // 3. LOADED. The note is in the extend and the extend is retracted.
+ // Wait for a note goal to switch state to MOVING.
+ // For AMP/TRAP goals, check that the turret is in a position to avoid
+ // collision.
+ // 4. MOVING. The extend is moving towards a goal (AMP, TRAP, or CATAPULT).
+ // For CATAPULT goals, wait for the turret and altitude to be in a position to
+ // accept the note from the extend.
+ // Wait for the extend to reach the goal and switch state to READY if
+ // AMP or TRAP, or to LOADING_CATAPULT if CATAPULT.
+ // 5. LOADING_CATAPULT. The extend is at the position to load the catapult.
+ // Activate the extend roller to transfer the note to the catapult.
+ // Switch state to READY when the catapult beambreak is triggered.
+ // 6. READY. Ready for fire command. The note is either loaded in the catapult
+ // or in the extend and at the AMP or TRAP position. Wait for a fire command.
+ // 7. FIRING. The note is being fired, either from the extend or the catapult.
+ // Switch state back to IDLE when the note is fired.
+
+ switch (state_) {
+ case SuperstructureState::IDLE:
+ requested_note_goal_ = NoteGoal::NONE;
+
+ if (unsafe_goal != nullptr &&
+ unsafe_goal->intake_goal() == IntakeGoal::INTAKE &&
+ extend_at_retracted) {
+ state_ = SuperstructureState::INTAKING;
+ }
+
+ extend_goal_location = ExtendStatus::RETRACTED;
+ catapult_requested_ = false;
+ break;
+ case SuperstructureState::INTAKING:
+ // Switch to LOADED state when the extend beambreak is triggered
+ // meaning the note is loaded in the extend
+ if (position->extend_beambreak()) {
+ state_ = SuperstructureState::LOADED;
+ }
+ intake_roller_state = IntakeRollerStatus::INTAKING;
+ transfer_roller_status = TransferRollerStatus::TRANSFERING_IN;
+ extend_roller_status = ExtendRollerStatus::TRANSFERING_TO_EXTEND;
+ extend_goal_location = ExtendStatus::RETRACTED;
+
+ if (!catapult_requested_ && unsafe_goal != nullptr &&
+ unsafe_goal->note_goal() == NoteGoal::CATAPULT) {
+ catapult_requested_ = true;
+ }
+
+ // If we are no longer requesting INTAKE or we are no longer requesting
+ // an INTAKE goal, wait 0.5 seconds then go back to IDLE.
+ if (!(unsafe_goal != nullptr &&
+ unsafe_goal->intake_goal() == IntakeGoal::INTAKE) &&
+ timestamp > intake_end_time_ + kExtraIntakingTime) {
+ state_ = SuperstructureState::IDLE;
+ }
+
+ break;
+ case SuperstructureState::LOADED:
+ if (!position->extend_beambreak() && !position->catapult_beambreak()) {
+ state_ = SuperstructureState::IDLE;
+ }
+
+ switch (requested_note_goal_) {
+ case NoteGoal::NONE:
+ break;
+ case NoteGoal::CATAPULT:
+ state_ = SuperstructureState::MOVING;
+ transfer_roller_status = TransferRollerStatus::TRANSFERING_IN;
+ break;
+ case NoteGoal::TRAP:
+ [[fallthrough]];
+ case NoteGoal::AMP:
+ transfer_roller_status = TransferRollerStatus::EXTEND_MOVING;
+ state_ = SuperstructureState::MOVING;
+ break;
+ }
+ extend_goal_location = ExtendStatus::RETRACTED;
+ break;
+ case SuperstructureState::MOVING:
+ transfer_roller_status = TransferRollerStatus::EXTEND_MOVING;
+ switch (requested_note_goal_) {
+ case NoteGoal::NONE:
+ extend_goal_location = ExtendStatus::RETRACTED;
+ if (extend_at_retracted) {
+ state_ = SuperstructureState::LOADED;
+ }
+ break;
+ case NoteGoal::CATAPULT:
+ extend_goal_location = ExtendStatus::CATAPULT;
+ if (extend_ready_for_catapult_transfer && turret_ready_for_load &&
+ altitude_ready_for_load) {
+ loading_catapult_start_time_ = timestamp;
+ state_ = SuperstructureState::LOADING_CATAPULT;
+ }
+ break;
+ case NoteGoal::TRAP:
+ extend_goal_location = ExtendStatus::TRAP;
+ // Check if the extend is at the TRAP position and if it is
+ // switch to READY state
+ if (PositionNear(extend_.position(), extend_set_points->trap(),
+ kExtendThreshold)) {
+ state_ = SuperstructureState::READY;
+ }
+ break;
+ case NoteGoal::AMP:
+ extend_goal_location = ExtendStatus::AMP;
+ // Check if the extend is at the AMP position and if it is
+ // switch to READY state
+ if (PositionNear(extend_.position(), extend_set_points->amp(),
+ kExtendThreshold)) {
+ state_ = SuperstructureState::READY;
+ }
+ break;
+ }
+
+ extend_moving = true;
+ break;
+ case SuperstructureState::LOADING_CATAPULT:
+ extend_moving = false;
+ extend_goal_location = ExtendStatus::CATAPULT;
+ extend_roller_status = ExtendRollerStatus::TRANSFERING_TO_CATAPULT;
+
+ // If we lost the game piece, reset state to idle.
+ if (((timestamp - loading_catapult_start_time_) >
+ kMaxCatapultLoadingTime) &&
+ !position->catapult_beambreak() && !position->extend_beambreak()) {
+ state_ = SuperstructureState::IDLE;
+ }
+
+ // Switch to READY state when the catapult beambreak is triggered
+ if (position->catapult_beambreak()) {
+ state_ = SuperstructureState::READY;
+ }
+ break;
+ case SuperstructureState::READY:
+ extend_moving = false;
+
+ // Switch to FIRING state when the fire button is pressed
+ if (unsafe_goal != nullptr && unsafe_goal->fire()) {
+ state_ = SuperstructureState::FIRING;
+ }
+
+ switch (requested_note_goal_) {
+ case NoteGoal::NONE:
+ extend_goal_location = ExtendStatus::RETRACTED;
+ extend_moving = true;
+ state_ = SuperstructureState::MOVING;
+ break;
+ case NoteGoal::CATAPULT:
+ extend_goal_location = ExtendStatus::CATAPULT;
+ break;
+ case NoteGoal::TRAP:
+ extend_goal_location = ExtendStatus::TRAP;
+ break;
+ case NoteGoal::AMP:
+ extend_goal_location = ExtendStatus::AMP;
+ break;
+ }
+ break;
+ case SuperstructureState::FIRING:
+ switch (requested_note_goal_) {
+ case NoteGoal::NONE:
+
+ break;
+ case NoteGoal::CATAPULT:
+ extend_goal_location = ExtendStatus::CATAPULT;
+ // Reset the state to IDLE when the game piece is fired from the
+ // catapult. We consider the game piece to be fired from the catapult
+ // when the catapultbeambreak is no longer triggered.
+ if (!position->catapult_beambreak()) {
+ state_ = SuperstructureState::IDLE;
+ }
+ break;
+ case NoteGoal::TRAP:
+ extend_roller_status = ExtendRollerStatus::SCORING_IN_TRAP;
+ extend_goal_location = ExtendStatus::TRAP;
+ if (!position->extend_beambreak() && unsafe_goal != nullptr &&
+ !unsafe_goal->fire()) {
+ state_ = SuperstructureState::IDLE;
+ }
+ break;
+ case NoteGoal::AMP:
+ extend_roller_status = ExtendRollerStatus::SCORING_IN_AMP;
+ extend_goal_location = ExtendStatus::AMP;
+ if (!position->extend_beambreak() && unsafe_goal != nullptr &&
+ !unsafe_goal->fire()) {
+ state_ = SuperstructureState::IDLE;
+ }
+ break;
+ }
+ break;
+ }
+
+ if (unsafe_goal != nullptr &&
+ unsafe_goal->intake_goal() == IntakeGoal::SPIT) {
+ intake_roller_state = IntakeRollerStatus::SPITTING;
+ transfer_roller_status = TransferRollerStatus::TRANSFERING_OUT;
+ }
+
+ // Update Intake Roller voltage based on status from state machine.
+ switch (intake_roller_state) {
+ case IntakeRollerStatus::NONE:
+ output_struct.intake_roller_voltage = 0.0;
+ break;
+ case IntakeRollerStatus::SPITTING:
+ output_struct.intake_roller_voltage =
+ robot_constants_->common()->intake_roller_voltages()->spitting();
+ break;
+ case IntakeRollerStatus::INTAKING:
+ output_struct.intake_roller_voltage =
+ robot_constants_->common()->intake_roller_voltages()->intaking();
+ break;
+ }
+
+ // Update Transfer Roller voltage based on status from state machine.
+ switch (transfer_roller_status) {
+ case TransferRollerStatus::NONE:
+ output_struct.transfer_roller_voltage = 0.0;
+ break;
+ case TransferRollerStatus::TRANSFERING_IN:
+ output_struct.transfer_roller_voltage =
+ robot_constants_->common()->transfer_roller_voltages()->transfer_in();
+ break;
+ case TransferRollerStatus::TRANSFERING_OUT:
+ output_struct.transfer_roller_voltage = robot_constants_->common()
+ ->transfer_roller_voltages()
+ ->transfer_out();
+ break;
+ case TransferRollerStatus::EXTEND_MOVING:
+ output_struct.transfer_roller_voltage = robot_constants_->common()
+ ->transfer_roller_voltages()
+ ->extend_moving();
+ break;
+ }
+
+ // Update Extend Roller voltage based on status from state machine.
+ const ExtendRollerVoltages *extend_roller_voltages =
+ robot_constants_->common()->extend_roller_voltages();
+ switch (extend_roller_status) {
+ case ExtendRollerStatus::IDLE:
+ // No voltage applied when idle
+ output_struct.extend_roller_voltage = 0.0;
+ break;
+ case ExtendRollerStatus::TRANSFERING_TO_EXTEND:
+ output_struct.extend_roller_voltage = extend_roller_voltages->scoring();
+ break;
+ case ExtendRollerStatus::SCORING_IN_AMP:
+ [[fallthrough]];
+ case ExtendRollerStatus::SCORING_IN_TRAP:
+ // Apply scoring voltage during scoring in amp or trap
+ output_struct.extend_roller_voltage = extend_roller_voltages->scoring();
+ break;
+ case ExtendRollerStatus::TRANSFERING_TO_CATAPULT:
+ // Apply scoring voltage during transferring to catapult
+ output_struct.extend_roller_voltage = extend_roller_voltages->scoring();
+ break;
+ }
+
+ double extend_goal_position = 0.0;
+
+ if (unsafe_goal != nullptr && unsafe_goal->note_goal() == NoteGoal::TRAP) {
+ extend_goal_location = ExtendStatus::TRAP;
+ }
+
+ // Set the extend position based on the state machine output
+ switch (extend_goal_location) {
+ case ExtendStatus::RETRACTED:
+ extend_goal_position = extend_set_points->retracted();
+ break;
+ case ExtendStatus::AMP:
+ extend_goal_position = extend_set_points->amp();
+ break;
+ case ExtendStatus::TRAP:
+ extend_goal_position = extend_set_points->trap();
+ break;
+ case ExtendStatus::CATAPULT:
+ extend_goal_position = extend_set_points->catapult();
+ break;
+ case ExtendStatus::MOVING:
+ // Should never happen
+ break;
+ }
+
+ NoteStatus uncompleted_note_goal_status = NoteStatus::NONE;
+
+ switch (requested_note_goal_) {
+ case NoteGoal::NONE:
+ uncompleted_note_goal_status = NoteStatus::NONE;
+ break;
+ case NoteGoal::CATAPULT:
+ uncompleted_note_goal_status = NoteStatus::CATAPULT;
+ break;
+ case NoteGoal::AMP:
+ uncompleted_note_goal_status = NoteStatus::AMP;
+ break;
+ case NoteGoal::TRAP:
+ uncompleted_note_goal_status = NoteStatus::TRAP;
+ break;
+ }
+
+ // Set the extend status based on the state machine output
+ // If the extend is moving, the status is MOVING, otherwise it is the same
+ // as extend_status
+ ExtendStatus extend_status =
+ (extend_moving ? ExtendStatus::MOVING : extend_goal_location);
+
if (joystick_state_fetcher_.Fetch() &&
joystick_state_fetcher_->has_alliance()) {
alliance_ = joystick_state_fetcher_->alliance();
@@ -142,22 +489,11 @@
drivetrain_status_fetcher_.Fetch();
- aos::FlatbufferFixedAllocatorArray<
- frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
- intake_pivot_goal_buffer;
-
- intake_pivot_goal_buffer.Finish(
- frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
- *intake_pivot_goal_buffer.fbb(), intake_pivot_position));
-
- const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
- *intake_pivot_goal = &intake_pivot_goal_buffer.message();
-
- const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
- intake_pivot_status_offset = intake_pivot_.Iterate(
- intake_pivot_goal, position->intake_pivot(),
- output != nullptr ? &output_struct.intake_pivot_voltage : nullptr,
- status->fbb());
+ const bool collided = collision_avoidance_.IsCollided({
+ .intake_pivot_position = intake_pivot_.estimated_position(),
+ .turret_position = shooter_.turret().estimated_position(),
+ .extend_position = extend_.estimated_position(),
+ });
aos::FlatbufferFixedAllocatorArray<
frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
@@ -176,21 +512,116 @@
output != nullptr ? &output_struct.climber_voltage : nullptr,
status->fbb());
+ double max_intake_pivot_position = 0;
+ double min_intake_pivot_position = 0;
+ double max_extend_position = 0;
+ double min_extend_position = 0;
+
+ aos::FlatbufferFixedAllocatorArray<
+ frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
+ intake_pivot_goal_buffer;
+
+ intake_pivot_goal_buffer.Finish(
+ frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *intake_pivot_goal_buffer.fbb(), intake_pivot_position));
+
+ const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
+ *intake_pivot_goal = &intake_pivot_goal_buffer.message();
+
+ double *intake_output =
+ (output != nullptr ? &output_struct.intake_pivot_voltage : nullptr);
+
+ const bool disabled = intake_pivot_.Correct(
+ intake_pivot_goal, position->intake_pivot(), intake_output == nullptr);
+
+ aos::FlatbufferFixedAllocatorArray<
+ frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
+ extend_goal_buffer;
+
+ extend_goal_buffer.Finish(
+ frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *extend_goal_buffer.fbb(), extend_goal_position));
+
+ const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
+ *extend_goal = &extend_goal_buffer.message();
+
+ // TODO(max): Change how we handle the collision with the turret and
+ // intake to be clearer
+ const flatbuffers::Offset<ShooterStatus> shooter_status_offset =
+ shooter_.Iterate(
+ position,
+ unsafe_goal != nullptr ? unsafe_goal->shooter_goal() : nullptr,
+ unsafe_goal != nullptr ? unsafe_goal->fire() : false,
+ output != nullptr ? &output_struct.catapult_voltage : nullptr,
+ output != nullptr ? &output_struct.altitude_voltage : nullptr,
+ output != nullptr ? &output_struct.turret_voltage : nullptr,
+ output != nullptr ? &output_struct.retention_roller_voltage : nullptr,
+ output != nullptr
+ ? &output_struct.retention_roller_stator_current_limit
+ : nullptr,
+ robot_state().voltage_battery(), &collision_avoidance_,
+ extend_goal_position, extend_.estimated_position(),
+ &max_extend_position, &min_extend_position,
+ intake_pivot_.estimated_position(), &max_intake_pivot_position,
+ &min_intake_pivot_position, status->fbb(), timestamp);
+
+ intake_pivot_.set_min_position(min_intake_pivot_position);
+ intake_pivot_.set_max_position(max_intake_pivot_position);
+
+ extend_.set_min_position(min_extend_position);
+ extend_.set_max_position(max_extend_position);
+
+ // Calculate the loops for a cycle.
+ const double voltage = intake_pivot_.UpdateController(disabled);
+
+ intake_pivot_.UpdateObserver(voltage);
+
+ // Write out all the voltages.
+ if (intake_output) {
+ *intake_output = voltage;
+ }
+
+ const flatbuffers::Offset<AbsoluteEncoderProfiledJointStatus>
+ intake_pivot_status_offset = intake_pivot_.MakeStatus(status->fbb());
+
+ const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
+ extend_status_offset = extend_.Iterate(
+ extend_goal, position->extend(),
+ output != nullptr ? &output_struct.extend_voltage : nullptr,
+ status->fbb());
+
if (output) {
output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
}
Status::Builder status_builder = status->MakeBuilder<Status>();
- const bool zeroed = intake_pivot_.zeroed() && climber_.zeroed();
- const bool estopped = intake_pivot_.estopped() || climber_.estopped();
+ const bool zeroed = intake_pivot_.zeroed() && climber_.zeroed() &&
+ shooter_.zeroed() && extend_.zeroed();
+ const bool estopped = intake_pivot_.estopped() || climber_.estopped() ||
+ shooter_.estopped() || extend_.estopped();
status_builder.add_zeroed(zeroed);
status_builder.add_estopped(estopped);
- status_builder.add_intake_roller_state(intake_roller_state);
- status_builder.add_intake_pivot_state(intake_pivot_status_offset);
- status_builder.add_transfer_roller_state(transfer_roller_state);
- status_builder.add_climber_state(climber_status_offset);
+ status_builder.add_intake_roller(intake_roller_state);
+ status_builder.add_intake_pivot(intake_pivot_status_offset);
+ status_builder.add_transfer_roller(transfer_roller_status);
+ status_builder.add_climber(climber_status_offset);
+ status_builder.add_shooter(shooter_status_offset);
+ status_builder.add_collided(collided);
+ status_builder.add_extend_roller(extend_roller_status);
+ status_builder.add_extend_status(extend_status);
+ status_builder.add_extend(extend_status_offset);
+ status_builder.add_state(state_);
+ status_builder.add_uncompleted_note_goal(uncompleted_note_goal_status);
+ status_builder.add_extend_ready_for_transfer(extend_at_retracted);
+ status_builder.add_extend_at_retracted(extend_at_retracted);
+ status_builder.add_turret_ready_for_load(turret_ready_for_load);
+ status_builder.add_altitude_ready_for_load(altitude_ready_for_load);
+ status_builder.add_extend_ready_for_catapult_transfer(
+ extend_ready_for_catapult_transfer);
+ status_builder.add_extend_beambreak(position->extend_beambreak());
+ status_builder.add_catapult_beambreak(position->catapult_beambreak());
(void)status->Send(status_builder.Finish());
}
diff --git a/y2024/control_loops/superstructure/superstructure.h b/y2024/control_loops/superstructure/superstructure.h
index 88db2e2..1c2d119 100644
--- a/y2024/control_loops/superstructure/superstructure.h
+++ b/y2024/control_loops/superstructure/superstructure.h
@@ -3,6 +3,7 @@
#include "aos/events/event_loop.h"
#include "aos/json_to_flatbuffer.h"
+#include "aos/time/time.h"
#include "frc971/constants/constants_sender_lib.h"
#include "frc971/control_loops/control_loop.h"
#include "frc971/control_loops/drivetrain/drivetrain_can_position_generated.h"
@@ -11,6 +12,7 @@
#include "frc971/zeroing/pot_and_absolute_encoder.h"
#include "y2024/constants.h"
#include "y2024/constants/constants_generated.h"
+#include "y2024/control_loops/superstructure/shooter.h"
#include "y2024/control_loops/superstructure/superstructure_goal_generated.h"
#include "y2024/control_loops/superstructure/superstructure_output_generated.h"
#include "y2024/control_loops/superstructure/superstructure_position_generated.h"
@@ -21,16 +23,20 @@
class Superstructure
: public ::frc971::controls::ControlLoop<Goal, Position, Status, Output> {
public:
+ using AbsoluteEncoderSubsystem =
+ ::frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystem<
+ ::frc971::zeroing::AbsoluteEncoderZeroingEstimator,
+ ::frc971::control_loops::AbsoluteEncoderProfiledJointStatus>;
+
using PotAndAbsoluteEncoderSubsystem =
::frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystem<
::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator,
::frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus>;
explicit Superstructure(::aos::EventLoop *event_loop,
- std::shared_ptr<const constants::Values> values,
const ::std::string &name = "/superstructure");
- inline const PotAndAbsoluteEncoderSubsystem &intake_pivot() const {
+ inline const AbsoluteEncoderSubsystem &intake_pivot() const {
return intake_pivot_;
}
@@ -38,6 +44,11 @@
return climber_;
}
+ inline const Shooter &shooter() const { return shooter_; }
+ inline const PotAndAbsoluteEncoderSubsystem &extend() const {
+ return extend_;
+ }
+
double robot_velocity() const;
protected:
@@ -46,18 +57,34 @@
aos::Sender<Status>::Builder *status) override;
private:
- std::shared_ptr<const constants::Values> values_;
frc971::constants::ConstantsFetcher<Constants> constants_fetcher_;
const Constants *robot_constants_;
aos::Fetcher<frc971::control_loops::drivetrain::Status>
drivetrain_status_fetcher_;
aos::Fetcher<aos::JoystickState> joystick_state_fetcher_;
+ CollisionAvoidance collision_avoidance_;
+
aos::Alliance alliance_ = aos::Alliance::kInvalid;
- TransferRollerGoal transfer_goal_;
- PotAndAbsoluteEncoderSubsystem intake_pivot_;
+ bool catapult_requested_ = false;
+
+ SuperstructureState state_ = SuperstructureState::IDLE;
+
+ NoteGoal requested_note_goal_ = NoteGoal::NONE;
+
+ aos::monotonic_clock::time_point intake_end_time_ =
+ aos::monotonic_clock::time_point::min();
+
+ aos::monotonic_clock::time_point loading_catapult_start_time_ =
+ aos::monotonic_clock::time_point::min();
+
+ AbsoluteEncoderSubsystem intake_pivot_;
PotAndAbsoluteEncoderSubsystem climber_;
+
+ Shooter shooter_;
+
+ PotAndAbsoluteEncoderSubsystem extend_;
DISALLOW_COPY_AND_ASSIGN(Superstructure);
};
diff --git a/y2024/control_loops/superstructure/superstructure_can_position.fbs b/y2024/control_loops/superstructure/superstructure_can_position.fbs
index 6cd3f1e..e809adf 100644
--- a/y2024/control_loops/superstructure/superstructure_can_position.fbs
+++ b/y2024/control_loops/superstructure/superstructure_can_position.fbs
@@ -23,6 +23,23 @@
// CAN Position of the climber falcon
climber:frc971.control_loops.CANTalonFX (id: 5);
+
+ // CAN Position of the retention roller falcon
+ retention_roller:frc971.control_loops.CANTalonFX (id: 6);
+
+ // CAN Position of the shooter turret falcon
+ turret:frc971.control_loops.CANTalonFX (id: 7);
+
+ // CAN Position of the shooter altitude falcon
+ altitude:frc971.control_loops.CANTalonFX (id: 8);
+
+ // CAN Position of the extend falcon
+ extend:frc971.control_loops.CANTalonFX (id: 9);
+
+ // CAN Position of the extend roller fancon
+ extend_roller:frc971.control_loops.CANTalonFX (id: 10);
+ catapult_one:frc971.control_loops.CANTalonFX (id: 11);
+ catapult_two:frc971.control_loops.CANTalonFX (id: 12);
}
-root_type CANPosition;
\ No newline at end of file
+root_type CANPosition;
diff --git a/y2024/control_loops/superstructure/superstructure_goal.fbs b/y2024/control_loops/superstructure/superstructure_goal.fbs
index bb7b706..42caf9b 100644
--- a/y2024/control_loops/superstructure/superstructure_goal.fbs
+++ b/y2024/control_loops/superstructure/superstructure_goal.fbs
@@ -3,43 +3,60 @@
namespace y2024.control_loops.superstructure;
-// Represents goal for intake rollers
-enum IntakeRollerGoal : ubyte {
+// Represents goal for the intake pivot and rollers
+// INTAKE will extend the pivot and turn on the rollers to intake the note.
+// SPIT will extend the pivot and turn on the rollers (in reverse) to spit out the note.
+enum IntakeGoal : ubyte {
NONE = 0,
- SPIT = 1,
- INTAKE = 2,
-}
-
-// Represents goal for pivot on intake
-enum IntakePivotGoal : ubyte {
- EXTENDED = 0,
- RETRACTED = 1,
-}
-
-// Represents goal of transfer rollers
-// TRANSFER_IN is for transfering game piece in from the intake to the shooter
-// TRANSFER_OUT is for transfering game piece out to the intake for spitting
-enum TransferRollerGoal : ubyte {
- NONE = 0,
- TRANSFER_IN = 1,
- TRANSFER_OUT = 2,
+ INTAKE = 1,
+ SPIT = 2,
}
// Represents goal for climber
// FULL_EXTEND is for fully extending the climber
-// HALF_EXTEND is for partially extending the climber
// RETRACT is for retracting the climber
enum ClimberGoal : ubyte {
FULL_EXTEND = 0,
- HALF_EXTEND = 1,
- RETRACT = 2,
+ RETRACT = 1,
+ STOWED = 2,
}
+table ShooterGoal {
+ catapult_goal:frc971.control_loops.catapult.CatapultGoal (id: 0);
+
+ // If true we ignore the other provided positions
+ auto_aim: bool (id: 1);
+
+ // Position for the turret when we aren't auto aiming
+ turret_position: frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemGoal (id: 2);
+
+ // Position for the altitude when we aren't auto aiming
+ altitude_position: frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemGoal (id: 3);
+
+ // If true, we started with the ball loaded and should proceed to that state.
+ preloaded:bool = false (id: 4);
+}
+
+// Represents goal for the note movement through the robot
+// to various scoring positions
+// NONE represents no goal for the note
+// AMP represents the goal to move the note and the extend to the AMP scoring position
+// TRAP represents the goal to move the note and the extend to the TRAP scoring position
+// CATAPULT represents the goal to load the note in the catapult.
+// It will complete the catapult goal before accepting new goals.
+enum NoteGoal : ubyte {
+ NONE = 0,
+ AMP = 1,
+ TRAP = 2,
+ CATAPULT = 3,
+}
+
+
table Goal {
- intake_roller_goal:IntakeRollerGoal (id: 0);
- intake_pivot_goal:IntakePivotGoal (id: 1);
- catapult_goal:frc971.control_loops.catapult.CatapultGoal (id: 2);
- transfer_roller_goal:TransferRollerGoal (id: 3);
- climber_goal:ClimberGoal (id: 4);
+ intake_goal:IntakeGoal = NONE (id: 0);
+ climber_goal:ClimberGoal (id: 1);
+ shooter_goal:ShooterGoal (id: 2);
+ note_goal:NoteGoal (id: 3);
+ fire: bool (id: 4);
}
root_type Goal;
diff --git a/y2024/control_loops/superstructure/superstructure_lib_test.cc b/y2024/control_loops/superstructure/superstructure_lib_test.cc
index 8020a4d..56452c8 100644
--- a/y2024/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2024/control_loops/superstructure/superstructure_lib_test.cc
@@ -5,6 +5,7 @@
#include "aos/events/logging/log_writer.h"
#include "frc971/control_loops/capped_test_plant.h"
+#include "frc971/control_loops/catapult/catapult_goal_static.h"
#include "frc971/control_loops/control_loop_test.h"
#include "frc971/control_loops/position_sensor_sim.h"
#include "frc971/control_loops/subsystem_simulator.h"
@@ -12,9 +13,13 @@
#include "frc971/zeroing/absolute_encoder.h"
#include "y2024/constants/simulated_constants_sender.h"
#include "y2024/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+#include "y2024/control_loops/superstructure/altitude/altitude_plant.h"
+#include "y2024/control_loops/superstructure/catapult/catapult_plant.h"
#include "y2024/control_loops/superstructure/climber/climber_plant.h"
+#include "y2024/control_loops/superstructure/extend/extend_plant.h"
#include "y2024/control_loops/superstructure/intake_pivot/intake_pivot_plant.h"
#include "y2024/control_loops/superstructure/superstructure.h"
+#include "y2024/control_loops/superstructure/turret/turret_plant.h"
DEFINE_string(output_folder, "",
"If set, logs all channels to the provided logfile.");
@@ -33,11 +38,16 @@
using DrivetrainStatus = ::frc971::control_loops::drivetrain::Status;
typedef Superstructure::PotAndAbsoluteEncoderSubsystem
PotAndAbsoluteEncoderSubsystem;
+typedef Superstructure::AbsoluteEncoderSubsystem AbsoluteEncoderSubsystem;
using PotAndAbsoluteEncoderSimulator =
frc971::control_loops::SubsystemSimulator<
frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus,
PotAndAbsoluteEncoderSubsystem::State,
constants::Values::PotAndAbsEncoderConstants>;
+using AbsoluteEncoderSimulator = frc971::control_loops::SubsystemSimulator<
+ frc971::control_loops::AbsoluteEncoderProfiledJointStatus,
+ AbsoluteEncoderSubsystem::State,
+ constants::Values::AbsoluteEncoderConstants>;
class SuperstructureSimulation {
public:
@@ -48,30 +58,26 @@
dt_(dt),
superstructure_position_sender_(
event_loop_->MakeSender<Position>("/superstructure")),
+ superstructure_can_position_sender_(
+ event_loop_->MakeSender<CANPosition>("/superstructure/rio")),
superstructure_status_fetcher_(
event_loop_->MakeFetcher<Status>("/superstructure")),
superstructure_output_fetcher_(
event_loop_->MakeFetcher<Output>("/superstructure")),
- transfer_beambreak_(false),
+ extend_beambreak_(false),
+ catapult_beambreak_(false),
intake_pivot_(
new CappedTestPlant(intake_pivot::MakeIntakePivotPlant()),
PositionSensorSimulator(simulated_robot_constants->robot()
->intake_constants()
- ->zeroing_constants()
->one_revolution_distance()),
{.subsystem_params =
{simulated_robot_constants->common()->intake_pivot(),
- simulated_robot_constants->robot()
- ->intake_constants()
- ->zeroing_constants()},
- .potentiometer_offset = simulated_robot_constants->robot()
- ->intake_constants()
- ->potentiometer_offset()},
+ simulated_robot_constants->robot()->intake_constants()}},
frc971::constants::Range::FromFlatbuffer(
simulated_robot_constants->common()->intake_pivot()->range()),
simulated_robot_constants->robot()
->intake_constants()
- ->zeroing_constants()
->measured_absolute_position(),
dt_),
climber_(new CappedTestPlant(climber::MakeClimberPlant()),
@@ -93,7 +99,87 @@
->climber_constants()
->zeroing_constants()
->measured_absolute_position(),
- dt_) {
+ dt_),
+ catapult_(new CappedTestPlant(catapult::MakeCatapultPlant()),
+ PositionSensorSimulator(simulated_robot_constants->robot()
+ ->catapult_constants()
+ ->zeroing_constants()
+ ->one_revolution_distance()),
+ {.subsystem_params =
+ {simulated_robot_constants->common()->catapult(),
+ simulated_robot_constants->robot()
+ ->catapult_constants()
+ ->zeroing_constants()},
+ .potentiometer_offset = simulated_robot_constants->robot()
+ ->catapult_constants()
+ ->potentiometer_offset()},
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->catapult()->range()),
+ simulated_robot_constants->robot()
+ ->catapult_constants()
+ ->zeroing_constants()
+ ->measured_absolute_position(),
+ dt_),
+ altitude_(new CappedTestPlant(altitude::MakeAltitudePlant()),
+ PositionSensorSimulator(simulated_robot_constants->robot()
+ ->altitude_constants()
+ ->zeroing_constants()
+ ->one_revolution_distance()),
+ {.subsystem_params =
+ {simulated_robot_constants->common()->altitude(),
+ simulated_robot_constants->robot()
+ ->altitude_constants()
+ ->zeroing_constants()},
+ .potentiometer_offset = simulated_robot_constants->robot()
+ ->altitude_constants()
+ ->potentiometer_offset()},
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->altitude()->range()),
+ simulated_robot_constants->robot()
+ ->altitude_constants()
+ ->zeroing_constants()
+ ->measured_absolute_position(),
+ dt_),
+ turret_(
+ new CappedTestPlant(turret::MakeTurretPlant()),
+ PositionSensorSimulator(simulated_robot_constants->robot()
+ ->turret_constants()
+ ->zeroing_constants()
+ ->one_revolution_distance()),
+ {.subsystem_params = {simulated_robot_constants->common()->turret(),
+ simulated_robot_constants->robot()
+ ->turret_constants()
+ ->zeroing_constants()},
+ .potentiometer_offset = simulated_robot_constants->robot()
+ ->turret_constants()
+ ->potentiometer_offset()},
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->turret()->range()),
+ simulated_robot_constants->robot()
+ ->turret_constants()
+ ->zeroing_constants()
+ ->measured_absolute_position(),
+ dt_),
+ extend_(
+ new CappedTestPlant(extend::MakeExtendPlant()),
+ PositionSensorSimulator(simulated_robot_constants->robot()
+ ->extend_constants()
+ ->zeroing_constants()
+ ->one_revolution_distance()),
+ {.subsystem_params = {simulated_robot_constants->common()->extend(),
+ simulated_robot_constants->robot()
+ ->extend_constants()
+ ->zeroing_constants()},
+ .potentiometer_offset = simulated_robot_constants->robot()
+ ->extend_constants()
+ ->potentiometer_offset()},
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->extend()->range()),
+ simulated_robot_constants->robot()
+ ->extend_constants()
+ ->zeroing_constants()
+ ->measured_absolute_position(),
+ dt_) {
intake_pivot_.InitializePosition(
frc971::constants::Range::FromFlatbuffer(
simulated_robot_constants->common()->intake_pivot()->range())
@@ -102,6 +188,23 @@
frc971::constants::Range::FromFlatbuffer(
simulated_robot_constants->common()->climber()->range())
.middle());
+ catapult_.InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->catapult()->range())
+ .middle());
+ altitude_.InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->altitude()->range())
+ .middle());
+ turret_.InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->turret()->range())
+ .middle());
+ extend_.InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants->common()->extend()->range())
+ .middle());
+
phased_loop_handle_ = event_loop_->AddPhasedLoop(
[this](int) {
// Skip this the first time.
@@ -111,10 +214,24 @@
intake_pivot_.Simulate(
superstructure_output_fetcher_->intake_pivot_voltage(),
- superstructure_status_fetcher_->intake_pivot_state());
+ superstructure_status_fetcher_->intake_pivot());
climber_.Simulate(superstructure_output_fetcher_->climber_voltage(),
- superstructure_status_fetcher_->climber_state());
+ superstructure_status_fetcher_->climber());
+ catapult_.Simulate(
+ superstructure_output_fetcher_->catapult_voltage(),
+ superstructure_status_fetcher_->shooter()->catapult());
+
+ altitude_.Simulate(
+ superstructure_output_fetcher_->altitude_voltage(),
+ superstructure_status_fetcher_->shooter()->altitude());
+
+ turret_.Simulate(
+ superstructure_output_fetcher_->turret_voltage(),
+ superstructure_status_fetcher_->shooter()->turret());
+
+ extend_.Simulate(superstructure_output_fetcher_->extend_voltage(),
+ superstructure_status_fetcher_->extend());
}
first_ = false;
SendPositionMessage();
@@ -127,47 +244,85 @@
::aos::Sender<Position>::Builder builder =
superstructure_position_sender_.MakeBuilder();
- frc971::PotAndAbsolutePosition::Builder intake_pivot_builder =
- builder.MakeBuilder<frc971::PotAndAbsolutePosition>();
- flatbuffers::Offset<frc971::PotAndAbsolutePosition> intake_pivot_offset =
+ frc971::AbsolutePosition::Builder intake_pivot_builder =
+ builder.MakeBuilder<frc971::AbsolutePosition>();
+ flatbuffers::Offset<frc971::AbsolutePosition> intake_pivot_offset =
intake_pivot_.encoder()->GetSensorValues(&intake_pivot_builder);
frc971::PotAndAbsolutePosition::Builder climber_builder =
builder.MakeBuilder<frc971::PotAndAbsolutePosition>();
+
flatbuffers::Offset<frc971::PotAndAbsolutePosition> climber_offset =
climber_.encoder()->GetSensorValues(&climber_builder);
+ frc971::PotAndAbsolutePosition::Builder catapult_builder =
+ builder.MakeBuilder<frc971::PotAndAbsolutePosition>();
+
+ flatbuffers::Offset<frc971::PotAndAbsolutePosition> catapult_offset =
+ catapult_.encoder()->GetSensorValues(&catapult_builder);
+
+ frc971::PotAndAbsolutePosition::Builder altitude_builder =
+ builder.MakeBuilder<frc971::PotAndAbsolutePosition>();
+ flatbuffers::Offset<frc971::PotAndAbsolutePosition> altitude_offset =
+ altitude_.encoder()->GetSensorValues(&altitude_builder);
+
+ frc971::PotAndAbsolutePosition::Builder turret_builder =
+ builder.MakeBuilder<frc971::PotAndAbsolutePosition>();
+ flatbuffers::Offset<frc971::PotAndAbsolutePosition> turret_offset =
+ turret_.encoder()->GetSensorValues(&turret_builder);
+
+ frc971::PotAndAbsolutePosition::Builder extend_builder =
+ builder.MakeBuilder<frc971::PotAndAbsolutePosition>();
+ flatbuffers::Offset<frc971::PotAndAbsolutePosition> extend_offset =
+ extend_.encoder()->GetSensorValues(&extend_builder);
Position::Builder position_builder = builder.MakeBuilder<Position>();
- position_builder.add_transfer_beambreak(transfer_beambreak_);
+ position_builder.add_extend_beambreak(extend_beambreak_);
+ position_builder.add_catapult_beambreak(catapult_beambreak_);
position_builder.add_intake_pivot(intake_pivot_offset);
-
+ position_builder.add_catapult(catapult_offset);
+ position_builder.add_altitude(altitude_offset);
+ position_builder.add_turret(turret_offset);
position_builder.add_climber(climber_offset);
+ position_builder.add_extend(extend_offset);
+
CHECK_EQ(builder.Send(position_builder.Finish()),
aos::RawSender::Error::kOk);
}
- void set_transfer_beambreak(bool triggered) {
- transfer_beambreak_ = triggered;
+ void set_extend_beambreak(bool triggered) { extend_beambreak_ = triggered; }
+
+ void set_catapult_beambreak(bool triggered) {
+ catapult_beambreak_ = triggered;
}
- PotAndAbsoluteEncoderSimulator *intake_pivot() { return &intake_pivot_; }
-
+ AbsoluteEncoderSimulator *intake_pivot() { return &intake_pivot_; }
+ PotAndAbsoluteEncoderSimulator *catapult() { return &catapult_; }
+ PotAndAbsoluteEncoderSimulator *altitude() { return &altitude_; }
+ PotAndAbsoluteEncoderSimulator *turret() { return &turret_; }
PotAndAbsoluteEncoderSimulator *climber() { return &climber_; }
+ PotAndAbsoluteEncoderSimulator *extend() { return &extend_; }
+
private:
::aos::EventLoop *event_loop_;
const chrono::nanoseconds dt_;
::aos::PhasedLoopHandler *phased_loop_handle_ = nullptr;
::aos::Sender<Position> superstructure_position_sender_;
+ ::aos::Sender<CANPosition> superstructure_can_position_sender_;
::aos::Fetcher<Status> superstructure_status_fetcher_;
::aos::Fetcher<Output> superstructure_output_fetcher_;
- bool transfer_beambreak_;
+ bool extend_beambreak_;
+ bool catapult_beambreak_;
- PotAndAbsoluteEncoderSimulator intake_pivot_;
+ AbsoluteEncoderSimulator intake_pivot_;
PotAndAbsoluteEncoderSimulator climber_;
+ PotAndAbsoluteEncoderSimulator catapult_;
+ PotAndAbsoluteEncoderSimulator altitude_;
+ PotAndAbsoluteEncoderSimulator turret_;
+ PotAndAbsoluteEncoderSimulator extend_;
bool first_ = true;
};
@@ -178,13 +333,12 @@
: ::frc971::testing::ControlLoopTest(
aos::configuration::ReadConfig("y2024/aos_config.json"),
std::chrono::microseconds(5050)),
- values_(std::make_shared<constants::Values>(constants::MakeValues())),
simulated_constants_dummy_(SendSimulationConstants(
event_loop_factory(), 7971, "y2024/constants/test_constants.json")),
roborio_(aos::configuration::GetNode(configuration(), "roborio")),
logger_pi_(aos::configuration::GetNode(configuration(), "logger")),
superstructure_event_loop(MakeEventLoop("Superstructure", roborio_)),
- superstructure_(superstructure_event_loop.get(), (values_)),
+ superstructure_(superstructure_event_loop.get()),
test_event_loop_(MakeEventLoop("test", roborio_)),
constants_fetcher_(test_event_loop_.get()),
simulated_robot_constants_(
@@ -201,6 +355,8 @@
test_event_loop_->MakeFetcher<Position>("/superstructure")),
superstructure_position_sender_(
test_event_loop_->MakeSender<Position>("/superstructure")),
+ superstructure_can_position_sender_(
+ test_event_loop_->MakeSender<CANPosition>("/superstructure/rio")),
drivetrain_status_sender_(
test_event_loop_->MakeSender<DrivetrainStatus>("/drivetrain")),
superstructure_plant_event_loop_(MakeEventLoop("plant", roborio_)),
@@ -223,30 +379,64 @@
superstructure_status_fetcher_.Fetch();
superstructure_output_fetcher_.Fetch();
+ ASSERT_FALSE(superstructure_status_fetcher_->estopped());
+
ASSERT_TRUE(superstructure_goal_fetcher_.get() != nullptr) << ": No goal";
ASSERT_TRUE(superstructure_status_fetcher_.get() != nullptr)
<< ": No status";
ASSERT_TRUE(superstructure_output_fetcher_.get() != nullptr)
<< ": No output";
- double set_point = simulated_robot_constants_->common()
- ->intake_pivot_set_points()
- ->retracted();
+ EXPECT_FALSE(superstructure_status_fetcher_->collided());
- if (superstructure_goal_fetcher_->intake_pivot_goal() ==
- IntakePivotGoal::EXTENDED) {
+ double set_point =
+ superstructure_status_fetcher_->intake_pivot()->goal_position();
+
+ if (superstructure_goal_fetcher_->intake_goal() == IntakeGoal::INTAKE) {
set_point = simulated_robot_constants_->common()
->intake_pivot_set_points()
->extended();
}
- EXPECT_NEAR(
- set_point,
- superstructure_status_fetcher_->intake_pivot_state()->position(),
- 0.001);
+ EXPECT_NEAR(set_point,
+ superstructure_status_fetcher_->intake_pivot()->position(),
+ 0.03);
- if (superstructure_status_fetcher_->intake_roller_state() ==
- IntakeRollerState::NONE) {
+ if (superstructure_goal_fetcher_->has_shooter_goal() &&
+ superstructure_status_fetcher_->uncompleted_note_goal() !=
+ NoteStatus::AMP &&
+ superstructure_status_fetcher_->uncompleted_note_goal() !=
+ NoteStatus::TRAP) {
+ if (superstructure_goal_fetcher_->shooter_goal()->has_turret_position() &&
+ !superstructure_goal_fetcher_->shooter_goal()->auto_aim()) {
+ EXPECT_NEAR(
+ superstructure_goal_fetcher_->shooter_goal()
+ ->turret_position()
+ ->unsafe_goal(),
+ superstructure_status_fetcher_->shooter()->turret()->position(),
+ 0.001);
+ }
+ }
+
+ if (superstructure_goal_fetcher_->has_shooter_goal()) {
+ if (superstructure_goal_fetcher_->shooter_goal()
+ ->has_altitude_position() &&
+ !superstructure_goal_fetcher_->shooter_goal()->auto_aim()) {
+ EXPECT_NEAR(
+ superstructure_goal_fetcher_->shooter_goal()
+ ->altitude_position()
+ ->unsafe_goal(),
+ superstructure_status_fetcher_->shooter()->altitude()->position(),
+ 0.001);
+ EXPECT_NEAR(superstructure_goal_fetcher_->shooter_goal()
+ ->altitude_position()
+ ->unsafe_goal(),
+ superstructure_plant_.altitude()->position(), 0.001);
+ }
+ }
+
+ if (superstructure_status_fetcher_->intake_roller() ==
+ IntakeRollerStatus::NONE) {
EXPECT_EQ(superstructure_output_fetcher_->intake_roller_voltage(), 0.0);
}
@@ -259,16 +449,38 @@
set_point = simulated_robot_constants_->common()
->climber_set_points()
->full_extend();
- } else if (superstructure_goal_fetcher_->climber_goal() ==
- ClimberGoal::HALF_EXTEND) {
+ }
+
+ if (superstructure_goal_fetcher_->climber_goal() == ClimberGoal::STOWED) {
set_point = simulated_robot_constants_->common()
->climber_set_points()
- ->half_extend();
+ ->stowed();
+ }
+ EXPECT_NEAR(set_point,
+ superstructure_status_fetcher_->climber()->position(), 0.001);
+ }
+
+ if (superstructure_status_fetcher_->has_uncompleted_note_goal()) {
+ double set_point = simulated_robot_constants_->common()
+ ->extend_set_points()
+ ->retracted();
+ if (superstructure_status_fetcher_->uncompleted_note_goal() ==
+ NoteStatus::TRAP) {
+ set_point =
+ simulated_robot_constants_->common()->extend_set_points()->trap();
+ } else if (superstructure_status_fetcher_->uncompleted_note_goal() ==
+ NoteStatus::AMP) {
+ set_point =
+ simulated_robot_constants_->common()->extend_set_points()->amp();
+ } else if (superstructure_status_fetcher_->uncompleted_note_goal() ==
+ NoteStatus::CATAPULT) {
+ set_point = simulated_robot_constants_->common()
+ ->extend_set_points()
+ ->catapult();
}
EXPECT_NEAR(set_point,
- superstructure_status_fetcher_->climber_state()->position(),
- 0.001);
+ superstructure_status_fetcher_->extend()->position(), 0.001);
}
}
@@ -293,6 +505,25 @@
!superstructure_status_fetcher_.get()->zeroed());
}
+ void WaitUntilNear(double turret_goal, double altitude_goal) {
+ int i = 0;
+ do {
+ i++;
+ RunFor(dt());
+ superstructure_status_fetcher_.Fetch();
+ // 10 Seconds
+
+ ASSERT_LE(i, 10.0 / ::aos::time::DurationInSeconds(dt()));
+
+ // Since there is a delay when sending running, make sure we have a
+ // status before checking it.
+ } while (superstructure_status_fetcher_.get() == nullptr ||
+ std::abs(superstructure_plant_.altitude()->position() -
+ altitude_goal) > 1e-3 ||
+ std::abs(superstructure_plant_.turret()->position() -
+ turret_goal) > 1e-3);
+ }
+
void SendRobotVelocity(double robot_velocity) {
SendDrivetrainStatus(robot_velocity, {0.0, 0.0}, 0.0);
}
@@ -311,7 +542,6 @@
builder.CheckOk(builder.Send(drivetrain_status_builder.Finish()));
}
- std::shared_ptr<const constants::Values> values_;
const bool simulated_constants_dummy_;
const aos::Node *const roborio_;
@@ -331,6 +561,7 @@
::aos::Fetcher<Output> superstructure_output_fetcher_;
::aos::Fetcher<Position> superstructure_position_fetcher_;
::aos::Sender<Position> superstructure_position_sender_;
+ ::aos::Sender<CANPosition> superstructure_can_position_sender_;
::aos::Sender<DrivetrainStatus> drivetrain_status_sender_;
::std::unique_ptr<::aos::EventLoop> superstructure_plant_event_loop_;
@@ -349,20 +580,57 @@
SetEnabled(true);
WaitUntilZeroed();
+ superstructure_plant_.turret()->InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->turret()->range())
+ .middle());
+ superstructure_plant_.altitude()->InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->altitude()->range())
+ .middle());
+
{
auto builder = superstructure_goal_sender_.MakeBuilder();
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ turret_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->turret()->range())
+ .middle());
+
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ altitude_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->altitude()->range())
+ .middle());
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_turret_position(turret_offset);
+ shooter_goal_builder.add_altitude_position(altitude_offset);
+ shooter_goal_builder.add_auto_aim(false);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_pivot_goal(IntakePivotGoal::RETRACTED);
goal_builder.add_climber_goal(ClimberGoal::RETRACT);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
+ goal_builder.add_note_goal(NoteGoal::NONE);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
RunFor(chrono::seconds(10));
- EXPECT_TRUE(superstructure_output_fetcher_.Fetch());
-
VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(), SuperstructureState::IDLE);
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::READY);
}
// Tests that loops can reach a goal.
@@ -371,26 +639,74 @@
superstructure_plant_.intake_pivot()->InitializePosition(
frc971::constants::Range::FromFlatbuffer(
simulated_robot_constants_->common()->intake_pivot()->range())
+ .lower);
+
+ superstructure_plant_.turret()->InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->turret()->range())
+ .lower);
+
+ superstructure_plant_.altitude()->InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->altitude()->range())
.middle());
+
superstructure_plant_.climber()->InitializePosition(
frc971::constants::Range::FromFlatbuffer(
simulated_robot_constants_->common()->climber()->range())
.lower);
+
+ superstructure_plant_.extend()->InitializePosition(
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->extend()->range())
+ .lower);
WaitUntilZeroed();
+
{
auto builder = superstructure_goal_sender_.MakeBuilder();
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ turret_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->turret()->range())
+ .upper);
+
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ altitude_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->altitude()->range())
+ .upper);
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_turret_position(turret_offset);
+ shooter_goal_builder.add_altitude_position(altitude_offset);
+ shooter_goal_builder.add_auto_aim(false);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_pivot_goal(IntakePivotGoal::EXTENDED);
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
goal_builder.add_climber_goal(ClimberGoal::FULL_EXTEND);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_note_goal(NoteGoal::NONE);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
+ superstructure_plant_.set_extend_beambreak(true);
+
// Give it a lot of time to get there.
RunFor(chrono::seconds(15));
VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::LOADED);
}
// Makes sure that the voltage on a motor is properly pulled back after
@@ -404,29 +720,93 @@
{
auto builder = superstructure_goal_sender_.MakeBuilder();
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ turret_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->turret()->range())
+ .upper);
+
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ altitude_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->altitude()->range())
+ .upper);
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_turret_position(turret_offset);
+ shooter_goal_builder.add_altitude_position(altitude_offset);
+ shooter_goal_builder.add_auto_aim(false);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_pivot_goal(IntakePivotGoal::EXTENDED);
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
goal_builder.add_climber_goal(ClimberGoal::FULL_EXTEND);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_note_goal(NoteGoal::AMP);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
+ superstructure_plant_.set_extend_beambreak(true);
+
RunFor(chrono::seconds(20));
VerifyNearGoal();
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::READY);
+
// Try a low acceleration move with a high max velocity and verify the
// acceleration is capped like expected.
{
auto builder = superstructure_goal_sender_.MakeBuilder();
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ turret_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->turret()->range())
+ .lower,
+ CreateProfileParameters(*builder.fbb(), 20.0, 10));
+
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ altitude_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(),
+ frc971::constants::Range::FromFlatbuffer(
+ simulated_robot_constants_->common()->altitude()->range())
+ .lower,
+ CreateProfileParameters(*builder.fbb(), 20.0, 10));
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_turret_position(turret_offset);
+ shooter_goal_builder.add_altitude_position(altitude_offset);
+ shooter_goal_builder.add_auto_aim(false);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_pivot_goal(IntakePivotGoal::RETRACTED);
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
goal_builder.add_climber_goal(ClimberGoal::RETRACT);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_note_goal(NoteGoal::NONE);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
+ superstructure_plant_.set_extend_beambreak(true);
+
RunFor(chrono::seconds(10));
VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::READY);
}
// Tests that the loop zeroes when run for a while without a goal.
@@ -435,11 +815,19 @@
WaitUntilZeroed();
RunFor(chrono::seconds(2));
- EXPECT_EQ(PotAndAbsoluteEncoderSubsystem::State::RUNNING,
+ EXPECT_EQ(AbsoluteEncoderSubsystem::State::RUNNING,
superstructure_.intake_pivot().state());
EXPECT_EQ(PotAndAbsoluteEncoderSubsystem::State::RUNNING,
superstructure_.climber().state());
+
+ EXPECT_EQ(PotAndAbsoluteEncoderSubsystem::State::RUNNING,
+ superstructure_.shooter().turret().state());
+
+ EXPECT_EQ(PotAndAbsoluteEncoderSubsystem::State::RUNNING,
+ superstructure_.shooter().altitude().state());
+ EXPECT_EQ(PotAndAbsoluteEncoderSubsystem::State::RUNNING,
+ superstructure_.extend().state());
}
// Tests that running disabled works
@@ -448,65 +836,6 @@
CheckIfZeroed();
}
-// Tests Climber in multiple scenarios
-TEST_F(SuperstructureTest, ClimberTest) {
- SetEnabled(true);
- WaitUntilZeroed();
-
- superstructure_plant_.climber()->InitializePosition(
- frc971::constants::Range::FromFlatbuffer(
- simulated_robot_constants_->common()->climber()->range())
- .middle());
-
- WaitUntilZeroed();
-
- {
- auto builder = superstructure_goal_sender_.MakeBuilder();
-
- Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
-
- goal_builder.add_climber_goal(ClimberGoal::FULL_EXTEND);
-
- ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
- }
-
- RunFor(chrono::seconds(5));
-
- VerifyNearGoal();
-
- WaitUntilZeroed();
-
- {
- auto builder = superstructure_goal_sender_.MakeBuilder();
-
- Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
-
- goal_builder.add_climber_goal(ClimberGoal::HALF_EXTEND);
-
- ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
- }
-
- RunFor(chrono::seconds(5));
-
- VerifyNearGoal();
-
- WaitUntilZeroed();
-
- {
- auto builder = superstructure_goal_sender_.MakeBuilder();
-
- Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
-
- goal_builder.add_climber_goal(ClimberGoal::RETRACT);
-
- ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
- }
-
- RunFor(chrono::seconds(5));
-
- VerifyNearGoal();
-}
-
// Tests intake and transfer in multiple scenarios
TEST_F(SuperstructureTest, IntakeGoal) {
SetEnabled(true);
@@ -524,18 +853,18 @@
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_pivot_goal(IntakePivotGoal::RETRACTED);
- goal_builder.add_intake_roller_goal(IntakeRollerGoal::NONE);
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
+ goal_builder.add_note_goal(NoteGoal::NONE);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
- superstructure_plant_.set_transfer_beambreak(false);
-
RunFor(chrono::seconds(5));
VerifyNearGoal();
+ EXPECT_EQ(superstructure_status_fetcher_->state(), SuperstructureState::IDLE);
+
WaitUntilZeroed();
{
@@ -543,14 +872,12 @@
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_pivot_goal(IntakePivotGoal::EXTENDED);
- goal_builder.add_intake_roller_goal(IntakeRollerGoal::SPIT);
+ goal_builder.add_intake_goal(IntakeGoal::SPIT);
+ goal_builder.add_note_goal(NoteGoal::NONE);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
- superstructure_plant_.set_transfer_beambreak(false);
-
RunFor(chrono::seconds(5));
VerifyNearGoal();
@@ -565,29 +892,587 @@
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_pivot_goal(IntakePivotGoal::EXTENDED);
- goal_builder.add_intake_roller_goal(IntakeRollerGoal::INTAKE);
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
+ goal_builder.add_note_goal(NoteGoal::NONE);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
- superstructure_plant_.set_transfer_beambreak(false);
-
RunFor(chrono::seconds(5));
VerifyNearGoal();
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::INTAKING);
+
+ EXPECT_EQ(superstructure_output_fetcher_->transfer_roller_voltage(),
+ simulated_robot_constants_->common()
+ ->transfer_roller_voltages()
+ ->transfer_in());
+
{
auto builder = superstructure_goal_sender_.MakeBuilder();
Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
- goal_builder.add_intake_roller_goal(IntakeRollerGoal::INTAKE);
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
+ goal_builder.add_note_goal(NoteGoal::NONE);
ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
}
- superstructure_plant_.set_transfer_beambreak(false);
+ RunFor(chrono::milliseconds(500));
+
+ // Make sure we're still intaking for 500 ms after we stop giving it an
+ // intaking goal.
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_note_goal(NoteGoal::NONE);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ RunFor(chrono::milliseconds(200));
+
+ ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::INTAKING);
+ EXPECT_EQ(superstructure_status_fetcher_->intake_roller(),
+ IntakeRollerStatus::INTAKING);
+
+ // Make sure we stop when loaded
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
+ goal_builder.add_note_goal(NoteGoal::NONE);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_extend_beambreak(true);
+
+ RunFor(chrono::seconds(2));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::LOADED);
+
+ EXPECT_EQ(superstructure_output_fetcher_->transfer_roller_voltage(), 0.0);
+}
+
+// Tests the full range of activities we need to be doing from loading ->
+// shooting
+TEST_F(SuperstructureTest, LoadingToShooting) {
+ SetEnabled(true);
+
+ WaitUntilZeroed();
+
+ constexpr double kTurretGoal = 2.0;
+ constexpr double kAltitudeGoal = 0.55;
+
+ set_alliance(aos::Alliance::kRed);
+ SendDrivetrainStatus(0.0, {0.0, 5.0}, 0.0);
+
+ // Auto aim, but don't fire.
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(true);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_fire(false);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_catapult_beambreak(false);
+
+ RunFor(chrono::seconds(5));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(), SuperstructureState::IDLE);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->turret()->position(),
+ simulated_robot_constants_->common()->turret_loading_position(),
+ 0.01);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->altitude()->position(),
+ simulated_robot_constants_->common()->altitude_loading_position(),
+ 0.01);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::READY);
+
+ // Now, extend the intake.
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(true);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_fire(false);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ RunFor(chrono::seconds(5));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::INTAKING);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->turret()->position(),
+ simulated_robot_constants_->common()->turret_loading_position(),
+ 0.01);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->altitude()->position(),
+ simulated_robot_constants_->common()->altitude_loading_position(),
+ 0.01);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::READY);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(true);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_note_goal(NoteGoal::NONE);
+ goal_builder.add_fire(false);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_extend_beambreak(false);
+
+ RunFor(10 * dt());
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::INTAKING);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::READY);
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_status(),
+ ExtendStatus::RETRACTED);
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::TRANSFERING_TO_EXTEND);
+
+ EXPECT_LT(4.0, superstructure_output_fetcher_->transfer_roller_voltage());
+ EXPECT_LT(4.0, superstructure_output_fetcher_->extend_roller_voltage());
+
+ superstructure_plant_.set_extend_beambreak(true);
+
+ RunFor(chrono::milliseconds(750));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::LOADED);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::READY);
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_status(),
+ ExtendStatus::RETRACTED);
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::IDLE);
+
+ EXPECT_EQ(superstructure_output_fetcher_->transfer_roller_voltage(), 0.0);
+ EXPECT_EQ(superstructure_output_fetcher_->extend_roller_voltage(), 0.0);
+
+ // Verify we are in the loading position.
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->turret()->position(),
+ simulated_robot_constants_->common()->turret_loading_position(),
+ 0.01);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(true);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_note_goal(NoteGoal::CATAPULT);
+ goal_builder.add_fire(false);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_extend_beambreak(true);
+
+ RunFor(chrono::milliseconds(500));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::LOADING_CATAPULT);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::READY);
+
+ superstructure_plant_.set_catapult_beambreak(true);
+ // Set retention roller to show that we are loaded.
+ EXPECT_EQ(superstructure_status_fetcher_->extend_status(),
+ ExtendStatus::CATAPULT);
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::TRANSFERING_TO_CATAPULT);
+
+ superstructure_plant_.set_extend_beambreak(false);
+
+ RunFor(chrono::seconds(10));
+
+ ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::READY);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::LOADED);
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_status(),
+ ExtendStatus::CATAPULT);
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::IDLE);
+
+ // Should now be loaded.
+ EXPECT_EQ(superstructure_output_fetcher_->transfer_roller_voltage(), 0.0);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->altitude()->position(),
+ simulated_robot_constants_->common()->altitude_loading_position(),
+ 0.01);
+
+ // Fire. Start by triggering a motion and then firing all in 1 go.
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ turret_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(), kTurretGoal);
+
+ flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
+ altitude_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ *builder.fbb(), kAltitudeGoal);
+
+ auto catapult_builder =
+ builder.MakeBuilder<frc971::control_loops::catapult::CatapultGoal>();
+ catapult_builder.add_shot_velocity(15.0);
+
+ const auto catapult_offset = catapult_builder.Finish();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(false);
+ shooter_goal_builder.add_catapult_goal(catapult_offset);
+ shooter_goal_builder.add_altitude_position(altitude_offset);
+ shooter_goal_builder.add_turret_position(turret_offset);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_fire(true);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ // Wait until the bot finishes auto-aiming.
+ WaitUntilNear(kTurretGoal, kAltitudeGoal);
+
+ RunFor(chrono::milliseconds(1000));
+
+ ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::FIRING);
+
+ // Make sure it stays at firing for a bit.
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::FIRING);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->turret()->position(),
+ kTurretGoal, 0.001);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->altitude()->position(),
+ kAltitudeGoal, 0.001);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(false);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+ goal_builder.add_fire(false);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_catapult_beambreak(false);
+
+ RunFor(chrono::seconds(5));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(), SuperstructureState::IDLE);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::READY);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->turret()->position(),
+ simulated_robot_constants_->common()->turret_loading_position(),
+ 0.01);
+
+ EXPECT_NEAR(superstructure_status_fetcher_->shooter()->altitude()->position(),
+ simulated_robot_constants_->common()->altitude_loading_position(),
+ 0.01);
+}
+
+// Test that we are able to signal that the note was preloaded
+TEST_F(SuperstructureTest, Preloaded) {
+ // Put the bucket at the starting position.
+ superstructure_plant_.catapult()->InitializePosition(
+ simulated_robot_constants_->common()->catapult_return_position());
+
+ SetEnabled(true);
+ WaitUntilZeroed();
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_preloaded(true);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ RunFor(chrono::milliseconds(200));
+
+ superstructure_status_fetcher_.Fetch();
+ ASSERT_TRUE(superstructure_status_fetcher_.get() != nullptr);
+
+ LOG(INFO) << EnumNameNoteStatus(
+ superstructure_status_fetcher_->uncompleted_note_goal());
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::READY);
+
+ EXPECT_EQ(superstructure_status_fetcher_->shooter()->catapult_state(),
+ CatapultState::LOADED);
+}
+
+// Tests that auto aim works properly for the shooter
+TEST_F(SuperstructureTest, AutoAim) {
+ superstructure_plant_.catapult()->InitializePosition(
+ simulated_robot_constants_->common()->catapult_return_position());
+ SetEnabled(true);
+ WaitUntilZeroed();
+
+ constexpr double kDistanceFromSpeaker = 5.0;
+
+ const double kRedSpeakerX = simulated_robot_constants_->common()
+ ->shooter_targets()
+ ->red_alliance()
+ ->pos()
+ ->data()
+ ->Get(0);
+ const double kRedSpeakerY = simulated_robot_constants_->common()
+ ->shooter_targets()
+ ->red_alliance()
+ ->pos()
+ ->data()
+ ->Get(1);
+
+ const double kBlueSpeakerX = simulated_robot_constants_->common()
+ ->shooter_targets()
+ ->blue_alliance()
+ ->pos()
+ ->data()
+ ->Get(0);
+ const double kBlueSpeakerY = simulated_robot_constants_->common()
+ ->shooter_targets()
+ ->blue_alliance()
+ ->pos()
+ ->data()
+ ->Get(1);
+
+ set_alliance(aos::Alliance::kRed);
+ // Set the robot facing 90 degrees away from the speaker
+ SendDrivetrainStatus(
+ 0.0, {(kRedSpeakerX - kDistanceFromSpeaker), kRedSpeakerY}, M_PI_2);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(true);
+ shooter_goal_builder.add_preloaded(true);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_catapult_beambreak(true);
+
+ RunFor(chrono::seconds(5));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::READY);
+
+ EXPECT_NEAR(
+ -M_PI_2,
+ superstructure_status_fetcher_->shooter()->aimer()->turret_position() -
+ M_PI,
+ 5e-4);
+ EXPECT_NEAR(
+ -M_PI_2,
+ superstructure_status_fetcher_->shooter()->turret()->position() - M_PI,
+ 5e-4);
+
+ EXPECT_EQ(
+ kDistanceFromSpeaker,
+ superstructure_status_fetcher_->shooter()->aimer()->target_distance());
+
+ set_alliance(aos::Alliance::kBlue);
+ // Set the robot facing 90 degrees away from the speaker
+ SendDrivetrainStatus(
+ 0.0, {(kBlueSpeakerX + kDistanceFromSpeaker), kBlueSpeakerY}, M_PI_2);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ ShooterGoal::Builder shooter_goal_builder =
+ builder.MakeBuilder<ShooterGoal>();
+
+ shooter_goal_builder.add_auto_aim(true);
+
+ flatbuffers::Offset<ShooterGoal> shooter_goal_offset =
+ shooter_goal_builder.Finish();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_shooter_goal(shooter_goal_offset);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_catapult_beambreak(true);
+
+ RunFor(chrono::seconds(5));
+
+ VerifyNearGoal();
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::READY);
+
+ EXPECT_NEAR(
+ M_PI_2,
+ superstructure_status_fetcher_->shooter()->aimer()->turret_position() +
+ M_PI,
+ 5e-4);
+ EXPECT_NEAR(
+ M_PI_2,
+ superstructure_status_fetcher_->shooter()->turret()->position() + M_PI,
+ 5e-4);
+ EXPECT_EQ(
+ kDistanceFromSpeaker,
+ superstructure_status_fetcher_->shooter()->aimer()->target_distance());
+}
+
+// Test entire sequence of loading, transfering, and scoring at amp position.
+TEST_F(SuperstructureTest, ScoreInAmp) {
+ SetEnabled(true);
+
+ WaitUntilZeroed();
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
+ goal_builder.add_note_goal(NoteGoal::NONE);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
RunFor(chrono::seconds(5));
@@ -598,12 +1483,167 @@
->transfer_roller_voltages()
->transfer_in());
- superstructure_plant_.set_transfer_beambreak(true);
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::TRANSFERING_TO_EXTEND);
- RunFor(chrono::seconds(2));
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::INTAKING);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::INTAKE);
+ goal_builder.add_note_goal(NoteGoal::NONE);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_extend_beambreak(true);
+
+ RunFor(chrono::milliseconds(10));
VerifyNearGoal();
EXPECT_EQ(superstructure_output_fetcher_->transfer_roller_voltage(), 0.0);
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::IDLE);
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::LOADED);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
+ goal_builder.add_note_goal(NoteGoal::AMP);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ RunFor(10 * dt());
+
+ ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_status(),
+ ExtendStatus::MOVING);
+
+ RunFor(chrono::seconds(5));
+
+ ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
+
+ EXPECT_NEAR(superstructure_status_fetcher_->extend()->position(),
+ simulated_robot_constants_->common()->extend_set_points()->amp(),
+ 0.01);
+ EXPECT_EQ(superstructure_status_fetcher_->extend_status(), ExtendStatus::AMP);
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::READY);
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
+ goal_builder.add_note_goal(NoteGoal::AMP);
+ goal_builder.add_fire(true);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_extend_beambreak(true);
+
+ RunFor(chrono::milliseconds(10));
+
+ ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
+ ASSERT_TRUE(superstructure_output_fetcher_.Fetch());
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(),
+ SuperstructureState::FIRING);
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::SCORING_IN_AMP);
+ EXPECT_LT(4.0, superstructure_output_fetcher_->extend_roller_voltage());
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_intake_goal(IntakeGoal::NONE);
+ goal_builder.add_note_goal(NoteGoal::AMP);
+ goal_builder.add_fire(false);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ superstructure_plant_.set_extend_beambreak(false);
+
+ RunFor(chrono::milliseconds(100));
+
+ ASSERT_TRUE(superstructure_status_fetcher_.Fetch());
+ ASSERT_TRUE(superstructure_output_fetcher_.Fetch());
+
+ EXPECT_EQ(superstructure_status_fetcher_->state(), SuperstructureState::IDLE);
+
+ EXPECT_EQ(superstructure_status_fetcher_->extend_roller(),
+ ExtendRollerStatus::IDLE);
+ EXPECT_EQ(superstructure_output_fetcher_->extend_roller_voltage(), 0.0);
}
-} // namespace y2024::control_loops::superstructure::testing
\ No newline at end of file
+
+TEST_F(SuperstructureTest, Climbing) {
+ SetEnabled(true);
+
+ WaitUntilZeroed();
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_climber_goal(ClimberGoal::STOWED);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ RunFor(chrono::seconds(10));
+
+ VerifyNearGoal();
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_climber_goal(ClimberGoal::FULL_EXTEND);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ RunFor(chrono::seconds(10));
+
+ VerifyNearGoal();
+
+ {
+ auto builder = superstructure_goal_sender_.MakeBuilder();
+
+ Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+
+ goal_builder.add_climber_goal(ClimberGoal::RETRACT);
+
+ ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+ }
+
+ RunFor(chrono::seconds(10));
+
+ VerifyNearGoal();
+
+ // TODO(max): Fill this with the logic to move the altitude and turret on the
+ // chain.
+}
+} // namespace y2024::control_loops::superstructure::testing
diff --git a/y2024/control_loops/superstructure/superstructure_main.cc b/y2024/control_loops/superstructure/superstructure_main.cc
index 58932c7..a5ab8ed 100644
--- a/y2024/control_loops/superstructure/superstructure_main.cc
+++ b/y2024/control_loops/superstructure/superstructure_main.cc
@@ -17,10 +17,7 @@
frc971::constants::WaitForConstants<y2024::Constants>(&config.message());
- std::shared_ptr<const y2024::constants::Values> values =
- std::make_shared<const y2024::constants::Values>(
- y2024::constants::MakeValues());
- Superstructure superstructure(&event_loop, values);
+ Superstructure superstructure(&event_loop);
event_loop.Run();
diff --git a/y2024/control_loops/superstructure/superstructure_output.fbs b/y2024/control_loops/superstructure/superstructure_output.fbs
index a429976..24970d0 100644
--- a/y2024/control_loops/superstructure/superstructure_output.fbs
+++ b/y2024/control_loops/superstructure/superstructure_output.fbs
@@ -2,18 +2,24 @@
table Output {
// Voltage of rollers on intake
+ // Positive means intaking a game piece.
intake_roller_voltage:double (id: 0);
// Voltage of intake pivot
+ // Positive voltage causes pivot to retract.
intake_pivot_voltage:double (id: 1);
// Voltage of the turret
+ // Positive voltage is for rotating the turret clockwise.
turret_voltage: double (id: 2);
// Voltage of the altitude
+ // Positive voltage is for raising it upward.
altitude_voltage: double (id: 3);
// Voltage of the catapult
+ // Positive voltage is for shooting the game piece,
+ // rotating the catapult counter-clockwise.
catapult_voltage: double (id: 4);
// Voltage of transfer rollers
@@ -25,6 +31,23 @@
// Positive voltage is for climber up
// Negative voltage is for climber down
climber_voltage:double (id: 6);
+
+ // Voltage of the retention rollers
+ // Positive voltage will hold the game piece in the catapult.
+ retention_roller_voltage: double (id: 7);
+
+ // Voltage of extend
+ // Positive voltage extends the extend
+ // Negative voltage retracts the extend
+ extend_voltage:double (id: 8);
+
+ // Voltage of the extend rollers
+ // Positive voltage rolls the game piece up towards the scoring mechanisms,
+ // either the catapult or amp/trap mechanism
+ extend_roller_voltage:double (id: 9);
+
+ // Dynamically sets the stator current limit for the retention_rollers
+ retention_roller_stator_current_limit: double (id: 10);
}
root_type Output;
diff --git a/y2024/control_loops/superstructure/superstructure_plotter.ts b/y2024/control_loops/superstructure/superstructure_plotter.ts
index 3498f77..3a2dcaa 100644
--- a/y2024/control_loops/superstructure/superstructure_plotter.ts
+++ b/y2024/control_loops/superstructure/superstructure_plotter.ts
@@ -1,5 +1,5 @@
// Provides a plot for debugging robot state-related issues.
-import {AosPlotter} from '../../../aos/network/www/aos_plotter';
+import {AosPlotter, MessageHandler} from '../../../aos/network/www/aos_plotter';
import {BLUE, BROWN, CYAN, GREEN, PINK, RED, WHITE} from '../../../aos/network/www/colors';
import * as proxy from '../../../aos/network/www/proxy';
@@ -7,24 +7,214 @@
const TIME = AosPlotter.TIME;
const DEFAULT_WIDTH = AosPlotter.DEFAULT_WIDTH * 2;
-const DEFAULT_HEIGHT = AosPlotter.DEFAULT_HEIGHT * 3;
+const DEFAULT_HEIGHT = AosPlotter.DEFAULT_HEIGHT * 1;
+
+function plotSzsdofSubsystem(
+ name: string, plotter: AosPlotter, element: Element, position: MessageHandler, positionName: string,
+ status: MessageHandler, statusName: string, output: MessageHandler, outputName: string, hasPot:boolean = true): void {
+ {
+ const positionPlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ positionPlot.plot.getAxisLabels().setTitle(name + ' Position');
+ positionPlot.plot.getAxisLabels().setXLabel(TIME);
+ positionPlot.plot.getAxisLabels().setYLabel('Position [rad,m]');
+ positionPlot.addMessageLine(position, [positionName, 'encoder'])
+ .setColor(RED);
+ positionPlot.addMessageLine(position, [positionName, 'absolute_encoder'])
+ .setColor(GREEN);
+ if (hasPot) {
+ positionPlot.addMessageLine(position, [positionName, 'pot'])
+ .setColor(BLUE);
+ }
+ positionPlot
+ .addMessageLine(status, [statusName, 'estimator_state', 'position'])
+ .setColor(BROWN);
+ positionPlot.addMessageLine(status, [statusName, 'position'])
+ .setColor(WHITE);
+ }
+ {
+ const statesPlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT / 2]);
+ statesPlot.plot.getAxisLabels().setTitle(name + ' State');
+ statesPlot.plot.getAxisLabels().setXLabel(TIME);
+ statesPlot.plot.getAxisLabels().setYLabel('[bool,ZeroingError]');
+ statesPlot.addMessageLine(status, [statusName, 'estopped']).setColor(RED);
+ statesPlot.addMessageLine(status, [statusName, 'zeroed']).setColor(GREEN);
+ statesPlot
+ .addMessageLine(status, [statusName, 'estimator_state', 'errors[]'])
+ .setColor(BLUE)
+ .setDrawLine(false);
+ }
+ {
+ const positionConvergencePlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ positionConvergencePlot.plot.getAxisLabels().setTitle(name + ' Position Goals');
+ positionConvergencePlot.plot.getAxisLabels().setXLabel(TIME);
+ positionConvergencePlot.plot.getAxisLabels().setYLabel('[rad,m]');
+ positionConvergencePlot.addMessageLine(status, [statusName, 'position'])
+ .setColor(RED);
+ positionConvergencePlot.addMessageLine(status, [statusName, 'goal_position'])
+ .setColor(GREEN);
+ positionConvergencePlot
+ .addMessageLine(status, [statusName, 'unprofiled_goal_position'])
+ .setColor(BROWN);
+ }
+ {
+ const velocityConvergencePlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ velocityConvergencePlot.plot.getAxisLabels().setTitle(name + ' Velocity Goals');
+ velocityConvergencePlot.plot.getAxisLabels().setXLabel(TIME);
+ velocityConvergencePlot.plot.getAxisLabels().setYLabel('[rad,m]');
+ velocityConvergencePlot.addMessageLine(status, [statusName, 'velocity'])
+ .setColor(RED);
+ velocityConvergencePlot.addMessageLine(status, [statusName, 'calculated_velocity'])
+ .setColor(RED).setDrawLine(false);
+ velocityConvergencePlot.addMessageLine(status, [statusName, 'goal_velocity'])
+ .setColor(GREEN);
+ velocityConvergencePlot
+ .addMessageLine(status, [statusName, 'unprofiled_goal_velocity'])
+ .setColor(BROWN);
+ }
+ {
+ const outputPlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ outputPlot.plot.getAxisLabels().setTitle(name + ' Outputs');
+ outputPlot.plot.getAxisLabels().setXLabel(TIME);
+ outputPlot.plot.getAxisLabels().setYLabel('[volts]');
+ outputPlot.addMessageLine(output, [outputName])
+ .setColor(RED);
+ outputPlot.addMessageLine(status, [statusName, 'voltage_error'])
+ .setColor(GREEN);
+ outputPlot.addMessageLine(status, [statusName, 'position_power'])
+ .setColor(BLUE);
+ outputPlot.addMessageLine(status, [statusName, 'velocity_power'])
+ .setColor(BROWN);
+ outputPlot.addMessageLine(status, [statusName, 'feedforwards_power'])
+ .setColor(WHITE);
+ }
+}
export function plotSuperstructure(conn: Connection, element: Element): void {
const aosPlotter = new AosPlotter(conn);
- //const goal = aosPlotter.addMessageSource(
- // '/superstructure', 'y2024.control_loops.superstructure.Goal');
- //const output = aosPlotter.addMessageSource(
- // '/superstructure', 'y2024.control_loops.superstructure.Output');
- //const status = aosPlotter.addMessageSource(
- // '/superstructure', 'y2024.control_loops.superstructure.Status');
+ const status = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Status');
+ const robotState = aosPlotter.addMessageSource('/aos', 'aos.RobotState');
+
+ {
+ const robotStatePlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ robotStatePlot.plot.getAxisLabels().setTitle('Robot State Plot');
+ robotStatePlot.plot.getAxisLabels().setXLabel(TIME);
+ robotStatePlot.plot.getAxisLabels().setYLabel('[bool]');
+ robotStatePlot.addMessageLine(robotState, ['outputs_enabled'])
+ .setColor(RED);
+ robotStatePlot.addMessageLine(status, ['zeroed'])
+ .setColor(GREEN);
+ robotStatePlot.addMessageLine(status, ['estopped'])
+ .setColor(BLUE);
+ }
+}
+
+export function plotClimber(conn: Connection, element: Element): void {
+ const aosPlotter = new AosPlotter(conn);
+ const goal = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Goal');
+ const output = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Output');
+ const status = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Status');
const position = aosPlotter.addMessageSource(
'/superstructure', 'y2024.control_loops.superstructure.Position');
- //const robotState = aosPlotter.addMessageSource('/aos', 'aos.RobotState');
+ {
+ const goalPlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ goalPlot.plot.getAxisLabels().setTitle('Climber Goal');
+ goalPlot.plot.getAxisLabels().setXLabel(TIME);
+ goalPlot.plot.getAxisLabels().setYLabel('[enum]');
+ goalPlot.addMessageLine(goal, ['climber_goal']).setColor(RED);
+ }
- const positionPlot =
- aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT / 2]);
- positionPlot.plot.getAxisLabels().setTitle('States');
- positionPlot.plot.getAxisLabels().setXLabel(TIME);
- positionPlot.plot.getAxisLabels().setYLabel('wonky state units');
- positionPlot.plot.setDefaultYRange([-1.0, 2.0]);
+ plotSzsdofSubsystem(
+ 'Climber', aosPlotter, element, position, 'climber', status, 'climber',
+ output, 'climber_voltage');
+}
+
+export function plotIntake(conn: Connection, element: Element): void {
+ const aosPlotter = new AosPlotter(conn);
+ const goal = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Goal');
+ const output = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Output');
+ const status = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Status');
+ const position = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Position');
+
+ {
+ const goalPlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ goalPlot.plot.getAxisLabels().setTitle('Intake Goal');
+ goalPlot.plot.getAxisLabels().setXLabel(TIME);
+ goalPlot.plot.getAxisLabels().setYLabel('[enum]');
+ goalPlot.addMessageLine(goal, ['intake_goal']).setColor(RED);
+ }
+ {
+ const rollerPlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ rollerPlot.plot.getAxisLabels().setTitle('Intake Rollers');
+ rollerPlot.plot.getAxisLabels().setXLabel(TIME);
+ rollerPlot.plot.getAxisLabels().setYLabel('[enum,voltage]');
+ rollerPlot.addMessageLine(status, ['intake_roller']).setColor(RED);
+ rollerPlot.addMessageLine(status, ['transfer_roller']).setColor(BLUE);
+ rollerPlot.addMessageLine(output, ['intake_roller_voltage'])
+ .setColor(RED)
+ .setPointSize(0);
+ rollerPlot.addMessageLine(output, ['transfer_roller_voltage'])
+ .setColor(BLUE)
+ .setPointSize(0);
+ }
+
+ plotSzsdofSubsystem(
+ 'Intake', aosPlotter, element, position, 'intake_pivot', status, 'intake_pivot',
+ output, 'intake_pivot_voltage', false);
+}
+
+export function plotExtend(conn: Connection, element: Element): void {
+ const aosPlotter = new AosPlotter(conn);
+ const goal = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Goal');
+ const output = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Output');
+ const status = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Status');
+ const position = aosPlotter.addMessageSource(
+ '/superstructure', 'y2024.control_loops.superstructure.Position');
+
+ {
+ const goalPlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ goalPlot.plot.getAxisLabels().setTitle('Extend Goal');
+ goalPlot.plot.getAxisLabels().setXLabel(TIME);
+ goalPlot.plot.getAxisLabels().setYLabel('[enum]');
+ goalPlot.addMessageLine(goal, ['intake_goal']).setColor(RED);
+ }
+ {
+ const rollerPlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ rollerPlot.plot.getAxisLabels().setTitle('Extend Rollers');
+ rollerPlot.plot.getAxisLabels().setXLabel(TIME);
+ rollerPlot.plot.getAxisLabels().setYLabel('[enum,voltage]');
+ rollerPlot.addMessageLine(status, ['intake_roller']).setColor(RED);
+ rollerPlot.addMessageLine(status, ['transfer_roller']).setColor(BLUE);
+ rollerPlot.addMessageLine(output, ['intake_roller_voltage'])
+ .setColor(RED)
+ .setPointSize(0);
+ rollerPlot.addMessageLine(output, ['transfer_roller_voltage'])
+ .setColor(BLUE)
+ .setPointSize(0);
+ }
+
+ plotSzsdofSubsystem(
+ 'Extend', aosPlotter, element, position, 'extend', status, 'extend',
+ output, 'extend_voltage');
}
diff --git a/y2024/control_loops/superstructure/superstructure_position.fbs b/y2024/control_loops/superstructure/superstructure_position.fbs
index cd12da0..b1facba 100644
--- a/y2024/control_loops/superstructure/superstructure_position.fbs
+++ b/y2024/control_loops/superstructure/superstructure_position.fbs
@@ -5,23 +5,44 @@
table Position {
// Values of the encoder and potentiometer at the intake pivot
- intake_pivot:frc971.PotAndAbsolutePosition (id: 0);
+ // Zero is when the lowest extent of the lexan is level with the
+ // bellypan, positive is retracted inward.
+ intake_pivot:frc971.AbsolutePosition (id: 0);
// Values of the encoder and potentiometer at the turret
+ // Zero is facing backwards, positive is rotated counter-clockwise.
+ // I.e., zero is at approximately the loading position for getting
+ // the game piece from the extend into the catapult.
turret:frc971.PotAndAbsolutePosition (id: 1);
// Values of the encoder and potentiometer at the altitude
+ // Zero is level with the ground, positive is raised upward.
altitude:frc971.PotAndAbsolutePosition (id: 2);
// Values of the encoder and potentiometer at the catapult
+ // Zero is when the note is fully seated in the catapult and the catapult
+ // arm is just touching the note. Positive is rotated counter-clockwise, to
+ // launch game piece.
catapult:frc971.PotAndAbsolutePosition (id: 3);
// True means there is a game piece in the transfer.
transfer_beambreak:bool (id: 4);
// Values of the encoder and potentiometer at the climber.
- // Zero is fully retracted, positive is extended upward.
+ // Zero is fully extended, with top of the highest slider aligned with the
+ // caps on the tubes for the climber.
+ // Positive is more extended.
climber:frc971.PotAndAbsolutePosition (id: 5);
+
+ // True if there is a game piece in the catapult
+ catapult_beambreak:bool (id: 6);
+
+ // Values of the encoder and potentiometer at the extend motor
+ // Zero is fully retracted, positive is extended upward.
+ extend:frc971.PotAndAbsolutePosition (id: 7);
+
+ // True means there is a game piece in the extend.
+ extend_beambreak:bool (id: 8);
}
root_type Position;
diff --git a/y2024/control_loops/superstructure/superstructure_replay.cc b/y2024/control_loops/superstructure/superstructure_replay.cc
index bd7f9fd..ab88a46 100644
--- a/y2024/control_loops/superstructure/superstructure_replay.cc
+++ b/y2024/control_loops/superstructure/superstructure_replay.cc
@@ -48,8 +48,7 @@
roborio->OnStartup([roborio]() {
roborio->AlwaysStart<y2024::control_loops::superstructure::Superstructure>(
- "superstructure", std::make_shared<y2024::constants::Values>(
- y2024::constants::MakeValues()));
+ "superstructure");
});
std::unique_ptr<aos::EventLoop> print_loop = roborio->MakeEventLoop("print");
diff --git a/y2024/control_loops/superstructure/superstructure_status.fbs b/y2024/control_loops/superstructure/superstructure_status.fbs
index cd727ac..8d6b14f 100644
--- a/y2024/control_loops/superstructure/superstructure_status.fbs
+++ b/y2024/control_loops/superstructure/superstructure_status.fbs
@@ -3,29 +3,115 @@
namespace y2024.control_loops.superstructure;
+enum SuperstructureState : ubyte {
+ // Before a note has been intaked, the extend should be retracted.
+ IDLE = 0,
+ // Intaking a note and transferring it to the extned through the
+ // intake, transfer, and extend rollers.
+ INTAKING = 1,
+ // The note is in the extend and the extend is not moving.
+ LOADED = 2,
+ // The note is in the extend and the extend is moving towards a goal,
+ // either the catapult, amp, or trap.
+ MOVING = 3,
+ // For Catapult Path, the note is being transferred between the extend and the catapult.
+ LOADING_CATAPULT = 4,
+ // The note is either:
+ // 1. Loaded in the catapult and ready to fire
+ // 2. In the extend and the extend is at the amp or trap scoring position.
+ READY = 5,
+ // Fire goal recieved and the note is being fired from the catapult or being scored in the amp or trap.
+ FIRING = 6,
+}
+
// Contains if intake is intaking
-enum IntakeRollerState : ubyte {
+enum IntakeRollerStatus : ubyte {
NONE = 0,
SPITTING = 1,
INTAKING = 2,
}
+enum CatapultState: ubyte {
+ // Means we are waiting for a game piece
+ READY = 0,
+ // Means we have a game piece
+ LOADED = 1,
+ // Means we are firing a game piece
+ FIRING = 2,
+ // We are retracting the bucket to do it again.
+ RETRACTING = 3,
+}
+
+table AimerStatus {
+ // The current goal angle for the turret auto-tracking, in radians.
+ turret_position:double (id: 0);
+ // The current goal velocity for the turret, in radians / sec.
+ turret_velocity:double (id: 1);
+ // The current distance to the target, in meters.
+ target_distance:double (id: 2);
+ // The current "shot distance." When shooting on the fly, this may be
+ // different from the static distance to the target.
+ shot_distance:double (id: 3);
+}
+
+// Enum representing where the superstructure
+// is currently trying to send the note.
+enum NoteStatus : ubyte {
+ NONE = 0,
+ CATAPULT = 1,
+ AMP = 2,
+ TRAP = 3,
+}
+
table ShooterStatus {
// Estimated angle and angular velocitiy of the turret.
- turret_state:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 0);
+ turret:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 0);
// Estimated angle and angular velocitiy of the catapult.
- catapult_state:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 1);
+ catapult:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 1);
// Estimated angle and angular velocitiy of the altitude.
- altitude_state:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 2);
+ altitude:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 2);
+
+ catapult_state: CatapultState (id: 3);
+
+ // Status of the aimer
+ aimer:AimerStatus (id: 4);
}
// Contains status of transfer rollers
-enum TransferRollerState : ubyte {
+enum TransferRollerStatus : ubyte {
NONE = 0,
TRANSFERING_IN = 1,
TRANSFERING_OUT = 2,
+ EXTEND_MOVING = 3,
+}
+
+// Contains status of extend rollers
+enum ExtendRollerStatus: ubyte {
+ // Means the rollers are not moving.
+ IDLE = 0,
+ // Means we're transfer from the transfer rollers into the extend rollers.
+ TRANSFERING_TO_EXTEND = 1,
+ // Means we are transfering from the extend to the catapult.
+ TRANSFERING_TO_CATAPULT = 2,
+ // Means we're trying to score in the amp/trap
+ SCORING_IN_AMP = 3,
+ SCORING_IN_TRAP = 4,
+}
+
+// Contains the status of the extend subsystem
+enum ExtendStatus : ubyte {
+ // Means we are near 0 and ready to transfer a game piece.
+ RETRACTED = 0,
+ // Means we are moving to some goal.
+ MOVING = 1,
+ // Means we are currently at the catapult.
+ CATAPULT = 2,
+ // Means we are at the amp position.
+ AMP = 3,
+ // Means we are at the trap positon.
+ TRAP = 4,
}
table Status {
@@ -35,17 +121,59 @@
// If true, we have aborted. This is the or of all subsystem estops.
estopped:bool (id: 1);
- // State of the rollers
- intake_roller_state:IntakeRollerState (id: 2);
+ state : SuperstructureState (id: 2);
+
+ // Status of the rollers
+ intake_roller:IntakeRollerStatus (id: 3);
// Estimated angle and angular velocitiy of the intake.
- intake_pivot_state:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 3);
+ intake_pivot:frc971.control_loops.AbsoluteEncoderProfiledJointStatus (id: 4);
- // State of transfer rollers
- transfer_roller_state:TransferRollerState (id: 4);
+ // Status of transfer rollers
+ transfer_roller:TransferRollerStatus (id: 5);
// Estimated angle and angular velocitiy of the climber.
- climber_state:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 5);
+ climber:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 6);
+
+ // Status of the subsytems involved in the shooter
+ shooter:ShooterStatus (id: 7);
+
+ // Estimated angle and angular velocitiy of the extend.
+ extend:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 8);
+
+ // State of the extender rollers
+ extend_roller:ExtendRollerStatus (id: 9);
+
+ // The status of if the turret and intake is colliding
+ collided: bool (id: 10);
+
+ extend_status:ExtendStatus (id: 11);
+
+ // Indicates that the extend is in position to allow a game piece to
+ // be transfered into it.
+ extend_ready_for_transfer:bool (id: 12);
+
+ // Indicates that the turret is in position to avoid the extend.
+ turret_ready_for_extend_move:bool (id: 13, deprecated);
+
+ uncompleted_note_goal:NoteStatus = NONE (id: 14);
+
+ // Indicates if the extend is close enough to the retracted position to be
+ // considered ready to accept note from the transfer rollers.
+ extend_at_retracted:bool (id: 15);
+
+ // Indicates if the turret is at the position to accept the note from extend
+ turret_ready_for_load:bool (id: 16);
+
+ // Indicates if the altitude is at the position to accept the note from
+ // extend
+ altitude_ready_for_load:bool (id: 17);
+
+ // Indicates if the extend is at the position to load the catapult
+ extend_ready_for_catapult_transfer:bool (id: 18);
+
+ extend_beambreak:bool (id: 19);
+ catapult_beambreak:bool (id: 20);
}
root_type Status;
diff --git a/y2024/control_loops/superstructure/turret/BUILD b/y2024/control_loops/superstructure/turret/BUILD
new file mode 100644
index 0000000..c67eb89
--- /dev/null
+++ b/y2024/control_loops/superstructure/turret/BUILD
@@ -0,0 +1,42 @@
+package(default_visibility = ["//y2024:__subpackages__"])
+
+genrule(
+ name = "genrule_turret",
+ outs = [
+ "turret_plant.h",
+ "turret_plant.cc",
+ "turret_plant.json",
+ "integral_turret_plant.h",
+ "integral_turret_plant.cc",
+ "integral_turret_plant.json",
+ ],
+ cmd = "$(location //y2024/control_loops/python:turret) $(OUTS)",
+ target_compatible_with = ["@platforms//os:linux"],
+ tools = [
+ "//y2024/control_loops/python:turret",
+ ],
+)
+
+cc_library(
+ name = "turret_plants",
+ srcs = [
+ "integral_turret_plant.cc",
+ "turret_plant.cc",
+ ],
+ hdrs = [
+ "integral_turret_plant.h",
+ "turret_plant.h",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops:hybrid_state_feedback_loop",
+ "//frc971/control_loops:state_feedback_loop",
+ ],
+)
+
+filegroup(
+ name = "turret_json",
+ srcs = ["integral_turret_plant.json"],
+ visibility = ["//visibility:public"],
+)
diff --git a/y2024/joystick_reader.cc b/y2024/joystick_reader.cc
index baac66f..caa6805 100644
--- a/y2024/joystick_reader.cc
+++ b/y2024/joystick_reader.cc
@@ -13,6 +13,7 @@
#include "frc971/constants/constants_sender_lib.h"
#include "frc971/control_loops/drivetrain/localizer_generated.h"
#include "frc971/control_loops/profiled_subsystem_generated.h"
+#include "frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h"
#include "frc971/input/action_joystick_input.h"
#include "frc971/input/driver_station_data.h"
#include "frc971/input/drivetrain_input.h"
@@ -21,11 +22,10 @@
#include "frc971/zeroing/wrap.h"
#include "y2024/constants/constants_generated.h"
#include "y2024/control_loops/drivetrain/drivetrain_base.h"
-#include "y2024/control_loops/superstructure/superstructure_goal_generated.h"
-#include "y2024/control_loops/superstructure/superstructure_status_generated.h"
+#include "y2024/control_loops/superstructure/superstructure_goal_static.h"
+#include "y2024/control_loops/superstructure/superstructure_status_static.h"
+
using frc971::CreateProfileParameters;
-using frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal;
-using frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal;
using frc971::input::driver_station::ButtonLocation;
using frc971::input::driver_station::ControlBit;
using frc971::input::driver_station::JoystickAxis;
@@ -34,38 +34,120 @@
namespace y2024::input::joysticks {
+namespace superstructure = y2024::control_loops::superstructure;
+
+// TODO(Xander): add button location from physical wiring
+// Note: Due to use_redundant_joysticks, the AOS_LOG statements
+// for the internal joystick code will give offset joystick numbering.
+const ButtonLocation kIntake(2, 8);
+const ButtonLocation kSpit(0, 0);
+const ButtonLocation kCatapultLoad(1, 7);
+const ButtonLocation kAmp(2, 7);
+const ButtonLocation kFire(2, 6);
+const ButtonLocation kTrap(2, 5);
+const ButtonLocation kAutoAim(0, 0);
+const ButtonLocation kAimSpeaker(1, 6);
+const ButtonLocation kAimPodium(0, 0);
+const ButtonLocation kShoot(0, 0);
+const ButtonLocation kRaiseClimber(3, 2);
+const ButtonLocation kRetractClimber(2, 4);
+const ButtonLocation kExtraButtonOne(0, 0);
+const ButtonLocation kExtraButtonTwo(0, 0);
+const ButtonLocation kExtraButtonThree(0, 0);
+const ButtonLocation kExtraButtonFour(0, 0);
+
class Reader : public ::frc971::input::ActionJoystickInput {
public:
- Reader(::aos::EventLoop *event_loop)
+ Reader(::aos::EventLoop *event_loop, const y2024::Constants *robot_constants)
: ::frc971::input::ActionJoystickInput(
event_loop,
::y2024::control_loops::drivetrain::GetDrivetrainConfig(event_loop),
::frc971::input::DrivetrainInputReader::InputType::kPistol,
{.use_redundant_joysticks = true}),
superstructure_goal_sender_(
- event_loop->MakeSender<control_loops::superstructure::Goal>(
+ event_loop->MakeSender<control_loops::superstructure::GoalStatic>(
"/superstructure")),
superstructure_status_fetcher_(
event_loop->MakeFetcher<control_loops::superstructure::Status>(
- "/superstructure")) {}
+ "/superstructure")),
+ robot_constants_(CHECK_NOTNULL(robot_constants)) {}
void AutoEnded() override { AOS_LOG(INFO, "Auto ended.\n"); }
void HandleTeleop(
const ::frc971::input::driver_station::Data &data) override {
- (void)data;
superstructure_status_fetcher_.Fetch();
if (!superstructure_status_fetcher_.get()) {
AOS_LOG(ERROR, "Got no superstructure status message.\n");
return;
}
+
+ aos::Sender<superstructure::GoalStatic>::StaticBuilder
+ superstructure_goal_builder =
+ superstructure_goal_sender_.MakeStaticBuilder();
+
+ if (data.IsPressed(kIntake)) {
+ // Intake is pressed
+ superstructure_goal_builder->set_intake_goal(
+ superstructure::IntakeGoal::INTAKE);
+ } else {
+ superstructure_goal_builder->set_intake_goal(
+ superstructure::IntakeGoal::NONE);
+ }
+ if (data.IsPressed(kAmp)) {
+ superstructure_goal_builder->set_note_goal(superstructure::NoteGoal::AMP);
+ } else if (data.IsPressed(kTrap)) {
+ superstructure_goal_builder->set_note_goal(
+ superstructure::NoteGoal::TRAP);
+ } else if (data.IsPressed(kCatapultLoad)) {
+ superstructure_goal_builder->set_note_goal(
+ superstructure::NoteGoal::CATAPULT);
+ } else {
+ superstructure_goal_builder->set_note_goal(
+ superstructure::NoteGoal::NONE);
+ }
+ auto shooter_goal = superstructure_goal_builder->add_shooter_goal();
+ shooter_goal->set_auto_aim(false);
+
+ // Updating aiming for shooter goal, only one type of aim should be possible
+ // at a time, auto-aiming is preferred over the setpoints.
+ if (data.IsPressed(kAimSpeaker)) {
+ auto catapult_goal = shooter_goal->add_catapult_goal();
+ catapult_goal->set_shot_velocity(robot_constants_->common()
+ ->shooter_speaker_set_point()
+ ->shot_velocity());
+ PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ shooter_goal->add_altitude_position(),
+ robot_constants_->common()
+ ->shooter_speaker_set_point()
+ ->altitude_position());
+ PopulateStaticZeroingSingleDOFProfiledSubsystemGoal(
+ shooter_goal->add_turret_position(), robot_constants_->common()
+ ->shooter_speaker_set_point()
+ ->turret_position());
+ }
+ superstructure_goal_builder->set_fire(data.IsPressed(kFire));
+
+ if (data.IsPressed(kRaiseClimber)) {
+ superstructure_goal_builder->set_climber_goal(
+ superstructure::ClimberGoal::FULL_EXTEND);
+ } else if (data.IsPressed(kRetractClimber)) {
+ superstructure_goal_builder->set_climber_goal(
+ superstructure::ClimberGoal::RETRACT);
+ } else {
+ superstructure_goal_builder->set_climber_goal(
+ superstructure::ClimberGoal::STOWED);
+ }
+
+ superstructure_goal_builder.CheckOk(superstructure_goal_builder.Send());
}
private:
- ::aos::Sender<control_loops::superstructure::Goal>
+ ::aos::Sender<control_loops::superstructure::GoalStatic>
superstructure_goal_sender_;
::aos::Fetcher<control_loops::superstructure::Status>
superstructure_status_fetcher_;
+ const y2024::Constants *robot_constants_;
};
} // namespace y2024::input::joysticks
@@ -77,8 +159,13 @@
aos::configuration::ReadConfig("aos_config.json");
frc971::constants::WaitForConstants<y2024::Constants>(&config.message());
+ ::aos::ShmEventLoop constant_fetcher_event_loop(&config.message());
+ frc971::constants::ConstantsFetcher<y2024::Constants> constants_fetcher(
+ &constant_fetcher_event_loop);
+ const y2024::Constants *robot_constants = &constants_fetcher.constants();
+
::aos::ShmEventLoop event_loop(&config.message());
- ::y2024::input::joysticks::Reader reader(&event_loop);
+ ::y2024::input::joysticks::Reader reader(&event_loop, robot_constants);
event_loop.Run();
diff --git a/y2024/localizer/BUILD b/y2024/localizer/BUILD
new file mode 100644
index 0000000..caec284
--- /dev/null
+++ b/y2024/localizer/BUILD
@@ -0,0 +1,168 @@
+load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
+load("//tools/build_rules:js.bzl", "ts_project")
+load("@com_github_google_flatbuffers//:typescript.bzl", "flatbuffer_ts_library")
+
+ts_project(
+ name = "localizer_plotter",
+ srcs = ["localizer_plotter.ts"],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos/network/www:aos_plotter",
+ "//aos/network/www:colors",
+ "//aos/network/www:proxy",
+ "//frc971/wpilib:imu_plot_utils",
+ ],
+)
+
+static_flatbuffer(
+ name = "status_fbs",
+ srcs = [
+ "status.fbs",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops/drivetrain:drivetrain_status_fbs",
+ "//frc971/imu_reader:imu_failures_fbs",
+ ],
+)
+
+flatbuffer_ts_library(
+ name = "status_ts_fbs",
+ srcs = ["status.fbs"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops/drivetrain:drivetrain_status_ts_fbs",
+ "//frc971/imu_reader:imu_failures_ts_fbs",
+ ],
+)
+
+static_flatbuffer(
+ name = "visualization_fbs",
+ srcs = [
+ "visualization.fbs",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":status_fbs",
+ ],
+)
+
+flatbuffer_ts_library(
+ name = "visualization_ts_fbs",
+ srcs = ["visualization.fbs"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":status_ts_fbs",
+ ],
+)
+
+cc_library(
+ name = "localizer",
+ srcs = ["localizer.cc"],
+ hdrs = ["localizer.h"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":status_fbs",
+ ":visualization_fbs",
+ "//aos/containers:sized_array",
+ "//aos/events:event_loop",
+ "//aos/network:message_bridge_client_fbs",
+ "//frc971/constants:constants_sender_lib",
+ "//frc971/control_loops:pose",
+ "//frc971/control_loops/drivetrain:hybrid_ekf",
+ "//frc971/control_loops/drivetrain:improved_down_estimator",
+ "//frc971/control_loops/drivetrain:localizer_fbs",
+ "//frc971/control_loops/drivetrain/localization:localizer_output_fbs",
+ "//frc971/control_loops/drivetrain/localization:utils",
+ "//frc971/imu_reader:imu_watcher",
+ "//frc971/vision:target_map_fbs",
+ "//frc971/vision:target_map_utils",
+ "//y2024:constants",
+ "//y2024/constants:constants_fbs",
+ ],
+)
+
+cc_test(
+ name = "localizer_test",
+ srcs = ["localizer_test.cc"],
+ data = ["//y2024:aos_config"],
+ deps = [
+ ":localizer",
+ ":status_fbs",
+ "//aos/events:simulated_event_loop",
+ "//aos/events/logging:log_writer",
+ "//aos/testing:googletest",
+ "//frc971/control_loops/drivetrain:drivetrain_test_lib",
+ "//frc971/control_loops/drivetrain:localizer_fbs",
+ "//frc971/control_loops/drivetrain/localization:utils",
+ "//y2024/constants:simulated_constants_sender",
+ "//y2024/control_loops/drivetrain:drivetrain_base",
+ ],
+)
+
+cc_binary(
+ name = "localizer_main",
+ srcs = ["localizer_main.cc"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":localizer",
+ "//aos:init",
+ "//aos/events:shm_event_loop",
+ "//frc971/constants:constants_sender_lib",
+ "//y2024/control_loops/drivetrain:drivetrain_base",
+ ],
+)
+
+ts_project(
+ name = "corrections_plotter",
+ srcs = ["corrections_plotter.ts"],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":visualization_ts_fbs",
+ "//aos/network/www:aos_plotter",
+ "//aos/network/www:colors",
+ "//aos/network/www:proxy",
+ ],
+)
+
+cc_binary(
+ name = "localizer_replay",
+ srcs = ["localizer_replay.cc"],
+ data = [
+ "//y2024:aos_config",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":localizer",
+ "//aos:configuration",
+ "//aos:init",
+ "//aos:json_to_flatbuffer",
+ "//aos/events:simulated_event_loop",
+ "//aos/events/logging:log_reader",
+ "//aos/events/logging:log_writer",
+ "//aos/util:simulation_logger",
+ "//y2024/control_loops/drivetrain:drivetrain_base",
+ ],
+)
+
+cc_binary(
+ name = "localizer_logger",
+ srcs = [
+ "localizer_logger.cc",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos:configuration",
+ "//aos:init",
+ "//aos/events:shm_event_loop",
+ "//aos/events/logging:log_writer",
+ "//aos/events/logging:snappy_encoder",
+ "//aos/logging:log_namer",
+ "@com_github_gflags_gflags//:gflags",
+ "@com_github_google_glog//:glog",
+ ],
+)
diff --git a/y2024/localizer/corrections_plotter.ts b/y2024/localizer/corrections_plotter.ts
new file mode 100644
index 0000000..c883759
--- /dev/null
+++ b/y2024/localizer/corrections_plotter.ts
@@ -0,0 +1,177 @@
+import {ByteBuffer} from 'flatbuffers';
+import {AosPlotter} from '../../aos/network/www/aos_plotter';
+import {MessageHandler, TimestampedMessage} from '../../aos/network/www/aos_plotter';
+import {BLUE, BROWN, CYAN, GREEN, PINK, RED, WHITE} from '../../aos/network/www/colors';
+import {Connection} from '../../aos/network/www/proxy';
+import {Table} from '../../aos/network/www/reflection';
+import {Schema} from 'flatbuffers_reflection/reflection_generated';
+import {Visualization, TargetEstimateDebug} from './visualization_generated';
+
+
+const TIME = AosPlotter.TIME;
+// magenta, yellow, cyan, black
+const PI_COLORS = [[255, 0, 255], [255, 255, 0], [0, 255, 255], [0, 0, 0]];
+
+class VisionMessageHandler extends MessageHandler {
+ constructor(private readonly schema: Schema) {
+ super(schema);
+ }
+
+ private readScalar(table: Table, fieldName: string): number|BigInt|null {
+ return this.parser.readScalar(table, fieldName);
+ }
+
+ addMessage(data: Uint8Array, time: number): void {
+ const message = Visualization.getRootAsVisualization(new ByteBuffer(data));
+ for (let ii = 0; ii < message.targetsLength(); ++ii) {
+ const target = message.targets(ii);
+ const time = Number(target.imageMonotonicTimestampNs()) * 1e-9;
+ if (time == 0) {
+ console.log('Dropping message without populated time?');
+ continue;
+ }
+ const table = Table.getNamedTable(
+ target.bb, this.schema, 'y2024.localizer.TargetEstimateDebug', target.bb_pos);
+ this.messages.push(new TimestampedMessage(table, time));
+ }
+ }
+}
+
+export function plotVision(conn: Connection, element: Element): void {
+ const aosPlotter = new AosPlotter(conn);
+
+ const targets = [];
+ const targetLabels = [];
+ for (const orin of ['orin1', 'imu']) {
+ for (const camera of ['camera0', 'camera1']) {
+ targetLabels.push(orin + ' ' + camera);
+ targets.push(aosPlotter.addRawMessageSource(
+ '/' + orin + '/' + camera, 'y2024.localizer.Visualization',
+ new VisionMessageHandler(
+ conn.getSchema('y2024.localizer.Visualization'))));
+ }
+ }
+ const localizerStatus = aosPlotter.addMessageSource(
+ '/localizer', 'y2024.localizer.Status');
+ const localizerOutput = aosPlotter.addMessageSource(
+ '/localizer', 'frc971.controls.LocalizerOutput');
+
+ const statsPlot = aosPlotter.addPlot(element);
+ statsPlot.plot.getAxisLabels().setTitle('Statistics');
+ statsPlot.plot.getAxisLabels().setXLabel(TIME);
+ statsPlot.plot.getAxisLabels().setYLabel('[bool, enum]');
+
+ statsPlot
+ .addMessageLine(localizerStatus, ['statistics[]', 'total_accepted'])
+ .setDrawLine(false)
+ .setColor(BLUE);
+ statsPlot
+ .addMessageLine(localizerStatus, ['statistics[]', 'total_candidates'])
+ .setDrawLine(false)
+ .setColor(RED);
+
+ const rejectionPlot = aosPlotter.addPlot(element);
+ rejectionPlot.plot.getAxisLabels().setTitle('Rejection Reasons');
+ rejectionPlot.plot.getAxisLabels().setXLabel(TIME);
+ rejectionPlot.plot.getAxisLabels().setYLabel('[bool, enum]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ rejectionPlot.addMessageLine(targets[ii], ['rejection_reason'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+
+ const xPlot = aosPlotter.addPlot(element);
+ xPlot.plot.getAxisLabels().setTitle('X Position');
+ xPlot.plot.getAxisLabels().setXLabel(TIME);
+ xPlot.plot.getAxisLabels().setYLabel('[m]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ xPlot.addMessageLine(targets[ii], ['implied_robot_x'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+ xPlot.addMessageLine(localizerOutput, ['x'])
+ .setDrawLine(false)
+ .setColor(BLUE);
+
+ const correctionXPlot = aosPlotter.addPlot(element);
+ correctionXPlot.plot.getAxisLabels().setTitle('X Corrections');
+ correctionXPlot.plot.getAxisLabels().setXLabel(TIME);
+ correctionXPlot.plot.getAxisLabels().setYLabel('[m]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ correctionXPlot.addMessageLine(targets[ii], ['correction_x'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+
+ const yPlot = aosPlotter.addPlot(element);
+ yPlot.plot.getAxisLabels().setTitle('Y Position');
+ yPlot.plot.getAxisLabels().setXLabel(TIME);
+ yPlot.plot.getAxisLabels().setYLabel('[m]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ yPlot.addMessageLine(targets[ii], ['implied_robot_y'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+ yPlot.addMessageLine(localizerOutput, ['y'])
+ .setDrawLine(false)
+ .setColor(BLUE);
+
+ const correctionYPlot = aosPlotter.addPlot(element);
+ correctionYPlot.plot.getAxisLabels().setTitle('Y Corrections');
+ correctionYPlot.plot.getAxisLabels().setXLabel(TIME);
+ correctionYPlot.plot.getAxisLabels().setYLabel('[m]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ correctionYPlot.addMessageLine(targets[ii], ['correction_y'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+
+ const thetaPlot = aosPlotter.addPlot(element);
+ thetaPlot.plot.getAxisLabels().setTitle('Yaw');
+ thetaPlot.plot.getAxisLabels().setXLabel(TIME);
+ thetaPlot.plot.getAxisLabels().setYLabel('[m]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ thetaPlot.addMessageLine(targets[ii], ['implied_robot_theta'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+ thetaPlot.addMessageLine(localizerOutput, ['theta'])
+ .setDrawLine(false)
+ .setColor(BLUE);
+
+ const aprilTagPlot = aosPlotter.addPlot(element);
+ aprilTagPlot.plot.getAxisLabels().setTitle('April Tag IDs');
+ aprilTagPlot.plot.getAxisLabels().setXLabel(TIME);
+ aprilTagPlot.plot.getAxisLabels().setYLabel('[id]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ aprilTagPlot.addMessageLine(targets[ii], ['april_tag'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+
+ const imageAgePlot = aosPlotter.addPlot(element);
+ imageAgePlot.plot.getAxisLabels().setTitle('Image Age');
+ imageAgePlot.plot.getAxisLabels().setXLabel(TIME);
+ imageAgePlot.plot.getAxisLabels().setYLabel('[sec]');
+
+ for (let ii = 0; ii < targets.length; ++ii) {
+ imageAgePlot.addMessageLine(targets[ii], ['image_age_sec'])
+ .setDrawLine(false)
+ .setColor(PI_COLORS[ii])
+ .setLabel(targetLabels[ii]);
+ }
+}
diff --git a/y2024/localizer/localizer.cc b/y2024/localizer/localizer.cc
new file mode 100644
index 0000000..a177188
--- /dev/null
+++ b/y2024/localizer/localizer.cc
@@ -0,0 +1,601 @@
+#include "y2024/localizer/localizer.h"
+
+#include "gflags/gflags.h"
+
+#include "aos/containers/sized_array.h"
+#include "frc971/control_loops/drivetrain/localizer_generated.h"
+#include "frc971/control_loops/pose.h"
+#include "frc971/vision/target_map_utils.h"
+#include "y2024/constants.h"
+
+DEFINE_double(max_pose_error, 1e-6,
+ "Throw out target poses with a higher pose error than this");
+DEFINE_double(
+ max_pose_error_ratio, 0.4,
+ "Throw out target poses with a higher pose error ratio than this");
+DEFINE_double(distortion_noise_scalar, 1.0,
+ "Scale the target pose distortion factor by this when computing "
+ "the noise.");
+DEFINE_double(
+ max_implied_yaw_error, 3.0,
+ "Reject target poses that imply a robot yaw of more than this many degrees "
+ "off from our estimate.");
+DEFINE_double(
+ max_implied_teleop_yaw_error, 30.0,
+ "Reject target poses that imply a robot yaw of more than this many degrees "
+ "off from our estimate.");
+DEFINE_double(max_distance_to_target, 5.0,
+ "Reject target poses that have a 3d distance of more than this "
+ "many meters.");
+DEFINE_double(max_auto_image_robot_speed, 2.0,
+ "Reject target poses when the robot is travelling faster than "
+ "this speed in auto.");
+
+namespace y2024::localizer {
+namespace {
+constexpr std::array<std::string_view, Localizer::kNumCameras>
+ kDetectionChannels{"/orin1/camera0", "/orin1/camera1", "/imu/camera0",
+ "/imu/camera1"};
+
+size_t CameraIndexForName(std::string_view name) {
+ for (size_t index = 0; index < kDetectionChannels.size(); ++index) {
+ if (name == kDetectionChannels.at(index)) {
+ return index;
+ }
+ }
+ LOG(FATAL) << "No camera channel named " << name;
+}
+
+std::map<uint64_t, Localizer::Transform> GetTargetLocations(
+ const Constants &constants) {
+ CHECK(constants.has_common());
+ CHECK(constants.common()->has_target_map());
+ CHECK(constants.common()->target_map()->has_target_poses());
+ std::map<uint64_t, Localizer::Transform> transforms;
+ for (const frc971::vision::TargetPoseFbs *target :
+ *constants.common()->target_map()->target_poses()) {
+ CHECK(target->has_id());
+ CHECK(target->has_position());
+ CHECK(target->has_orientation());
+ CHECK_EQ(0u, transforms.count(target->id()));
+ transforms[target->id()] = PoseToTransform(target);
+ }
+ return transforms;
+}
+} // namespace
+
+std::array<Localizer::CameraState, Localizer::kNumCameras>
+Localizer::MakeCameras(const Constants &constants, aos::EventLoop *event_loop) {
+ CHECK(constants.has_cameras());
+ std::array<Localizer::CameraState, Localizer::kNumCameras> cameras;
+ for (const CameraConfiguration *camera : *constants.cameras()) {
+ CHECK(camera->has_calibration());
+ const frc971::vision::calibration::CameraCalibration *calibration =
+ camera->calibration();
+ CHECK(!calibration->has_turret_extrinsics())
+ << "The 2024 robot does not have cameras on a turret.";
+ CHECK(calibration->has_node_name());
+ const std::string channel_name =
+ absl::StrFormat("/%s/camera%d", calibration->node_name()->string_view(),
+ calibration->camera_number());
+ const size_t index = CameraIndexForName(channel_name);
+ // We default-construct the extrinsics matrix to all-zeros; use that to
+ // sanity-check whether we have populated the matrix yet or not.
+ CHECK(cameras.at(index).extrinsics.norm() == 0)
+ << "Got multiple calibrations for "
+ << calibration->node_name()->string_view();
+ CHECK(calibration->has_fixed_extrinsics());
+ cameras.at(index).extrinsics =
+ frc971::control_loops::drivetrain::FlatbufferToTransformationMatrix(
+ *calibration->fixed_extrinsics());
+ cameras.at(index).debug_sender =
+ event_loop->MakeSender<VisualizationStatic>(channel_name);
+ }
+ for (const CameraState &camera : cameras) {
+ CHECK(camera.extrinsics.norm() != 0) << "Missing a camera calibration.";
+ }
+ return cameras;
+}
+
+Localizer::Localizer(aos::EventLoop *event_loop)
+ : event_loop_(event_loop),
+ constants_fetcher_(event_loop),
+ dt_config_(
+ frc971::control_loops::drivetrain::DrivetrainConfig<double>::
+ FromFlatbuffer(*CHECK_NOTNULL(
+ constants_fetcher_.constants().common()->drivetrain()))),
+ cameras_(MakeCameras(constants_fetcher_.constants(), event_loop)),
+ target_poses_(GetTargetLocations(constants_fetcher_.constants())),
+ down_estimator_(dt_config_),
+ ekf_(dt_config_),
+ observations_(&ekf_),
+ imu_watcher_(event_loop, dt_config_,
+ y2024::constants::Values::DrivetrainEncoderToMeters(1),
+ std::bind(&Localizer::HandleImu, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3,
+ std::placeholders::_4, std::placeholders::_5),
+ frc971::controls::ImuWatcher::TimestampSource::kPi),
+ utils_(event_loop),
+ status_sender_(event_loop->MakeSender<Status>("/localizer")),
+ output_sender_(event_loop->MakeSender<frc971::controls::LocalizerOutput>(
+ "/localizer")),
+ server_statistics_fetcher_(
+ event_loop_->MakeFetcher<aos::message_bridge::ServerStatistics>(
+ "/aos")),
+ client_statistics_fetcher_(
+ event_loop_->MakeFetcher<aos::message_bridge::ClientStatistics>(
+ "/aos")) {
+ if (dt_config_.is_simulated) {
+ down_estimator_.assume_perfect_gravity();
+ }
+
+ for (size_t camera_index = 0; camera_index < kNumCameras; ++camera_index) {
+ const std::string_view channel_name = kDetectionChannels.at(camera_index);
+ const aos::Channel *const channel = CHECK_NOTNULL(
+ event_loop->GetChannel<frc971::vision::TargetMap>(channel_name));
+ event_loop->MakeWatcher(
+ channel_name, [this, channel,
+ camera_index](const frc971::vision::TargetMap &targets) {
+ CHECK(targets.has_target_poses());
+ CHECK(targets.has_monotonic_timestamp_ns());
+ const std::optional<aos::monotonic_clock::duration> clock_offset =
+ utils_.ClockOffset(channel->source_node()->string_view());
+ if (!clock_offset.has_value()) {
+ VLOG(1) << "Rejecting image due to disconnected message bridge at "
+ << event_loop_->monotonic_now();
+ cameras_.at(camera_index)
+ .rejection_counter.IncrementError(
+ RejectionReason::MESSAGE_BRIDGE_DISCONNECTED);
+ return;
+ }
+ const aos::monotonic_clock::time_point orin_capture_time(
+ std::chrono::nanoseconds(targets.monotonic_timestamp_ns()) -
+ clock_offset.value());
+ if (orin_capture_time > event_loop_->context().monotonic_event_time) {
+ VLOG(1) << "Rejecting image due to being from future at "
+ << event_loop_->monotonic_now() << " with timestamp of "
+ << orin_capture_time << " and event time pf "
+ << event_loop_->context().monotonic_event_time;
+ cameras_.at(camera_index)
+ .rejection_counter.IncrementError(
+ RejectionReason::IMAGE_FROM_FUTURE);
+ return;
+ }
+ auto debug_builder =
+ cameras_.at(camera_index).debug_sender.MakeStaticBuilder();
+ auto target_debug_list = debug_builder->add_targets();
+ // The static_length should already be 20.
+ CHECK(target_debug_list->reserve(20));
+ for (const frc971::vision::TargetPoseFbs *target :
+ *targets.target_poses()) {
+ VLOG(1) << "Handling target from " << camera_index;
+ HandleTarget(camera_index, orin_capture_time, *target,
+ target_debug_list->emplace_back());
+ }
+ StatisticsForCamera(cameras_.at(camera_index),
+ debug_builder->add_statistics());
+ debug_builder.CheckOk(debug_builder.Send());
+ SendStatus();
+ });
+ }
+
+ event_loop_->AddPhasedLoop([this](int) { SendOutput(); },
+ std::chrono::milliseconds(20));
+
+ event_loop_->MakeWatcher(
+ "/drivetrain",
+ [this](
+ const frc971::control_loops::drivetrain::LocalizerControl &control) {
+ // This is triggered whenever we need to force the X/Y/(maybe theta)
+ // position of the robot to a particular point---e.g., during pre-match
+ // setup, or when commanded by a button on the driverstation.
+
+ // For some forms of reset, we choose to keep our current yaw estimate
+ // rather than overriding it from the control message.
+ const double theta = control.keep_current_theta()
+ ? ekf_.X_hat(StateIdx::kTheta)
+ : control.theta();
+ // Encoder values need to be reset based on the current values to ensure
+ // that we don't get weird corrections on the next encoder update.
+ const double left_encoder = ekf_.X_hat(StateIdx::kLeftEncoder);
+ const double right_encoder = ekf_.X_hat(StateIdx::kRightEncoder);
+ ekf_.ResetInitialState(
+ t_,
+ (HybridEkf::State() << control.x(), control.y(), theta,
+ left_encoder, 0, right_encoder, 0, 0, 0, 0, 0, 0)
+ .finished(),
+ ekf_.P());
+ });
+
+ ekf_.set_ignore_accel(true);
+ // Priority should be lower than the imu reading process, but non-zero.
+ event_loop->SetRuntimeRealtimePriority(10);
+ event_loop->OnRun([this, event_loop]() {
+ ekf_.ResetInitialState(event_loop->monotonic_now(),
+ HybridEkf::State::Zero(), ekf_.P());
+ });
+}
+
+void Localizer::HandleImu(aos::monotonic_clock::time_point /*sample_time_pico*/,
+ aos::monotonic_clock::time_point sample_time_orin,
+ std::optional<Eigen::Vector2d> /*encoders*/,
+ Eigen::Vector3d gyro, Eigen::Vector3d accel) {
+ std::optional<Eigen::Vector2d> encoders = utils_.Encoders(sample_time_orin);
+ last_encoder_readings_ = encoders;
+ // Ignore invalid readings; the HybridEkf will handle it reasonably.
+ if (!encoders.has_value()) {
+ return;
+ }
+ if (t_ == aos::monotonic_clock::min_time) {
+ t_ = sample_time_orin;
+ }
+ if (t_ + 10 * frc971::controls::ImuWatcher::kNominalDt < sample_time_orin) {
+ t_ = sample_time_orin;
+ ++clock_resets_;
+ }
+ const aos::monotonic_clock::duration dt = sample_time_orin - t_;
+ t_ = sample_time_orin;
+ // We don't actually use the down estimator currently, but it's really
+ // convenient for debugging.
+ down_estimator_.Predict(gyro, accel, dt);
+ const double yaw_rate = (dt_config_.imu_transform * gyro)(2);
+ ekf_.UpdateEncodersAndGyro(encoders.value()(0), encoders.value()(1), yaw_rate,
+ utils_.VoltageOrZero(sample_time_orin), accel, t_);
+ SendStatus();
+}
+
+void Localizer::RejectImage(int camera_index, RejectionReason reason,
+ TargetEstimateDebugStatic *builder) {
+ if (builder != nullptr) {
+ builder->set_accepted(false);
+ builder->set_rejection_reason(reason);
+ }
+ cameras_.at(camera_index).rejection_counter.IncrementError(reason);
+}
+
+// Only use april tags present in the target map; this method has also been used
+// (in the past) for ignoring april tags that tend to produce problematic
+// readings.
+bool Localizer::UseAprilTag(uint64_t target_id) {
+ return target_poses_.count(target_id) != 0;
+}
+
+namespace {
+// Converts a camera transformation matrix from treating the +Z axis from
+// pointing straight out the lens to having the +X pointing straight out the
+// lens, with +Z going "up" (i.e., -Y in the normal convention) and +Y going
+// leftwards (i.e., -X in the normal convention).
+Localizer::Transform ZToXCamera(const Localizer::Transform &transform) {
+ return transform *
+ Eigen::Matrix4d{
+ {0, -1, 0, 0}, {0, 0, -1, 0}, {1, 0, 0, 0}, {0, 0, 0, 1}};
+}
+} // namespace
+
+void Localizer::HandleTarget(
+ int camera_index, const aos::monotonic_clock::time_point capture_time,
+ const frc971::vision::TargetPoseFbs &target,
+ TargetEstimateDebugStatic *debug_builder) {
+ ++total_candidate_targets_;
+ ++cameras_.at(camera_index).total_candidate_targets;
+ const uint64_t target_id = target.id();
+
+ if (debug_builder == nullptr) {
+ AOS_LOG(ERROR, "Dropped message from debug vector.");
+ } else {
+ debug_builder->set_camera(camera_index);
+ debug_builder->set_image_age_sec(aos::time::DurationInSeconds(
+ event_loop_->monotonic_now() - capture_time));
+ debug_builder->set_image_monotonic_timestamp_ns(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(
+ capture_time.time_since_epoch())
+ .count());
+ debug_builder->set_april_tag(target_id);
+ }
+ VLOG(2) << aos::FlatbufferToJson(&target);
+ if (!UseAprilTag(target_id)) {
+ VLOG(1) << "Rejecting target due to invalid ID " << target_id;
+ RejectImage(camera_index, RejectionReason::NO_SUCH_TARGET, debug_builder);
+ return;
+ }
+
+ const Transform &H_field_target = target_poses_.at(target_id);
+ const Transform &H_robot_camera = cameras_.at(camera_index).extrinsics;
+
+ const Transform H_camera_target = PoseToTransform(&target);
+
+ // In order to do the EKF correction, we determine the expected state based
+ // on the state at the time the image was captured; however, we insert the
+ // correction update itself at the current time. This is technically not
+ // quite correct, but saves substantial CPU usage & code complexity by
+ // making it so that we don't have to constantly rewind the entire EKF
+ // history.
+ const std::optional<State> state_at_capture =
+ ekf_.LastStateBeforeTime(capture_time);
+
+ if (!state_at_capture.has_value()) {
+ VLOG(1) << "Rejecting image due to being too old.";
+ return RejectImage(camera_index, RejectionReason::IMAGE_TOO_OLD,
+ debug_builder);
+ } else if (target.pose_error() > FLAGS_max_pose_error) {
+ VLOG(1) << "Rejecting target due to high pose error "
+ << target.pose_error();
+ return RejectImage(camera_index, RejectionReason::HIGH_POSE_ERROR,
+ debug_builder);
+ } else if (target.pose_error_ratio() > FLAGS_max_pose_error_ratio) {
+ VLOG(1) << "Rejecting target due to high pose error ratio "
+ << target.pose_error_ratio();
+ return RejectImage(camera_index, RejectionReason::HIGH_POSE_ERROR_RATIO,
+ debug_builder);
+ }
+
+ Corrector corrector(state_at_capture.value(), H_field_target, H_robot_camera,
+ H_camera_target);
+ const double distance_to_target = corrector.observed()(Corrector::kDistance);
+
+ // Heading, distance, skew at 1 meter.
+ Eigen::Matrix<double, 3, 1> noises(0.01, 0.05, 0.05);
+ const double distance_noise_scalar = std::pow(distance_to_target, 2.0);
+ noises(Corrector::kDistance) *= distance_noise_scalar;
+ noises(Corrector::kSkew) *= distance_noise_scalar;
+ // TODO(james): This is leftover from last year; figure out if we want it.
+ // Scale noise by the distortion factor for this detection
+ noises *= (1.0 + FLAGS_distortion_noise_scalar * target.distortion_factor());
+
+ Eigen::Matrix3d R = Eigen::Matrix3d::Zero();
+ R.diagonal() = noises.cwiseAbs2();
+ if (debug_builder != nullptr) {
+ const Eigen::Vector3d camera_position =
+ corrector.observed_camera_pose().abs_pos();
+ debug_builder->set_camera_x(camera_position.x());
+ debug_builder->set_camera_y(camera_position.y());
+ debug_builder->set_camera_theta(
+ corrector.observed_camera_pose().abs_theta());
+ // Calculate the camera-to-robot transformation matrix ignoring the
+ // pitch/roll of the camera.
+ const Transform H_camera_robot_stripped =
+ frc971::control_loops::Pose(ZToXCamera(H_robot_camera))
+ .AsTransformationMatrix()
+ .inverse();
+ const frc971::control_loops::Pose measured_pose(
+ corrector.observed_camera_pose().AsTransformationMatrix() *
+ H_camera_robot_stripped);
+ debug_builder->set_implied_robot_x(measured_pose.rel_pos().x());
+ debug_builder->set_implied_robot_y(measured_pose.rel_pos().y());
+ debug_builder->set_implied_robot_theta(measured_pose.rel_theta());
+
+ Corrector::PopulateMeasurement(corrector.expected(),
+ debug_builder->add_expected_observation());
+ Corrector::PopulateMeasurement(corrector.observed(),
+ debug_builder->add_actual_observation());
+ Corrector::PopulateMeasurement(noises, debug_builder->add_modeled_noise());
+ }
+
+ const double camera_yaw_error =
+ aos::math::NormalizeAngle(corrector.expected_camera_pose().abs_theta() -
+ corrector.observed_camera_pose().abs_theta());
+ constexpr double kDegToRad = M_PI / 180.0;
+
+ const double robot_speed =
+ (state_at_capture.value()(StateIdx::kLeftVelocity) +
+ state_at_capture.value()(StateIdx::kRightVelocity)) /
+ 2.0;
+ const double yaw_threshold =
+ (utils_.MaybeInAutonomous() ? FLAGS_max_implied_yaw_error
+ : FLAGS_max_implied_teleop_yaw_error) *
+ kDegToRad;
+
+ if (utils_.MaybeInAutonomous() &&
+ (std::abs(robot_speed) > FLAGS_max_auto_image_robot_speed)) {
+ return RejectImage(camera_index, RejectionReason::ROBOT_TOO_FAST,
+ debug_builder);
+ } else if (std::abs(camera_yaw_error) > yaw_threshold) {
+ return RejectImage(camera_index, RejectionReason::HIGH_IMPLIED_YAW_ERROR,
+ debug_builder);
+ } else if (distance_to_target > FLAGS_max_distance_to_target) {
+ return RejectImage(camera_index, RejectionReason::HIGH_DISTANCE_TO_TARGET,
+ debug_builder);
+ }
+
+ const Input U = ekf_.MostRecentInput();
+ VLOG(1) << "previous state " << ekf_.X_hat().topRows<3>().transpose();
+ const State prior_state = ekf_.X_hat();
+ // For the correction step, instead of passing in the measurement directly,
+ // we pass in (0, 0, 0) as the measurement and then for the expected
+ // measurement (Zhat) we calculate the error between the pose implied by
+ // the camera measurement and the current estimate of the
+ // pose. This doesn't affect any of the math, it just makes the code a bit
+ // more convenient to write given the Correct() interface we already have.
+ observations_.CorrectKnownH(Eigen::Vector3d::Zero(), &U, corrector, R, t_);
+ ++total_accepted_targets_;
+ ++cameras_.at(camera_index).total_accepted_targets;
+ VLOG(1) << "new state " << ekf_.X_hat().topRows<3>().transpose();
+ if (debug_builder != nullptr) {
+ debug_builder->set_correction_x(ekf_.X_hat()(StateIdx::kX) -
+ prior_state(StateIdx::kX));
+ debug_builder->set_correction_y(ekf_.X_hat()(StateIdx::kY) -
+ prior_state(StateIdx::kY));
+ debug_builder->set_correction_theta(ekf_.X_hat()(StateIdx::kTheta) -
+ prior_state(StateIdx::kTheta));
+ debug_builder->set_accepted(true);
+ }
+}
+
+void Localizer::SendOutput() {
+ auto builder = output_sender_.MakeBuilder();
+ frc971::controls::LocalizerOutput::Builder output_builder =
+ builder.MakeBuilder<frc971::controls::LocalizerOutput>();
+ output_builder.add_monotonic_timestamp_ns(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(
+ event_loop_->context().monotonic_event_time.time_since_epoch())
+ .count());
+ output_builder.add_x(ekf_.X_hat(StateIdx::kX));
+ output_builder.add_y(ekf_.X_hat(StateIdx::kY));
+ output_builder.add_theta(ekf_.X_hat(StateIdx::kTheta));
+ output_builder.add_zeroed(imu_watcher_.zeroer().Zeroed());
+ output_builder.add_image_accepted_count(total_accepted_targets_);
+ const Eigen::Quaterniond &orientation =
+ Eigen::AngleAxis<double>(ekf_.X_hat(StateIdx::kTheta),
+ Eigen::Vector3d::UnitZ()) *
+ down_estimator_.X_hat();
+ frc971::controls::Quaternion quaternion;
+ quaternion.mutate_x(orientation.x());
+ quaternion.mutate_y(orientation.y());
+ quaternion.mutate_z(orientation.z());
+ quaternion.mutate_w(orientation.w());
+ output_builder.add_orientation(&quaternion);
+ server_statistics_fetcher_.Fetch();
+ client_statistics_fetcher_.Fetch();
+
+ bool orins_connected = true;
+
+ if (server_statistics_fetcher_.get()) {
+ for (const auto *orin_server_status :
+ *server_statistics_fetcher_->connections()) {
+ if (orin_server_status->state() ==
+ aos::message_bridge::State::DISCONNECTED) {
+ orins_connected = false;
+ }
+ }
+ }
+
+ if (client_statistics_fetcher_.get()) {
+ for (const auto *pi_client_status :
+ *client_statistics_fetcher_->connections()) {
+ if (pi_client_status->state() ==
+ aos::message_bridge::State::DISCONNECTED) {
+ orins_connected = false;
+ }
+ }
+ }
+
+ // The output message is year-agnostic, and retains "pi" naming for histrocial
+ // reasons.
+ output_builder.add_all_pis_connected(orins_connected);
+ builder.CheckOk(builder.Send(output_builder.Finish()));
+}
+
+flatbuffers::Offset<frc971::control_loops::drivetrain::LocalizerState>
+Localizer::PopulateState(const State &X_hat,
+ flatbuffers::FlatBufferBuilder *fbb) {
+ frc971::control_loops::drivetrain::LocalizerState::Builder builder(*fbb);
+ builder.add_x(X_hat(StateIdx::kX));
+ builder.add_y(X_hat(StateIdx::kY));
+ builder.add_theta(X_hat(StateIdx::kTheta));
+ builder.add_left_velocity(X_hat(StateIdx::kLeftVelocity));
+ builder.add_right_velocity(X_hat(StateIdx::kRightVelocity));
+ builder.add_left_encoder(X_hat(StateIdx::kLeftEncoder));
+ builder.add_right_encoder(X_hat(StateIdx::kRightEncoder));
+ builder.add_left_voltage_error(X_hat(StateIdx::kLeftVoltageError));
+ builder.add_right_voltage_error(X_hat(StateIdx::kRightVoltageError));
+ builder.add_angular_error(X_hat(StateIdx::kAngularError));
+ builder.add_longitudinal_velocity_offset(
+ X_hat(StateIdx::kLongitudinalVelocityOffset));
+ builder.add_lateral_velocity(X_hat(StateIdx::kLateralVelocity));
+ return builder.Finish();
+}
+
+flatbuffers::Offset<ImuStatus> Localizer::PopulateImu(
+ flatbuffers::FlatBufferBuilder *fbb) const {
+ const auto zeroer_offset = imu_watcher_.zeroer().PopulateStatus(fbb);
+ const auto failures_offset = imu_watcher_.PopulateImuFailures(fbb);
+ ImuStatus::Builder builder(*fbb);
+ builder.add_zeroed(imu_watcher_.zeroer().Zeroed());
+ builder.add_faulted_zero(imu_watcher_.zeroer().Faulted());
+ builder.add_zeroing(zeroer_offset);
+ if (imu_watcher_.pico_offset().has_value()) {
+ builder.add_board_offset_ns(imu_watcher_.pico_offset().value().count());
+ builder.add_board_offset_error_ns(imu_watcher_.pico_offset_error().count());
+ }
+ if (last_encoder_readings_.has_value()) {
+ builder.add_left_encoder(last_encoder_readings_.value()(0));
+ builder.add_right_encoder(last_encoder_readings_.value()(1));
+ }
+ builder.add_imu_failures(failures_offset);
+ return builder.Finish();
+}
+
+flatbuffers::Offset<CumulativeStatistics> Localizer::StatisticsForCamera(
+ const CameraState &camera, flatbuffers::FlatBufferBuilder *fbb) {
+ const auto counts_offset = camera.rejection_counter.PopulateCounts(fbb);
+ CumulativeStatistics::Builder stats_builder(*fbb);
+ stats_builder.add_total_accepted(camera.total_accepted_targets);
+ stats_builder.add_total_candidates(camera.total_candidate_targets);
+ stats_builder.add_rejection_reasons(counts_offset);
+ return stats_builder.Finish();
+}
+
+void Localizer::StatisticsForCamera(const CameraState &camera,
+ CumulativeStatisticsStatic *builder) {
+ camera.rejection_counter.PopulateCountsStaticFbs(
+ builder->add_rejection_reasons());
+ builder->set_total_accepted(camera.total_accepted_targets);
+ builder->set_total_candidates(camera.total_candidate_targets);
+}
+
+void Localizer::SendStatus() {
+ auto builder = status_sender_.MakeBuilder();
+ std::array<flatbuffers::Offset<CumulativeStatistics>, kNumCameras>
+ stats_offsets;
+ for (size_t ii = 0; ii < kNumCameras; ++ii) {
+ stats_offsets.at(ii) = StatisticsForCamera(cameras_.at(ii), builder.fbb());
+ }
+ auto stats_offset =
+ builder.fbb()->CreateVector(stats_offsets.data(), stats_offsets.size());
+ auto down_estimator_offset =
+ down_estimator_.PopulateStatus(builder.fbb(), t_);
+ auto imu_offset = PopulateImu(builder.fbb());
+ auto state_offset = PopulateState(ekf_.X_hat(), builder.fbb());
+ Status::Builder status_builder = builder.MakeBuilder<Status>();
+ status_builder.add_state(state_offset);
+ status_builder.add_down_estimator(down_estimator_offset);
+ status_builder.add_imu(imu_offset);
+ status_builder.add_statistics(stats_offset);
+ builder.CheckOk(builder.Send(status_builder.Finish()));
+}
+
+Eigen::Vector3d Localizer::Corrector::HeadingDistanceSkew(
+ const Pose &relative_pose) {
+ const double heading = relative_pose.heading();
+ const double distance = relative_pose.xy_norm();
+ const double skew =
+ ::aos::math::NormalizeAngle(relative_pose.rel_theta() - heading);
+ return {heading, distance, skew};
+}
+
+Localizer::Corrector Localizer::Corrector::CalculateHeadingDistanceSkewH(
+ const State &state_at_capture, const Transform &H_field_target,
+ const Transform &H_robot_camera, const Transform &H_camera_target) {
+ const Transform H_field_camera = H_field_target * H_camera_target.inverse();
+ const Pose expected_robot_pose(
+ {state_at_capture(StateIdx::kX), state_at_capture(StateIdx::kY), 0.0},
+ state_at_capture(StateIdx::kTheta));
+ // Observed position on the field, reduced to just the 2-D pose.
+ const Pose observed_camera(ZToXCamera(H_field_camera));
+ const Pose expected_camera(expected_robot_pose.AsTransformationMatrix() *
+ ZToXCamera(H_robot_camera));
+ const Pose nominal_target(ZToXCamera(H_field_target));
+ const Pose observed_target = nominal_target.Rebase(&observed_camera);
+ const Pose expected_target = nominal_target.Rebase(&expected_camera);
+ return Localizer::Corrector{
+ expected_robot_pose,
+ observed_camera,
+ expected_camera,
+ HeadingDistanceSkew(expected_target),
+ HeadingDistanceSkew(observed_target),
+ frc971::control_loops::drivetrain::HMatrixForCameraHeadingDistanceSkew(
+ nominal_target, observed_camera)};
+}
+
+Localizer::Corrector::Corrector(const State &state_at_capture,
+ const Transform &H_field_target,
+ const Transform &H_robot_camera,
+ const Transform &H_camera_target)
+ : Corrector(CalculateHeadingDistanceSkewH(
+ state_at_capture, H_field_target, H_robot_camera, H_camera_target)) {}
+
+Localizer::Output Localizer::Corrector::H(const State &, const Input &) {
+ return expected_ - observed_;
+}
+
+} // namespace y2024::localizer
diff --git a/y2024/localizer/localizer.h b/y2024/localizer/localizer.h
new file mode 100644
index 0000000..8235f13
--- /dev/null
+++ b/y2024/localizer/localizer.h
@@ -0,0 +1,158 @@
+#ifndef Y2024_LOCALIZER_LOCALIZER_H_
+#define Y2024_LOCALIZER_LOCALIZER_H_
+
+#include <array>
+#include <map>
+
+#include "aos/network/message_bridge_client_generated.h"
+#include "aos/network/message_bridge_server_generated.h"
+#include "frc971/constants/constants_sender_lib.h"
+#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
+#include "frc971/control_loops/drivetrain/improved_down_estimator.h"
+#include "frc971/control_loops/drivetrain/localization/localizer_output_generated.h"
+#include "frc971/control_loops/drivetrain/localization/utils.h"
+#include "frc971/imu_reader/imu_watcher.h"
+#include "frc971/vision/target_map_generated.h"
+#include "y2024/constants/constants_generated.h"
+#include "y2024/localizer/status_generated.h"
+#include "y2024/localizer/visualization_static.h"
+
+namespace y2024::localizer {
+
+class Localizer {
+ public:
+ static constexpr size_t kNumCameras = 4;
+ using Pose = frc971::control_loops::Pose;
+ typedef Eigen::Matrix<double, 4, 4> Transform;
+ typedef frc971::control_loops::drivetrain::HybridEkf<double> HybridEkf;
+ typedef HybridEkf::State State;
+ typedef HybridEkf::Output Output;
+ typedef HybridEkf::Input Input;
+ typedef HybridEkf::StateIdx StateIdx;
+ Localizer(aos::EventLoop *event_loop);
+
+ private:
+ class Corrector : public HybridEkf::ExpectedObservationFunctor {
+ public:
+ // Indices used for each of the members of the output vector for this
+ // Corrector.
+ enum OutputIdx {
+ kHeading = 0,
+ kDistance = 1,
+ kSkew = 2,
+ };
+ Corrector(const State &state_at_capture, const Transform &H_field_target,
+ const Transform &H_robot_camera,
+ const Transform &H_camera_target);
+
+ using HMatrix = Eigen::Matrix<double, Localizer::HybridEkf::kNOutputs,
+ Localizer::HybridEkf::kNStates>;
+
+ Output H(const State &, const Input &) final;
+ HMatrix DHDX(const State &) final { return H_; }
+ const Eigen::Vector3d &observed() const { return observed_; }
+ const Eigen::Vector3d &expected() const { return expected_; }
+ const Pose &expected_robot_pose() const { return expected_robot_pose_; }
+ const Pose &expected_camera_pose() const { return expected_camera_; }
+ const Pose &observed_camera_pose() const { return observed_camera_; }
+
+ static Eigen::Vector3d HeadingDistanceSkew(const Pose &relative_pose);
+
+ static Corrector CalculateHeadingDistanceSkewH(
+ const State &state_at_capture, const Transform &H_field_target,
+ const Transform &H_robot_camera, const Transform &H_camera_target);
+
+ static void PopulateMeasurement(const Eigen::Vector3d &vector,
+ MeasurementStatic *builder) {
+ builder->set_heading(vector(kHeading));
+ builder->set_distance(vector(kDistance));
+ builder->set_skew(vector(kSkew));
+ }
+
+ private:
+ Corrector(const Pose &expected_robot_pose, const Pose &observed_camera,
+ const Pose &expected_camera, const Eigen::Vector3d &expected,
+ const Eigen::Vector3d &observed, const HMatrix &H)
+ : expected_robot_pose_(expected_robot_pose),
+ observed_camera_(observed_camera),
+ expected_camera_(expected_camera),
+ expected_(expected),
+ observed_(observed),
+ H_(H) {}
+ // For debugging.
+ const Pose expected_robot_pose_;
+ const Pose observed_camera_;
+ const Pose expected_camera_;
+ // Actually used.
+ const Eigen::Vector3d expected_;
+ const Eigen::Vector3d observed_;
+ const HMatrix H_;
+ };
+
+ struct CameraState {
+ aos::Sender<VisualizationStatic> debug_sender;
+ Transform extrinsics = Transform::Zero();
+ aos::util::ArrayErrorCounter<RejectionReason, RejectionCount>
+ rejection_counter;
+ size_t total_candidate_targets = 0;
+ size_t total_accepted_targets = 0;
+ };
+
+ static std::array<CameraState, kNumCameras> MakeCameras(
+ const Constants &constants, aos::EventLoop *event_loop);
+ void HandleTarget(int camera_index,
+ const aos::monotonic_clock::time_point capture_time,
+ const frc971::vision::TargetPoseFbs &target,
+ TargetEstimateDebugStatic *debug_builder);
+ void HandleImu(aos::monotonic_clock::time_point sample_time_pico,
+ aos::monotonic_clock::time_point sample_time_pi,
+ std::optional<Eigen::Vector2d> encoders, Eigen::Vector3d gyro,
+ Eigen::Vector3d accel);
+ void RejectImage(int camera_index, RejectionReason reason,
+ TargetEstimateDebugStatic *builder);
+
+ void SendOutput();
+ static flatbuffers::Offset<frc971::control_loops::drivetrain::LocalizerState>
+ PopulateState(const State &X_hat, flatbuffers::FlatBufferBuilder *fbb);
+ flatbuffers::Offset<ImuStatus> PopulateImu(
+ flatbuffers::FlatBufferBuilder *fbb) const;
+ void SendStatus();
+ static flatbuffers::Offset<CumulativeStatistics> StatisticsForCamera(
+ const CameraState &camera, flatbuffers::FlatBufferBuilder *fbb);
+ static void StatisticsForCamera(const CameraState &camera,
+ CumulativeStatisticsStatic *builder);
+
+ bool UseAprilTag(uint64_t target_id);
+
+ aos::EventLoop *const event_loop_;
+ frc971::constants::ConstantsFetcher<Constants> constants_fetcher_;
+ const frc971::control_loops::drivetrain::DrivetrainConfig<double> dt_config_;
+ std::array<CameraState, kNumCameras> cameras_;
+ const std::array<Transform, kNumCameras> camera_extrinsics_;
+ const std::map<uint64_t, Transform> target_poses_;
+
+ frc971::control_loops::drivetrain::DrivetrainUkf down_estimator_;
+ HybridEkf ekf_;
+ HybridEkf::ExpectedObservationAllocator<Corrector> observations_;
+
+ frc971::controls::ImuWatcher imu_watcher_;
+ frc971::control_loops::drivetrain::LocalizationUtils utils_;
+
+ aos::Sender<Status> status_sender_;
+ aos::Sender<frc971::controls::LocalizerOutput> output_sender_;
+ aos::monotonic_clock::time_point t_ = aos::monotonic_clock::min_time;
+ size_t clock_resets_ = 0;
+
+ size_t total_candidate_targets_ = 0;
+ size_t total_accepted_targets_ = 0;
+
+ // For the status message.
+ std::optional<Eigen::Vector2d> last_encoder_readings_;
+
+ aos::Fetcher<aos::message_bridge::ServerStatistics>
+ server_statistics_fetcher_;
+ aos::Fetcher<aos::message_bridge::ClientStatistics>
+ client_statistics_fetcher_;
+};
+} // namespace y2024::localizer
+#endif // Y2024_LOCALIZER_LOCALIZER_H_
diff --git a/y2024/localizer/localizer_logger.cc b/y2024/localizer/localizer_logger.cc
new file mode 100644
index 0000000..ec8d3ac
--- /dev/null
+++ b/y2024/localizer/localizer_logger.cc
@@ -0,0 +1,80 @@
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+#include "aos/configuration.h"
+#include "aos/events/logging/log_writer.h"
+#include "aos/events/logging/snappy_encoder.h"
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "aos/logging/log_namer.h"
+
+DEFINE_string(config, "aos_config.json", "Config file to use.");
+
+DEFINE_double(rotate_every, 30.0,
+ "If set, rotate the logger after this many seconds");
+
+DECLARE_int32(flush_size);
+
+int main(int argc, char *argv[]) {
+ gflags::SetUsageMessage(
+ "This program provides a simple logger binary that logs all SHMEM data "
+ "directly to a file specified at the command line. It does not manage "
+ "filenames, so it will just crash if you attempt to overwrite an "
+ "existing file, and the user must specify the logfile manually at the "
+ "command line.");
+ aos::InitGoogle(&argc, &argv);
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(FLAGS_config);
+
+ aos::ShmEventLoop event_loop(&config.message());
+
+ auto log_namer = std::make_unique<aos::logger::MultiNodeFilesLogNamer>(
+ &event_loop,
+ std::make_unique<aos::logger::RenamableFileBackend>(
+ absl::StrCat(aos::logging::GetLogName("localizer_log"), "/"),
+ /*O_DIRECT*/ true));
+
+ log_namer->set_extension(aos::logger::SnappyDecoder::kExtension);
+ log_namer->set_encoder_factory([](size_t max_message_size) {
+ return std::make_unique<aos::logger::SnappyEncoder>(max_message_size,
+ FLAGS_flush_size);
+ });
+
+ aos::monotonic_clock::time_point last_rotation_time =
+ event_loop.monotonic_now();
+ aos::logger::Logger logger(
+ &event_loop, event_loop.configuration(),
+ // Only log channels smaller than ~10 MB / sec.
+ [](const aos::Channel *channel) {
+ return (channel->max_size() * channel->frequency()) < 10e6;
+ });
+
+ if (FLAGS_rotate_every != 0.0) {
+ logger.set_on_logged_period(
+ [&logger, &last_rotation_time](aos::monotonic_clock::time_point t) {
+ if (t > last_rotation_time +
+ std::chrono::duration<double>(FLAGS_rotate_every)) {
+ logger.Rotate();
+ last_rotation_time = t;
+ }
+ });
+ }
+
+ event_loop.OnRun([&log_namer, &logger]() {
+ errno = 0;
+ setpriority(PRIO_PROCESS, 0, -20);
+ PCHECK(errno == 0)
+ << ": Renicing to -20 failed, use --skip_renicing to skip renicing.";
+ logger.StartLogging(std::move(log_namer));
+ });
+
+ event_loop.Run();
+
+ LOG(INFO) << "Shutting down";
+
+ return 0;
+}
diff --git a/y2024/localizer/localizer_main.cc b/y2024/localizer/localizer_main.cc
new file mode 100644
index 0000000..25466d2
--- /dev/null
+++ b/y2024/localizer/localizer_main.cc
@@ -0,0 +1,23 @@
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "frc971/constants/constants_sender_lib.h"
+#include "y2024/control_loops/drivetrain/drivetrain_base.h"
+#include "y2024/localizer/localizer.h"
+
+DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
+
+int main(int argc, char *argv[]) {
+ aos::InitGoogle(&argc, &argv);
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(FLAGS_config);
+
+ frc971::constants::WaitForConstants<y2024::Constants>(&config.message());
+
+ aos::ShmEventLoop event_loop(&config.message());
+ y2024::localizer::Localizer localizer(&event_loop);
+
+ event_loop.Run();
+
+ return 0;
+}
diff --git a/y2024/localizer/localizer_plotter.ts b/y2024/localizer/localizer_plotter.ts
new file mode 100644
index 0000000..b5e764b
--- /dev/null
+++ b/y2024/localizer/localizer_plotter.ts
@@ -0,0 +1,203 @@
+// Provides a plot for debugging drivetrain-related issues.
+import {AosPlotter} from '../../aos/network/www/aos_plotter';
+import {ImuMessageHandler} from '../../frc971/wpilib/imu_plot_utils';
+import * as proxy from '../../aos/network/www/proxy';
+import {BLUE, BROWN, CYAN, GREEN, PINK, RED, WHITE} from '../../aos/network/www/colors';
+
+import Connection = proxy.Connection;
+
+const TIME = AosPlotter.TIME;
+const DEFAULT_WIDTH = AosPlotter.DEFAULT_WIDTH;
+const DEFAULT_HEIGHT = AosPlotter.DEFAULT_HEIGHT;
+
+export function plotLocalizer(conn: Connection, element: Element): void {
+ const aosPlotter = new AosPlotter(conn);
+
+ const position = aosPlotter.addMessageSource("/drivetrain",
+ "frc971.control_loops.drivetrain.Position");
+ const status = aosPlotter.addMessageSource(
+ '/drivetrain', 'frc971.control_loops.drivetrain.Status');
+ const output = aosPlotter.addMessageSource(
+ '/drivetrain', 'frc971.control_loops.drivetrain.Output');
+ const localizer = aosPlotter.addMessageSource(
+ '/localizer', 'y2024.localizer.Status');
+ const imu = aosPlotter.addRawMessageSource(
+ '/localizer', 'frc971.IMUValuesBatch',
+ new ImuMessageHandler(conn.getSchema('frc971.IMUValuesBatch')));
+
+ // Drivetrain Status estimated relative position
+ const positionPlot = aosPlotter.addPlot(element);
+ positionPlot.plot.getAxisLabels().setTitle("Estimated Relative Position " +
+ "of the Drivetrain");
+ positionPlot.plot.getAxisLabels().setXLabel(TIME);
+ positionPlot.plot.getAxisLabels().setYLabel("Relative Position (m)");
+ const leftPosition =
+ positionPlot.addMessageLine(status, ["estimated_left_position"]);
+ leftPosition.setColor(RED);
+ const rightPosition =
+ positionPlot.addMessageLine(status, ["estimated_right_position"]);
+ rightPosition.setColor(GREEN);
+ positionPlot.addMessageLine(position, ['left_encoder'])
+ .setColor(BROWN)
+ .setDrawLine(false);
+ positionPlot.addMessageLine(position, ['right_encoder'])
+ .setColor(CYAN)
+ .setDrawLine(false);
+
+
+ // Drivetrain Velocities
+ const velocityPlot = aosPlotter.addPlot(element);
+ velocityPlot.plot.getAxisLabels().setTitle('Velocity Plots');
+ velocityPlot.plot.getAxisLabels().setXLabel(TIME);
+ velocityPlot.plot.getAxisLabels().setYLabel('Wheel Velocity (m/s)');
+
+ const leftVelocity =
+ velocityPlot.addMessageLine(status, ['estimated_left_velocity']);
+ leftVelocity.setColor(RED);
+ const rightVelocity =
+ velocityPlot.addMessageLine(status, ['estimated_right_velocity']);
+ rightVelocity.setColor(GREEN);
+
+ const leftSpeed = velocityPlot.addMessageLine(position, ["left_speed"]);
+ leftSpeed.setColor(BLUE);
+ const rightSpeed = velocityPlot.addMessageLine(position, ["right_speed"]);
+ rightSpeed.setColor(BROWN);
+
+ const ekfLeftVelocity = velocityPlot.addMessageLine(
+ localizer, ['state', 'left_velocity']);
+ ekfLeftVelocity.setColor(RED);
+ ekfLeftVelocity.setPointSize(0.0);
+ const ekfRightVelocity = velocityPlot.addMessageLine(
+ localizer, ['state', 'right_velocity']);
+ ekfRightVelocity.setColor(GREEN);
+ ekfRightVelocity.setPointSize(0.0);
+
+ // Lateral velocity
+ const lateralPlot = aosPlotter.addPlot(element);
+ lateralPlot.plot.getAxisLabels().setTitle('Lateral Velocity');
+ lateralPlot.plot.getAxisLabels().setXLabel(TIME);
+ lateralPlot.plot.getAxisLabels().setYLabel('Velocity (m/s)');
+
+ lateralPlot.addMessageLine(localizer, ['state', 'lateral_velocity']).setColor(CYAN);
+
+ // Drivetrain Voltage
+ const voltagePlot = aosPlotter.addPlot(element);
+ voltagePlot.plot.getAxisLabels().setTitle('Voltage Plots');
+ voltagePlot.plot.getAxisLabels().setXLabel(TIME);
+ voltagePlot.plot.getAxisLabels().setYLabel('Voltage (V)')
+
+ voltagePlot.addMessageLine(localizer, ['state', 'left_voltage_error'])
+ .setColor(RED)
+ .setDrawLine(false);
+ voltagePlot.addMessageLine(localizer, ['state', 'right_voltage_error'])
+ .setColor(GREEN)
+ .setDrawLine(false);
+ voltagePlot.addMessageLine(output, ['left_voltage'])
+ .setColor(RED)
+ .setPointSize(0);
+ voltagePlot.addMessageLine(output, ['right_voltage'])
+ .setColor(GREEN)
+ .setPointSize(0);
+
+ // Heading
+ const yawPlot = aosPlotter.addPlot(element);
+ yawPlot.plot.getAxisLabels().setTitle('Robot Yaw');
+ yawPlot.plot.getAxisLabels().setXLabel(TIME);
+ yawPlot.plot.getAxisLabels().setYLabel('Yaw (rad)');
+
+ yawPlot.addMessageLine(status, ['localizer', 'theta']).setColor(GREEN);
+
+ yawPlot.addMessageLine(localizer, ['down_estimator', 'yaw']).setColor(BLUE);
+
+ yawPlot.addMessageLine(localizer, ['state', 'theta']).setColor(RED);
+
+ // Pitch/Roll
+ const orientationPlot = aosPlotter.addPlot(element);
+ orientationPlot.plot.getAxisLabels().setTitle('Orientation');
+ orientationPlot.plot.getAxisLabels().setXLabel(TIME);
+ orientationPlot.plot.getAxisLabels().setYLabel('Angle (rad)');
+
+ orientationPlot.addMessageLine(localizer, ['down_estimator', 'lateral_pitch'])
+ .setColor(RED)
+ .setLabel('roll');
+ orientationPlot
+ .addMessageLine(localizer, ['down_estimator', 'longitudinal_pitch'])
+ .setColor(GREEN)
+ .setLabel('pitch');
+
+ const stillPlot = aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT / 3]);
+ stillPlot.plot.getAxisLabels().setTitle('Still Plot');
+ stillPlot.plot.getAxisLabels().setXLabel(TIME);
+ stillPlot.plot.getAxisLabels().setYLabel('bool, g\'s');
+ stillPlot.plot.setDefaultYRange([-0.1, 1.1]);
+
+ stillPlot.addMessageLine(localizer, ['down_estimator', 'gravity_magnitude'])
+ .setColor(WHITE)
+ .setDrawLine(false);
+
+ // Absolute X Position
+ const xPositionPlot = aosPlotter.addPlot(element);
+ xPositionPlot.plot.getAxisLabels().setTitle('X Position');
+ xPositionPlot.plot.getAxisLabels().setXLabel(TIME);
+ xPositionPlot.plot.getAxisLabels().setYLabel('X Position (m)');
+
+ xPositionPlot.addMessageLine(status, ['x']).setColor(RED);
+ xPositionPlot.addMessageLine(localizer, ['down_estimator', 'position_x'])
+ .setColor(BLUE);
+ xPositionPlot.addMessageLine(localizer, ['state', 'x']).setColor(CYAN);
+
+ // Absolute Y Position
+ const yPositionPlot = aosPlotter.addPlot(element);
+ yPositionPlot.plot.getAxisLabels().setTitle('Y Position');
+ yPositionPlot.plot.getAxisLabels().setXLabel(TIME);
+ yPositionPlot.plot.getAxisLabels().setYLabel('Y Position (m)');
+
+ const localizerY = yPositionPlot.addMessageLine(status, ['y']);
+ localizerY.setColor(RED);
+ yPositionPlot.addMessageLine(localizer, ['down_estimator', 'position_y'])
+ .setColor(BLUE);
+ yPositionPlot.addMessageLine(localizer, ['state', 'y']).setColor(CYAN);
+
+ // Gyro
+ const gyroPlot = aosPlotter.addPlot(element);
+ gyroPlot.plot.getAxisLabels().setTitle('Gyro Readings');
+ gyroPlot.plot.getAxisLabels().setYLabel('Angular Velocity (rad / sec)');
+ gyroPlot.plot.getAxisLabels().setXLabel('Monotonic Reading Time (sec)');
+
+ const gyroX = gyroPlot.addMessageLine(imu, ['gyro_x']);
+ gyroX.setColor(RED);
+ const gyroY = gyroPlot.addMessageLine(imu, ['gyro_y']);
+ gyroY.setColor(GREEN);
+ const gyroZ = gyroPlot.addMessageLine(imu, ['gyro_z']);
+ gyroZ.setColor(BLUE);
+
+
+ const timingPlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ timingPlot.plot.getAxisLabels().setTitle('Fault Counting');
+ timingPlot.plot.getAxisLabels().setXLabel(TIME);
+
+ timingPlot
+ .addMessageLine(
+ localizer, ['imu', 'imu_failures', 'imu_to_pico_checksum_mismatch'])
+ .setColor(BLUE)
+ .setDrawLine(false);
+
+ timingPlot
+ .addMessageLine(
+ localizer, ['imu', 'imu_failures', 'pico_to_pi_checksum_mismatch'])
+ .setColor(RED)
+ .setDrawLine(false);
+
+ timingPlot
+ .addMessageLine(
+ localizer, ['imu', 'imu_failures', 'other_zeroing_faults'])
+ .setColor(CYAN)
+ .setDrawLine(false);
+
+ timingPlot
+ .addMessageLine(
+ localizer, ['imu', 'imu_failures', 'missed_messages'])
+ .setColor(PINK)
+ .setDrawLine(false);
+}
diff --git a/y2024/localizer/localizer_replay.cc b/y2024/localizer/localizer_replay.cc
new file mode 100644
index 0000000..c2c4c16
--- /dev/null
+++ b/y2024/localizer/localizer_replay.cc
@@ -0,0 +1,68 @@
+#include "gflags/gflags.h"
+
+#include "aos/configuration.h"
+#include "aos/events/logging/log_reader.h"
+#include "aos/events/logging/log_writer.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/init.h"
+#include "aos/json_to_flatbuffer.h"
+#include "aos/network/team_number.h"
+#include "aos/util/simulation_logger.h"
+#include "y2024/control_loops/drivetrain/drivetrain_base.h"
+#include "y2024/localizer/localizer.h"
+
+DEFINE_string(config, "y2024/aos_config.json",
+ "Name of the config file to replay using.");
+DEFINE_int32(team, 9971, "Team number to use for logfile replay.");
+DEFINE_string(output_folder, "/tmp/replayed",
+ "Name of the folder to write replayed logs to.");
+
+int main(int argc, char **argv) {
+ aos::InitGoogle(&argc, &argv);
+
+ aos::network::OverrideTeamNumber(FLAGS_team);
+
+ const aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(FLAGS_config);
+
+ // sort logfiles
+ const std::vector<aos::logger::LogFile> logfiles =
+ aos::logger::SortParts(aos::logger::FindLogs(argc, argv));
+
+ // open logfiles
+ aos::logger::LogReader reader(logfiles, &config.message());
+
+ reader.RemapLoggedChannel("/localizer", "y2024.localizer.Status");
+ for (const auto orin : {"orin1", "imu"}) {
+ for (const auto camera : {"camera0", "camera1"}) {
+ reader.RemapLoggedChannel(absl::StrCat("/", orin, "/", camera),
+ "y2024.localizer.Visualization");
+ }
+ }
+ reader.RemapLoggedChannel("/localizer", "frc971.controls.LocalizerOutput");
+
+ auto factory =
+ std::make_unique<aos::SimulatedEventLoopFactory>(reader.configuration());
+
+ reader.RegisterWithoutStarting(factory.get());
+
+ const aos::Node *node = nullptr;
+ if (aos::configuration::MultiNode(reader.configuration())) {
+ node = aos::configuration::GetNode(reader.configuration(), "imu");
+ }
+ std::vector<std::unique_ptr<aos::util::LoggerState>> loggers;
+
+ reader.OnStart(node, [&factory, node, &loggers]() {
+ aos::NodeEventLoopFactory *node_factory =
+ factory->GetNodeEventLoopFactory(node);
+ node_factory->AlwaysStart<y2024::localizer::Localizer>("localizer");
+ loggers.push_back(std::make_unique<aos::util::LoggerState>(
+ factory.get(), node, FLAGS_output_folder));
+ });
+
+ reader.event_loop_factory()->Run();
+
+ reader.Deregister();
+
+ return 0;
+}
diff --git a/y2024/localizer/localizer_test.cc b/y2024/localizer/localizer_test.cc
new file mode 100644
index 0000000..a2fc200
--- /dev/null
+++ b/y2024/localizer/localizer_test.cc
@@ -0,0 +1,569 @@
+#include "y2024/localizer/localizer.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/events/logging/log_writer.h"
+#include "aos/events/simulated_event_loop.h"
+#include "frc971/control_loops/drivetrain/drivetrain_test_lib.h"
+#include "frc971/control_loops/drivetrain/localizer_generated.h"
+#include "frc971/control_loops/drivetrain/rio_localizer_inputs_static.h"
+#include "frc971/control_loops/pose.h"
+#include "frc971/vision/target_map_generated.h"
+#include "frc971/vision/target_map_utils.h"
+#include "y2024/constants/simulated_constants_sender.h"
+#include "y2024/control_loops/drivetrain/drivetrain_base.h"
+#include "y2024/localizer/status_generated.h"
+
+DEFINE_string(output_folder, "",
+ "If set, logs all channels to the provided logfile.");
+DECLARE_double(max_distance_to_target);
+
+namespace y2024::localizer::testing {
+
+using frc971::control_loops::drivetrain::Output;
+
+class LocalizerTest : public ::testing::Test {
+ protected:
+ static constexpr uint64_t kTargetId = 1;
+ LocalizerTest()
+ : configuration_(aos::configuration::ReadConfig("y2024/aos_config.json")),
+ event_loop_factory_(&configuration_.message()),
+ roborio_node_([this]() {
+ // Get the constants sent before anything else happens.
+ // It has nothing to do with the roborio node.
+ SendSimulationConstants(&event_loop_factory_, 7971,
+ "y2024/constants/test_constants.json");
+ return aos::configuration::GetNode(&configuration_.message(),
+ "roborio");
+ }()),
+ imu_node_(
+ aos::configuration::GetNode(&configuration_.message(), "imu")),
+ camera_node_(
+ aos::configuration::GetNode(&configuration_.message(), "orin1")),
+ roborio_test_event_loop_(
+ event_loop_factory_.MakeEventLoop("test", roborio_node_)),
+ dt_config_(y2024::control_loops::drivetrain::GetDrivetrainConfig(
+ roborio_test_event_loop_.get())),
+ localizer_event_loop_(
+ event_loop_factory_.MakeEventLoop("localizer", imu_node_)),
+ localizer_(localizer_event_loop_.get()),
+ drivetrain_plant_event_loop_(event_loop_factory_.MakeEventLoop(
+ "drivetrain_plant", roborio_node_)),
+ drivetrain_plant_imu_event_loop_(
+ event_loop_factory_.MakeEventLoop("drivetrain_plant", imu_node_)),
+ drivetrain_plant_(drivetrain_plant_event_loop_.get(),
+ drivetrain_plant_imu_event_loop_.get(), dt_config_,
+ std::chrono::microseconds(1000)),
+ imu_test_event_loop_(
+ event_loop_factory_.MakeEventLoop("test", imu_node_)),
+ camera_test_event_loop_(
+ event_loop_factory_.MakeEventLoop("test", camera_node_)),
+ constants_fetcher_(imu_test_event_loop_.get()),
+ output_sender_(
+ roborio_test_event_loop_->MakeSender<Output>("/drivetrain")),
+ combined_sender_(
+ roborio_test_event_loop_->MakeSender<
+ frc971::control_loops::drivetrain::RioLocalizerInputsStatic>(
+ "/drivetrain")),
+ target_sender_(
+ camera_test_event_loop_->MakeSender<frc971::vision::TargetMap>(
+ "/camera0")),
+ control_sender_(roborio_test_event_loop_->MakeSender<
+ frc971::control_loops::drivetrain::LocalizerControl>(
+ "/drivetrain")),
+ output_fetcher_(
+ roborio_test_event_loop_
+ ->MakeFetcher<frc971::controls::LocalizerOutput>("/localizer")),
+ status_fetcher_(
+ imu_test_event_loop_->MakeFetcher<Status>("/localizer")) {
+ FLAGS_max_distance_to_target = 100.0;
+ {
+ aos::TimerHandler *timer = roborio_test_event_loop_->AddTimer([this]() {
+ {
+ auto builder = output_sender_.MakeBuilder();
+ auto output_builder = builder.MakeBuilder<Output>();
+ output_builder.add_left_voltage(output_voltages_(0));
+ output_builder.add_right_voltage(output_voltages_(1));
+ builder.CheckOk(builder.Send(output_builder.Finish()));
+ }
+ {
+ auto builder = combined_sender_.MakeStaticBuilder();
+ builder->set_left_voltage(output_voltages_(0));
+ builder->set_right_voltage(output_voltages_(1));
+ builder->set_left_encoder(drivetrain_plant_.GetLeftPosition());
+ builder->set_right_encoder(drivetrain_plant_.GetRightPosition());
+ builder.CheckOk(builder.Send());
+ }
+ });
+ roborio_test_event_loop_->OnRun([timer, this]() {
+ timer->Schedule(roborio_test_event_loop_->monotonic_now(),
+ std::chrono::milliseconds(5));
+ });
+ }
+ {
+ // Sanity check that the test calibration files look like what we
+ // expect.
+ CHECK_EQ("orin1", constants_fetcher_.constants()
+ .cameras()
+ ->Get(0)
+ ->calibration()
+ ->node_name()
+ ->string_view());
+ CHECK_EQ(0, constants_fetcher_.constants()
+ .cameras()
+ ->Get(0)
+ ->calibration()
+ ->camera_number());
+ const Eigen::Matrix<double, 4, 4> H_robot_camera =
+ frc971::control_loops::drivetrain::FlatbufferToTransformationMatrix(
+ *constants_fetcher_.constants()
+ .cameras()
+ ->Get(0)
+ ->calibration()
+ ->fixed_extrinsics());
+
+ CHECK(constants_fetcher_.constants().common()->has_target_map());
+ CHECK(constants_fetcher_.constants()
+ .common()
+ ->target_map()
+ ->has_target_poses());
+ CHECK_LE(1u, constants_fetcher_.constants()
+ .common()
+ ->target_map()
+ ->target_poses()
+ ->size());
+ CHECK_EQ(kTargetId, constants_fetcher_.constants()
+ .common()
+ ->target_map()
+ ->target_poses()
+ ->Get(0)
+ ->id());
+ const Eigen::Matrix<double, 4, 4> H_field_target =
+ PoseToTransform(constants_fetcher_.constants()
+ .common()
+ ->target_map()
+ ->target_poses()
+ ->Get(0));
+ // For reference, the camera should pointed straight forwards on the
+ // robot, offset by 1 meter.
+ aos::TimerHandler *timer = camera_test_event_loop_->AddTimer(
+ [this, H_robot_camera, H_field_target]() {
+ if (!send_targets_) {
+ return;
+ }
+ const frc971::control_loops::Pose robot_pose(
+ {drivetrain_plant_.GetPosition().x(),
+ drivetrain_plant_.GetPosition().y(), 0.0},
+ drivetrain_plant_.state()(2, 0) + implied_yaw_error_);
+
+ const Eigen::Matrix<double, 4, 4> H_field_camera =
+ robot_pose.AsTransformationMatrix() * H_robot_camera;
+ const Eigen::Matrix<double, 4, 4> H_camera_target =
+ H_field_camera.inverse() * H_field_target;
+
+ Eigen::Quaterniond quat(H_camera_target.block<3, 3>(0, 0));
+ quat.normalize();
+ const Eigen::Vector3d translation(
+ H_camera_target.block<3, 1>(0, 3));
+
+ auto builder = target_sender_.MakeBuilder();
+ frc971::vision::Quaternion::Builder quat_builder(*builder.fbb());
+ quat_builder.add_w(quat.w());
+ quat_builder.add_x(quat.x());
+ quat_builder.add_y(quat.y());
+ quat_builder.add_z(quat.z());
+ auto quat_offset = quat_builder.Finish();
+ frc971::vision::Position::Builder position_builder(*builder.fbb());
+ position_builder.add_x(translation.x());
+ position_builder.add_y(translation.y());
+ position_builder.add_z(translation.z());
+ auto position_offset = position_builder.Finish();
+
+ frc971::vision::TargetPoseFbs::Builder target_builder(
+ *builder.fbb());
+ target_builder.add_id(send_target_id_);
+ target_builder.add_position(position_offset);
+ target_builder.add_orientation(quat_offset);
+ target_builder.add_pose_error(pose_error_);
+ target_builder.add_pose_error_ratio(pose_error_ratio_);
+ auto target_offset = target_builder.Finish();
+
+ auto targets_offset = builder.fbb()->CreateVector({target_offset});
+ frc971::vision::TargetMap::Builder map_builder(*builder.fbb());
+ map_builder.add_target_poses(targets_offset);
+ map_builder.add_monotonic_timestamp_ns(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(
+ camera_test_event_loop_->monotonic_now().time_since_epoch())
+ .count());
+
+ builder.CheckOk(builder.Send(map_builder.Finish()));
+ });
+ camera_test_event_loop_->OnRun([timer, this]() {
+ timer->Schedule(camera_test_event_loop_->monotonic_now(),
+ std::chrono::milliseconds(50));
+ });
+ }
+
+ localizer_control_send_timer_ =
+ roborio_test_event_loop_->AddTimer([this]() {
+ auto builder = control_sender_.MakeBuilder();
+ auto control_builder = builder.MakeBuilder<
+ frc971::control_loops::drivetrain::LocalizerControl>();
+ control_builder.add_x(localizer_control_x_);
+ control_builder.add_y(localizer_control_y_);
+ control_builder.add_theta(localizer_control_theta_);
+ control_builder.add_theta_uncertainty(0.01);
+ control_builder.add_keep_current_theta(false);
+ builder.CheckOk(builder.Send(control_builder.Finish()));
+ });
+
+ // Get things zeroed.
+ event_loop_factory_.RunFor(std::chrono::seconds(10));
+ CHECK(status_fetcher_.Fetch());
+ CHECK(status_fetcher_->imu()->zeroed());
+
+ if (!FLAGS_output_folder.empty()) {
+ logger_event_loop_ =
+ event_loop_factory_.MakeEventLoop("logger", imu_node_);
+ logger_ = std::make_unique<aos::logger::Logger>(logger_event_loop_.get());
+ logger_->StartLoggingOnRun(FLAGS_output_folder);
+ }
+ }
+
+ void SendLocalizerControl(double x, double y, double theta) {
+ localizer_control_x_ = x;
+ localizer_control_y_ = y;
+ localizer_control_theta_ = theta;
+ localizer_control_send_timer_->Schedule(
+ roborio_test_event_loop_->monotonic_now());
+ }
+
+ ::testing::AssertionResult IsNear(double expected, double actual,
+ double epsilon) {
+ if (std::abs(expected - actual) < epsilon) {
+ return ::testing::AssertionSuccess();
+ } else {
+ return ::testing::AssertionFailure()
+ << "Expected " << expected << " but got " << actual
+ << " with a max difference of " << epsilon
+ << " and an actual difference of " << std::abs(expected - actual);
+ }
+ }
+ ::testing::AssertionResult VerifyEstimatorAccurate(double eps) {
+ const Eigen::Matrix<double, 5, 1> true_state = drivetrain_plant_.state();
+ ::testing::AssertionResult result(true);
+ status_fetcher_.Fetch();
+ if (!(result = IsNear(status_fetcher_->state()->x(), true_state(0), eps))) {
+ return result;
+ }
+ if (!(result = IsNear(status_fetcher_->state()->y(), true_state(1), eps))) {
+ return result;
+ }
+ if (!(result =
+ IsNear(status_fetcher_->state()->theta(), true_state(2), eps))) {
+ return result;
+ }
+ return result;
+ }
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> configuration_;
+ aos::SimulatedEventLoopFactory event_loop_factory_;
+ const aos::Node *const roborio_node_;
+ const aos::Node *const imu_node_;
+ const aos::Node *const camera_node_;
+ std::unique_ptr<aos::EventLoop> roborio_test_event_loop_;
+ const frc971::control_loops::drivetrain::DrivetrainConfig<double> dt_config_;
+ std::unique_ptr<aos::EventLoop> localizer_event_loop_;
+ Localizer localizer_;
+
+ std::unique_ptr<aos::EventLoop> drivetrain_plant_event_loop_;
+ std::unique_ptr<aos::EventLoop> drivetrain_plant_imu_event_loop_;
+ frc971::control_loops::drivetrain::testing::DrivetrainSimulation
+ drivetrain_plant_;
+
+ std::unique_ptr<aos::EventLoop> imu_test_event_loop_;
+ std::unique_ptr<aos::EventLoop> camera_test_event_loop_;
+
+ frc971::constants::ConstantsFetcher<Constants> constants_fetcher_;
+
+ aos::Sender<Output> output_sender_;
+ aos::Sender<frc971::control_loops::drivetrain::RioLocalizerInputsStatic>
+ combined_sender_;
+ aos::Sender<frc971::vision::TargetMap> target_sender_;
+ aos::Sender<frc971::control_loops::drivetrain::LocalizerControl>
+ control_sender_;
+ aos::Fetcher<frc971::controls::LocalizerOutput> output_fetcher_;
+ aos::Fetcher<Status> status_fetcher_;
+
+ Eigen::Vector2d output_voltages_ = Eigen::Vector2d::Zero();
+
+ aos::TimerHandler *localizer_control_send_timer_;
+
+ bool send_targets_ = false;
+
+ double localizer_control_x_ = 0.0;
+ double localizer_control_y_ = 0.0;
+ double localizer_control_theta_ = 0.0;
+
+ std::unique_ptr<aos::EventLoop> logger_event_loop_;
+ std::unique_ptr<aos::logger::Logger> logger_;
+
+ uint64_t send_target_id_ = kTargetId;
+ double pose_error_ = 1e-7;
+ double pose_error_ratio_ = 0.1;
+ double implied_yaw_error_ = 0.0;
+
+ gflags::FlagSaver flag_saver_;
+};
+
+// Test a simple scenario with no errors where the robot should just drive
+// straight forwards.
+TEST_F(LocalizerTest, Nominal) {
+ output_voltages_ << 1.0, 1.0;
+ event_loop_factory_.RunFor(std::chrono::seconds(2));
+ CHECK(output_fetcher_.Fetch());
+ CHECK(status_fetcher_.Fetch());
+ // The two can be different because they may've been sent at different
+ // times.
+ EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 1e-2);
+ EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 1e-6);
+ EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
+ 1e-6);
+ // Confirm that we did indeed drive forwards (and straight), as expected.
+ EXPECT_LT(0.1, output_fetcher_->x());
+ EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
+ EXPECT_NEAR(0.0, output_fetcher_->theta(), 1e-10);
+ EXPECT_NEAR(0.0, status_fetcher_->state()->left_voltage_error(), 1e-1);
+ EXPECT_NEAR(0.0, status_fetcher_->state()->right_voltage_error(), 1e-1);
+
+ // And check that we actually think that we are near where the simulator
+ // says we are.
+ EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
+}
+
+// Confirm that when the robot drives backwards that we localize correctly.
+TEST_F(LocalizerTest, NominalReverse) {
+ output_voltages_ << -1.0, -1.0;
+ event_loop_factory_.RunFor(std::chrono::seconds(2));
+ CHECK(output_fetcher_.Fetch());
+ CHECK(status_fetcher_.Fetch());
+ // The two can be different because they may've been sent at different
+ // times.
+ EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 1e-2);
+ EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 1e-6);
+ EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
+ 1e-6);
+ // Confirm that we did indeed drive backwards (and straight), as expected.
+ EXPECT_GT(-0.1, output_fetcher_->x());
+ EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
+ EXPECT_NEAR(0.0, output_fetcher_->theta(), 1e-10);
+ EXPECT_NEAR(0.0, status_fetcher_->state()->left_voltage_error(), 1e-1);
+ EXPECT_NEAR(0.0, status_fetcher_->state()->right_voltage_error(), 1e-1);
+
+ // And check that we actually think that we are near where the simulator
+ // says we are.
+ EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
+}
+
+// Confirm that when the robot turns counter-clockwise that we localize
+// correctly.
+TEST_F(LocalizerTest, NominalSpinInPlace) {
+ output_voltages_ << -1.0, 1.0;
+ // Go 1 ms over 2 sec to make sure we actually see relatively recent messages
+ // on each channel.
+ event_loop_factory_.RunFor(std::chrono::milliseconds(2001));
+ CHECK(output_fetcher_.Fetch());
+ CHECK(status_fetcher_.Fetch());
+ // The two can be different because they may've been sent at different
+ // times.
+ EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 1e-6);
+ EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 1e-6);
+ EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
+ 1e-2);
+ // Confirm that we did indeed turn counter-clockwise.
+ EXPECT_NEAR(0.0, output_fetcher_->x(), 1e-10);
+ EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
+ EXPECT_LT(0.1, output_fetcher_->theta());
+ EXPECT_NEAR(0.0, status_fetcher_->state()->left_voltage_error(), 1e-1);
+ EXPECT_NEAR(0.0, status_fetcher_->state()->right_voltage_error(), 1e-1);
+
+ // And check that we actually think that we are near where the simulator
+ // says we are.
+ EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
+}
+
+// Confirm that when the robot drives in a curve that we localize
+// successfully.
+TEST_F(LocalizerTest, NominalCurve) {
+ output_voltages_ << 2.0, 3.0;
+ event_loop_factory_.RunFor(std::chrono::seconds(4));
+ CHECK(output_fetcher_.Fetch());
+ CHECK(status_fetcher_.Fetch());
+ // The two can be different because they may've been sent at different
+ // times.
+ EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 2e-2);
+ EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 2e-2);
+ EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
+ 2e-2);
+ // Confirm that we did indeed drive in a rough, counter-clockwise, curve.
+ EXPECT_LT(0.1, output_fetcher_->x());
+ EXPECT_LT(0.1, output_fetcher_->y());
+ EXPECT_LT(0.1, output_fetcher_->theta());
+
+ // And check that we actually think that we are near where the simulator
+ // says we are.
+ EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
+}
+
+// Tests that, in the presence of a non-zero voltage error, that we correct
+// for it.
+TEST_F(LocalizerTest, VoltageErrorDisabled) {
+ output_voltages_ << 0.0, 0.0;
+ drivetrain_plant_.set_left_voltage_offset(2.0);
+ drivetrain_plant_.set_right_voltage_offset(2.0);
+
+ event_loop_factory_.RunFor(std::chrono::seconds(2));
+ CHECK(output_fetcher_.Fetch());
+ CHECK(status_fetcher_.Fetch());
+ // We should've just ended up driving straight forwards.
+ EXPECT_LT(0.1, output_fetcher_->x());
+ EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
+ EXPECT_NEAR(0.0, output_fetcher_->theta(), 1e-10);
+ EXPECT_NEAR(2.0, status_fetcher_->state()->left_voltage_error(), 1.0);
+ EXPECT_NEAR(2.0, status_fetcher_->state()->right_voltage_error(), 1.0);
+
+ // And check that we actually think that we are near where the simulator
+ // says we are.
+ EXPECT_TRUE(VerifyEstimatorAccurate(0.05));
+}
+
+// Tests that image corrections in the nominal case (no errors) causes no
+// issues.
+TEST_F(LocalizerTest, NominalImageCorrections) {
+ output_voltages_ << 3.0, 2.0;
+ send_targets_ = true;
+
+ event_loop_factory_.RunFor(std::chrono::seconds(4));
+ CHECK(status_fetcher_.Fetch());
+ EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
+ ASSERT_TRUE(status_fetcher_->has_statistics());
+ ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
+ ASSERT_EQ(status_fetcher_->statistics()->Get(0)->total_candidates(),
+ status_fetcher_->statistics()->Get(0)->total_accepted());
+ ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
+}
+
+// Tests that image corrections when there is an error at the start results
+// in us actually getting corrected over time.
+TEST_F(LocalizerTest, ImageCorrections) {
+ output_voltages_ << 0.0, 0.0;
+ // Put ourselves somewhat near the target so that we don't ignore its
+ // corrections too much.
+ drivetrain_plant_.mutable_state()->x() = 3.0;
+ drivetrain_plant_.mutable_state()->y() = -2.0;
+ SendLocalizerControl(5.0, 0.0, 0.0);
+ event_loop_factory_.RunFor(std::chrono::seconds(4));
+ ASSERT_TRUE(output_fetcher_.Fetch());
+ EXPECT_NEAR(5.0, output_fetcher_->x(), 1e-5);
+ EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-5);
+ EXPECT_NEAR(0.0, output_fetcher_->theta(), 1e-5);
+
+ send_targets_ = true;
+
+ event_loop_factory_.RunFor(std::chrono::seconds(10));
+ CHECK(status_fetcher_.Fetch());
+ EXPECT_TRUE(VerifyEstimatorAccurate(0.1));
+ ASSERT_TRUE(status_fetcher_->has_statistics());
+ ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
+ ASSERT_EQ(status_fetcher_->statistics()->Get(0)->total_candidates(),
+ status_fetcher_->statistics()->Get(0)->total_accepted());
+ ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
+}
+
+// Tests that we correctly reject an invalid target.
+TEST_F(LocalizerTest, InvalidTargetId) {
+ output_voltages_ << 0.0, 0.0;
+ send_targets_ = true;
+ send_target_id_ = 100;
+
+ event_loop_factory_.RunFor(std::chrono::seconds(4));
+ CHECK(status_fetcher_.Fetch());
+ ASSERT_TRUE(status_fetcher_->has_statistics());
+ ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
+ ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
+ ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
+ ASSERT_EQ(status_fetcher_->statistics()
+ ->Get(0)
+ ->rejection_reasons()
+ ->Get(static_cast<size_t>(RejectionReason::NO_SUCH_TARGET))
+ ->count(),
+ status_fetcher_->statistics()->Get(0)->total_candidates());
+}
+
+// Tests that we correctly reject a detection with a high pose error.
+TEST_F(LocalizerTest, HighPoseError) {
+ output_voltages_ << 0.0, 0.0;
+ send_targets_ = true;
+ // Send the minimum pose error to be rejected
+ constexpr double kEps = 1e-9;
+ pose_error_ = 1e-6 + kEps;
+
+ event_loop_factory_.RunFor(std::chrono::seconds(4));
+ CHECK(status_fetcher_.Fetch());
+ ASSERT_TRUE(status_fetcher_->has_statistics());
+ ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
+ ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
+ ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
+ ASSERT_EQ(status_fetcher_->statistics()
+ ->Get(0)
+ ->rejection_reasons()
+ ->Get(static_cast<size_t>(RejectionReason::HIGH_POSE_ERROR))
+ ->count(),
+ status_fetcher_->statistics()->Get(0)->total_candidates());
+}
+
+// Tests that we correctly reject a detection with a high implied yaw error.
+TEST_F(LocalizerTest, HighImpliedYawError) {
+ output_voltages_ << 0.0, 0.0;
+ send_targets_ = true;
+ implied_yaw_error_ = 31.0 * M_PI / 180.0;
+
+ event_loop_factory_.RunFor(std::chrono::seconds(4));
+ CHECK(status_fetcher_.Fetch());
+ ASSERT_TRUE(status_fetcher_->has_statistics());
+ ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
+ ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
+ ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
+ ASSERT_EQ(
+ status_fetcher_->statistics()
+ ->Get(0)
+ ->rejection_reasons()
+ ->Get(static_cast<size_t>(RejectionReason::HIGH_IMPLIED_YAW_ERROR))
+ ->count(),
+ status_fetcher_->statistics()->Get(0)->total_candidates());
+}
+
+// Tests that we correctly reject a detection with a high pose error ratio.
+TEST_F(LocalizerTest, HighPoseErrorRatio) {
+ output_voltages_ << 0.0, 0.0;
+ send_targets_ = true;
+ // Send the minimum pose error to be rejected
+ constexpr double kEps = 1e-9;
+ pose_error_ratio_ = 0.4 + kEps;
+
+ event_loop_factory_.RunFor(std::chrono::seconds(4));
+ CHECK(status_fetcher_.Fetch());
+ ASSERT_TRUE(status_fetcher_->has_statistics());
+ ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
+ ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
+ ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
+ ASSERT_EQ(
+ status_fetcher_->statistics()
+ ->Get(0)
+ ->rejection_reasons()
+ ->Get(static_cast<size_t>(RejectionReason::HIGH_POSE_ERROR_RATIO))
+ ->count(),
+ status_fetcher_->statistics()->Get(0)->total_candidates());
+}
+
+} // namespace y2024::localizer::testing
diff --git a/y2024/localizer/status.fbs b/y2024/localizer/status.fbs
new file mode 100644
index 0000000..ad2ac72
--- /dev/null
+++ b/y2024/localizer/status.fbs
@@ -0,0 +1,73 @@
+include "frc971/control_loops/drivetrain/drivetrain_status.fbs";
+include "frc971/imu_reader/imu_failures.fbs";
+
+namespace y2024.localizer;
+
+attribute "static_length";
+
+enum RejectionReason : uint8 {
+ // For some reason, the image timestamp indicates that the image was taken
+ // in the future.
+ IMAGE_FROM_FUTURE = 0,
+ // The image was too old for the buffer of old state estimates that we
+ // maintain.
+ IMAGE_TOO_OLD = 1,
+ // Message bridge is not yet connected, and so we can't get accurate
+ // time offsets betwee nnodes.
+ MESSAGE_BRIDGE_DISCONNECTED = 2,
+ // The target ID does not exist.
+ NO_SUCH_TARGET = 3,
+ // Pose estimation error was higher than any normal detection.
+ HIGH_POSE_ERROR = 4,
+ // Pose estimate implied a robot yaw far off from our estimate.
+ HIGH_IMPLIED_YAW_ERROR = 5,
+ // Pose estimate had a high distance to target.
+ // We don't trust estimates very far out.
+ HIGH_DISTANCE_TO_TARGET = 6,
+ // The robot was travelling too fast; we don't trust the target.
+ ROBOT_TOO_FAST = 7,
+ // Pose estimation error ratio was higher than any normal detection.
+ HIGH_POSE_ERROR_RATIO = 8,
+}
+
+table RejectionCount {
+ error:RejectionReason (id: 0);
+ count:uint (id: 1);
+}
+
+table CumulativeStatistics {
+ total_accepted:int (id: 0);
+ total_candidates:int (id: 1);
+ rejection_reasons:[RejectionCount] (id: 2, static_length: 9);
+}
+
+table ImuStatus {
+ // Whether the IMU is zeroed or not.
+ zeroed:bool (id: 0);
+ // Whether the IMU zeroing is faulted or not.
+ faulted_zero:bool (id: 1);
+ zeroing:frc971.control_loops.drivetrain.ImuZeroerState (id: 2);
+ // Offset between the IMU board clock and the orin clock, such that
+ // board_timestamp + board_offset_ns = orin_timestamp
+ board_offset_ns:int64 (id: 3);
+ // Error in the offset, if we assume that the orin/board clocks are
+ // identical and that there is a perfectly consistent latency between the
+ // two. Will be zero for the very first cycle, and then referenced off of
+ // the initial offset thereafter. If greater than zero, implies that the
+ // board is "behind", whether due to unusually large latency or due to
+ // clock drift.
+ board_offset_error_ns:int64 (id: 4);
+ left_encoder:double (id: 5);
+ right_encoder:double (id: 6);
+ imu_failures:frc971.controls.ImuFailures (id: 7);
+}
+
+table Status {
+ state: frc971.control_loops.drivetrain.LocalizerState (id: 0);
+ down_estimator:frc971.control_loops.drivetrain.DownEstimatorState (id: 1);
+ imu:ImuStatus (id: 2);
+ // Statistics are per-camera, by camera index.
+ statistics:[CumulativeStatistics] (id: 3);
+}
+
+root_type Status;
diff --git a/y2024/localizer/visualization.fbs b/y2024/localizer/visualization.fbs
new file mode 100644
index 0000000..d903e3a
--- /dev/null
+++ b/y2024/localizer/visualization.fbs
@@ -0,0 +1,47 @@
+include "y2024/localizer/status.fbs";
+
+namespace y2024.localizer;
+
+attribute "static_length";
+
+table Measurement {
+ heading:double (id: 0);
+ distance:double (id: 1);
+ skew:double (id: 2);
+}
+
+table TargetEstimateDebug {
+ camera:uint8 (id: 0);
+ camera_x:double (id: 1);
+ camera_y:double (id: 2);
+ camera_theta:double (id: 3);
+ implied_robot_x:double (id: 4);
+ implied_robot_y:double (id: 5);
+ implied_robot_theta:double (id: 6);
+ accepted:bool (id: 7);
+ rejection_reason:RejectionReason (id: 8);
+ // Image age (more human-readable than trying to interpret raw nanosecond
+ // values).
+ image_age_sec:double (id: 9);
+ // Time at which the image was captured.
+ image_monotonic_timestamp_ns:uint64 (id: 10);
+ // April tag ID used for this image detection.
+ april_tag:uint (id: 11);
+ // If the image was accepted, the total correction that occurred as a result.
+ // These numbers will be equal to the value after the correction - the value
+ // before.
+ correction_x: double (id: 12);
+ correction_y: double (id: 13);
+ correction_theta: double (id: 14);
+ // The expected observation given the current estimate of the robot pose.
+ expected_observation:Measurement (id: 15);
+ actual_observation:Measurement (id: 16);
+ modeled_noise:Measurement (id: 17);
+}
+
+table Visualization {
+ targets:[TargetEstimateDebug] (id: 0, static_length: 20);
+ statistics:CumulativeStatistics (id: 1);
+}
+
+root_type Visualization;
diff --git a/y2024/vision/BUILD b/y2024/vision/BUILD
index 4b515cf..d36c956 100644
--- a/y2024/vision/BUILD
+++ b/y2024/vision/BUILD
@@ -16,6 +16,38 @@
)
cc_binary(
+ name = "target_mapping",
+ srcs = [
+ "target_mapping.cc",
+ "vision_util.cc",
+ "vision_util.h",
+ ],
+ data = [
+ "//y2024:aos_config",
+ "//y2024/constants:constants.json",
+ "//y2024/vision:maps",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//y2023:__subpackages__"],
+ deps = [
+ "//aos:init",
+ "//aos/events:simulated_event_loop",
+ "//aos/events/logging:log_reader",
+ "//aos/util:mcap_logger",
+ "//frc971/constants:constants_sender_lib",
+ "//frc971/control_loops:pose",
+ "//frc971/vision:calibration_fbs",
+ "//frc971/vision:charuco_lib",
+ "//frc971/vision:target_mapper",
+ "//frc971/vision:vision_util_lib",
+ "//frc971/vision:visualize_robot",
+ "//third_party:opencv",
+ "//y2024/constants:constants_fbs",
+ "//y2024/constants:simulated_constants_sender",
+ ],
+)
+
+cc_binary(
name = "image_logger",
srcs = [
"image_logger.cc",
@@ -115,3 +147,22 @@
"@org_tuxfamily_eigen//:eigen",
],
)
+
+cc_binary(
+ name = "modify_extrinsics",
+ srcs = [
+ "modify_extrinsics.cc",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//y2024:__subpackages__"],
+ deps = [
+ "//aos:configuration",
+ "//aos:init",
+ "//aos/events:event_loop",
+ "//frc971/vision:calibration_fbs",
+ "//frc971/vision:vision_util_lib",
+ "//y2024/constants:constants_fbs",
+ "@com_google_absl//absl/strings:str_format",
+ "@org_tuxfamily_eigen//:eigen",
+ ],
+)
diff --git a/y2024/vision/apriltag_detector.cc b/y2024/vision/apriltag_detector.cc
index 96ff869..5edcf36 100644
--- a/y2024/vision/apriltag_detector.cc
+++ b/y2024/vision/apriltag_detector.cc
@@ -20,10 +20,12 @@
const frc971::constants::ConstantsFetcher<y2024::Constants> calibration_data(
&event_loop);
+ CHECK(FLAGS_channel.length() == 8);
+ int camera_id = std::stoi(FLAGS_channel.substr(7, 1));
const frc971::vision::calibration::CameraCalibration *calibration =
y2024::vision::FindCameraCalibration(
calibration_data.constants(),
- event_loop.node()->name()->string_view());
+ event_loop.node()->name()->string_view(), camera_id);
frc971::apriltag::ApriltagDetector detector(&event_loop, FLAGS_channel,
calibration);
diff --git a/y2024/vision/calibrate_multi_cameras.cc b/y2024/vision/calibrate_multi_cameras.cc
index 9148217..8a4bbaa 100644
--- a/y2024/vision/calibrate_multi_cameras.cc
+++ b/y2024/vision/calibrate_multi_cameras.cc
@@ -31,7 +31,7 @@
"If true, show visualization from field level, rather than above");
DEFINE_string(config, "",
"If set, override the log's config file with this one.");
-DEFINE_string(constants_path, "y2023/constants/constants.json",
+DEFINE_string(constants_path, "y2024/constants/constants.json",
"Path to the constant file");
DEFINE_double(max_pose_error, 5e-5,
"Throw out target poses with a higher pose error than this");
@@ -44,12 +44,14 @@
"Type of target being used [aruco, charuco, charuco_diamond]");
DEFINE_int32(team_number, 0,
"Required: Use the calibration for a node with this team number");
-DEFINE_bool(use_full_logs, false,
- "If true, extract data from logs with images");
DEFINE_uint64(
wait_key, 1,
"Time in ms to wait between images (0 to wait indefinitely until click)");
+DEFINE_bool(robot, false,
+ "If true we're calibrating extrinsics for the robot, use the "
+ "correct node path for the robot.");
+
DECLARE_int32(min_target_id);
DECLARE_int32(max_target_id);
@@ -64,11 +66,11 @@
// and then map each subsequent camera based on the data collected and
// the extrinsic poses computed here.
-// TODO<Jim>: Not currently using estimate from pi1->pi4-- should do full
-// estimation, and probably also include camera->imu extrinsics from all
-// cameras, not just pi1
+// TODO<Jim>: Not currently using estimate from first camera to last camera--
+// should do full estimation, and probably also include camera->imu extrinsics
+// from all cameras, not just first camera
-namespace y2023::vision {
+namespace y2024::vision {
using frc971::vision::DataAdapter;
using frc971::vision::ImageCallback;
using frc971::vision::PoseUtils;
@@ -78,7 +80,7 @@
namespace calibration = frc971::vision::calibration;
static constexpr double kImagePeriodMs =
- 1.0 / 30.0 * 1000.0; // Image capture period in ms
+ 1.0 / 60.0 * 1000.0; // Image capture period in ms
// Change reference frame from camera to robot
Eigen::Affine3d CameraToRobotDetection(Eigen::Affine3d H_camera_target,
@@ -97,6 +99,38 @@
int board_id;
};
+struct CameraNode {
+ std::string node_name;
+ int camera_number;
+
+ inline const std::string camera_name() const {
+ return "/" + node_name + "/camera" + std::to_string(camera_number);
+ }
+};
+
+std::vector<CameraNode> CreateNodeList() {
+ std::vector<CameraNode> list;
+
+ list.push_back({.node_name = "orin1", .camera_number = 0});
+ list.push_back({.node_name = "orin1", .camera_number = 1});
+ list.push_back({.node_name = "imu", .camera_number = 0});
+ list.push_back({.node_name = "imu", .camera_number = 1});
+
+ return list;
+}
+
+std::vector<CameraNode> node_list(CreateNodeList());
+std::map<std::string, int> CreateOrderingMap() {
+ std::map<std::string, int> map;
+
+ for (uint i = 0; i < node_list.size(); i++) {
+ map.insert({node_list.at(i).camera_name(), i});
+ }
+
+ return map;
+}
+std::map<std::string, int> ordering_map(CreateOrderingMap());
+
TimestampedPiDetection last_observation;
std::vector<std::pair<TimestampedPiDetection, TimestampedPiDetection>>
detection_list;
@@ -252,14 +286,14 @@
kImagePeriodMs / 2.0 * 1000000.0) {
// Sort by pi numbering, since this is how we will handle them
std::pair<TimestampedPiDetection, TimestampedPiDetection> new_pair;
- if (last_observation.pi_name < new_observation.pi_name) {
+ if (ordering_map.at(last_observation.pi_name) <
+ ordering_map.at(new_observation.pi_name)) {
new_pair = std::pair(last_observation, new_observation);
- } else if (last_observation.pi_name > new_observation.pi_name) {
+ } else if (ordering_map.at(last_observation.pi_name) >
+ ordering_map.at(new_observation.pi_name)) {
new_pair = std::pair(new_observation, last_observation);
- } else {
- LOG(WARNING) << "Got 2 observations in a row from same pi. Probably "
- "not too much of an issue???";
}
+
detection_list.push_back(new_pair);
// This bit is just for visualization and checking purposes-- use the
@@ -366,7 +400,7 @@
Eigen::Affine3d H_camera_target =
PoseUtils::Pose3dToAffine3d(target_pose.pose);
- VLOG(2) << node_name << " saw target " << target_pose.id
+ VLOG(1) << node_name << " saw target " << target_pose.id
<< " from TargetMap at timestamp " << distributed_eof
<< " with pose = " << H_camera_target.matrix();
}
@@ -425,26 +459,18 @@
aos::logger::SortParts(aos::logger::FindLogs(argc, argv)),
config.has_value() ? &config->message() : nullptr);
- constexpr size_t kNumPis = 4;
- for (size_t i = 1; i <= kNumPis; i++) {
- reader.RemapLoggedChannel(absl::StrFormat("/pi%u/constants", i),
- "y2023.Constants");
+ reader.RemapLoggedChannel("/imu/constants", "y2024.Constants");
+ reader.RemapLoggedChannel("/orin1/constants", "y2024.Constants");
+ if (FLAGS_robot) {
+ reader.RemapLoggedChannel("/roborio/constants", "y2024.Constants");
}
-
- reader.RemapLoggedChannel("/imu/constants", "y2023.Constants");
- reader.RemapLoggedChannel("/logger/constants", "y2023.Constants");
- reader.RemapLoggedChannel("/roborio/constants", "y2023.Constants");
reader.Register();
y2024::SendSimulationConstants(reader.event_loop_factory(), FLAGS_team_number,
FLAGS_constants_path);
VLOG(1) << "Using target type " << FLAGS_target_type;
- std::vector<std::string> node_list;
- node_list.push_back("pi1");
- node_list.push_back("pi2");
- node_list.push_back("pi3");
- node_list.push_back("pi4");
+
std::vector<const calibration::CameraCalibration *> calibration_list;
std::vector<std::unique_ptr<aos::EventLoop>> detection_event_loops;
@@ -452,29 +478,23 @@
std::vector<frc971::vision::ImageCallback *> image_callbacks;
std::vector<Eigen::Affine3d> default_extrinsics;
- for (uint i = 0; i < node_list.size(); i++) {
- std::string node = node_list[i];
- const aos::Node *pi =
- aos::configuration::GetNode(reader.configuration(), node.c_str());
+ for (const CameraNode &camera_node : node_list) {
+ const aos::Node *pi = aos::configuration::GetNode(
+ reader.configuration(), camera_node.node_name.c_str());
detection_event_loops.emplace_back(
reader.event_loop_factory()->MakeEventLoop(
- (node + "_detection").c_str(), pi));
-
+ (camera_node.camera_name() + "_detection").c_str(), pi));
frc971::constants::ConstantsFetcher<y2024::Constants> constants_fetcher(
detection_event_loops.back().get());
-
+ // Get the calibration for this orin/camera pair
const calibration::CameraCalibration *calibration =
y2024::vision::FindCameraCalibration(constants_fetcher.constants(),
- node);
+ camera_node.node_name,
+ camera_node.camera_number);
calibration_list.push_back(calibration);
- frc971::vision::TargetType target_type =
- frc971::vision::TargetTypeFromString(FLAGS_target_type);
- frc971::vision::CharucoExtractor *charuco_ext =
- new frc971::vision::CharucoExtractor(calibration, target_type);
- charuco_extractors.emplace_back(charuco_ext);
-
+ // Extract the extrinsics from the calibration, and save as "defaults"
cv::Mat extrinsics_cv =
frc971::vision::CameraExtrinsics(calibration).value();
Eigen::Matrix4d extrinsics_matrix;
@@ -482,45 +502,25 @@
const auto ext_H_robot_pi = Eigen::Affine3d(extrinsics_matrix);
default_extrinsics.emplace_back(ext_H_robot_pi);
- VLOG(1) << "Got extrinsics for " << node << " as\n"
+ VLOG(1) << "Got extrinsics for " << camera_node.camera_name() << " as\n"
<< default_extrinsics.back().matrix();
- if (FLAGS_use_full_logs) {
- LOG(INFO) << "Set up image callback for node " << node_list[i];
- frc971::vision::ImageCallback *image_callback =
- new frc971::vision::ImageCallback(
- detection_event_loops[i].get(), "/" + node_list[i] + "/camera",
- [&reader, &charuco_extractors, &detection_event_loops, &node_list,
- i](cv::Mat rgb_image,
- const aos::monotonic_clock::time_point eof) {
- aos::distributed_clock::time_point pi_distributed_time =
- reader.event_loop_factory()
- ->GetNodeEventLoopFactory(
- detection_event_loops[i].get()->node())
- ->ToDistributedClock(eof);
- HandleImage(detection_event_loops[i].get(), rgb_image, eof,
- pi_distributed_time, *charuco_extractors[i],
- node_list[i]);
- });
+ detection_event_loops.back()->MakeWatcher(
+ camera_node.camera_name(),
+ [&reader, &detection_event_loops, camera_node](const TargetMap &map) {
+ aos::distributed_clock::time_point pi_distributed_time =
+ reader.event_loop_factory()
+ ->GetNodeEventLoopFactory(
+ detection_event_loops.back().get()->node())
+ ->ToDistributedClock(aos::monotonic_clock::time_point(
+ aos::monotonic_clock::duration(
+ map.monotonic_timestamp_ns())));
- image_callbacks.emplace_back(image_callback);
- } else {
- detection_event_loops[i]->MakeWatcher(
- "/camera", [&reader, &detection_event_loops, &node_list,
- i](const TargetMap &map) {
- aos::distributed_clock::time_point pi_distributed_time =
- reader.event_loop_factory()
- ->GetNodeEventLoopFactory(detection_event_loops[i]->node())
- ->ToDistributedClock(aos::monotonic_clock::time_point(
- aos::monotonic_clock::duration(
- map.monotonic_timestamp_ns())));
-
- HandleTargetMap(map, pi_distributed_time, node_list[i]);
- });
- LOG(INFO) << "Created watcher for using the detection event loop for "
- << node_list[i] << " with i = " << i << " and size "
- << detection_event_loops.size();
- }
+ HandleTargetMap(map, pi_distributed_time, camera_node.camera_name());
+ });
+ LOG(INFO) << "Created watcher for using the detection event loop for "
+ << camera_node.camera_name() << " and size "
+ << detection_event_loops.size();
}
reader.event_loop_factory()->Run();
@@ -531,26 +531,27 @@
<< "Must have at least one view of both boards";
int base_target_id = two_board_extrinsics_list[0].board_id;
VLOG(1) << "Base id for two_board_extrinsics_list is " << base_target_id;
- for (auto node : node_list) {
+
+ for (auto camera_node : node_list) {
std::vector<TimestampedPiDetection> pose_list;
for (auto ext : two_board_extrinsics_list) {
CHECK_EQ(base_target_id, ext.board_id)
<< " All boards should have same reference id";
- if (ext.pi_name == node) {
+ if (ext.pi_name == camera_node.camera_name()) {
pose_list.push_back(ext);
}
}
Eigen::Affine3d avg_pose_from_pi = ComputeAveragePose(pose_list);
- VLOG(1) << "Estimate from " << node << " with " << pose_list.size()
- << " observations is:\n"
+ VLOG(1) << "Estimate from " << camera_node.camera_name() << " with "
+ << pose_list.size() << " observations is:\n"
<< avg_pose_from_pi.matrix();
}
Eigen::Affine3d H_boardA_boardB_avg =
ComputeAveragePose(two_board_extrinsics_list);
// TODO: Should probably do some outlier rejection
- LOG(INFO) << "Estimate of two board pose using all nodes with "
- << two_board_extrinsics_list.size() << " observations is:\n"
- << H_boardA_boardB_avg.matrix() << "\n";
+ VLOG(1) << "Estimate of two board pose using all nodes with "
+ << two_board_extrinsics_list.size() << " observations is:\n"
+ << H_boardA_boardB_avg.matrix() << "\n";
// Next, compute the relative camera poses
LOG(INFO) << "Got " << detection_list.size() << " extrinsic observations";
@@ -558,14 +559,14 @@
std::vector<Eigen::Affine3d> updated_extrinsics;
// Use the first node's extrinsics as our base, and fix from there
updated_extrinsics.push_back(default_extrinsics[0]);
- LOG(INFO) << "Default extrinsic for node " << node_list[0] << " is "
- << default_extrinsics[0].matrix();
+ LOG(INFO) << "Default extrinsic for camera " << node_list.at(0).camera_name()
+ << " is " << default_extrinsics[0].matrix();
for (uint i = 0; i < node_list.size() - 1; i++) {
H_camera1_camera2_list.clear();
// Go through the list, and find successive pairs of cameras
for (auto [pose1, pose2] : detection_list) {
- if ((pose1.pi_name == node_list[i]) &&
- (pose2.pi_name == node_list[i + 1])) {
+ if ((pose1.pi_name == node_list.at(i).camera_name()) &&
+ (pose2.pi_name == node_list.at(i + 1).camera_name())) {
Eigen::Affine3d H_camera1_boardA = pose1.H_camera_target;
// If pose1 isn't referenced to base_target_id, correct that
if (pose1.board_id != base_target_id) {
@@ -613,12 +614,14 @@
// TODO<Jim>: If we don't get any matches, we could just use default
// extrinsics
CHECK(H_camera1_camera2_list.size() > 0)
- << "Failed with zero poses for node " << node_list[i];
+ << "Failed with zero poses for node " << node_list.at(i).camera_name()
+ << " and " << node_list.at(i + 1).camera_name();
if (H_camera1_camera2_list.size() > 0) {
Eigen::Affine3d H_camera1_camera2_avg =
ComputeAveragePose(H_camera1_camera2_list);
- LOG(INFO) << "From " << node_list[i] << " to " << node_list[i + 1]
- << " found " << H_camera1_camera2_list.size()
+ LOG(INFO) << "From " << node_list.at(i).camera_name() << " to "
+ << node_list.at(i + 1).camera_name() << " found "
+ << H_camera1_camera2_list.size()
<< " observations, and the average pose is:\n"
<< H_camera1_camera2_avg.matrix();
Eigen::Affine3d H_camera1_camera2_default =
@@ -640,9 +643,11 @@
Eigen::Affine3d next_extrinsic =
updated_extrinsics.back() * H_camera1_camera2_avg;
updated_extrinsics.push_back(next_extrinsic);
- LOG(INFO) << "Default Extrinsic for " << node_list[i + 1] << " is \n"
+ LOG(INFO) << "Default Extrinsic for " << node_list.at(i + 1).camera_name()
+ << " is \n"
<< default_extrinsics[i + 1].matrix();
- LOG(INFO) << "--> Updated Extrinsic for " << node_list[i + 1] << " is \n"
+ LOG(INFO) << "--> Updated Extrinsic for "
+ << node_list.at(i + 1).camera_name() << " is \n"
<< next_extrinsic.matrix();
// Wirte out this extrinsic to a file
@@ -677,13 +682,16 @@
std::stringstream time_ss;
time_ss << realtime_now;
- // Assumes node_list name is of form "pi#" to create camera id
+ // TODO: This breaks because we're naming orin1 and imu as nodes
+ // Assumes camera_list name is of form "/orin#/cameraX" to create
+ // calibration filename
const std::string calibration_filename =
FLAGS_output_folder +
- absl::StrFormat("/calibration_pi-%d-%s_cam-%s_%s.json",
- FLAGS_team_number, node_list[i + 1].substr(2, 3),
- calibration_list[i + 1]->camera_id()->data(),
- time_ss.str());
+ absl::StrFormat(
+ "/calibration_orin-%d-%s-%d_cam-%s_%s.json", FLAGS_team_number,
+ node_list.at(i + 1).camera_name().substr(5, 1),
+ calibration_list[i + 1]->camera_number(),
+ calibration_list[i + 1]->camera_id()->data(), time_ss.str());
LOG(INFO) << calibration_filename << " -> "
<< aos::FlatbufferToJson(merged_calibration,
@@ -701,9 +709,9 @@
delete image_callbacks[i];
}
}
-} // namespace y2023::vision
+} // namespace y2024::vision
int main(int argc, char **argv) {
aos::InitGoogle(&argc, &argv);
- y2023::vision::ExtrinsicsMain(argc, argv);
+ y2024::vision::ExtrinsicsMain(argc, argv);
}
diff --git a/y2024/vision/image_logger.cc b/y2024/vision/image_logger.cc
index 45e25f6..55a4e12 100644
--- a/y2024/vision/image_logger.cc
+++ b/y2024/vision/image_logger.cc
@@ -25,7 +25,7 @@
std::unique_ptr<aos::logger::MultiNodeFilesLogNamer> MakeLogNamer(
aos::EventLoop *event_loop) {
std::optional<std::string> log_name =
- aos::logging::MaybeGetLogName("fbs_log");
+ aos::logging::MaybeGetLogName("image_log");
if (!log_name.has_value()) {
return nullptr;
diff --git a/y2024/vision/maps/target_map.json b/y2024/vision/maps/target_map.json
index a87e9a3..2a8dfef 100644
--- a/y2024/vision/maps/target_map.json
+++ b/y2024/vision/maps/target_map.json
@@ -1,41 +1,46 @@
{
-/* Targets have positive Z pointing out of the board, positive X to the left and
- positive Y is down. This means that the camera lines up with the target if the
- board is upright and the camera is facing away from the board.*/
+/* Targets have positive Z axis pointing into the board, positive X to the right
+ when looking at the board, and positive Y is down when looking at the board.
+ This means that you will get an identity rotation from the camera to target
+ frame when the target is upright, flat, and centered in the camera's view.
+
+ The global frame as the origin at the center of the field, positive X points
+ at the red driver's station, and positive Z points straight up.
+ */
"target_poses": [
{
"id": 1,
"position": {
- "x": -6.809,
- "y": 3.860,
+ "x": 6.809,
+ "y": -3.860,
"z": 1.361
},
"orientation": {
- "w": -0.18,
- "x": -0.68,
- "y": 0.68,
- "z": 0.18
+ "w": 0.1830127,
+ "x": -0.1830127,
+ "y": 0.6830127,
+ "z": -0.6830127
}
},
{
"id": 2,
"position": {
- "x": -7.915,
- "y": 3.223,
+ "x": 7.915,
+ "y": -3.223,
"z": 1.361
},
"orientation": {
- "w": -0.18,
- "x": -0.68,
- "y": 0.68,
- "z": 0.18
+ "w": 0.1830127,
+ "x": -0.1830127,
+ "y": 0.6830127,
+ "z": -0.6830127
}
},
{
"id": 3,
"position": {
- "x": -8.309,
- "y": -0.877,
+ "x": 8.309,
+ "y": 0.877,
"z": 1.456
},
"orientation": {
@@ -48,8 +53,8 @@
{
"id": 4,
"position": {
- "x": -8.309,
- "y": -1.442,
+ "x": 8.309,
+ "y": 1.442,
"z": 1.456
},
"orientation": {
@@ -62,120 +67,120 @@
{
"id": 5,
"position": {
- "x": -6.428,
- "y": -4.099,
+ "x": 6.428,
+ "y": 4.099,
"z": 1.361
},
"orientation": {
- "w": 0.707,
- "x": 0.0,
- "y": -0.707,
+ "w": 0.7071068,
+ "x": -0.7071068,
+ "y": 0.0,
"z": 0.0
}
},
{
"id": 6,
"position": {
- "x": 6.430,
- "y": -4.099,
+ "x": -6.430,
+ "y": 4.099,
"z": 1.361
},
"orientation": {
- "w": 0.707,
- "x": 0.0,
- "y": -0.707,
+ "w": 0.7071068,
+ "x": -0.7071068,
+ "y": 0.0,
"z": 0.0
}
},
{
"id": 7,
"position": {
- "x": 8.309,
- "y": -1.442,
+ "x": -8.309,
+ "y": 1.442,
"z": 1.474
},
"orientation": {
"w": 0.5,
- "x": 0.5,
+ "x": -0.5,
"y": -0.5,
- "z": -0.5
+ "z": 0.5
}
},
{
"id": 8,
"position": {
- "x": 8.309,
- "y": -0.877,
+ "x": -8.309,
+ "y": 0.877,
"z": 1.474
},
"orientation": {
"w": 0.5,
- "x": 0.5,
+ "x": -0.5,
"y": -0.5,
- "z": -0.5
+ "z": 0.5
}
},
{
"id": 9,
"position": {
- "x": 7.915,
- "y": 3.223,
+ "x": -7.915,
+ "y": -3.223,
"z": 1.361
},
"orientation": {
- "w": -0.18,
- "x": -0.68,
- "y": 0.68,
- "z": 0.18
+ "w": 0.1830127,
+ "x": -0.1830127,
+ "y": -0.6830127,
+ "z": 0.6830127
}
},
{
"id": 10,
"position": {
- "x": 6.809,
- "y": 3.860,
+ "x": -6.809,
+ "y": -3.860,
"z": 1.361
},
"orientation": {
- "w": -0.18,
- "x": -0.68,
- "y": 0.68,
- "z": 0.18
+ "w": 0.1830127,
+ "x": -0.1830127,
+ "y": -0.6830127,
+ "z": 0.6830127
}
},
{
"id": 11,
"position": {
- "x": -3.629,
- "y": 0.393,
+ "x": 3.629,
+ "y": -0.393,
"z": 1.326
},
"orientation": {
- "w": 0.68,
- "x": 0.18,
- "y": -0.18,
- "z": -0.68
+ "w": 0.6830127,
+ "x": -0.6830127,
+ "y": -0.1830127,
+ "z": 0.1830127
}
},
{
"id": 12,
"position": {
- "x": -3.630,
- "y": -0.392,
+ "x": 3.630,
+ "y": 0.392,
"z": 1.326
},
"orientation": {
- "w": -0.18,
- "x": -0.68,
- "y": 0.68,
- "z": 0.18
+ "w": 0.1830127,
+ "x": -0.1830127,
+ "y": -0.6830127,
+ "z": 0.6830127
}
},
{
"id": 13,
"position": {
- "x": -2.949,
- "y": 0.000,
+ "x": 2.949,
+ "y": -0.000,
"z": 1.326
},
"orientation": {
@@ -188,43 +193,43 @@
{
"id": 14,
"position": {
- "x": 2.949,
- "y": 0.000,
+ "x": -2.949,
+ "y": -0.000,
"z": 1.326
},
"orientation": {
"w": 0.5,
- "x": 0.5,
+ "x": -0.5,
"y": -0.5,
- "z": -0.5
+ "z": 0.5
}
},
{
"id": 15,
"position": {
- "x": 3.629,
- "y": -0.393,
+ "x": -3.629,
+ "y": 0.393,
"z": 1.326
},
"orientation": {
- "w": -0.18,
- "x": -0.68,
- "y": 0.68,
- "z": 0.18
+ "w": 0.1830127,
+ "x": -0.1830127,
+ "y": 0.6830127,
+ "z": -0.6830127
}
},
{
"id": 16,
"position": {
- "x": 3.630,
- "y": 0.392,
+ "x": -3.630,
+ "y": -0.392,
"z": 1.326
},
"orientation": {
- "w": 0.68,
- "x": 0.18,
- "y": -0.18,
- "z": -0.68
+ "w": 0.6830127,
+ "x": -0.6830127,
+ "y": 0.1830127,
+ "z": -0.1830127
}
}
]
diff --git a/y2024/vision/modify_extrinsics.cc b/y2024/vision/modify_extrinsics.cc
new file mode 100644
index 0000000..9122191
--- /dev/null
+++ b/y2024/vision/modify_extrinsics.cc
@@ -0,0 +1,189 @@
+#include <cmath>
+#include <filesystem>
+#include <regex>
+
+#include "Eigen/Dense"
+#include "Eigen/Geometry"
+#include "absl/strings/str_format.h"
+
+#include "aos/configuration.h"
+#include "aos/events/event_loop.h"
+#include "aos/flatbuffer_merge.h"
+#include "aos/init.h"
+#include "aos/network/team_number.h"
+#include "aos/time/time.h"
+#include "aos/util/file.h"
+#include "frc971/vision/calibration_generated.h"
+
+// This is a helper program to build and rename calibration files
+// You can:
+// (1) pass it in a new set of orin #, team #, camera #, to rename the file
+// (2) Pass in extrinsics to set the extrinsics
+// By default, writes to /tmp, but will write to calib_files folder if
+// full path is given and calibration_folder is blank
+
+DEFINE_string(orig_calib_file, "",
+ "Intrinsics to use for estimating board pose prior to solving "
+ "for the new intrinsics.");
+DEFINE_string(calibration_folder, "/tmp", "Folder to place calibration files.");
+DEFINE_string(node_name, "",
+ "Node name to use, e.g. orin1, imu; unchanged if blank");
+DEFINE_int32(team_number, -1, "Team number to use; unchanged if -1");
+DEFINE_int32(camera_number, -1, "Camera number to use; unchanged if -1");
+
+DEFINE_bool(set_extrinsics, true, "Set to false to ignore extrinsic data");
+DEFINE_bool(use_inches, true,
+ "Whether to use inches as units (meters if false)");
+DEFINE_bool(use_degrees, true,
+ "Whether to use degrees as units (radians if false)");
+DEFINE_double(camera_x, 0.0, "x location of camera");
+DEFINE_double(camera_y, 0.0, "y location of camera");
+DEFINE_double(camera_z, 0.0, "z location of camera");
+// Don't currently allow for roll of cameras
+DEFINE_double(camera_yaw, 0.0, "yaw of camera about robot z axis");
+DEFINE_double(camera_pitch, 0.0, "pitch of camera relative to robot y axis");
+// TODO: This could be done by setting the pixel size and using the intrinsics
+DEFINE_double(focal_length, 0.002, "Focal length in meters");
+
+namespace frc971::vision {
+namespace {
+
+// TODO: Put this in vision_util_lib? Except, it depends on Eigen
+std::vector<float> MatrixToVector(const Eigen::Matrix<double, 4, 4> &H) {
+ std::vector<float> data;
+ for (int row = 0; row < 4; ++row) {
+ for (int col = 0; col < 4; ++col) {
+ data.push_back(H(row, col));
+ }
+ }
+ return data;
+}
+
+// Merge the original calibration file with all its changes
+aos::FlatbufferDetachedBuffer<calibration::CameraCalibration> BuildCalibration(
+ const frc971::vision::calibration::CameraCalibration *calibration,
+ std::string node_name, uint16_t camera_number, uint16_t team_number) {
+ aos::FlatbufferDetachedBuffer<frc971::vision::calibration::CameraCalibration>
+ cal_copy = aos::RecursiveCopyFlatBuffer(calibration);
+
+ flatbuffers::FlatBufferBuilder fbb;
+ flatbuffers::Offset<flatbuffers::String> node_name_offset =
+ fbb.CreateString(absl::StrFormat("%s", node_name.c_str()));
+
+ // If we're told to set the extrinsics, clear old and add in new
+ flatbuffers::Offset<calibration::TransformationMatrix>
+ fixed_extrinsics_offset;
+ if (FLAGS_set_extrinsics) {
+ cal_copy.mutable_message()->clear_fixed_extrinsics();
+ Eigen::Affine3d extrinsic_matrix;
+ // Convert to metric
+ double translation_scale = (FLAGS_use_inches ? 0.0254 : 1.0);
+ Eigen::Translation3d translation(FLAGS_camera_x * translation_scale,
+ FLAGS_camera_y * translation_scale,
+ FLAGS_camera_z * translation_scale);
+
+ // convert to radians
+ double angle_scale = (FLAGS_use_degrees ? M_PI / 180.0 : 1.0);
+ // The rotation that takes robot coordinates (x forward, z up) to camera
+ // coordiantes (z forward, x right)
+ Eigen::Quaterniond R_robo_cam =
+ Eigen::AngleAxisd(-M_PI / 2.0, Eigen::Vector3d::UnitZ()) *
+ Eigen::AngleAxisd(-M_PI / 2.0, Eigen::Vector3d::UnitX());
+ Eigen::AngleAxisd pitchAngle(FLAGS_camera_pitch * angle_scale,
+ Eigen::Vector3d::UnitY());
+ Eigen::AngleAxisd yawAngle(FLAGS_camera_yaw * angle_scale,
+ Eigen::Vector3d::UnitZ());
+
+ Eigen::Quaterniond rotation = yawAngle * pitchAngle * R_robo_cam;
+ Eigen::Vector3d focal_length_offset =
+ (rotation * Eigen::Translation3d(0.0, 0.0, FLAGS_focal_length))
+ .translation();
+ translation = translation * Eigen::Translation3d(focal_length_offset);
+
+ extrinsic_matrix = translation * rotation;
+ flatbuffers::Offset<flatbuffers::Vector<float>> data_offset =
+ fbb.CreateVector(
+ frc971::vision::MatrixToVector(extrinsic_matrix.matrix()));
+ calibration::TransformationMatrix::Builder matrix_builder(fbb);
+ matrix_builder.add_data(data_offset);
+ fixed_extrinsics_offset = matrix_builder.Finish();
+ }
+
+ calibration::CameraCalibration::Builder camera_calibration_builder(fbb);
+ camera_calibration_builder.add_node_name(node_name_offset);
+ camera_calibration_builder.add_team_number(team_number);
+ if (FLAGS_set_extrinsics) {
+ camera_calibration_builder.add_fixed_extrinsics(fixed_extrinsics_offset);
+ }
+ camera_calibration_builder.add_camera_number(camera_number);
+ fbb.Finish(camera_calibration_builder.Finish());
+
+ aos::FlatbufferDetachedBuffer<calibration::CameraCalibration> updated_cal =
+ fbb.Release();
+
+ aos::FlatbufferDetachedBuffer<calibration::CameraCalibration>
+ merged_calibration =
+ aos::MergeFlatBuffers(&cal_copy.message(), &updated_cal.message());
+ return merged_calibration;
+}
+
+void Main(std::string orig_calib_filename) {
+ LOG(INFO) << "Reading from file: " << orig_calib_filename;
+ aos::FlatbufferDetachedBuffer<calibration::CameraCalibration>
+ base_calibration =
+ aos::JsonFileToFlatbuffer<calibration::CameraCalibration>(
+ orig_calib_filename);
+
+ // Populate the new variables from command-line or from base_calibration
+ std::string node_name =
+ (FLAGS_node_name == "" ? base_calibration.message().node_name()->str()
+ : FLAGS_node_name);
+ int team_number =
+ (FLAGS_team_number == -1 ? base_calibration.message().team_number()
+ : FLAGS_team_number);
+ int camera_number =
+ (FLAGS_camera_number == -1 ? base_calibration.message().camera_number()
+ : FLAGS_camera_number);
+ aos::FlatbufferDetachedBuffer<calibration::CameraCalibration>
+ new_calibration = BuildCalibration(&base_calibration.message(), node_name,
+ camera_number, team_number);
+
+ // Set a new timestamp on the file, but leave calibration_timestamp unchanged
+ const aos::realtime_clock::time_point realtime_now =
+ aos::realtime_clock::now();
+ std::stringstream time_ss;
+ time_ss << realtime_now;
+ // Use the camera_id that is set in the json file (not the filename)
+ std::string camera_id = base_calibration.message().camera_id()->str();
+
+ const std::string dirname =
+ (FLAGS_calibration_folder == ""
+ ? std::filesystem::path(orig_calib_filename).parent_path().string()
+ : FLAGS_calibration_folder);
+ const std::string new_calib_filename =
+ dirname + "/" +
+ absl::StrFormat("calibration_%s-%d-%d_cam-%s_%s.json", node_name.c_str(),
+ team_number, camera_number, camera_id.c_str(),
+ time_ss.str());
+
+ VLOG(1) << "From: " << orig_calib_filename << " -> "
+ << aos::FlatbufferToJson(base_calibration, {.multi_line = true});
+
+ VLOG(1) << "Writing: " << new_calib_filename << " -> "
+ << aos::FlatbufferToJson(new_calibration, {.multi_line = true});
+
+ LOG(INFO) << "Writing to file: " << new_calib_filename;
+ aos::util::WriteStringToFileOrDie(
+ new_calib_filename,
+ aos::FlatbufferToJson(new_calibration, {.multi_line = true}));
+}
+
+} // namespace
+} // namespace frc971::vision
+
+int main(int argc, char **argv) {
+ aos::InitGoogle(&argc, &argv);
+ CHECK(argc == 2) << "Must supply a starting calibration filename";
+ std::string filename = argv[1];
+ frc971::vision::Main(filename);
+}
diff --git a/y2024/vision/rename_calibration_file.sh b/y2024/vision/rename_calibration_file.sh
new file mode 100755
index 0000000..dcdf2f2
--- /dev/null
+++ b/y2024/vision/rename_calibration_file.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+# Helper script to rename the camera calibration file when moving to new robot
+
+# grep isn't happy with set
+# set -e
+
+
+usage_and_exit () {
+ echo
+ echo "Usage:"
+ echo "$0 ORIG_FILENAME NEW_TEAM_NUMBER NEW_ORIN_NUMBER NEW_CAMERA_NUMBER"
+ echo
+ exit 2
+}
+
+if [[ $# -ne 4 ]]; then
+ echo "ERROR: Requires 4 parameters"
+ usage_and_exit
+fi
+
+ORIG_FILENAME=$1
+NEW_TEAM_NUMBER=$2
+NEW_ORIN_NUMBER=$3
+NEW_CAMERA_NUMBER=$4
+
+if [[ ! -x ${ORIG_FILENAME} ]]; then
+ echo "${ORIG_FILENAME} does not exist"
+ usage_and_exit
+fi
+
+check_971=`echo "${NEW_TEAM_NUMBER}" | grep "971"`
+if [[ ${check_971} == "" ]]; then
+ echo "NEW_TEAM_NUMBER (${NEW_TEAM_NUMBER}) does not contain '971'"
+ usage_and_exit
+fi
+
+if [[ ${NEW_ORIN_NUMBER} != 1 && ${NEW_ORIN_NUMBER} != 2 ]]; then
+ echo "NEW_ORIN_NUMBER (${NEW_ORIN_NUMBER}) must be either 1 or 2"
+ usage_and_exit
+fi
+
+if [[ ${NEW_CAMERA_NUMBER} != 0 && ${NEW_CAMERA_NUMBER} != 1 ]]; then
+ echo "NEW_CAMERA_NUMBER (${NEW_CAMERA_NUMBER}) must be either 0 or 1"
+ usage_and_exit
+fi
+
+# Extract parts of the filename, based on just the basename
+# This assumes filenames of the form:
+# calibration_orin-971-1-0_cam-24-01_2024-02-07_20-11-35.566609408.json
+IFS='_' read -r -a name_parts <<< `basename "${ORIG_FILENAME}"`
+
+echo "For ${ORIG_FILENAME}:"
+for element in "${name_parts[@]}"
+do
+ echo "$element"
+done
+
+# Rename file based on this new info (be sure to handle paths properly)
+NEW_FILENAME=`dirname ${ORIG_FILENAME}`"/${name_parts[0]}_orin-${NEW_TEAM_NUMBER}-${NEW_ORIN_NUMBER}-${NEW_CAMERA_NUMBER}_${name_parts[2]}_${name_parts[3]}_${name_parts[4]}"
+
+echo
+echo "For camera id: ${name_parts[2]}"
+echo "Renaming from:"
+echo "${ORIG_FILENAME} to: "
+echo "${NEW_FILENAME}"
+echo
+echo "and changing from "
+echo "${name_parts[1]} to: "
+echo "orin-${NEW_TEAM_NUMBER}-${NEW_ORIN_NUMBER}-${NEW_CAMERA_NUMBER}"
+echo
+
+mv ${ORIG_FILENAME} ${NEW_FILENAME}
+
+
+echo "REPLACING ORIN_NUMBER"
+sed -i s/orin./orin${NEW_ORIN_NUMBER}/ ${NEW_FILENAME}
+
+echo "Replacing TEAM NUMBER"
+sed -i s/\"team_number\"\:\ [1-9]*\,/\"team_number\"\:\ ${NEW_TEAM_NUMBER},/ ${NEW_FILENAME}
+
+echo "REPLACING CAMERA_NUMBER"
+sed -i s/\"camera_number\"\:\ [0-9]/\"camera_number\"\:\ ${NEW_CAMERA_NUMBER}/ ${NEW_FILENAME}
+
+
+
diff --git a/y2024/vision/target_mapping.cc b/y2024/vision/target_mapping.cc
new file mode 100644
index 0000000..7548445
--- /dev/null
+++ b/y2024/vision/target_mapping.cc
@@ -0,0 +1,438 @@
+#include <string>
+
+#include "Eigen/Dense"
+#include "opencv2/aruco.hpp"
+#include "opencv2/calib3d.hpp"
+#include "opencv2/core/eigen.hpp"
+#include "opencv2/features2d.hpp"
+#include "opencv2/highgui.hpp"
+#include "opencv2/highgui/highgui.hpp"
+#include "opencv2/imgproc.hpp"
+
+#include "aos/configuration.h"
+#include "aos/events/logging/log_reader.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/init.h"
+#include "aos/util/mcap_logger.h"
+#include "frc971/constants/constants_sender_lib.h"
+#include "frc971/control_loops/pose.h"
+#include "frc971/vision/calibration_generated.h"
+#include "frc971/vision/charuco_lib.h"
+#include "frc971/vision/target_mapper.h"
+#include "frc971/vision/vision_util_lib.h"
+#include "frc971/vision/visualize_robot.h"
+#include "y2024/constants/simulated_constants_sender.h"
+#include "y2024/vision/vision_util.h"
+
+DEFINE_string(config, "",
+ "If set, override the log's config file with this one.");
+DEFINE_string(constants_path, "y2024/constants/constants.json",
+ "Path to the constant file");
+DEFINE_string(dump_constraints_to, "/tmp/mapping_constraints.txt",
+ "Write the target constraints to this path");
+DEFINE_string(dump_stats_to, "/tmp/mapping_stats.txt",
+ "Write the mapping stats to this path");
+DEFINE_string(field_name, "charged_up",
+ "Field name, for the output json filename and flatbuffer field");
+DEFINE_string(json_path, "y2024/vision/maps/target_map.json",
+ "Specify path for json with initial pose guesses.");
+DEFINE_double(max_pose_error, 1e-6,
+ "Throw out target poses with a higher pose error than this");
+DEFINE_double(
+ max_pose_error_ratio, 0.4,
+ "Throw out target poses with a higher pose error ratio than this");
+DEFINE_string(mcap_output_path, "", "Log to output.");
+DEFINE_string(output_dir, "y2024/vision/maps",
+ "Directory to write solved target map to");
+DEFINE_double(pause_on_distance, 2.0,
+ "Pause if two consecutive implied robot positions differ by more "
+ "than this many meters");
+DEFINE_string(orin, "orin1",
+ "Orin name to generate mcap log for; defaults to pi1.");
+DEFINE_uint64(skip_to, 1,
+ "Start at combined image of this number (1 is the first image)");
+DEFINE_bool(solve, true, "Whether to solve for the field's target map.");
+DEFINE_int32(team_number, 0,
+ "Required: Use the calibration for a node with this team number");
+DEFINE_uint64(wait_key, 1,
+ "Time in ms to wait between images, if no click (0 to wait "
+ "indefinitely until click).");
+
+DECLARE_int32(frozen_target_id);
+DECLARE_int32(min_target_id);
+DECLARE_int32(max_target_id);
+DECLARE_bool(visualize_solver);
+
+namespace y2024::vision {
+using frc971::vision::DataAdapter;
+using frc971::vision::ImageCallback;
+using frc971::vision::PoseUtils;
+using frc971::vision::TargetMap;
+using frc971::vision::TargetMapper;
+using frc971::vision::VisualizeRobot;
+namespace calibration = frc971::vision::calibration;
+
+// Class to handle reading target poses from a replayed log,
+// displaying various debug info, and passing the poses to
+// frc971::vision::TargetMapper for field mapping.
+class TargetMapperReplay {
+ public:
+ TargetMapperReplay(aos::logger::LogReader *reader);
+
+ // Solves for the target poses with the accumulated detections if FLAGS_solve.
+ void MaybeSolve();
+
+ private:
+ static constexpr int kImageWidth = 1280;
+ // Map from pi node name to color for drawing
+ static const std::map<std::string, cv::Scalar> kOrinColors;
+ // Contains fixed target poses without solving, for use with visualization
+ static const TargetMapper kFixedTargetMapper;
+
+ // Change reference frame from camera to robot
+ static Eigen::Affine3d CameraToRobotDetection(Eigen::Affine3d H_camera_target,
+ Eigen::Affine3d extrinsics);
+
+ // Adds april tag detections into the detection list, and handles
+ // visualization
+ void HandleAprilTags(const TargetMap &map,
+ aos::distributed_clock::time_point node_distributed_time,
+ std::string camera_name, Eigen::Affine3d extrinsics);
+ // Gets images from the given pi and passes apriltag positions to
+ // HandleAprilTags()
+ void HandleNodeCaptures(
+ aos::EventLoop *mapping_event_loop,
+ frc971::constants::ConstantsFetcher<y2024::Constants> *constants_fetcher,
+ int camera_number);
+
+ aos::logger::LogReader *reader_;
+ // April tag detections from all pis
+ std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections_;
+
+ VisualizeRobot vis_robot_;
+ // Set of node names which are currently drawn on the display
+ std::set<std::string> drawn_nodes_;
+ // Number of frames displayed
+ size_t display_count_;
+ // Last time we drew onto the display image.
+ // This is different from when we actually call imshow() to update
+ // the display window
+ aos::distributed_clock::time_point last_draw_time_;
+
+ Eigen::Affine3d last_H_world_robot_;
+ // Maximum distance between consecutive T_world_robot's in one display frame,
+ // used to determine if we need to pause for the user to see this frame
+ // clearly
+ double max_delta_T_world_robot_;
+
+ std::vector<std::unique_ptr<aos::EventLoop>> mapping_event_loops_;
+
+ std::unique_ptr<aos::EventLoop> mcap_event_loop_;
+ std::unique_ptr<aos::McapLogger> relogger_;
+};
+
+const auto TargetMapperReplay::kOrinColors = std::map<std::string, cv::Scalar>{
+ {"/orin1/camera0", cv::Scalar(255, 0, 255)},
+ {"/orin1/camera1", cv::Scalar(255, 255, 0)},
+ {"/imu/camera0", cv::Scalar(0, 255, 255)},
+ {"/imu/camera1", cv::Scalar(255, 165, 0)},
+};
+
+const auto TargetMapperReplay::kFixedTargetMapper =
+ TargetMapper(FLAGS_json_path, ceres::examples::VectorOfConstraints{});
+
+Eigen::Affine3d TargetMapperReplay::CameraToRobotDetection(
+ Eigen::Affine3d H_camera_target, Eigen::Affine3d extrinsics) {
+ const Eigen::Affine3d H_robot_camera = extrinsics;
+ const Eigen::Affine3d H_robot_target = H_robot_camera * H_camera_target;
+ return H_robot_target;
+}
+
+TargetMapperReplay::TargetMapperReplay(aos::logger::LogReader *reader)
+ : reader_(reader),
+ timestamped_target_detections_(),
+ vis_robot_(cv::Size(1280, 1000)),
+ drawn_nodes_(),
+ display_count_(0),
+ last_draw_time_(aos::distributed_clock::min_time),
+ last_H_world_robot_(Eigen::Matrix4d::Identity()),
+ max_delta_T_world_robot_(0.0) {
+ reader_->RemapLoggedChannel("/orin1/constants", "y2024.Constants");
+ reader_->RemapLoggedChannel("/imu/constants", "y2024.Constants");
+ reader_->Register();
+
+ SendSimulationConstants(reader_->event_loop_factory(), FLAGS_team_number,
+ FLAGS_constants_path);
+
+ std::vector<std::string> node_list;
+ node_list.push_back("imu");
+ node_list.push_back("orin1");
+
+ for (std::string node : node_list) {
+ const aos::Node *pi =
+ aos::configuration::GetNode(reader->configuration(), node);
+
+ mapping_event_loops_.emplace_back(
+ reader_->event_loop_factory()->MakeEventLoop(node + "_mapping_camera0",
+ pi));
+ mapping_event_loops_.emplace_back(
+ reader_->event_loop_factory()->MakeEventLoop(node + "_mapping_camera1",
+ pi));
+ frc971::constants::ConstantsFetcher<y2024::Constants> constants_fetcher(
+ mapping_event_loops_[mapping_event_loops_.size() - 1].get());
+ HandleNodeCaptures(
+ mapping_event_loops_[mapping_event_loops_.size() - 2].get(),
+ &constants_fetcher, 0);
+ HandleNodeCaptures(
+ mapping_event_loops_[mapping_event_loops_.size() - 1].get(),
+ &constants_fetcher, 1);
+ }
+
+ if (FLAGS_visualize_solver) {
+ vis_robot_.ClearImage();
+ const double kFocalLength = 500.0;
+ vis_robot_.SetDefaultViewpoint(kImageWidth, kFocalLength);
+ }
+}
+
+// Add detected apriltag poses relative to the robot to
+// timestamped_target_detections
+void TargetMapperReplay::HandleAprilTags(
+ const TargetMap &map,
+ aos::distributed_clock::time_point node_distributed_time,
+ std::string camera_name, Eigen::Affine3d extrinsics) {
+ bool drew = false;
+ std::stringstream label;
+ label << camera_name << " - ";
+
+ for (const auto *target_pose_fbs : *map.target_poses()) {
+ // Skip detections with invalid ids
+ if (static_cast<TargetMapper::TargetId>(target_pose_fbs->id()) <
+ FLAGS_min_target_id ||
+ static_cast<TargetMapper::TargetId>(target_pose_fbs->id()) >
+ FLAGS_max_target_id) {
+ VLOG(1) << "Skipping tag with invalid id of " << target_pose_fbs->id();
+ continue;
+ }
+
+ // Skip detections with high pose errors
+ if (target_pose_fbs->pose_error() > FLAGS_max_pose_error) {
+ VLOG(1) << "Skipping tag " << target_pose_fbs->id()
+ << " due to pose error of " << target_pose_fbs->pose_error();
+ continue;
+ }
+ // Skip detections with high pose error ratios
+ if (target_pose_fbs->pose_error_ratio() > FLAGS_max_pose_error_ratio) {
+ VLOG(1) << "Skipping tag " << target_pose_fbs->id()
+ << " due to pose error ratio of "
+ << target_pose_fbs->pose_error_ratio();
+ continue;
+ }
+
+ const TargetMapper::TargetPose target_pose =
+ PoseUtils::TargetPoseFromFbs(*target_pose_fbs);
+
+ Eigen::Affine3d H_camera_target =
+ Eigen::Translation3d(target_pose.pose.p) * target_pose.pose.q;
+ Eigen::Affine3d H_robot_target =
+ CameraToRobotDetection(H_camera_target, extrinsics);
+
+ ceres::examples::Pose3d target_pose_camera =
+ PoseUtils::Affine3dToPose3d(H_camera_target);
+ double distance_from_camera = target_pose_camera.p.norm();
+ double distortion_factor = target_pose_fbs->distortion_factor();
+
+ if (distance_from_camera > 5.0) {
+ continue;
+ }
+
+ CHECK(map.has_monotonic_timestamp_ns())
+ << "Need detection timestamps for mapping";
+
+ timestamped_target_detections_.emplace_back(
+ DataAdapter::TimestampedDetection{
+ .time = node_distributed_time,
+ .H_robot_target = H_robot_target,
+ .distance_from_camera = distance_from_camera,
+ .distortion_factor = distortion_factor,
+ .id = static_cast<TargetMapper::TargetId>(target_pose.id)});
+
+ if (FLAGS_visualize_solver) {
+ // If we've already drawn this camera_name in the current image,
+ // display the image before clearing and adding the new poses
+ if (drawn_nodes_.count(camera_name) != 0) {
+ display_count_++;
+ cv::putText(vis_robot_.image_,
+ "Poses #" + std::to_string(display_count_),
+ cv::Point(600, 10), cv::FONT_HERSHEY_PLAIN, 1.0,
+ cv::Scalar(255, 255, 255));
+
+ if (display_count_ >= FLAGS_skip_to) {
+ VLOG(1) << "Showing image for node " << camera_name
+ << " since we've drawn it already";
+ cv::imshow("View", vis_robot_.image_);
+ // Pause if delta_T is too large, but only after first image (to make
+ // sure the delta's are correct
+ if (max_delta_T_world_robot_ > FLAGS_pause_on_distance &&
+ display_count_ > 1) {
+ LOG(INFO) << "Pausing since the delta between robot estimates is "
+ << max_delta_T_world_robot_ << " which is > threshold of "
+ << FLAGS_pause_on_distance;
+ cv::waitKey(0);
+ } else {
+ cv::waitKey(FLAGS_wait_key);
+ }
+ max_delta_T_world_robot_ = 0.0;
+ } else {
+ VLOG(1) << "At poses #" << std::to_string(display_count_);
+ }
+ vis_robot_.ClearImage();
+ drawn_nodes_.clear();
+ }
+
+ Eigen::Affine3d H_world_target = PoseUtils::Pose3dToAffine3d(
+ kFixedTargetMapper.GetTargetPoseById(target_pose_fbs->id())->pose);
+ Eigen::Affine3d H_world_robot = H_world_target * H_robot_target.inverse();
+ VLOG(2) << camera_name << ", id " << target_pose_fbs->id()
+ << ", t = " << node_distributed_time
+ << ", pose_error = " << target_pose_fbs->pose_error()
+ << ", pose_error_ratio = " << target_pose_fbs->pose_error_ratio()
+ << ", robot_pos (x,y,z) = "
+ << H_world_robot.translation().transpose();
+
+ label << "id " << target_pose_fbs->id() << ": err (% of max): "
+ << (target_pose_fbs->pose_error() / FLAGS_max_pose_error)
+ << " err_ratio: " << target_pose_fbs->pose_error_ratio() << " ";
+
+ vis_robot_.DrawRobotOutline(H_world_robot, camera_name,
+ kOrinColors.at(camera_name));
+ vis_robot_.DrawFrameAxes(H_world_target,
+ std::to_string(target_pose_fbs->id()),
+ kOrinColors.at(camera_name));
+
+ double delta_T_world_robot =
+ (H_world_robot.translation() - last_H_world_robot_.translation())
+ .norm();
+ max_delta_T_world_robot_ =
+ std::max(delta_T_world_robot, max_delta_T_world_robot_);
+
+ VLOG(1) << "Drew in info for robot " << camera_name << " and target #"
+ << target_pose_fbs->id();
+ drew = true;
+ last_draw_time_ = node_distributed_time;
+ last_H_world_robot_ = H_world_robot;
+ }
+ }
+ if (FLAGS_visualize_solver) {
+ if (drew) {
+ // Collect all the labels from a given node, and add the text
+ size_t pi_number =
+ static_cast<size_t>(camera_name[camera_name.size() - 1] - '0');
+ cv::putText(vis_robot_.image_, label.str(),
+ cv::Point(10, 10 + 20 * pi_number), cv::FONT_HERSHEY_PLAIN,
+ 1.0, kOrinColors.at(camera_name));
+
+ drawn_nodes_.emplace(camera_name);
+ } else if (node_distributed_time - last_draw_time_ >
+ std::chrono::milliseconds(30) &&
+ display_count_ >= FLAGS_skip_to) {
+ cv::putText(vis_robot_.image_, "No detections", cv::Point(10, 0),
+ cv::FONT_HERSHEY_PLAIN, 1.0, kOrinColors.at(camera_name));
+ // Display and clear the image if we haven't draw in a while
+ VLOG(1) << "Displaying image due to time lapse";
+ cv::imshow("View", vis_robot_.image_);
+ cv::waitKey(FLAGS_wait_key);
+ vis_robot_.ClearImage();
+ max_delta_T_world_robot_ = 0.0;
+ drawn_nodes_.clear();
+ }
+ }
+}
+
+void TargetMapperReplay::HandleNodeCaptures(
+ aos::EventLoop *mapping_event_loop,
+ frc971::constants::ConstantsFetcher<y2024::Constants> *constants_fetcher,
+ int camera_number) {
+ // Get the camera extrinsics
+ const auto *calibration = FindCameraCalibration(
+ constants_fetcher->constants(),
+ mapping_event_loop->node()->name()->string_view(), camera_number);
+ cv::Mat extrinsics_cv = frc971::vision::CameraExtrinsics(calibration).value();
+ Eigen::Matrix4d extrinsics_matrix;
+ cv::cv2eigen(extrinsics_cv, extrinsics_matrix);
+ const auto extrinsics = Eigen::Affine3d(extrinsics_matrix);
+ std::string camera_name = absl::StrFormat(
+ "/%s/camera%d", mapping_event_loop->node()->name()->str(), camera_number);
+
+ mapping_event_loop->MakeWatcher(
+ camera_name.c_str(), [this, mapping_event_loop, extrinsics,
+ camera_name](const TargetMap &map) {
+ aos::distributed_clock::time_point node_distributed_time =
+ reader_->event_loop_factory()
+ ->GetNodeEventLoopFactory(mapping_event_loop->node())
+ ->ToDistributedClock(aos::monotonic_clock::time_point(
+ aos::monotonic_clock::duration(
+ map.monotonic_timestamp_ns())));
+
+ HandleAprilTags(map, node_distributed_time, camera_name, extrinsics);
+ });
+}
+
+void TargetMapperReplay::MaybeSolve() {
+ if (FLAGS_solve) {
+ auto target_constraints =
+ DataAdapter::MatchTargetDetections(timestamped_target_detections_);
+
+ // Remove constraints between the two sides of the field - these are
+ // basically garbage because of how far the camera is. We will use seeding
+ // below to connect the two sides
+ target_constraints.erase(
+ std::remove_if(target_constraints.begin(), target_constraints.end(),
+ [](const auto &constraint) {
+ constexpr TargetMapper::TargetId kMaxRedId = 4;
+ TargetMapper::TargetId min_id =
+ std::min(constraint.id_begin, constraint.id_end);
+ TargetMapper::TargetId max_id =
+ std::max(constraint.id_begin, constraint.id_end);
+ return (min_id <= kMaxRedId && max_id > kMaxRedId);
+ }),
+ target_constraints.end());
+
+ LOG(INFO) << "Solving for locations of tags with "
+ << target_constraints.size() << " constraints";
+ TargetMapper mapper(FLAGS_json_path, target_constraints);
+ mapper.Solve(FLAGS_field_name, FLAGS_output_dir);
+
+ if (!FLAGS_dump_constraints_to.empty()) {
+ mapper.DumpConstraints(FLAGS_dump_constraints_to);
+ }
+ if (!FLAGS_dump_stats_to.empty()) {
+ mapper.DumpStats(FLAGS_dump_stats_to);
+ }
+ }
+}
+
+void MappingMain(int argc, char *argv[]) {
+ std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections;
+
+ std::optional<aos::FlatbufferDetachedBuffer<aos::Configuration>> config =
+ (FLAGS_config.empty()
+ ? std::nullopt
+ : std::make_optional(aos::configuration::ReadConfig(FLAGS_config)));
+
+ // Open logfiles
+ aos::logger::LogReader reader(
+ aos::logger::SortParts(aos::logger::FindLogs(argc, argv)),
+ config.has_value() ? &config->message() : nullptr);
+
+ TargetMapperReplay mapper_replay(&reader);
+ reader.event_loop_factory()->Run();
+ mapper_replay.MaybeSolve();
+}
+
+} // namespace y2024::vision
+
+int main(int argc, char **argv) {
+ aos::InitGoogle(&argc, &argv);
+ y2024::vision::MappingMain(argc, argv);
+}
diff --git a/y2024/vision/viewer.cc b/y2024/vision/viewer.cc
index 6f83ec2..d5ada14 100644
--- a/y2024/vision/viewer.cc
+++ b/y2024/vision/viewer.cc
@@ -85,8 +85,11 @@
frc971::constants::ConstantsFetcher<y2024::Constants> constants_fetcher(
&event_loop);
+ CHECK(FLAGS_channel.length() == 8);
+ int camera_id = std::stoi(FLAGS_channel.substr(7, 1));
const auto *calibration_data = FindCameraCalibration(
- constants_fetcher.constants(), event_loop.node()->name()->string_view());
+ constants_fetcher.constants(), event_loop.node()->name()->string_view(),
+ camera_id);
const cv::Mat intrinsics = frc971::vision::CameraIntrinsics(calibration_data);
const cv::Mat dist_coeffs =
frc971::vision::CameraDistCoeffs(calibration_data);
diff --git a/y2024/vision/vision_util.cc b/y2024/vision/vision_util.cc
index 4c196ce..6acd234 100644
--- a/y2024/vision/vision_util.cc
+++ b/y2024/vision/vision_util.cc
@@ -5,17 +5,20 @@
namespace y2024::vision {
const frc971::vision::calibration::CameraCalibration *FindCameraCalibration(
- const y2024::Constants &calibration_data, std::string_view node_name) {
+ const y2024::Constants &calibration_data, std::string_view node_name,
+ int camera_number) {
CHECK(calibration_data.has_cameras());
for (const y2024::CameraConfiguration *candidate :
*calibration_data.cameras()) {
CHECK(candidate->has_calibration());
- if (candidate->calibration()->node_name()->string_view() != node_name) {
+ if (candidate->calibration()->node_name()->string_view() != node_name ||
+ candidate->calibration()->camera_number() != camera_number) {
continue;
}
return candidate->calibration();
}
- LOG(FATAL) << ": Failed to find camera calibration for " << node_name;
+ LOG(FATAL) << ": Failed to find camera calibration for " << node_name
+ << " and camera number " << camera_number;
}
} // namespace y2024::vision
diff --git a/y2024/vision/vision_util.h b/y2024/vision/vision_util.h
index d8fa562..c903760 100644
--- a/y2024/vision/vision_util.h
+++ b/y2024/vision/vision_util.h
@@ -9,7 +9,8 @@
namespace y2024::vision {
const frc971::vision::calibration::CameraCalibration *FindCameraCalibration(
- const y2024::Constants &calibration_data, std::string_view node_name);
+ const y2024::Constants &calibration_data, std::string_view node_name,
+ int camera_number);
} // namespace y2024::vision
diff --git a/y2024/wpilib_interface.cc b/y2024/wpilib_interface.cc
index 4a6c00b..d286939 100644
--- a/y2024/wpilib_interface.cc
+++ b/y2024/wpilib_interface.cc
@@ -77,12 +77,24 @@
constexpr double kMaxBringupPower = 12.0;
-double intake_pot_translate(double voltage) {
- return voltage * Values::kIntakePivotPotRadiansPerVolt();
+double climber_pot_translate(double voltage) {
+ return -1 * voltage * Values::kClimberPotMetersPerVolt();
}
-double climber_pot_translate(double voltage) {
- return voltage * Values::kClimberPotRadiansPerVolt();
+double extend_pot_translate(double voltage) {
+ return voltage * Values::kExtendPotMetersPerVolt();
+}
+
+double catapult_pot_translate(double voltage) {
+ return voltage * Values::kCatapultPotRadiansPerVolt();
+}
+
+double turret_pot_translate(double voltage) {
+ return -1 * voltage * Values::kTurretPotRadiansPerVolt();
+}
+
+double altitude_pot_translate(double voltage) {
+ return -1 * voltage * Values::kAltitudePotRadiansPerVolt();
}
double drivetrain_velocity_translate(double in) {
@@ -96,7 +108,10 @@
Values::kMaxDrivetrainEncoderPulsesPerSecond(),
Values::kMaxIntakePivotEncoderPulsesPerSecond(),
Values::kMaxClimberEncoderPulsesPerSecond(),
+ Values::kMaxExtendEncoderPulsesPerSecond(),
+ Values::kMaxCatapultEncoderPulsesPerSecond(),
});
+
static_assert(kMaxFastEncoderPulsesPerSecond <= 1300000,
"fast encoders are too fast");
@@ -120,8 +135,15 @@
::frc971::control_loops::drivetrain::PositionStatic>(
"/drivetrain")),
gyro_sender_(event_loop->MakeSender<::frc971::sensors::GyroReading>(
- "/drivetrain")){};
- void Start() override { AddToDMA(&imu_yaw_rate_reader_); }
+ "/drivetrain")) {
+ UpdateFastEncoderFilterHz(kMaxFastEncoderPulsesPerSecond);
+ event_loop->SetRuntimeAffinity(aos::MakeCpusetFromCpus({0}));
+ };
+ void Start() override {
+ AddToDMA(&imu_yaw_rate_reader_);
+ AddToDMA(&turret_encoder_.reader());
+ AddToDMA(&altitude_encoder_.reader());
+ }
// Auto mode switches.
void set_autonomous_mode(int i, ::std::unique_ptr<frc::DigitalInput> sensor) {
@@ -140,26 +162,56 @@
CopyPosition(intake_pivot_encoder_, builder->add_intake_pivot(),
Values::kIntakePivotEncoderCountsPerRevolution(),
- Values::kIntakePivotEncoderRatio(), intake_pot_translate,
- true,
- robot_constants_->robot()
- ->intake_constants()
- ->potentiometer_offset());
+ Values::kIntakePivotEncoderRatio(), /* reversed: */ false);
CopyPosition(climber_encoder_, builder->add_climber(),
Values::kClimberEncoderCountsPerRevolution(),
- Values::kClimberEncoderRatio(), climber_pot_translate, true,
+ Values::kClimberEncoderMetersPerRadian(),
+ climber_pot_translate, false,
robot_constants_->robot()
->climber_constants()
->potentiometer_offset());
+ CopyPosition(extend_encoder_, builder->add_extend(),
+ Values::kExtendEncoderCountsPerRevolution(),
+ Values::kExtendEncoderMetersPerRadian(),
+ extend_pot_translate, false,
+ robot_constants_->robot()
+ ->extend_constants()
+ ->potentiometer_offset());
+
+ CopyPosition(catapult_encoder_, builder->add_catapult(),
+ Values::kCatapultEncoderCountsPerRevolution(),
+ Values::kCatapultEncoderRatio(), catapult_pot_translate,
+ true,
+ robot_constants_->robot()
+ ->catapult_constants()
+ ->potentiometer_offset());
+
+ CopyPosition(turret_encoder_, builder->add_turret(),
+ Values::kTurretEncoderCountsPerRevolution(),
+ Values::kTurretEncoderRatio(), turret_pot_translate, true,
+ robot_constants_->robot()
+ ->turret_constants()
+ ->potentiometer_offset());
+
+ CopyPosition(altitude_encoder_, builder->add_altitude(),
+ Values::kAltitudeEncoderCountsPerRevolution(),
+ Values::kAltitudeEncoderRatio(), altitude_pot_translate,
+ true,
+ robot_constants_->robot()
+ ->altitude_constants()
+ ->potentiometer_offset());
+
builder->set_transfer_beambreak(transfer_beam_break_->Get());
+ builder->set_extend_beambreak(extend_beam_break_->Get());
+ builder->set_catapult_beambreak(catapult_beam_break_->Get());
builder.CheckOk(builder.Send());
}
SendDrivetrainPosition(drivetrain_position_sender_.MakeStaticBuilder(),
drivetrain_velocity_translate,
- constants::Values::DrivetrainEncoderToMeters, false,
+ constants::Values::DrivetrainEncoderToMeters, true,
false);
{
@@ -212,18 +264,24 @@
}
void set_intake_pivot(::std::unique_ptr<frc::Encoder> encoder,
- ::std::unique_ptr<frc::DigitalInput> absolute_pwm,
- ::std::unique_ptr<frc::AnalogInput> potentiometer) {
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm) {
fast_encoder_filter_.Add(encoder.get());
intake_pivot_encoder_.set_encoder(::std::move(encoder));
intake_pivot_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
- intake_pivot_encoder_.set_potentiometer(::std::move(potentiometer));
}
void set_transfer_beambreak(::std::unique_ptr<frc::DigitalInput> sensor) {
transfer_beam_break_ = ::std::move(sensor);
}
+ void set_extend_beambreak(::std::unique_ptr<frc::DigitalInput> sensor) {
+ extend_beam_break_ = ::std::move(sensor);
+ }
+
+ void set_catapult_beambreak(::std::unique_ptr<frc::DigitalInput> sensor) {
+ catapult_beam_break_ = ::std::move(sensor);
+ }
+
void set_climber(::std::unique_ptr<frc::Encoder> encoder,
::std::unique_ptr<frc::DigitalInput> absolute_pwm,
::std::unique_ptr<frc::AnalogInput> potentiometer) {
@@ -233,6 +291,42 @@
climber_encoder_.set_potentiometer(::std::move(potentiometer));
}
+ void set_extend(::std::unique_ptr<frc::Encoder> encoder,
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm,
+ ::std::unique_ptr<frc::AnalogInput> potentiometer) {
+ fast_encoder_filter_.Add(encoder.get());
+ extend_encoder_.set_encoder(::std::move(encoder));
+ extend_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
+ extend_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_catapult(::std::unique_ptr<frc::Encoder> encoder,
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm,
+ ::std::unique_ptr<frc::AnalogInput> potentiometer) {
+ fast_encoder_filter_.Add(encoder.get());
+ catapult_encoder_.set_encoder(::std::move(encoder));
+ catapult_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
+ catapult_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_turret(::std::unique_ptr<frc::Encoder> encoder,
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm,
+ ::std::unique_ptr<frc::AnalogInput> potentiometer) {
+ fast_encoder_filter_.Add(encoder.get());
+ turret_encoder_.set_encoder(::std::move(encoder));
+ turret_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
+ turret_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ void set_altitude(::std::unique_ptr<frc::Encoder> encoder,
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm,
+ ::std::unique_ptr<frc::AnalogInput> potentiometer) {
+ fast_encoder_filter_.Add(encoder.get());
+ altitude_encoder_.set_encoder(::std::move(encoder));
+ altitude_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
+ altitude_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
private:
const Constants *robot_constants_;
@@ -244,12 +338,51 @@
std::array<std::unique_ptr<frc::DigitalInput>, 2> autonomous_modes_;
- std::unique_ptr<frc::DigitalInput> imu_yaw_rate_input_, transfer_beam_break_;
+ std::unique_ptr<frc::DigitalInput> imu_yaw_rate_input_, transfer_beam_break_,
+ extend_beam_break_, catapult_beam_break_;
- frc971::wpilib::AbsoluteEncoderAndPotentiometer intake_pivot_encoder_;
- frc971::wpilib::AbsoluteEncoderAndPotentiometer climber_encoder_;
+ frc971::wpilib::AbsoluteEncoder intake_pivot_encoder_;
+ frc971::wpilib::AbsoluteEncoderAndPotentiometer climber_encoder_,
+ catapult_encoder_, extend_encoder_;
frc971::wpilib::DMAPulseWidthReader imu_yaw_rate_reader_;
+
+ frc971::wpilib::DMAAbsoluteEncoderAndPotentiometer turret_encoder_,
+ altitude_encoder_;
+};
+
+class SuperstructurePWMWriter
+ : public ::frc971::wpilib::LoopOutputHandler<superstructure::Output> {
+ public:
+ SuperstructurePWMWriter(aos::EventLoop *event_loop)
+ : frc971::wpilib::LoopOutputHandler<superstructure::Output>(
+ event_loop, "/superstructure") {}
+
+ void set_catapult_kraken_one(::std::unique_ptr<::frc::TalonFX> t) {
+ catapult_kraken_one_ = ::std::move(t);
+ }
+ void set_catapult_kraken_two(::std::unique_ptr<::frc::TalonFX> t) {
+ catapult_kraken_two_ = ::std::move(t);
+ }
+
+ private:
+ void Stop() override {
+ AOS_LOG(WARNING, "Superstructure output too old.\n");
+ catapult_kraken_one_->SetDisabled();
+ catapult_kraken_two_->SetDisabled();
+ }
+
+ void Write(const superstructure::Output &output) override {
+ WritePwm(output.catapult_voltage(), catapult_kraken_one_.get());
+ WritePwm(output.catapult_voltage(), catapult_kraken_two_.get());
+ }
+
+ template <typename T>
+ static void WritePwm(const double voltage, T *motor) {
+ motor->SetSpeed(std::clamp(voltage, -kMaxBringupPower, kMaxBringupPower) /
+ 12.0);
+ }
+ ::std::unique_ptr<::frc::TalonFX> catapult_kraken_one_, catapult_kraken_two_;
};
class WPILibRobot : public ::frc971::wpilib::WPILibRobotBase {
@@ -270,6 +403,8 @@
&constant_fetcher_event_loop);
const Constants *robot_constants = &constants_fetcher.constants();
+ AddLoop(&constant_fetcher_event_loop);
+
// Thread 1.
::aos::ShmEventLoop joystick_sender_event_loop(&config.message());
::frc971::wpilib::JoystickSender joystick_sender(
@@ -285,18 +420,30 @@
::aos::ShmEventLoop sensor_reader_event_loop(&config.message());
SensorReader sensor_reader(&sensor_reader_event_loop, robot_constants);
sensor_reader.set_pwm_trigger(true);
- sensor_reader.set_drivetrain_left_encoder(make_encoder(1));
- sensor_reader.set_drivetrain_right_encoder(make_encoder(0));
- sensor_reader.set_yaw_rate_input(make_unique<frc::DigitalInput>(0));
- // TODO: (niko) change values once robot is wired
- sensor_reader.set_intake_pivot(make_encoder(4),
- make_unique<frc::DigitalInput>(4),
- make_unique<frc::AnalogInput>(4));
- sensor_reader.set_transfer_beambreak(make_unique<frc::DigitalInput>(7));
+ sensor_reader.set_drivetrain_left_encoder(
+ std::make_unique<frc::Encoder>(8, 9));
+ sensor_reader.set_drivetrain_right_encoder(
+ std::make_unique<frc::Encoder>(6, 7));
+ sensor_reader.set_yaw_rate_input(make_unique<frc::DigitalInput>(25));
+ sensor_reader.set_intake_pivot(make_encoder(3),
+ make_unique<frc::DigitalInput>(3));
+ sensor_reader.set_transfer_beambreak(make_unique<frc::DigitalInput>(23));
+ sensor_reader.set_extend_beambreak(make_unique<frc::DigitalInput>(24));
+ sensor_reader.set_catapult_beambreak(make_unique<frc::DigitalInput>(22));
- sensor_reader.set_climber(make_encoder(5),
- make_unique<frc::DigitalInput>(5),
- make_unique<frc::AnalogInput>(5));
+ sensor_reader.set_climber(make_encoder(4),
+ make_unique<frc::DigitalInput>(4),
+ make_unique<frc::AnalogInput>(4));
+ sensor_reader.set_extend(make_encoder(5), make_unique<frc::DigitalInput>(5),
+ make_unique<frc::AnalogInput>(5));
+ sensor_reader.set_catapult(make_encoder(0),
+ make_unique<frc::DigitalInput>(0),
+ make_unique<frc::AnalogInput>(0));
+ sensor_reader.set_turret(make_encoder(2), make_unique<frc::DigitalInput>(2),
+ make_unique<frc::AnalogInput>(2));
+ sensor_reader.set_altitude(make_encoder(1),
+ make_unique<frc::DigitalInput>(1),
+ make_unique<frc::AnalogInput>(1));
AddLoop(&sensor_reader_event_loop);
@@ -307,44 +454,72 @@
c_Phoenix_Diagnostics_Dispose();
}
- std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry;
+ std::vector<ctre::phoenix6::BaseStatusSignal *> canivore_signal_registry;
+ std::vector<ctre::phoenix6::BaseStatusSignal *> rio_signal_registry;
const CurrentLimits *current_limits =
robot_constants->common()->current_limits();
std::shared_ptr<TalonFX> right_front = std::make_shared<TalonFX>(
- 0, false, "Drivetrain Bus", &signals_registry,
+ 2, true, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> right_back = std::make_shared<TalonFX>(
- 1, false, "Drivetrain Bus", &signals_registry,
+ 1, true, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> left_front = std::make_shared<TalonFX>(
- 2, false, "Drivetrain Bus", &signals_registry,
+ 4, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> left_back = std::make_shared<TalonFX>(
- 3, false, "Drivetrain Bus", &signals_registry,
+ 5, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> intake_pivot = std::make_shared<TalonFX>(
- 4, false, "Drivetrain Bus", &signals_registry,
+ 6, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->intake_pivot_stator_current_limit(),
current_limits->intake_pivot_supply_current_limit());
- std::shared_ptr<TalonFX> intake_roller = std::make_shared<TalonFX>(
- 5, false, "Drivetrain Bus", &signals_registry,
- current_limits->intake_roller_stator_current_limit(),
- current_limits->intake_roller_supply_current_limit());
- std::shared_ptr<TalonFX> transfer_roller = std::make_shared<TalonFX>(
- 6, false, "Drivetrain Bus", &signals_registry,
- current_limits->transfer_roller_stator_current_limit(),
- current_limits->transfer_roller_supply_current_limit());
-
+ std::shared_ptr<TalonFX> altitude = std::make_shared<TalonFX>(
+ 9, false, "Drivetrain Bus", &canivore_signal_registry,
+ current_limits->altitude_stator_current_limit(),
+ current_limits->altitude_supply_current_limit());
+ std::shared_ptr<TalonFX> turret = std::make_shared<TalonFX>(
+ 3, true, "Drivetrain Bus", &canivore_signal_registry,
+ current_limits->turret_stator_current_limit(),
+ current_limits->turret_supply_current_limit());
std::shared_ptr<TalonFX> climber = std::make_shared<TalonFX>(
- 7, false, "Drivetrain Bus", &signals_registry,
+ 7, true, "rio", &rio_signal_registry,
current_limits->climber_stator_current_limit(),
current_limits->climber_supply_current_limit());
+ std::shared_ptr<TalonFX> extend = std::make_shared<TalonFX>(
+ 12, false, "Drivetrain Bus", &canivore_signal_registry,
+ current_limits->extend_stator_current_limit(),
+ current_limits->extend_supply_current_limit());
+ std::shared_ptr<TalonFX> intake_roller = std::make_shared<TalonFX>(
+ 8, false, "rio", &rio_signal_registry,
+ current_limits->intake_roller_stator_current_limit(),
+ current_limits->intake_roller_supply_current_limit());
+ std::shared_ptr<TalonFX> retention_roller = std::make_shared<TalonFX>(
+ 10, true, "rio", &rio_signal_registry,
+ current_limits->retention_roller_stator_current_limit(),
+ current_limits->retention_roller_supply_current_limit());
+ std::shared_ptr<TalonFX> transfer_roller = std::make_shared<TalonFX>(
+ 11, true, "rio", &rio_signal_registry,
+ current_limits->transfer_roller_stator_current_limit(),
+ current_limits->transfer_roller_supply_current_limit());
+ std::shared_ptr<TalonFX> extend_roller = std::make_shared<TalonFX>(
+ 13, true, "rio", &rio_signal_registry,
+ current_limits->extend_roller_stator_current_limit(),
+ current_limits->extend_roller_supply_current_limit());
+ std::shared_ptr<TalonFX> catapult_one = std::make_shared<TalonFX>(
+ 14, false, "Drivetrain Bus", &canivore_signal_registry,
+ current_limits->catapult_stator_current_limit(),
+ current_limits->catapult_supply_current_limit());
+ std::shared_ptr<TalonFX> catapult_two = std::make_shared<TalonFX>(
+ 15, false, "Drivetrain Bus", &canivore_signal_registry,
+ current_limits->catapult_stator_current_limit(),
+ current_limits->catapult_supply_current_limit());
ctre::phoenix::platform::can::CANComm_SetRxSchedPriority(
constants::Values::kDrivetrainRxPriority, true, "Drivetrain Bus");
@@ -354,18 +529,27 @@
::aos::ShmEventLoop can_sensor_reader_event_loop(&config.message());
can_sensor_reader_event_loop.set_name("CANSensorReader");
+ ::aos::ShmEventLoop rio_sensor_reader_event_loop(&config.message());
+ rio_sensor_reader_event_loop.set_name("RioSensorReader");
+
// Creating list of talonfx for CANSensorReader
std::vector<std::shared_ptr<TalonFX>> drivetrain_talonfxs;
- std::vector<std::shared_ptr<TalonFX>> talonfxs;
+ std::vector<std::shared_ptr<TalonFX>> canivore_talonfxs;
+ std::vector<std::shared_ptr<TalonFX>> rio_talonfxs;
for (auto talonfx : {right_front, right_back, left_front, left_back}) {
drivetrain_talonfxs.push_back(talonfx);
- talonfxs.push_back(talonfx);
+ canivore_talonfxs.push_back(talonfx);
}
for (auto talonfx :
- {intake_pivot, intake_roller, transfer_roller, climber}) {
- talonfxs.push_back(talonfx);
+ {intake_pivot, turret, altitude, catapult_one, catapult_two, extend}) {
+ canivore_talonfxs.push_back(talonfx);
+ }
+
+ for (auto talonfx : {intake_roller, transfer_roller, climber, extend_roller,
+ retention_roller}) {
+ rio_talonfxs.push_back(talonfx);
}
aos::Sender<frc971::control_loops::drivetrain::CANPositionStatic>
@@ -378,22 +562,21 @@
superstructure_can_position_sender =
can_sensor_reader_event_loop.MakeSender<
y2024::control_loops::superstructure::CANPositionStatic>(
- "/superstructure");
+ "/superstructure/canivore");
- frc971::wpilib::CANSensorReader can_sensor_reader(
- &can_sensor_reader_event_loop, std::move(signals_registry), talonfxs,
- [drivetrain_talonfxs, &intake_pivot, &intake_roller, &transfer_roller,
- &climber, &drivetrain_can_position_sender,
- &superstructure_can_position_sender](
- ctre::phoenix::StatusCode status) {
+ frc971::wpilib::CANSensorReader canivore_can_sensor_reader(
+ &can_sensor_reader_event_loop, std::move(canivore_signal_registry),
+ canivore_talonfxs,
+ [drivetrain_talonfxs, &intake_pivot, &turret, &altitude, &catapult_one,
+ &catapult_two, &drivetrain_can_position_sender,
+ &superstructure_can_position_sender,
+ &extend](ctre::phoenix::StatusCode status) {
aos::Sender<frc971::control_loops::drivetrain::CANPositionStatic>::
StaticBuilder drivetrain_can_builder =
drivetrain_can_position_sender.MakeStaticBuilder();
auto drivetrain_falcon_vector =
- drivetrain_can_builder->add_talonfxs();
-
- CHECK(drivetrain_falcon_vector->reserve(drivetrain_talonfxs.size()));
+ CHECK_NOTNULL(drivetrain_can_builder->add_talonfxs());
for (auto talonfx : drivetrain_talonfxs) {
talonfx->SerializePosition(
@@ -411,26 +594,69 @@
StaticBuilder superstructure_can_builder =
superstructure_can_position_sender.MakeStaticBuilder();
- intake_roller->SerializePosition(
- superstructure_can_builder->add_intake_roller(),
- control_loops::drivetrain::kHighOutputRatio);
intake_pivot->SerializePosition(
superstructure_can_builder->add_intake_pivot(),
- control_loops::drivetrain::kHighOutputRatio);
+ control_loops::superstructure::intake_pivot::kOutputRatio);
+ turret->SerializePosition(
+ superstructure_can_builder->add_turret(),
+ control_loops::superstructure::turret::kOutputRatio);
+ altitude->SerializePosition(
+ superstructure_can_builder->add_altitude(),
+ control_loops::superstructure::altitude::kOutputRatio);
+ catapult_one->SerializePosition(
+ superstructure_can_builder->add_catapult_one(),
+ control_loops::superstructure::catapult::kOutputRatio);
+ catapult_two->SerializePosition(
+ superstructure_can_builder->add_catapult_two(),
+ control_loops::superstructure::catapult::kOutputRatio);
+ extend->SerializePosition(superstructure_can_builder->add_extend(),
+ superstructure::extend::kOutputRatio);
+
+ superstructure_can_builder->set_timestamp(
+ intake_pivot->GetTimestamp());
+ superstructure_can_builder->set_status(static_cast<int>(status));
+ superstructure_can_builder.CheckOk(superstructure_can_builder.Send());
+ });
+
+ aos::Sender<y2024::control_loops::superstructure::CANPositionStatic>
+ superstructure_rio_position_sender =
+ rio_sensor_reader_event_loop.MakeSender<
+ y2024::control_loops::superstructure::CANPositionStatic>(
+ "/superstructure/rio");
+
+ frc971::wpilib::CANSensorReader rio_can_sensor_reader(
+ &rio_sensor_reader_event_loop, std::move(rio_signal_registry),
+ rio_talonfxs,
+ [&intake_roller, &transfer_roller, &climber, &extend_roller,
+ &retention_roller, &superstructure_rio_position_sender](
+ ctre::phoenix::StatusCode status) {
+ aos::Sender<y2024::control_loops::superstructure::CANPositionStatic>::
+ StaticBuilder superstructure_can_builder =
+ superstructure_rio_position_sender.MakeStaticBuilder();
+
+ intake_roller->SerializePosition(
+ superstructure_can_builder->add_intake_roller(),
+ constants::Values::kIntakeRollerOutputRatio);
transfer_roller->SerializePosition(
superstructure_can_builder->add_transfer_roller(),
- control_loops::drivetrain::kHighOutputRatio);
- climber->SerializePosition(
- superstructure_can_builder->add_climber(),
- control_loops::drivetrain::kHighOutputRatio);
+ constants::Values::kIntakeRollerOutputRatio);
+ climber->SerializePosition(superstructure_can_builder->add_climber(),
+ superstructure::climber::kOutputRatio);
+ extend_roller->SerializePosition(
+ superstructure_can_builder->add_extend_roller(),
+ constants::Values::kExtendRollerOutputRatio);
+ retention_roller->SerializePosition(
+ superstructure_can_builder->add_retention_roller(), 1.0);
superstructure_can_builder->set_timestamp(
intake_roller->GetTimestamp());
superstructure_can_builder->set_status(static_cast<int>(status));
superstructure_can_builder.CheckOk(superstructure_can_builder.Send());
- });
+ },
+ frc971::wpilib::CANSensorReader::SignalSync::kNoSync);
AddLoop(&can_sensor_reader_event_loop);
+ AddLoop(&rio_sensor_reader_event_loop);
// Thread 5.
::aos::ShmEventLoop can_output_event_loop(&config.message());
@@ -447,21 +673,44 @@
&talonfx_map) {
talonfx_map.find("intake_pivot")
->second->WriteVoltage(output.intake_pivot_voltage());
+ talonfx_map.find("altitude")
+ ->second->WriteVoltage(output.altitude_voltage());
+ talonfx_map.find("catapult_one")
+ ->second->WriteVoltage(output.catapult_voltage());
+ talonfx_map.find("catapult_two")
+ ->second->WriteVoltage(output.catapult_voltage());
+ talonfx_map.find("turret")->second->WriteVoltage(
+ output.turret_voltage());
+ talonfx_map.find("climber")->second->WriteVoltage(
+ output.climber_voltage());
+ talonfx_map.find("extend")->second->WriteVoltage(
+ output.extend_voltage());
talonfx_map.find("intake_roller")
->second->WriteVoltage(output.intake_roller_voltage());
talonfx_map.find("transfer_roller")
->second->WriteVoltage(output.transfer_roller_voltage());
- talonfx_map.find("climber")->second->WriteVoltage(
- output.climber_voltage());
+ talonfx_map.find("extend_roller")
+ ->second->WriteVoltage(output.extend_roller_voltage());
+ talonfx_map.find("retention_roller")
+ ->second->WriteCurrent(
+ output.retention_roller_stator_current_limit(),
+ output.retention_roller_voltage());
});
can_drivetrain_writer.set_talonfxs({right_front, right_back},
{left_front, left_back});
can_superstructure_writer.add_talonfx("intake_pivot", intake_pivot);
+ can_superstructure_writer.add_talonfx("altitude", altitude);
+ can_superstructure_writer.add_talonfx("catapult_one", catapult_one);
+ can_superstructure_writer.add_talonfx("catapult_two", catapult_two);
+ can_superstructure_writer.add_talonfx("turret", turret);
+ can_superstructure_writer.add_talonfx("climber", climber);
+ can_superstructure_writer.add_talonfx("extend", extend);
can_superstructure_writer.add_talonfx("intake_roller", intake_roller);
can_superstructure_writer.add_talonfx("transfer_roller", transfer_roller);
- can_superstructure_writer.add_talonfx("climber", climber);
+ can_superstructure_writer.add_talonfx("extend_roller", extend_roller);
+ can_superstructure_writer.add_talonfx("retention_roller", retention_roller);
can_output_event_loop.MakeWatcher(
"/roborio", [&can_drivetrain_writer, &can_superstructure_writer](
@@ -472,6 +721,14 @@
AddLoop(&can_output_event_loop);
+ ::aos::ShmEventLoop pwm_event_loop(&config.message());
+ SuperstructurePWMWriter superstructure_pwm_writer(&pwm_event_loop);
+ superstructure_pwm_writer.set_catapult_kraken_one(
+ make_unique<frc::TalonFX>(0));
+ superstructure_pwm_writer.set_catapult_kraken_two(
+ make_unique<frc::TalonFX>(1));
+
+ AddLoop(&pwm_event_loop);
// Thread 6
RunLoops();
diff --git a/y2024/www/field.html b/y2024/www/field.html
index 8c3b291..cfb8778 100644
--- a/y2024/www/field.html
+++ b/y2024/www/field.html
@@ -1,15 +1,30 @@
<html>
- <head>
- <script src="field_main_bundle.min.js" defer></script>
- <link rel="stylesheet" href="styles.css">
- </head>
- <body>
- <div id="field"> </div>
- <div id="legend"> </div>
- <div id="readouts">
+
+<head>
+ <script src="field_main_bundle.min.js" defer></script>
+ <link rel="stylesheet" href="styles.css">
+</head>
+
+<body>
+ <div style="display: grid;
+ grid-template-columns: auto auto auto; gap: 5px;">
+ <div>
+ <div id="field"> </div>
+ <div id="legend"> </div>
+ <div id="vision_readouts">
+ </div>
+ <div id="message_bridge_status">
+ <div>
+ <div>Node</div>
+ <div>Client</div>
+ <div>Server</div>
+ </div>
+ </div>
+ </div>
+ <div>
<table>
<tr>
- <th colspan="2">Robot State</th>
+ <th colspan="2" style="text-align: center;">Robot State</th>
</tr>
<tr>
<td>X</td>
@@ -24,64 +39,233 @@
<td id="theta"> NA </td>
</tr>
</table>
-
<table>
<tr>
- <th colspan="2">Images</th>
+ <th colspan="2" style="text-align: center;">Images</th>
</tr>
<tr>
<td> Images Accepted </td>
<td id="images_accepted"> NA </td>
</tr>
</table>
-
<table>
<tr>
- <th colspan="2">Superstructure</th>
- </tr>
- <!-- TODO: Add superstructure state -->
- </table>
- <table>
- <tr>
- <th colspan="2">Game Piece</th>
- </tr>
- <tr>
- <td>Game Piece Held</td>
- <td id="game_piece"> NA </td>
- </tr>
- <tr>
- <td>Game Piece Position (+ = left, 0 = empty)</td>
- <td id="game_piece_position"> NA </td>
- </tr>
- </table>
+ <th colspan="2" style="text-align: center;">Superstructure States</th>
+ </tr>
+ <tr>
+ <td style="font-weight: bold;">Superstructure State</td>
+ <td id="superstructure_state" style="font-weight: bold;"> NA </td>
+ </tr>
+ <tr>
+ <td style="font-weight: bold;">Note Goal</td>
+ <td id="uncompleted_note_goal" style="font-weight: bold;"> NA </td>
+ </tr>
+ <tr>
+ <td>Catapult State</td>
+ <td id="catapult_state"> NA </td>
+ </tr>
+ <tr>
+ <td>Intake Roller State</td>
+ <td id="intake_roller_state"> NA </td>
+ </tr>
+ <tr>
+ <td>Transfer Roller State</td>
+ <td id="transfer_roller_state"> NA </td>
+ </tr>
+ <tr>
+ <td>Extend State</td>
+ <td id="extend_state"> NA </td>
+ </tr>
+ <tr>
+ <td>Extend Roller State</td>
+ <td id="extend_roller_state"> NA </td>
+ </tr>
+ <tr>
+ <th colspan="2">Beambreaks</th>
+ </tr>
+ <tr>
+ <td>Extend Beambreak</td>
+ <td id="extend_beambreak">FALSE</td>
+ </tr>
+ <tr>
+ <td>Catapult Beambreak</td>
+ <td id="catapult_beambreak">FALSE</td>
+ </tr>
+ <tr>
+ <th colspan="2">Subsytems At Position</th>
+ </tr>
+ <tr>
+ <td>Extend At Retracted Position</td>
+ <td id="extend_at_retracted">FALSE</td>
+ </tr>
+ <tr>
+ <td>Extend Ready For Transfer</td>
+ <td id="extend_ready_for_transfer">FALSE</td>
+ </tr>
+ <tr>
+ <td>Extend Ready to Transfer to Catapult</td>
+ <td id="extend_ready_for_catapult_transfer">FALSE</td>
+ </tr>
+ <tr>
+ <td>Turret at Loading Position</td>
+ <td id="turret_ready_for_load">FALSE</td>
+ </tr>
+ <tr>
+ <td>Altitude at Loading Position</td>
+ <td id="altitude_ready_for_load">FALSE</td>
+ </tr>
+ </table>
+ <table>
+ <tr>
+ <th colspan="2" style="text-align: center;">Aimer</th>
+ </tr>
+ <tr>
+ <td>Turret Position</td>
+ <td id="turret_position"> NA </td>
+ </tr>
+ <tr>
+ <td>Turret Velocity </td>
+ <td id="turret_velocity"> NA </td>
+ </tr>
+ <tr>
+ <td>Target Distance</td>
+ <td id="target_distance"> NA </td>
+ </tr>
+ <tr>
+ <td>Shot Distance</td>
+ <td id="shot_distance"> NA </td>
+ </tr>
+ </table>
- <h3>Zeroing Faults:</h3>
- <p id="zeroing_faults"> NA </p>
- </div>
- <div id="middle_readouts">
- <div id="vision_readouts">
+ <h3>Zeroing Faults:</h3>
+ <p id="zeroing_faults"> NA </p>
</div>
- <div id="message_bridge_status">
- <div>
- <div>Node</div>
- <div>Client</div>
- <div>Server</div>
- </div>
+ <div>
+ <table>
+ <tr>
+ <th colspan="2" style="text-align: center;">Subsystems</th>
+ </tr>
+ <tr>
+ <th colspan="2">Intake Pivot</th>
+ </tr>
+ <tr>
+ <td>Position</td>
+ <td id="intake_pivot"> NA </td>
+ </tr>
+ <tr>
+ <td>Absolute Position</td>
+ <td id="intake_pivot_abs"> NA </td>
+ </tr>
+ <tr>
+ <th colspan="2">Climber</th>
+ </tr>
+ <tr>
+ <td>Position</td>
+ <td id="climber"> NA </td>
+ </tr>
+ <tr>
+ <td>Absolute Position</td>
+ <td id="climber_abs"> NA </td>
+ </tr>
+ <tr>
+ <td>Pot Position</td>
+ <td id="climber_pot"> NA </td>
+ </tr>
+ <tr>
+ <th colspan="2">Extend</th>
+ </tr>
+ <tr>
+ <td>Position</td>
+ <td id="extend"> NA </td>
+ </tr>
+ <tr>
+ <td>Absolute Position</td>
+ <td id="extend_abs"> NA </td>
+ </tr>
+ <tr>
+ <td>Pot Position</td>
+ <td id="extend_pot"> NA </td>
+ </tr>
+ <tr>
+ <th colspan="2">Turret</th>
+ </tr>
+ <tr>
+ <td>Position</td>
+ <td id="turret"> NA </td>
+ </tr>
+ <tr>
+ <td>Absolute Position</td>
+ <td id="turret_abs"> NA </td>
+ </tr>
+ <tr>
+ <td>Pot Position</td>
+ <td id="turret_pot"> NA </td>
+ </tr>
+ <th colspan="2">Catapult</th>
+ </tr>
+ <tr>
+ <td>Catapult State</td>
+ <td id="catapult_state"> NA </td>
+ </tr>
+ <tr>
+ <td>Position</td>
+ <td id="catapult"> NA </td>
+ </tr>
+ <tr>
+ <td>Absolute Position</td>
+ <td id="catapult_abs"> NA </td>
+ </tr>
+ <tr>
+ <td>Pot Position</td>
+ <td id="catapult_pot"> NA </td>
+ </tr>
+ <tr>
+ <th colspan="2">Altitude</th>
+ </tr>
+ <tr>
+ <td>Position</td>
+ <td id="altitude"> NA </td>
+ </tr>
+ <tr>
+ <td>Absolute Position</td>
+ <td id="altitude_abs"> NA </td>
+ </tr>
+ <tr>
+ <td>Pot Position</td>
+ <td id="altitude_pot"> NA </td>
+ </tr>
+ </table>
+ <table>
+ <tr>
+ <th colspan="2" style="text-align: center;"> Drivetrain Encoder Positions </th>
+ </tr>
+ <tr>
+ <td> Left Encoder Position</td>
+ <td id="left_drivetrain_encoder"> NA </td>
+ </tr>
+ <tr>
+ <td> Right Encoder Position</td>
+ <td id="right_drivetrain_encoder"> NA </td>
+ </tr>
+ <tr>
+ <td> Right Front Falcon CAN Position</td>
+ <td id="falcon_right_front"> NA </td>
+ </tr>
+ <tr>
+ <td> Right Back Falcon CAN Position</td>
+ <td id="falcon_right_back"> NA </td>
+ </tr>
+ <tr>
+ <td> Left Front Falcon CAN Position</td>
+ <td id="falcon_left_front"> NA </td>
+ </tr>
+ <tr>
+ <td> Left Back Falcon CAN Position</td>
+ <td id="falcon_left_back"> NA </td>
+ </tr>
+ </table>
</div>
</div>
- <table>
- <tr>
- <th colspan="2"> Drivetrain Encoder Positions </th>
- </tr>
- <tr>
- <td> Left Encoder Position</td>
- <td id="left_drivetrain_encoder"> NA </td>
- </tr>
- <tr>
- <td> Right Encoder Position</td>
- <td id="right_drivetrain_encoder"> NA </td>
- </tr>
- </table>
- </body>
-</html>
+</body>
+</html>
\ No newline at end of file
diff --git a/y2024/www/field_handler.ts b/y2024/www/field_handler.ts
index f383c08..949b34b 100644
--- a/y2024/www/field_handler.ts
+++ b/y2024/www/field_handler.ts
@@ -6,6 +6,7 @@
import {Position as DrivetrainPosition} from '../../frc971/control_loops/drivetrain/drivetrain_position_generated'
import {CANPosition as DrivetrainCANPosition} from '../../frc971/control_loops/drivetrain/drivetrain_can_position_generated'
import {Status as DrivetrainStatus} from '../../frc971/control_loops/drivetrain/drivetrain_status_generated'
+import {SuperstructureState, IntakeRollerStatus, CatapultState, TransferRollerStatus, ExtendRollerStatus, ExtendStatus, NoteStatus, Status as SuperstructureStatus} from '../control_loops/superstructure/superstructure_status_generated'
import {LocalizerOutput} from '../../frc971/control_loops/drivetrain/localization/localizer_output_generated'
import {TargetMap} from '../../frc971/vision/target_map_generated'
@@ -21,11 +22,171 @@
export class FieldHandler {
private canvas = document.createElement('canvas');
+ private localizerOutput: LocalizerOutput|null = null;
+ private drivetrainStatus: DrivetrainStatus|null = null;
+ private drivetrainPosition: DrivetrainPosition|null = null;
+ private drivetrainCANPosition: DrivetrainCANPosition|null = null;
+ private superstructureStatus: SuperstructureStatus|null = null;
+
+ private x: HTMLElement = (document.getElementById('x') as HTMLElement);
+ private y: HTMLElement = (document.getElementById('y') as HTMLElement);
+ private theta: HTMLElement =
+ (document.getElementById('theta') as HTMLElement);
+
private fieldImage: HTMLImageElement = new Image();
+
+ private zeroingFaults: HTMLElement =
+ (document.getElementById('zeroing_faults') as HTMLElement);
+
+ private superstructureState: HTMLElement =
+ (document.getElementById('superstructure_state') as HTMLElement);
+
+ private intakeRollerState: HTMLElement =
+ (document.getElementById('intake_roller_state') as HTMLElement);
+ private transferRollerState: HTMLElement =
+ (document.getElementById('transfer_roller_state') as HTMLElement);
+ private extendState: HTMLElement =
+ (document.getElementById('extend_state') as HTMLElement);
+ private extendRollerState: HTMLElement =
+ (document.getElementById('extend_roller_state') as HTMLElement);
+ private catapultState: HTMLElement =
+ (document.getElementById('catapult_state') as HTMLElement);
+ private uncompletedNoteGoal: HTMLElement =
+ (document.getElementById('uncompleted_note_goal') as HTMLElement);
+
+ private extend_beambreak: HTMLElement =
+ (document.getElementById('extend_beambreak') as HTMLElement);
+ private catapult_beambreak: HTMLElement =
+ (document.getElementById('catapult_beambreak') as HTMLElement);
+
+ private extend_at_retracted: HTMLElement =
+ (document.getElementById('extend_at_retracted') as HTMLElement);
+ private extend_ready_for_transfer: HTMLElement =
+ (document.getElementById('extend_ready_for_transfer') as HTMLElement);
+ private extend_ready_for_catapult_transfer: HTMLElement =
+ (document.getElementById('extend_ready_for_catapult_transfer') as HTMLElement);
+ private turret_ready_for_load: HTMLElement =
+ (document.getElementById('turret_ready_for_load') as HTMLElement);
+ private altitude_ready_for_load: HTMLElement =
+ (document.getElementById('altitude_ready_for_load') as HTMLElement);
+
+
+ private intakePivot: HTMLElement =
+ (document.getElementById('intake_pivot') as HTMLElement);
+ private intakePivotAbs: HTMLElement =
+ (document.getElementById('intake_pivot_abs') as HTMLElement);
+
+ private climber: HTMLElement =
+ (document.getElementById('climber') as HTMLElement);
+ private climberAbs: HTMLElement =
+ (document.getElementById('climber_abs') as HTMLElement);
+ private climberPot: HTMLElement =
+ (document.getElementById('climber_pot') as HTMLElement);
+
+ private extend: HTMLElement =
+ (document.getElementById('extend') as HTMLElement);
+ private extendAbs: HTMLElement =
+ (document.getElementById('extend_abs') as HTMLElement);
+ private extendPot: HTMLElement =
+ (document.getElementById('extend_pot') as HTMLElement);
+
+ private turret: HTMLElement =
+ (document.getElementById('turret') as HTMLElement);
+ private turretAbs: HTMLElement =
+ (document.getElementById('turret_abs') as HTMLElement);
+ private turretPot: HTMLElement =
+ (document.getElementById('turret_pot') as HTMLElement);
+
+ private catapult: HTMLElement =
+ (document.getElementById('catapult') as HTMLElement);
+ private catapultAbs: HTMLElement =
+ (document.getElementById('catapult_abs') as HTMLElement);
+ private catapultPot: HTMLElement =
+ (document.getElementById('catapult_pot') as HTMLElement);
+
+ private altitude: HTMLElement =
+ (document.getElementById('altitude') as HTMLElement);
+ private altitudeAbs: HTMLElement =
+ (document.getElementById('altitude_abs') as HTMLElement);
+ private altitudePot: HTMLElement =
+ (document.getElementById('altitude_pot') as HTMLElement);
+
+ private turret_position: HTMLElement =
+ (document.getElementById('turret_position') as HTMLElement);
+ private turret_velocity: HTMLElement =
+ (document.getElementById('turret_velocity') as HTMLElement);
+ private target_distance: HTMLElement =
+ (document.getElementById('target_distance') as HTMLElement);
+ private shot_distance: HTMLElement =
+ (document.getElementById('shot_distance') as HTMLElement);
+
+ private leftDrivetrainEncoder: HTMLElement =
+ (document.getElementById('left_drivetrain_encoder') as HTMLElement);
+ private rightDrivetrainEncoder: HTMLElement =
+ (document.getElementById('right_drivetrain_encoder') as HTMLElement);
+ private falconRightFrontPosition: HTMLElement =
+ (document.getElementById('falcon_right_front') as HTMLElement);
+ private falconRightBackPosition: HTMLElement =
+ (document.getElementById('falcon_right_back') as HTMLElement);
+ private falconLeftFrontPosition: HTMLElement =
+ (document.getElementById('falcon_left_front') as HTMLElement);
+ private falconLeftBackPosition: HTMLElement =
+ (document.getElementById('falcon_left_back') as HTMLElement);
+
constructor(private readonly connection: Connection) {
(document.getElementById('field') as HTMLElement).appendChild(this.canvas);
this.fieldImage.src = '2024.png';
+
+ this.connection.addConfigHandler(() => {
+
+ this.connection.addHandler(
+ '/drivetrain', 'frc971.control_loops.drivetrain.Status', (data) => {
+ this.handleDrivetrainStatus(data);
+ });
+ this.connection.addHandler(
+ '/drivetrain', 'frc971.control_loops.drivetrain.Position', (data) => {
+ this.handleDrivetrainPosition(data);
+ });
+ this.connection.addHandler(
+ '/drivetrain', 'frc971.control_loops.drivetrain.CANPosition', (data) => {
+ this.handleDrivetrainCANPosition(data);
+ });
+ this.connection.addHandler(
+ '/localizer', 'frc971.controls.LocalizerOutput', (data) => {
+ this.handleLocalizerOutput(data);
+ });
+ this.connection.addHandler(
+ '/superstructure', "y2024.control_loops.superstructure.Status",
+ (data) => {
+ this.handleSuperstructureStatus(data)
+ });
+ });
+ }
+
+ private handleDrivetrainStatus(data: Uint8Array): void {
+ const fbBuffer = new ByteBuffer(data);
+ this.drivetrainStatus = DrivetrainStatus.getRootAsStatus(fbBuffer);
+ }
+
+ private handleDrivetrainPosition(data: Uint8Array): void {
+ const fbBuffer = new ByteBuffer(data);
+ this.drivetrainPosition = DrivetrainPosition.getRootAsPosition(fbBuffer);
+ }
+
+ private handleDrivetrainCANPosition(data: Uint8Array): void {
+ const fbBuffer = new ByteBuffer(data);
+ this.drivetrainCANPosition = DrivetrainCANPosition.getRootAsCANPosition(fbBuffer);
+ }
+
+ private handleLocalizerOutput(data: Uint8Array): void {
+ const fbBuffer = new ByteBuffer(data);
+ this.localizerOutput = LocalizerOutput.getRootAsLocalizerOutput(fbBuffer);
+ }
+
+ private handleSuperstructureStatus(data: Uint8Array): void {
+ const fbBuffer = new ByteBuffer(data);
+ this.superstructureStatus = SuperstructureStatus.getRootAsStatus(fbBuffer);
}
drawField(): void {
@@ -38,10 +199,345 @@
ctx.restore();
}
+ drawCamera(x: number, y: number, theta: number, color: string = 'blue'):
+ void {
+ const ctx = this.canvas.getContext('2d');
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(theta);
+ ctx.strokeStyle = color;
+ ctx.beginPath();
+ ctx.moveTo(0.5, 0.5);
+ ctx.lineTo(0, 0);
+ ctx.lineTo(0.5, -0.5);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(0, 0, 0.25, -Math.PI / 4, Math.PI / 4);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ drawRobot(
+ x: number, y: number, theta: number, color: string = 'blue',
+ dashed: boolean = false): void {
+ const ctx = this.canvas.getContext('2d');
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(theta);
+ ctx.strokeStyle = color;
+ ctx.lineWidth = ROBOT_WIDTH / 10.0;
+ if (dashed) {
+ ctx.setLineDash([0.05, 0.05]);
+ } else {
+ // Empty array = solid line.
+ ctx.setLineDash([]);
+ }
+ ctx.rect(-ROBOT_LENGTH / 2, -ROBOT_WIDTH / 2, ROBOT_LENGTH, ROBOT_WIDTH);
+ ctx.stroke();
+
+ // Draw line indicating which direction is forwards on the robot.
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(ROBOT_LENGTH / 2.0, 0);
+ ctx.stroke();
+
+ ctx.restore();
+}
+
+ setZeroing(div: HTMLElement): void {
+ div.innerHTML = 'zeroing';
+ div.classList.remove('faulted');
+ div.classList.add('zeroing');
+ div.classList.remove('near');
+ }
+
+ setEstopped(div: HTMLElement): void {
+ div.innerHTML = 'estopped';
+ div.classList.add('faulted');
+ div.classList.remove('zeroing');
+ div.classList.remove('near');
+ }
+
+ setTargetValue(
+ div: HTMLElement, target: number, val: number, tolerance: number): void {
+ div.innerHTML = val.toFixed(4);
+ div.classList.remove('faulted');
+ div.classList.remove('zeroing');
+ if (Math.abs(target - val) < tolerance) {
+ div.classList.add('near');
+ } else {
+ div.classList.remove('near');
+ }
+ }
+
+ setValue(div: HTMLElement, val: number): void {
+ div.innerHTML = val.toFixed(4);
+ div.classList.remove('faulted');
+ div.classList.remove('zeroing');
+ div.classList.remove('near');
+ }
+
+ setBoolean(div: HTMLElement, triggered: boolean): void {
+ div.innerHTML = ((triggered) ? "TRUE" : "FALSE")
+ if (triggered) {
+ div.classList.remove('false');
+ div.classList.add('true');
+ } else {
+ div.classList.remove('true');
+ div.classList.add('false');
+ }
+ }
+
draw(): void {
this.reset();
this.drawField();
+ if (this.superstructureStatus) {
+ this.superstructureState.innerHTML =
+ SuperstructureState[this.superstructureStatus.state()];
+
+ this.intakeRollerState.innerHTML =
+ IntakeRollerStatus[this.superstructureStatus.intakeRoller()];
+ this.transferRollerState.innerHTML =
+ TransferRollerStatus[this.superstructureStatus.transferRoller()];
+ this.extendState.innerHTML =
+ ExtendStatus[this.superstructureStatus.extendStatus()];
+ this.extendRollerState.innerHTML =
+ ExtendRollerStatus[this.superstructureStatus.extendRoller()];
+ this.catapultState.innerHTML =
+ CatapultState[this.superstructureStatus.shooter().catapultState()];
+ this.uncompletedNoteGoal.innerHTML =
+ NoteStatus[this.superstructureStatus.uncompletedNoteGoal()];
+
+ this.setBoolean(this.extend_beambreak, this.superstructureStatus.extendBeambreak());
+
+ this.setBoolean(this.catapult_beambreak, this.superstructureStatus.catapultBeambreak());
+
+ this.setBoolean(this.extend_ready_for_transfer, this.superstructureStatus.extendReadyForTransfer());
+
+ this.setBoolean(this.extend_at_retracted, this.superstructureStatus.extendAtRetracted());
+
+ this.setBoolean(this.turret_ready_for_load, this.superstructureStatus.turretReadyForLoad());
+
+ this.setBoolean(this.altitude_ready_for_load, this.superstructureStatus.altitudeReadyForLoad());
+
+ this.setBoolean(this.extend_ready_for_catapult_transfer, this.superstructureStatus.extendReadyForCatapultTransfer());
+
+ if (this.superstructureStatus.shooter() &&
+ this.superstructureStatus.shooter().aimer()) {
+ this.turret_position.innerHTML = this.superstructureStatus.shooter()
+ .aimer()
+ .turretPosition()
+ .toString();
+ this.turret_velocity.innerHTML = this.superstructureStatus.shooter()
+ .aimer()
+ .turretVelocity()
+ .toString();
+ this.target_distance.innerHTML = this.superstructureStatus.shooter()
+ .aimer()
+ .targetDistance()
+ .toString();
+ this.shot_distance.innerHTML = this.superstructureStatus.shooter()
+ .aimer()
+ .shotDistance()
+ .toString();
+ }
+
+ if (!this.superstructureStatus.intakePivot() ||
+ !this.superstructureStatus.intakePivot().zeroed()) {
+ this.setZeroing(this.intakePivot);
+ } else if (this.superstructureStatus.intakePivot().estopped()) {
+ this.setEstopped(this.intakePivot);
+ } else {
+ this.setTargetValue(
+ this.intakePivot,
+ this.superstructureStatus.intakePivot().unprofiledGoalPosition(),
+ this.superstructureStatus.intakePivot().estimatorState().position(),
+ 1e-3);
+ }
+
+ this.intakePivotAbs.innerHTML = this.superstructureStatus.intakePivot().estimatorState().absolutePosition().toString();
+
+ if (!this.superstructureStatus.climber() ||
+ !this.superstructureStatus.climber().zeroed()) {
+ this.setZeroing(this.climber);
+ } else if (this.superstructureStatus.climber().estopped()) {
+ this.setEstopped(this.climber);
+ } else {
+ this.setTargetValue(
+ this.climber,
+ this.superstructureStatus.climber().unprofiledGoalPosition(),
+ this.superstructureStatus.climber().estimatorState().position(),
+ 1e-3);
+ }
+
+ this.climberAbs.innerHTML = this.superstructureStatus.climber().estimatorState().absolutePosition().toString();
+ this.climberPot.innerHTML = this.superstructureStatus.climber().estimatorState().potPosition().toString();
+
+ if (!this.superstructureStatus.extend() ||
+ !this.superstructureStatus.extend().zeroed()) {
+ this.setZeroing(this.extend);
+ } else if (this.superstructureStatus.extend().estopped()) {
+ this.setEstopped(this.extend);
+ } else {
+ this.setTargetValue(
+ this.extend,
+ this.superstructureStatus.extend().unprofiledGoalPosition(),
+ this.superstructureStatus.extend().estimatorState().position(),
+ 1e-3);
+ }
+
+ this.extendAbs.innerHTML = this.superstructureStatus.extend().estimatorState().absolutePosition().toString();
+ this.extendPot.innerHTML = this.superstructureStatus.extend().estimatorState().potPosition().toString();
+
+ if (!this.superstructureStatus.shooter().turret() ||
+ !this.superstructureStatus.shooter().turret().zeroed()) {
+ this.setZeroing(this.turret);
+ } else if (this.superstructureStatus.shooter().turret().estopped()) {
+ this.setEstopped(this.turret);
+ } else {
+ this.setTargetValue(
+ this.turret,
+ this.superstructureStatus.shooter().turret().unprofiledGoalPosition(),
+ this.superstructureStatus.shooter().turret().estimatorState().position(),
+ 1e-3);
+ }
+
+ this.turretAbs.innerHTML = this.superstructureStatus.shooter().turret().estimatorState().absolutePosition().toString();
+ this.turretPot.innerHTML = this.superstructureStatus.shooter().turret().estimatorState().potPosition().toString();
+
+ if (!this.superstructureStatus.shooter().catapult() ||
+ !this.superstructureStatus.shooter().catapult().zeroed()) {
+ this.setZeroing(this.catapult);
+ } else if (this.superstructureStatus.shooter().catapult().estopped()) {
+ this.setEstopped(this.catapult);
+ } else {
+ this.setTargetValue(
+ this.catapult,
+ this.superstructureStatus.shooter().catapult().unprofiledGoalPosition(),
+ this.superstructureStatus.shooter().catapult().estimatorState().position(),
+ 1e-3);
+ }
+
+ this.catapultAbs.innerHTML = this.superstructureStatus.shooter().catapult().estimatorState().absolutePosition().toString();
+ this.catapultPot.innerHTML = this.superstructureStatus.shooter().catapult().estimatorState().potPosition().toString();
+
+ if (!this.superstructureStatus.shooter().altitude() ||
+ !this.superstructureStatus.shooter().altitude().zeroed()) {
+ this.setZeroing(this.altitude);
+ } else if (this.superstructureStatus.shooter().altitude().estopped()) {
+ this.setEstopped(this.altitude);
+ } else {
+ this.setTargetValue(
+ this.altitude,
+ this.superstructureStatus.shooter().altitude().unprofiledGoalPosition(),
+ this.superstructureStatus.shooter().altitude().estimatorState().position(),
+ 1e-3);
+ }
+
+ this.altitudeAbs.innerHTML = this.superstructureStatus.shooter().altitude().estimatorState().absolutePosition().toString();
+ this.altitudePot.innerHTML = this.superstructureStatus.shooter().altitude().estimatorState().potPosition().toString();
+
+ let zeroingErrors: string = 'Intake Pivot Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.intakePivot()
+ .estimatorState()
+ .errorsLength();
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.intakePivot()
+ .estimatorState()
+ .errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Climber Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.climber().estimatorState().errorsLength();
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.climber().estimatorState().errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Extend Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.extend().estimatorState().errorsLength();
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.extend().estimatorState().errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Turret Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.shooter().turret().estimatorState().errorsLength();
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.shooter().turret().estimatorState().errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Catapult Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.shooter().catapult().estimatorState().errorsLength();
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.shooter().catapult().estimatorState().errors(i)] +
+ '<br/>';
+ }
+ zeroingErrors += '<br/>' +
+ 'Altitude Errors:' +
+ '<br/>';
+ for (let i = 0; i < this.superstructureStatus.shooter().altitude().estimatorState().errorsLength();
+ i++) {
+ zeroingErrors += ZeroingError[this.superstructureStatus.shooter().altitude().estimatorState().errors(i)] +
+ '<br/>';
+ }
+ this.zeroingFaults.innerHTML = zeroingErrors;
+ }
+
+ if (this.drivetrainPosition) {
+ this.leftDrivetrainEncoder.innerHTML =
+ this.drivetrainPosition.leftEncoder().toString();
+
+ this.rightDrivetrainEncoder.innerHTML =
+ this.drivetrainPosition.rightEncoder().toString();
+ }
+
+ if (this.drivetrainCANPosition) {
+ this.falconRightFrontPosition.innerHTML =
+ this.drivetrainCANPosition.talonfxs(0).position().toString();
+
+ this.falconRightBackPosition.innerHTML =
+ this.drivetrainCANPosition.talonfxs(1).position().toString();
+
+ this.falconLeftFrontPosition.innerHTML =
+ this.drivetrainCANPosition.talonfxs(2).position().toString();
+
+ this.falconLeftBackPosition.innerHTML =
+ this.drivetrainCANPosition.talonfxs(3).position().toString();
+ }
+
+ if (this.drivetrainStatus && this.drivetrainStatus.trajectoryLogging()) {
+ this.drawRobot(
+ this.drivetrainStatus.trajectoryLogging().x(),
+ this.drivetrainStatus.trajectoryLogging().y(),
+ this.drivetrainStatus.trajectoryLogging().theta(), '#000000FF',
+ false);
+ }
+
+ if (this.localizerOutput) {
+ if (!this.localizerOutput.zeroed()) {
+ this.setZeroing(this.x);
+ this.setZeroing(this.y);
+ this.setZeroing(this.theta);
+ } else {
+ this.setValue(this.x, this.localizerOutput.x());
+ this.setValue(this.y, this.localizerOutput.y());
+ this.setValue(this.theta, this.localizerOutput.theta());
+ }
+
+ this.drawRobot(
+ this.localizerOutput.x(), this.localizerOutput.y(),
+ this.localizerOutput.theta());
+ }
+
window.requestAnimationFrame(() => this.draw());
}
diff --git a/y2024/www/plotter.html b/y2024/www/plotter.html
index 629ceaa..86f5aa8 100644
--- a/y2024/www/plotter.html
+++ b/y2024/www/plotter.html
@@ -1,6 +1,7 @@
<html>
<head>
<script src="plot_index_bundle.min.js" defer></script>
+ <link rel="stylesheet" href="styles.css">
</head>
<body>
</body>
diff --git a/y2024/www/styles.css b/y2024/www/styles.css
index c2c44d2..39b7519 100644
--- a/y2024/www/styles.css
+++ b/y2024/www/styles.css
@@ -15,7 +15,6 @@
float: right;
}
-
#legend {
display: inline-block;
}
@@ -25,13 +24,20 @@
border-collapse: collapse;
padding: 5px;
margin: 10px;
+ table-layout: fixed;
+ width: 100%;
+ overflow: hidden;
}
th, td {
- text-align: right;
+ text-align: left;
width: 70px;
}
+table:first-child {
+ text-align: center;
+}
+
td:first-child {
width: 150px;
}
@@ -72,3 +78,73 @@
padding: 5px;
text-align: right;
}
+
+.channel {
+ display: flex;
+ border-bottom: 1px solid;
+ font-size: 24px;
+}
+
+.aos_plot {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+}
+
+.aos_plot_text {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+}
+
+.aos_legend {
+ position: absolute;
+ z-index: 1;
+ pointer-events: none;
+}
+
+.aos_legend_line {
+ background: white;
+ padding: 2px;
+ border-radius: 2px;
+ margin-top: 3px;
+ margin-bottom: 3px;
+ font-size: 12;
+}
+
+.aos_legend_line>div {
+ display: inline-block;
+ vertical-align: middle;
+ margin-left: 5px;
+}
+.aos_legend_line>canvas {
+ vertical-align: middle;
+ pointer-events: all;
+}
+
+.aos_legend_line_hidden {
+ filter: contrast(0.75);
+}
+
+.aos_cpp_plot {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ align-items: flex-start;
+}
+
+.aos_cpp_plot>div {
+ flex: 1;
+ width: 100%;
+}
+
+.true {
+ background-color: LightGreen;
+}
+
+.false {
+ background-color: red;
+}
diff --git a/y2024/y2024.json b/y2024/y2024.json
index c901ac6..b55aebe 100644
--- a/y2024/y2024.json
+++ b/y2024/y2024.json
@@ -15,6 +15,5 @@
"y2024_roborio.json",
"y2024_imu.json",
"y2024_orin1.json",
- "y2024_orin2.json"
]
}
diff --git a/y2024/y2024_imu.json b/y2024/y2024_imu.json
index 5dd4710..155edb4 100644
--- a/y2024/y2024_imu.json
+++ b/y2024/y2024_imu.json
@@ -7,8 +7,7 @@
"frequency": 100,
"logger": "LOCAL_AND_REMOTE_LOGGER",
"logger_nodes": [
- "orin1",
- "orin2"
+ "orin1"
],
"destination_nodes": [
{
@@ -19,16 +18,7 @@
"timestamp_logger_nodes": [
"imu"
]
- },
- {
- "name": "orin2",
- "priority": 5,
- "time_to_live": 50000000,
- "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
- "timestamp_logger_nodes": [
- "imu"
- ]
- },
+ }
]
},
{
@@ -41,21 +31,12 @@
"max_size": 200
},
{
- "name": "/imu/aos/remote_timestamps/orin2/imu/aos/aos-JoystickState",
- "type": "aos.message_bridge.RemoteMessage",
- "source_node": "imu",
- "logger": "NOT_LOGGED",
- "frequency": 300,
- "num_senders": 2,
- "max_size": 200
- },
- {
"name": "/imu/aos",
"type": "aos.timing.Report",
"source_node": "imu",
"frequency": 50,
"num_senders": 20,
- "max_size": 4096
+ "max_size": 6184
},
{
"name": "/imu/aos",
@@ -70,7 +51,7 @@
"source_node": "imu",
"frequency": 50,
"num_senders": 20,
- "max_size": 2048
+ "max_size": 4096
},
{
"name": "/imu/aos",
@@ -150,6 +131,36 @@
}
]
},
+ {
+ "name": "/localizer",
+ "type": "frc971.controls.LocalizerOutput",
+ "source_node": "imu",
+ "frequency": 52,
+ "logger": "LOCAL_AND_REMOTE_LOGGER",
+ "logger_nodes": [
+ "roborio"
+ ],
+ "destination_nodes": [
+ {
+ "name": "roborio",
+ "priority": 5,
+ "time_to_live": 5000000,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": [
+ "imu"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "/imu/aos/remote_timestamps/roborio/localizer/frc971-controls-LocalizerOutput",
+ "type": "aos.message_bridge.RemoteMessage",
+ "source_node": "imu",
+ "logger": "NOT_LOGGED",
+ "frequency": 52,
+ "num_senders": 2,
+ "max_size": 200
+ },
{
"name": "/roborio/aos/remote_timestamps/imu/roborio/aos/aos-starter-StarterRpc",
"type": "aos.message_bridge.RemoteMessage",
@@ -160,6 +171,106 @@
"max_size": 200
},
{
+ "name": "/imu/camera0",
+ "type": "frc971.vision.CameraImage",
+ "source_node": "imu",
+ "channel_storage_duration": 1000000000,
+ "frequency": 65,
+ "max_size": 4752384,
+ "num_readers": 6,
+ "read_method": "PIN",
+ "num_senders": 18
+ },
+ {
+ "name": "/imu/camera1",
+ "type": "frc971.vision.CameraImage",
+ "source_node": "imu",
+ "channel_storage_duration": 1000000000,
+ "frequency": 65,
+ "max_size": 4752384,
+ "num_readers": 6,
+ "read_method": "PIN",
+ "num_senders": 18
+ },
+ {
+ "name": "/imu/camera0",
+ "type": "foxglove.CompressedImage",
+ "source_node": "imu",
+ "logger": "NOT_LOGGED",
+ "channel_storage_duration": 1000000000,
+ "frequency": 65,
+ "max_size": 622384
+ },
+ {
+ "name": "/imu/camera1",
+ "type": "foxglove.CompressedImage",
+ "source_node": "imu",
+ "logger": "NOT_LOGGED",
+ "channel_storage_duration": 1000000000,
+ "frequency": 65,
+ "max_size": 622384
+ },
+ {
+ "name": "/imu/camera0",
+ "type": "foxglove.ImageAnnotations",
+ "source_node": "imu",
+ "frequency": 65,
+ "max_size": 50000
+ },
+ {
+ "name": "/imu/camera1",
+ "type": "foxglove.ImageAnnotations",
+ "source_node": "imu",
+ "frequency": 65,
+ "max_size": 50000
+ },
+ {
+ "name": "/imu/camera0",
+ "type": "y2024.localizer.Visualization",
+ "source_node": "imu",
+ "frequency": 65,
+ "max_size": 50000
+ },
+ {
+ "name": "/imu/camera1",
+ "type": "y2024.localizer.Visualization",
+ "source_node": "imu",
+ "frequency": 65,
+ "max_size": 50000
+ },
+ {
+ "name": "/imu/camera0",
+ "type": "frc971.vision.TargetMap",
+ "source_node": "imu",
+ "frequency": 65,
+ "num_senders": 2,
+ "max_size": 1024
+ },
+ {
+ "name": "/imu/camera1",
+ "type": "frc971.vision.TargetMap",
+ "source_node": "imu",
+ "frequency": 65,
+ "num_senders": 2,
+ "max_size": 1024
+ },
+ {
+ "name": "/imu",
+ "type": "frc971.imu.DualImu",
+ "source_node": "imu",
+ "frequency": 1100,
+ "num_senders": 1,
+ "max_size": 496
+ },
+ {
+ "name": "/imu",
+ "type": "frc971.imu.CanTranslatorStatus",
+ "source_node": "imu",
+ "frequency": 1000,
+ "num_senders": 1,
+ "max_size": 200
+ },
+ {
"name": "/can/cana",
"type": "frc971.can_logger.CanFrame",
"source_node": "imu",
@@ -192,6 +303,22 @@
"num_senders": 2
},
{
+ "name": "/imu",
+ "type": "frc971.imu.DualImuBlenderStatus",
+ "source_node": "imu",
+ "frequency": 1100,
+ "num_senders": 1,
+ "max_size": 200
+ },
+ {
+ "name": "/localizer",
+ "type": "y2024.localizer.Status",
+ "source_node": "imu",
+ "frequency": 1600,
+ "max_size": 1600,
+ "num_senders": 2
+ },
+ {
"name": "/imu/constants",
"type": "y2024.Constants",
"source_node": "imu",
@@ -203,22 +330,10 @@
"applications": [
{
"name": "message_bridge_client",
- "nodes": [
- "imu"
- ]
- },
- {
- "name": "localizer",
- "executable_name": "localizer_main",
- "user": "pi",
- "nodes": [
- "imu"
- ]
- },
- {
- "name": "imu",
- "executable_name": "imu_main",
- "user": "pi",
+ "args": [
+ "--rt_priority=16",
+ "--sinit_max_init_timeout=5000"
+ ],
"nodes": [
"imu"
]
@@ -235,31 +350,54 @@
"name": "message_bridge_server",
"executable_name": "message_bridge_server",
"user": "pi",
+ "args": [
+ "--rt_priority=16"
+ ],
"nodes": [
"imu"
]
},
{
- "name": "localizer_logger",
- "executable_name": "logger_main",
- "args": [
- "--logging_folder",
- "",
- "--snappy_compress",
- "--rotate_every", "30.0"
- ],
+ "name": "localizer",
+ "executable_name": "localizer_main",
"user": "pi",
"nodes": [
"imu"
]
},
{
- "name": "can_logger",
+ "name": "localizer_logger",
+ "executable_name": "localizer_logger",
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "imu_can_logger",
"executable_name": "can_logger",
"nodes": [
"imu"
]
},
+ // TODO(max): Update the channel value with whatever channel the IMU is on.
+ {
+ "name": "can_translator",
+ "executable_name": "can_translator",
+ "args": [
+ "--channel=/can/canb"
+ ],
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "dual_imu_blender",
+ "executable_name": "dual_imu_blender",
+ "nodes": [
+ "imu"
+ ]
+ },
{
"name": "web_proxy",
"executable_name": "web_proxy_main",
@@ -286,6 +424,105 @@
"nodes": [
"imu"
]
+ },
+ {
+ "name": "image_logger",
+ "executable_name": "image_logger",
+ "args": [
+ "--rotate_every",
+ "30.0",
+ "--direct",
+ "--flush_size=4194304"
+ ],
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "foxglove_websocket",
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "foxglove_image_converter0",
+ "executable_name": "foxglove_image_converter",
+ "user": "pi",
+ "args": [
+ "--channel", "/camera0"
+ ],
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "foxglove_image_converter1",
+ "executable_name": "foxglove_image_converter",
+ "user": "pi",
+ "args": [
+ "--channel", "/camera1"
+ ],
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "constants_sender",
+ "autorestart": false,
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "argus_camera0",
+ "executable_name": "argus_camera",
+ "args": [
+ "--enable_ftrace",
+ "--camera=0",
+ "--channel=/camera0"
+ ],
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "argus_camera1",
+ "executable_name": "argus_camera",
+ "args": [
+ "--enable_ftrace",
+ "--camera=1",
+ "--channel=/camera1"
+ ],
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "apriltag_detector0",
+ "executable_name": "apriltag_detector",
+ "args": [
+ "--channel=/camera0"
+ ],
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
+ },
+ {
+ "name": "apriltag_detector1",
+ "executable_name": "apriltag_detector",
+ "args": [
+ "--channel=/camera1"
+ ],
+ "user": "pi",
+ "nodes": [
+ "imu"
+ ]
}
],
"maps": [
@@ -306,17 +543,26 @@
"rename": {
"name": "/imu/aos"
}
+ },
+ {
+ "match": {
+ "name": "/camera*",
+ "source_node": "imu"
+ },
+ "rename": {
+ "name": "/imu/camera"
+ }
}
],
"nodes": [
{
"name": "imu",
- "hostname": "orin3",
+ "hostname": "orin2",
"hostnames": [
- "orin-971-3",
- "orin-7971-3",
- "orin-8971-3",
- "orin-9971-3"
+ "orin-971-2",
+ "orin-7971-2",
+ "orin-8971-2",
+ "orin-9971-2"
],
"port": 9971
},
@@ -325,9 +571,6 @@
},
{
"name": "orin1"
- },
- {
- "name": "orin2"
- },
+ }
]
}
diff --git a/y2024/y2024_orin_template.json b/y2024/y2024_orin1.json
similarity index 70%
rename from y2024/y2024_orin_template.json
rename to y2024/y2024_orin1.json
index 2bb2f46..a9f75d5 100644
--- a/y2024/y2024_orin_template.json
+++ b/y2024/y2024_orin1.json
@@ -1,61 +1,61 @@
{
"channels": [
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.timing.Report",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 50,
"num_senders": 20,
"max_size": 8192
},
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.logging.LogMessageFbs",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 200,
"num_senders": 20
},
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.starter.Status",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 50,
"num_senders": 20,
"max_size": 2000
},
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.starter.StarterRpc",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 10,
"num_senders": 2
},
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.message_bridge.ServerStatistics",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"max_size": 2048,
"frequency": 10,
"num_senders": 2
},
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.message_bridge.ClientStatistics",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 20,
"num_senders": 2
},
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.logging.DynamicLogCommand",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 10,
"num_senders": 2
},
{
- "name": "/orin{{ NUM }}/aos",
+ "name": "/orin1/aos",
"type": "aos.message_bridge.Timestamp",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 15,
"num_senders": 2,
"logger": "LOCAL_AND_REMOTE_LOGGER",
@@ -70,16 +70,16 @@
"time_to_live": 5000000,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
}
]
},
{
- "name": "/orin{{ NUM }}/aos/remote_timestamps/imu/orin{{ NUM }}/aos/aos-message_bridge-Timestamp",
+ "name": "/orin1/aos/remote_timestamps/imu/orin1/aos/aos-message_bridge-Timestamp",
"type": "aos.message_bridge.RemoteMessage",
"frequency": 20,
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"max_size": 208
},
{
@@ -88,11 +88,11 @@
"source_node": "imu",
"logger": "LOCAL_AND_REMOTE_LOGGER",
"logger_nodes": [
- "orin{{ NUM }}"
+ "orin1"
],
"destination_nodes": [
{
- "name": "orin{{ NUM }}",
+ "name": "orin1",
"priority": 1,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
@@ -103,16 +103,16 @@
]
},
{
- "name": "/imu/aos/remote_timestamps/orin{{ NUM }}/imu/aos/aos-message_bridge-Timestamp",
+ "name": "/imu/aos/remote_timestamps/orin1/imu/aos/aos-message_bridge-Timestamp",
"type": "aos.message_bridge.RemoteMessage",
"frequency": 20,
"source_node": "imu",
"max_size": 208
},
{
- "name": "/orin{{ NUM }}/camera1",
+ "name": "/orin1/camera0",
"type": "frc971.vision.CameraImage",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"channel_storage_duration": 1000000000,
"frequency": 65,
"max_size": 4752384,
@@ -121,9 +121,9 @@
"num_senders": 18
},
{
- "name": "/orin{{ NUM }}/camera2",
+ "name": "/orin1/camera1",
"type": "frc971.vision.CameraImage",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"channel_storage_duration": 1000000000,
"frequency": 65,
"max_size": 4752384,
@@ -132,39 +132,53 @@
"num_senders": 18
},
{
- "name": "/orin{{ NUM }}/camera1",
+ "name": "/orin1/camera0",
"type": "foxglove.CompressedImage",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"channel_storage_duration": 1000000000,
"frequency": 65,
"max_size": 622384
},
{
- "name": "/orin{{ NUM }}/camera2",
+ "name": "/orin1/camera1",
"type": "foxglove.CompressedImage",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"channel_storage_duration": 1000000000,
"frequency": 65,
"max_size": 622384
},
{
- "name": "/orin{{ NUM }}/camera1",
+ "name": "/orin1/camera0",
"type": "foxglove.ImageAnnotations",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 65,
"max_size": 50000
},
{
- "name": "/orin{{ NUM }}/camera2",
+ "name": "/orin1/camera1",
"type": "foxglove.ImageAnnotations",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 65,
"max_size": 50000
},
{
- "name": "/orin{{ NUM }}/camera1",
+ "name": "/orin1/camera0",
+ "type": "y2024.localizer.Visualization",
+ "source_node": "imu",
+ "frequency": 65,
+ "max_size": 50000
+ },
+ {
+ "name": "/orin1/camera1",
+ "type": "y2024.localizer.Visualization",
+ "source_node": "imu",
+ "frequency": 65,
+ "max_size": 50000
+ },
+ {
+ "name": "/orin1/camera0",
"type": "frc971.vision.TargetMap",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 65,
"num_senders": 2,
"max_size": 1024,
@@ -178,16 +192,16 @@
"priority": 4,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "orin{{ NUM }}"
+ "orin1"
],
"time_to_live": 5000000
}
]
},
{
- "name": "/orin{{ NUM }}/camera2",
+ "name": "/orin1/camera1",
"type": "frc971.vision.TargetMap",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 65,
"num_senders": 2,
"max_size": 1024,
@@ -201,30 +215,30 @@
"priority": 4,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "orin{{ NUM }}"
+ "orin1"
],
"time_to_live": 5000000
}
]
},
{
- "name": "/orin{{ NUM }}/aos/remote_timestamps/imu/orin{{ NUM }}/camera1/frc971-vision-TargetMap",
+ "name": "/orin1/aos/remote_timestamps/imu/orin1/camera0/frc971-vision-TargetMap",
"type": "aos.message_bridge.RemoteMessage",
"frequency": 80,
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"max_size": 208
},
{
- "name": "/orin{{ NUM }}/aos/remote_timestamps/imu/orin{{ NUM }}/camera2/frc971-vision-TargetMap",
+ "name": "/orin1/aos/remote_timestamps/imu/orin1/camera1/frc971-vision-TargetMap",
"type": "aos.message_bridge.RemoteMessage",
"frequency": 80,
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"max_size": 208
},
{
- "name": "/orin{{ NUM }}/constants",
+ "name": "/orin1/constants",
"type": "y2024.Constants",
- "source_node": "orin{{ NUM }}",
+ "source_node": "orin1",
"frequency": 1,
"num_senders": 2,
"max_size": 65536
@@ -240,7 +254,7 @@
],
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
@@ -249,15 +263,18 @@
"user": "root",
"args": ["--user=pi"],
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
"name": "message_bridge_server",
"executable_name": "message_bridge_server",
+ "args": [
+ "--rt_priority=16"
+ ],
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
@@ -269,15 +286,13 @@
"--max_ice_port=5810"
],
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
"name": "image_logger",
"executable_name": "image_logger",
"args": [
- "--logging_folder",
- "",
"--rotate_every",
"30.0",
"--direct",
@@ -285,14 +300,25 @@
],
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
"name": "foxglove_websocket",
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
+ ]
+ },
+ {
+ "name": "foxglove_image_converter0",
+ "executable_name": "foxglove_image_converter",
+ "user": "pi",
+ "args": [
+ "--channel", "/camera0"
+ ],
+ "nodes": [
+ "orin1"
]
},
{
@@ -303,18 +329,7 @@
"--channel", "/camera1"
],
"nodes": [
- "orin{{ NUM }}"
- ]
- },
- {
- "name": "foxglove_image_converter2",
- "executable_name": "foxglove_image_converter",
- "user": "pi",
- "args": [
- "--channel", "/camera2"
- ],
- "nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
@@ -322,7 +337,20 @@
"autorestart": false,
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
+ ]
+ },
+ {
+ "name": "argus_camera0",
+ "executable_name": "argus_camera",
+ "args": [
+ "--enable_ftrace",
+ "--camera=0",
+ "--channel=/camera0",
+ ],
+ "user": "pi",
+ "nodes": [
+ "orin1"
]
},
{
@@ -330,25 +358,23 @@
"executable_name": "argus_camera",
"args": [
"--enable_ftrace",
- "--camera=0",
+ "--camera=1",
"--channel=/camera1",
],
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
- "name": "argus_camera2",
- "executable_name": "argus_camera",
+ "name": "apriltag_detector0",
+ "executable_name": "apriltag_detector",
"args": [
- "--enable_ftrace",
- "--camera=1",
- "--channel=/camera2",
+ "--channel=/camera0",
],
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
},
{
@@ -359,18 +385,7 @@
],
"user": "pi",
"nodes": [
- "orin{{ NUM }}"
- ]
- },
- {
- "name": "apriltag_detector2",
- "executable_name": "apriltag_detector",
- "args": [
- "--channel=/camera2",
- ],
- "user": "pi",
- "nodes": [
- "orin{{ NUM }}"
+ "orin1"
]
}
],
@@ -378,40 +393,40 @@
{
"match": {
"name": "/aos*",
- "source_node": "orin{{ NUM }}"
+ "source_node": "orin1"
},
"rename": {
- "name": "/orin{{ NUM }}/aos"
+ "name": "/orin1/aos"
}
},
{
"match": {
"name": "/constants*",
- "source_node": "orin{{ NUM }}"
+ "source_node": "orin1"
},
"rename": {
- "name": "/orin{{ NUM }}/constants"
+ "name": "/orin1/constants"
}
},
{
"match": {
"name": "/camera*",
- "source_node": "orin{{ NUM }}"
+ "source_node": "orin1"
},
"rename": {
- "name": "/orin{{ NUM }}/camera"
+ "name": "/orin1/camera"
}
}
],
"nodes": [
{
- "name": "orin{{ NUM }}",
- "hostname": "orin{{ NUM }}",
+ "name": "orin1",
+ "hostname": "orin1",
"hostnames": [
- "orin-971-{{ NUM }}",
- "orin-7971-{{ NUM }}",
- "orin-8971-{{ NUM }}",
- "orin-9971-{{ NUM }}"
+ "orin-971-1",
+ "orin-7971-1",
+ "orin-8971-1",
+ "orin-9971-1"
],
"port": 9971
},
diff --git a/y2024/y2024_roborio.json b/y2024/y2024_roborio.json
index 93caacc..5ba882a 100644
--- a/y2024/y2024_roborio.json
+++ b/y2024/y2024_roborio.json
@@ -19,6 +19,13 @@
},
{
"name": "/roborio/aos",
+ "type": "frc971.PDPValues",
+ "source_node": "roborio",
+ "frequency": 55,
+ "max_size": 368
+ },
+ {
+ "name": "/roborio/aos",
"type": "aos.RobotState",
"source_node": "roborio",
"frequency": 250
@@ -28,7 +35,7 @@
"type": "aos.timing.Report",
"source_node": "roborio",
"frequency": 50,
- "num_senders": 20,
+ "num_senders": 30,
"max_size": 8192
},
{
@@ -120,6 +127,7 @@
"type": "y2024.control_loops.superstructure.Status",
"source_node": "roborio",
"frequency": 400,
+ "max_size": 2048,
"num_senders": 2
},
{
@@ -139,6 +147,22 @@
"max_size": 448
},
{
+ "name": "/superstructure/canivore",
+ "type": "y2024.control_loops.superstructure.CANPosition",
+ "source_node": "roborio",
+ "frequency": 220,
+ "num_senders": 2,
+ "max_size": 1024
+ },
+ {
+ "name": "/superstructure/rio",
+ "type": "y2024.control_loops.superstructure.CANPosition",
+ "source_node": "roborio",
+ "frequency": 220,
+ "num_senders": 2,
+ "max_size": 1024
+ },
+ {
"name": "/can",
"type": "frc971.can_logger.CanFrame",
"source_node": "roborio",
@@ -152,7 +176,7 @@
"source_node": "roborio",
"frequency": 220,
"num_senders": 2,
- "max_size": 400
+ "max_size": 424
},
{
"name": "/drivetrain",
@@ -197,12 +221,12 @@
"type": "frc971.control_loops.drivetrain.Position",
"source_node": "roborio",
"frequency": 400,
- "max_size": 112,
+ "max_size": 128,
"num_senders": 2
},
{
"name": "/drivetrain",
- "type": "frc971.control_loops.drivetrain.Output",
+ "type": "frc971.control_loops.drivetrain.RioLocalizerInputs",
"source_node": "roborio",
"frequency": 400,
"max_size": 80,
@@ -221,6 +245,14 @@
},
{
"name": "/drivetrain",
+ "type": "frc971.control_loops.drivetrain.Output",
+ "source_node": "roborio",
+ "frequency": 400,
+ "max_size": 80,
+ "num_senders": 2
+ },
+ {
+ "name": "/drivetrain",
"type": "frc971.control_loops.drivetrain.Status",
"source_node": "roborio",
"frequency": 400,
@@ -406,8 +438,12 @@
]
},
{
- "name": "can_logger",
+ "name": "roborio_can_logger",
"executable_name": "can_logger",
+ "autostart": false,
+ "args": [
+ "--poll"
+ ],
"nodes": [
"roborio"
]