From b9cbd4acc0764b2aac0e30b804c05603576dcbcd Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Wed, 11 Jan 2023 21:54:49 +0000 Subject: [PATCH 01/20] Initial commit From 0105b26da7f4f11201ab50f557446a9e435fabed Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Sat, 14 Jan 2023 14:41:40 +0000 Subject: [PATCH 02/20] made first version of scouter UI and started making analysis pipeline & modules --- README.md | 233 +- config/analysis-modules.json | 438 +--- config/analysis-modules2.json | 428 ++++ config/analysis-pipeline.json | 479 +--- config/analysis-pipeline2.json | 453 ++++ config/match-scouting.json | 645 +++++- config/match-scouting2.json | 2016 +++++++++++++++++ replit.nix | 6 + .../transformers/testingtesting123.js | 28 + src/scouting/public/css/match-scouting.css | 19 +- 10 files changed, 3686 insertions(+), 1059 deletions(-) create mode 100644 config/analysis-modules2.json create mode 100644 config/analysis-pipeline2.json create mode 100644 config/match-scouting2.json create mode 100644 replit.nix create mode 100644 src/analysis/transformers/testingtesting123.js diff --git a/README.md b/README.md index ee7c1e03..1ddbf469 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,177 @@ -# SPOT - Scouting Platforms On Time -SPOT is an open-source modular scouting app framework for FRC developed by Team 3061 Huskie Robotics. SPOT provides a simple platform upon which a team can build a scouting app with little to no prior experience. +# Configuring a Repl -## Features +Every new repl comes with a `.replit` and a `replit.nix` file that let you configure your repl to do just about anything in any language! -* Easy to use platform for data entry throughout matches. -* Analysis page to display detailed statistics and charts about matches and teams. -* Admin view for live scouter management at competition. -* Preconfigured for the 2022 Rapid React game with no additional game-specific customization required. -* Easy deployment experience when teams run a server on Glitch -* Optimized for teams who have internet access while scouting, completely functional for teams with no internet access. -* Quick setup with a built-in first-run wizard to walk teams through configuring their scouting app, connecting to The Blue Alliance, and setting up their database. -* Completely configurable analysis and scouting view without the need for a single line of code. +### `replit.nix` -SPOT is built with HTML, JS, CSS, and Node.js and operates with a MongoDB database. +Every new repl is now a Nix repl, which means you can install any package available on Nix, and support any number of languages in a single repl. You can search for a list of available packages [here](https://search.nixos.org/packages). -### SPOT is designed for a wide range of teams with a wide range of expertise and requirements: +The `replit.nix` file should look something like the example below. The `deps` array specifies which Nix packages you would like to be available in your environment. -1. A team that is new to scouting and isn't sure what information to gather or how to analyze their data. SPOT comes as a preconfigured app that can be deployed with minimal setup and will provide all the basic functionality needed by most teams. -2. A team that has some experience with scouting and wants to collect specific data that may not be part of the default configuration but that doesn't have significant programming experience. SPOT can be configured by modifying configuration files rather than by writing code. -3. A team that has experience with scouting and wants to perform custom analysis operations or advanced custom scouting interfaces and has some programming experience. SPOT can utilize user-provided analysis modules to extend its functionality beyond the default configuration without requiring teams to modify the base code. -4. A team that has significant experience with scouting and significant programming expertise. SPOT is open source, documented, and designed to extensible such that teams can start from this code base and build their own custom scouting system. +```nix +{ pkgs }: { + deps = [ + pkgs.cowsay + ]; +} +``` +### Learn More About Nix - -## Guiding Principles -* SPOT's goal is to provide a universal scouting app platform that can function with any past or future FIRST Robotics game by just changing the configuration. -* SPOT does not require you to write code to configure, set up, or use SPOT. -* SPOT does not require you to be online for its most basic features (scouting, data analysis) - -## Getting Started - -These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. If you have any questions, feel free to contact us [spot@team3061.org](mailto:spot@team3061.org). - -### [Quickstart](https://docs.google.com/document/d/1dATXMC5U7aT0SfnYEOWFiafaeWbu8opabNglWSFCSPE/edit?usp=sharing) - -## Usage - -This is a guide on how to use SPOT as well as its features, with a text and a video walkthrough. - -### [Usage Guide](https://docs.google.com/document/d/16n0msw98T-HM7h5cdLdA946QHSB-SX_6boAxm6i8aos/edit?usp=sharing) - -## Configuration - -This is a guide to configuring SPOT. You can configure the buttons displayed in the scouting view as well as how data is transformed and displayed. - -### [Configuration Guide](https://docs.google.com/document/d/1_FHr61p2eROtALV-fx0giBmp7oqVfTxc7K5_kCuS-8Q/edit?usp=sharing) - - -## Contributing - -Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on contributing to SPOT. - -## Documentation -Documentation of the core codebase is currently partially included in files. Check back soon for in-depth documentation later! - -## Authors - -* **Dylan Schmit** - *Initial work* - [cyandev](https://github.com/cyandev) -* **Nithilan Kalidoss** - *Initial work* - [nithilan4](https://github.com/nithilan4) - -See also the list of [contributors](https://github.com/HuskieRobotics/SPOT/contributors) who participated in this project. -* **Maaz Zubair** - *Maintanence* - [maaz-zubair-99](https://github.com/maaz-zubair-99) -* **Faiz Muhammad** - *Maintanence* - [faizmuhammad12](https://github.com/faizmuhammad12) -* **Emma Shipley** - *Maintanence* - [emmashipley](https://github.com/emmashipley) - -## License - -This project is licensed under the Apache License 2.0 - see the [LICENSE.md](LICENSE) file for details +If you'd like to learn more about Nix, here are some great resources: + +#### Written Guides +- [Getting started with Nix](/programming-ide/getting-started-nix) — Our own getting started guide +- [Building with Nix on Replit](https://docs.replit.com/tutorials/30-build-with-nix) — Deploy a production web stack on Replit with Nix +- [Nix Pills](https://nixos.org/guides/nix-pills/) — Guided introduction to Nix +- [Nix Package Manager Guide](https://nixos.org/manual/nix/stable/) — A comprehensive guide of the Nix Package Manager +- [A tour of Nix](https://nixcloud.io/tour) — Learn the nix language itself + +#### Video Guides +- [Nixology](https://www.youtube.com/playlist?list=PLRGI9KQ3_HP_OFRG6R-p4iFgMSK1t5BHs) — A series of videos introducing Nix in a practical way +- [Taking the Nix pill](https://www.youtube.com/watch?v=QwLWIy2KleE) — An introduction to what Nix is, how it works, and a walkthrough of publishing several new languages to Replit within an hour. +- [Nix: A Deep Dive](https://www.youtube.com/watch?v=TsZte_9GfPE) — A deep dive on Nix: what Nix is, why you should use it, and how it works. + + +### `.replit` + +The `.replit` file allows you to configure many options for your repl, most basic of which is the `run` command. + +Check out how to use the `.replit` file to configure a repl to enable [Clojure](https://clojure.org): + + + +`.replit` files follow the [toml configuration format](https://learnxinyminutes.com/docs/toml/) and look something like this: + +```toml +# The command that is executed when the run button is clicked. +run = ["cargo", "run"] + +# The default file opened in the editor. +entrypoint = "src/main.rs" + +# Setting environment variables +[env] +FOO="foo" + +# Packager configuration for the Universal Package Manager +# See https://github.com/replit/upm for supported languages. +[packager] +language = "rust" + + [packager.features] + # Enables the package search sidebar + packageSearch = true + # Enabled package guessing + guessImports = false + +# Per language configuration: language. +[languages.rust] +# The glob pattern to match files for this programming language +pattern = "**/*.rs" + + # LSP configuration for code intelligence + [languages.rust.languageServer] + start = ["rust-analyzer"] +``` + +In the code above, the strings in the array assigned to `run` are executed in order in the shell whenever you hit the "Run" button. + +The `language` configuration option helps the IDE understand how to provide features like [packaging](https://blog.replit.com/upm) and [code intelligence](https://blog.replit.com/intel). + +And the `[languages.rust]` `pattern` option is configured so that all files ending with `.rs` are treated as Rust files. The name is user-defined and doesn't have any special meaning, we could have used `[languages.rs]` instead. + +We can now set up a language server specifically for Rust. Which is what we do with the next configuration option: `[languages.rust.languageServer]`. [Language servers](https://microsoft.github.io/language-server-protocol/#:~:text=A%20Language%20Server%20is%20meant,servers%20and%20development%20tools%20communicate.) add smart features to your editor like code intelligence, go-to-definition, and documentation on hover. + +Since repls are fully configurable, you're not limited to just one language. For example, you could install Clojure and its language server using `replit.nix`, add a `[languages.clojure]` configuration option to the above `.replit` file that matched all Clojure files and have code intelligence enabled for both languages in the same repl. + +### `.replit` reference + +A `Command` can either be a string or a list of strings. If the `Command` is a string (`"node index.js"`), it will be executed via `sh -c ""`. If the Command is a list of strings (`["node", "index.js"]`), it will be directly executed with the list of strings passed as arguments. When possible, it is preferred to pass a list of strings. + +- `run` + - **Type:** `Command` + - **Description:** The command to run the repl. +- `entrypoint` + - **Type:** `string` + - **Description:** The name of the main file including the extension. This is the file that will be run, and shown by default when opening the editor. +- `onBoot` + - **Type:** `Command` + - **Description:** The command that executes after your repl has booted. +- `compile` + - **Type:** `Command` + - **Description:** The shell command to compile the repl before the `run` command. Only for compiled languages like C, C++, and Java. +- `audio` + - **Type:** `boolean` + - **Description:** Enables [system-wide audio](https://docs.replit.com/misc/playing-audio-replit) for the repl when configured to `true`. +- `language` + - **Type:** `string` + - **Description:** Reserved. During a GitHub import, this tells the workspace which language should be used when creating the repl. For new repls, this option will always be Nix, so this field should generally not be touched. +- `[env]` + - **Description:** Set environment variables. Don't put secrets here—use the Secrets tab in the left sidebar. + - **Example:** `VIRTUAL_ENV = "/home/runner/${REPL_SLUG}/venv"` +- `interpreter` + - **Description:** Specifies the interpreter, which should be a compliant [prybar binary](https://github.com/replit/prybar). + - `command` + - **Type:** `[string]` + - **Description:** This is the command that will be run to start the interpreter. It has higher precedence than the `run` command (i.e. `interpreter` command will run instead of the `run` command). + - `prompt` + - **Type:** `[byte]` + - **Description:** This is the prompt used to detect running state, if unspecified it defaults to `[0xEE, 0xA7]`. +- `[unitTest]` + - Enables unit testing to the repl. + - `language` + - **Type:** `string` + - **Description:** The language you want the unit tests to run. Supported strings: `java`, `python`, and `nodejs`. +- `[packager]` + - **Description:** Package management configuration. Learn more about installing packages [here](https://docs.replit.com/repls/packages/#DirectImports). + - `afterInstall` + - **Type:** `Command` + - **Description:** The command that is executed after a new package is installed. + - `ignoredPaths` + - **Type:** `[string]` + - **Description:** List of paths to ignore while attempting to guess packages. + - `ignoredPackages` + - **Type:** `[string]` + - **Description:** List of modules to never attempt to guess a package for, when installing packages. + - `language` + - **Type:** `string` + - **Description:** Specifies the language to use for package operations. See available languages in the [Universal Package Manager](https://github.com/replit/upm) repository. + - `[packager.features]` + - **Description:** UPM features that are supported by the specified languages. + - `packageSearch` + - **Type:** Boolean + - **Description:** When set to `true`, enables a package search panel in the sidebar. + - `guessImports` + - **Type:** Boolean + - **Description:** When set to `true`, UPM will attempt to guess which packages need to be installed prior to running the repl. +- `[languages.]` + - **Description:** Per-language configuration. The language name has no special meaning other than to allow multiple languages to be configured at once. + - `pattern` + - **Type:** `string` + - **Description:** A [glob](https://en.wikipedia.org/wiki/Glob_(programming)) used to identify which files belong to this language configuration (`**/*.js`) + - `syntax` + - **Type:** `string` + - **Description:** The language to use for syntax highlighting. + - `[languages..languageServer]` + - **Description:** Configuration for setting up [LSP](https://microsoft.github.io/language-server-protocol/) for this language. This allows for code intelligence (autocomplete, underlined errors, etc...). + - `start` + - **Type:** `Command` + - **Description:** The command used to start the LSP server for the specified language. +- `[nix]` + - **Description:** Where you specify a [Nix channel](https://nixos.wiki/wiki/Nix_channels). + - `channel` + - **Type:** `string` + - **Description:** A nix channel id. +- `[debugger]` + - **Description:** Advanced users only. See field types & docstrings [here](https://gist.github.com/Bardia95/98987c69c6970b1bb0698b863e2a84de#file-dot-replit-debugger-config-go), and in the advanced section below. + +### Example configurations +#### Beginner +##### [LaTeX](https://replit.com/@ZachAtReplit/LaTeX?v=1#.replit) +##### [Clojure](https://replit.com/@replit/Clojure?v=1#.replit) +#### Advanced +##### [Python](https://replit.com/@replit/Python?v=1) +##### [HTML, CSS, JS](https://replit.com/@replit/HTML-CSS-JS?v=1#.replit) +##### [Java](https://replit.com/@replit/Java-Beta?v=1#.replit) +##### [Node.js](https://replit.com/@replit/Nodejs?v=1#.replit) +##### [C++](https://replit.com/@replit/CPlusPlus?v=1) \ No newline at end of file diff --git a/config/analysis-modules.json b/config/analysis-modules.json index 2dddec53..e120b9bf 100644 --- a/config/analysis-modules.json +++ b/config/analysis-modules.json @@ -1,428 +1,18 @@ [ { - "view": "team", - "module": "HeatmapScatterPlot", - "name": "Shot Plot", - "position": "main", - "options": { - "coordinatePath": "other.pos", - "aggregatedActionsPath": "aggregatedActions", - "actionGroups": [ - { - "name": "Shots", - "actions": ["upperHub", "lowerHub", "miss"] - } - ], - "actionLabels": { - "upperHub": "Upper", - "lowerHub": "Lower", - "miss": "Miss" - }, - "imgPath": "img/field.svg" - } - }, - { - "view": "team", - "module": "HeatmapScatterPlot", - "name": "Pickup Plot", - "position": "main", - "options": { - "coordinatePath": "other.pos", - "aggregatedActionsPath": "aggregatedActions", - "actionGroups": [ - { - "name": "Pickups", - "actions": ["groundPickup"] - } - ], - "actionLabels": { - "groundPickup": "Ground" - }, - "imgPath": "img/field.svg" - } - }, - { - "view": "team", - "module": "PerformanceTimePlot", - "name": "Performance Over Time", - "position": "main", - "options": { - "trackedStats": [ - "scores.auto", - "scores.teleop", - "scores.endgame", - "scores.all", - "accuracy" - ] - } - }, - { - "view": "team", - "module": "Stats", - "position": "side", - "name": "Team Stats", - "options": { - "list": [ - { - "name": "Initiation Cross", - "path": "averages.tarmacCrossTime", - "multiplier": -0.001, - "addend": 150, - "decimals": 2, - "hideIfValue": 150, - "sort": -1, - "unit": "s in" - }, - { - "name": "Climb Start Time", - "path": "averages.startClimbTime", - "multiplier": 0.001, - "decimals": 2, - "hideIfValue": 0, - "sort": -1, - "unit": "s to end" - }, - { - "name": "Climb End Time", - "path": "averages.endClimbTime", - "multiplier": 0.001, - "decimals": 2, - "hideIfValue": 0, - "sort": -1, - "unit": "s to end" - }, - { - "name": "Climb Duration", - "path": "averages.climbDuration", - "multiplier": 0.001, - "decimals": 2, - "hideIfValue": 0, - "sort": -1, - "unit": "s" - }, - { - "name": "Time Per Ball", - "path": "timePerBall", - "multiplier": 0.001, - "decimals": 2, - "hideIfValue": 0, - "sort": -1, - "unit": "s" - }, - { - "name": "Accuracy", - "path": "accuracy", - "multiplier": 100, - "decimals": 1, - "sort": 1, - "unit": "%" - }, - { - "name": "Average Auto Points", - "path": "averageScores.auto", - "decimals": 2, - "sort": 1 - }, - { - "name": "Average Climb Points", - "path": "averageScores.endgame", - "decimals": 2, - "sort": 1 - }, - { - "name": "Average Points", - "path": "averageScores.all", - "decimals": 2, - "sort": 1 - }, - { - "name": "Average Upper Hub Shots", - "path": "averages.upperHub", - "decimals": 2, - "sort": 1 - }, - { - "name": "Average Lower Hub Shots", - "path": "averages.lowerHub", - "decimals": 2, - "sort": 1 - }, - { - "name": "Average Misses", - "path": "averages.miss", - "decimals": 2, - "sort": -1 - } - ] - } - }, - { - "view": "team", - "module": "Pie", - "position": "main", - "name": "Climb Distribution", - "options": { - "slices": [ - { - "name": "No Climb", - "path": "climbLevels.noClimb" - }, - { - "name": "Fall", - "path": "climbLevels.climbF" - }, - { - "name": "Low Rung", - "path": "climbLevels.climb1" - }, - { - "name": "Mid Rung", - "path": "climbLevels.climb2" - }, - { - "name": "High Rung", - "path": "climbLevels.climb3" - }, - { - "name": "Traversal Rung", - "path": "climbLevels.climb4" - } - ] - } - }, - { - "view": "team", - "module": "Pie", - "position": "main", - "name": "Shot Distribution", - "options": { - "slices": [ - { - "name": "Upper Hub", - "path": "counts.upperHub" - }, - { - "name": "Lower Hub", - "path": "counts.lowerHub" - }, - { - "name": "Miss", - "path": "counts.miss" - } - ] - } - }, - { - "view": "match", - "module": "SingleDisplay", - "name": "Alliance Score", - "position": "main", - "options": { - "path": "averageScores.all", - "aggrMethod": "sum", - "decimals": 2 - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Auto Points", - "position": "main", - "options": { - "path": "averageScores.auto", - "sort": 1, - "decimals": 1 - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Climb Points", - "position": "main", - "options": { - "path": "averageScores.endgame", - "sort": 1, - "decimals": 1 - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Possible Climbs", - "position": "main", - "options": { - "string": true, - "path": "possibleClimbs" - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Climb Time", - "position": "main", - "options": { - "path": "averages.climbDuration", - "multiplier": 0.001, - "decimals": 2, - "hideIfValue": 0, - "sort": -1, - "unit": "s" - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Total Points", - "position": "main", - "options": { - "path": "averageScores.all", - "sort": 1, - "decimals": 1 - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Time Per Ball", - "position": "main", - "options": { - "path": "timePerBall", - "multiplier": 0.001, - "decimals": 2, - "hideIfValue": 0, - "sort": -1, - "unit": "s" - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Accuracy", - "position": "main", - "options": { - "path": "accuracy", - "sort": 1, - "decimals": 1, - "multiplier": 100, - "unit": "%" - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Possible Shots", - "position": "main", - "options": { - "string": true, - "path": "possibleShots" - } - }, - { - "view": "match", - "module": "ColumnDisplay", - "name": "Drivetrain", - "position": "main", - "options": { - "string": true, - "path": "manual.drivetrain" - } - }, - { - "view": "match", - "module": "HeatmapScatterPlot", - "name": "Alliance Shot Plot", - "position": "main", - "options": { - "coordinatePath": "other.pos", - "aggregatedActionsPath": "aggregatedActions", - "actionGroups": [ - { - "name": "Shots", - "actions": ["upperHub", "lowerHub", "miss"] - } - ], - "actionLabels": { - "upperHub": "Upper", - "lowerHub": "Lower", - "miss": "Miss" - }, - "imgPath": "img/field.svg" - } - }, - { - "view": "match", - "module": "HeatmapScatterPlot", - "name": "Alliance Pickup Plot", - "position": "main", - "options": { - "coordinatePath": "other.pos", - "aggregatedActionsPath": "aggregatedActions", - "actionGroups": [ - { - "name": "Pickups", - "actions": ["groundPickup"] - } - ], - "actionLabels": { - "groundPickup": "Ground" - }, - "imgPath": "img/field.svg" - } - }, - { - "view": "match", - "module": "Stats", - "position": "side", - "name": "Alliance Stats", - "options": { - "list": [ - { - "name": "Expected Auto Points", - "path": "averageScores.auto", - "decimals": 2, - "aggrMethod": "sum" - }, - { - "name": "Expected Teleop Points", - "path": "averageScores.teleop", - "decimals": 2, - "aggrMethod": "sum" - }, - { - "name": "Expected Climb Points", - "path": "averageScores.endgame", - "decimals": 2, - "aggrMethod": "sum" - }, - { - "name": "Expected Score", - "path": "averageScores.all", - "decimals": 2, - "aggrMethod": "sum" - } - ] - } - }, - { - "view": "match", - "module": "Pie", - "position": "main", - "name": "Shot Distribution", - "options": { - "slices": [ - { - "name": "Upper Hub", - "path": "averages.upperHub" - }, - { - "name": "Lower Hub", - "path": "averages.lowerHub" - }, - { - "name": "Miss", - "path": "averages.miss" - } - ] - } + "view":"team", + "module":"Stats", + "position":"side", + "name":"Stats that we hope work", + "options": { + "list":[ + { + "name":"Charging Dock", + "path":"counts.chargingDock", + "decimals":2, + "sort":1 + } + ] + } } ] \ No newline at end of file diff --git a/config/analysis-modules2.json b/config/analysis-modules2.json new file mode 100644 index 00000000..2dddec53 --- /dev/null +++ b/config/analysis-modules2.json @@ -0,0 +1,428 @@ +[ + { + "view": "team", + "module": "HeatmapScatterPlot", + "name": "Shot Plot", + "position": "main", + "options": { + "coordinatePath": "other.pos", + "aggregatedActionsPath": "aggregatedActions", + "actionGroups": [ + { + "name": "Shots", + "actions": ["upperHub", "lowerHub", "miss"] + } + ], + "actionLabels": { + "upperHub": "Upper", + "lowerHub": "Lower", + "miss": "Miss" + }, + "imgPath": "img/field.svg" + } + }, + { + "view": "team", + "module": "HeatmapScatterPlot", + "name": "Pickup Plot", + "position": "main", + "options": { + "coordinatePath": "other.pos", + "aggregatedActionsPath": "aggregatedActions", + "actionGroups": [ + { + "name": "Pickups", + "actions": ["groundPickup"] + } + ], + "actionLabels": { + "groundPickup": "Ground" + }, + "imgPath": "img/field.svg" + } + }, + { + "view": "team", + "module": "PerformanceTimePlot", + "name": "Performance Over Time", + "position": "main", + "options": { + "trackedStats": [ + "scores.auto", + "scores.teleop", + "scores.endgame", + "scores.all", + "accuracy" + ] + } + }, + { + "view": "team", + "module": "Stats", + "position": "side", + "name": "Team Stats", + "options": { + "list": [ + { + "name": "Initiation Cross", + "path": "averages.tarmacCrossTime", + "multiplier": -0.001, + "addend": 150, + "decimals": 2, + "hideIfValue": 150, + "sort": -1, + "unit": "s in" + }, + { + "name": "Climb Start Time", + "path": "averages.startClimbTime", + "multiplier": 0.001, + "decimals": 2, + "hideIfValue": 0, + "sort": -1, + "unit": "s to end" + }, + { + "name": "Climb End Time", + "path": "averages.endClimbTime", + "multiplier": 0.001, + "decimals": 2, + "hideIfValue": 0, + "sort": -1, + "unit": "s to end" + }, + { + "name": "Climb Duration", + "path": "averages.climbDuration", + "multiplier": 0.001, + "decimals": 2, + "hideIfValue": 0, + "sort": -1, + "unit": "s" + }, + { + "name": "Time Per Ball", + "path": "timePerBall", + "multiplier": 0.001, + "decimals": 2, + "hideIfValue": 0, + "sort": -1, + "unit": "s" + }, + { + "name": "Accuracy", + "path": "accuracy", + "multiplier": 100, + "decimals": 1, + "sort": 1, + "unit": "%" + }, + { + "name": "Average Auto Points", + "path": "averageScores.auto", + "decimals": 2, + "sort": 1 + }, + { + "name": "Average Climb Points", + "path": "averageScores.endgame", + "decimals": 2, + "sort": 1 + }, + { + "name": "Average Points", + "path": "averageScores.all", + "decimals": 2, + "sort": 1 + }, + { + "name": "Average Upper Hub Shots", + "path": "averages.upperHub", + "decimals": 2, + "sort": 1 + }, + { + "name": "Average Lower Hub Shots", + "path": "averages.lowerHub", + "decimals": 2, + "sort": 1 + }, + { + "name": "Average Misses", + "path": "averages.miss", + "decimals": 2, + "sort": -1 + } + ] + } + }, + { + "view": "team", + "module": "Pie", + "position": "main", + "name": "Climb Distribution", + "options": { + "slices": [ + { + "name": "No Climb", + "path": "climbLevels.noClimb" + }, + { + "name": "Fall", + "path": "climbLevels.climbF" + }, + { + "name": "Low Rung", + "path": "climbLevels.climb1" + }, + { + "name": "Mid Rung", + "path": "climbLevels.climb2" + }, + { + "name": "High Rung", + "path": "climbLevels.climb3" + }, + { + "name": "Traversal Rung", + "path": "climbLevels.climb4" + } + ] + } + }, + { + "view": "team", + "module": "Pie", + "position": "main", + "name": "Shot Distribution", + "options": { + "slices": [ + { + "name": "Upper Hub", + "path": "counts.upperHub" + }, + { + "name": "Lower Hub", + "path": "counts.lowerHub" + }, + { + "name": "Miss", + "path": "counts.miss" + } + ] + } + }, + { + "view": "match", + "module": "SingleDisplay", + "name": "Alliance Score", + "position": "main", + "options": { + "path": "averageScores.all", + "aggrMethod": "sum", + "decimals": 2 + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Auto Points", + "position": "main", + "options": { + "path": "averageScores.auto", + "sort": 1, + "decimals": 1 + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Climb Points", + "position": "main", + "options": { + "path": "averageScores.endgame", + "sort": 1, + "decimals": 1 + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Possible Climbs", + "position": "main", + "options": { + "string": true, + "path": "possibleClimbs" + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Climb Time", + "position": "main", + "options": { + "path": "averages.climbDuration", + "multiplier": 0.001, + "decimals": 2, + "hideIfValue": 0, + "sort": -1, + "unit": "s" + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Total Points", + "position": "main", + "options": { + "path": "averageScores.all", + "sort": 1, + "decimals": 1 + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Time Per Ball", + "position": "main", + "options": { + "path": "timePerBall", + "multiplier": 0.001, + "decimals": 2, + "hideIfValue": 0, + "sort": -1, + "unit": "s" + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Accuracy", + "position": "main", + "options": { + "path": "accuracy", + "sort": 1, + "decimals": 1, + "multiplier": 100, + "unit": "%" + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Possible Shots", + "position": "main", + "options": { + "string": true, + "path": "possibleShots" + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Drivetrain", + "position": "main", + "options": { + "string": true, + "path": "manual.drivetrain" + } + }, + { + "view": "match", + "module": "HeatmapScatterPlot", + "name": "Alliance Shot Plot", + "position": "main", + "options": { + "coordinatePath": "other.pos", + "aggregatedActionsPath": "aggregatedActions", + "actionGroups": [ + { + "name": "Shots", + "actions": ["upperHub", "lowerHub", "miss"] + } + ], + "actionLabels": { + "upperHub": "Upper", + "lowerHub": "Lower", + "miss": "Miss" + }, + "imgPath": "img/field.svg" + } + }, + { + "view": "match", + "module": "HeatmapScatterPlot", + "name": "Alliance Pickup Plot", + "position": "main", + "options": { + "coordinatePath": "other.pos", + "aggregatedActionsPath": "aggregatedActions", + "actionGroups": [ + { + "name": "Pickups", + "actions": ["groundPickup"] + } + ], + "actionLabels": { + "groundPickup": "Ground" + }, + "imgPath": "img/field.svg" + } + }, + { + "view": "match", + "module": "Stats", + "position": "side", + "name": "Alliance Stats", + "options": { + "list": [ + { + "name": "Expected Auto Points", + "path": "averageScores.auto", + "decimals": 2, + "aggrMethod": "sum" + }, + { + "name": "Expected Teleop Points", + "path": "averageScores.teleop", + "decimals": 2, + "aggrMethod": "sum" + }, + { + "name": "Expected Climb Points", + "path": "averageScores.endgame", + "decimals": 2, + "aggrMethod": "sum" + }, + { + "name": "Expected Score", + "path": "averageScores.all", + "decimals": 2, + "aggrMethod": "sum" + } + ] + } + }, + { + "view": "match", + "module": "Pie", + "position": "main", + "name": "Shot Distribution", + "options": { + "slices": [ + { + "name": "Upper Hub", + "path": "averages.upperHub" + }, + { + "name": "Lower Hub", + "path": "averages.lowerHub" + }, + { + "name": "Miss", + "path": "averages.miss" + } + ] + } + } +] \ No newline at end of file diff --git a/config/analysis-pipeline.json b/config/analysis-pipeline.json index d0b67b74..ad09ff60 100644 --- a/config/analysis-pipeline.json +++ b/config/analysis-pipeline.json @@ -1,453 +1,30 @@ [ - { - "type": "tmp", - "name": "countActions", - "outputPath": "counts", - "options": { - "all": true - } - }, - { - "type": "team", - "name": "countActions", - "outputPath": "counts", - "options": { - "all": true - } - }, - { - "type": "team", - "name": "sum", - "outputPath": "counts.balls", - "options": { - "addends": [ - "counts.upperHub", - "counts.lowerHub", - "counts.miss" - ] - } - }, - - { - "type": "team", - "name": "aggregateArray", - "outputPath": "aggregatedActions", - "options": { - "path": "actionQueue" - } - }, - - { - "type": "team", - "name": "countMatches", - "outputPath": "temp.totalTimeMs", - "options": { - "weight": 150000 - } - }, - { - "type": "team", - "name": "ratio", - "outputPath": "timePerBall", - "options": { - "numerator": ["temp.totalTimeMs"], - "denominator": ["counts.balls"] - }, - "divByZero": 150000 - }, - - { - "type": "tmp", - "name": "cycle", - "outputPath": "cycle", - "options": { - "pickups": [ - "groundPickup" - ], - "scores": [ - "upperHub", - "lowerHub" - ], - "misses": [ - "miss" - ] - } - }, - { - "type": "team", - "name": "aggregateArray", - "outputPath": "cycle.all", - "options": { - "path": "cycle.all" - } - }, - { - "type": "team", - "name": "aggregateArray", - "outputPath": "cycle.allComplete", - "options": { - "path": "cycle.allComplete" - } - }, - { - "type": "team", - "name": "averageArray", - "outputPath": "cycle.averageTime", - "options": { - "arrayPath": "cycle.all", - "valuePath": "timeDifferential" - } - }, - { - "type": "team", - "name": "averageArray", - "outputPath": "cycle.averageTimeComplete", - "options": { - "arrayPath": "cycle.allComplete", - "valuePath": "timeDifferential" - } - }, - { - "type": "team", - "name": "average", - "outputPath": "averages", - "options": { - "path": "counts" - } - }, - - { - "type": "tmp", - "name": "ratio", - "outputPath": "accuracy", - "options": { - "numerator": [ - "counts.lowerHub", - "counts.upperHub" - ], - "denominator": [ - "counts.lowerHub", - "counts.upperHub", - "counts.miss" - ], - "divByZero": 0 - } - }, - { - "type": "team", - "name": "ratio", - "outputPath": "accuracy", - "options": { - "numerator": [ - "counts.lowerHub", - "counts.upperHub" - ], - "denominator": [ - "counts.lowerHub", - "counts.upperHub", - "counts.miss" - ], - "divByZero": 0 - } - }, - - - - { - "type": "tmp", - "name": "actionTime", - "outputPath": "tarmacCrossTime", - "options": { - "actionId": "leaveTarmac" - } - }, - { - "type": "team", - "name": "average", - "outputPath": "averages.tarmacCrossTime", - "options": { - "path": "tarmacCrossTime" - } - }, - { - "type": "tmp", - "name": "actionTime", - "outputPath": "startClimbTime", - "options": { - "actionId": "startClimb" - } - }, - { - "type": "team", - "name": "average", - "outputPath": "averages.startClimbTime", - "options": { - "path": "startClimbTime" - } - }, - { - "type": "tmp", - "name": "actionTimeFilter", - "outputPath": "autoActions", - "options": { - "timeMin": 131000 - } - }, - { - "type": "tmp", - "name": "countActions", - "outputPath": "countsAuto", - "options": { - "all": true, - "actionArrayPath": "autoActions" - } - }, - { - "type": "tmp", - "name": "weightedSum", - "outputPath": "scores.auto", - "options": { - "weightedPaths": { - "countsAuto.leaveTarmac": 2, - "countsAuto.lowerHub": 2, - "countsAuto.upperHub": 4 - } - } - }, - - { - "type": "tmp", - "name": "actionTimeFilter", - "outputPath": "teleopActions", - "options": { - "timeMax": 131000 - } - }, - { - "type": "tmp", - "name": "countActions", - "outputPath": "countsTeleop", - "options": { - "all": true, - "actionArrayPath": "teleopActions" - } - }, - { - "type": "tmp", - "name": "weightedSum", - "outputPath": "scores.teleop", - "options": { - "weightedPaths": { - "countsTeleop.lowerHub": 1, - "countsTeleop.upperHub": 2 - } - } - }, - - { - "type": "tmp", - "name": "finalActionOccurence", - "outputPath": "finalClimbState", - "options": { - "ids": ["climb1","climb2","climb3","climb4","climbF"], - "default": { - "id": "noClimb", - "ts": 0 - } - } - }, - - { - "type": "tmp", - "name": "map", - "outputPath": "climbCounts.noClimb", - "options": { - "path": "finalClimbState.id", - "map": { - "noClimb": 1, - "climbF": 0, - "climb1": 0, - "climb2": 0, - "climb3": 0, - "climb4": 0 - } - } - }, - { - "type": "tmp", - "name": "map", - "outputPath": "climbCounts.climbF", - "options": { - "path": "finalClimbState.id", - "map": { - "noClimb": 0, - "climbF": 1, - "climb1": 0, - "climb2": 0, - "climb3": 0, - "climb4": 0 - } - } - }, - { - "type": "tmp", - "name": "map", - "outputPath": "climbCounts.climb1", - "options": { - "path": "finalClimbState.id", - "map": { - "noClimb": 0, - "climbF": 0, - "climb1": 1, - "climb2": 0, - "climb3": 0, - "climb4": 0 - } - } - }, - { - "type": "tmp", - "name": "map", - "outputPath": "climbCounts.climb2", - "options": { - "path": "finalClimbState.id", - "map": { - "noClimb": 0, - "climbF": 0, - "climb1": 0, - "climb2": 1, - "climb3": 0, - "climb4": 0 - } - } - }, - { - "type": "tmp", - "name": "map", - "outputPath": "climbCounts.climb3", - "options": { - "path": "finalClimbState.id", - "map": { - "noClimb": 0, - "climbF": 0, - "climb1": 0, - "climb2": 0, - "climb3": 1, - "climb4": 0 - } - } - }, - { - "type": "tmp", - "name": "map", - "outputPath": "climbCounts.climb4", - "options": { - "path": "finalClimbState.id", - "map": { - "noClimb": 0, - "climbF": 0, - "climb1": 0, - "climb2": 0, - "climb3": 0, - "climb4": 1 - } - } - }, - { - "type": "team", - "name": "average", - "outputPath": "climbLevels", - "options": { - "path": "climbCounts" - } - }, - { - "type": "team", - "name": "average", - "outputPath": "averages.endClimbTime", - "options": { - "path": "finalClimbState.ts" - } - }, - { - "type": "tmp", - "name": "subtract", - "outputPath": "climbDuration", - "options": { - "minuend": "startClimbTime", - "subtrahend": "finalClimbState.ts" - } - }, - { - "type": "team", - "name": "subtract", - "outputPath": "averages.climbDuration", - "options": { - "minuend": "averages.startClimbTime", - "subtrahend": "averages.endClimbTime" - } - }, - { - "type": "tmp", - "name": "map", - "outputPath": "scores.endgame", - "options": { - "path": "finalClimbState.id", - "map": { - "noClimb": 0, - "climbF": 0, - "climb1": 4, - "climb2": 6, - "climb3": 10, - "climb4": 15 - } - } - }, - - { - "type": "tmp", - "name": "sum", - "outputPath": "scores.all", - "options": { - "addends": ["scores.teleop","scores.auto","scores.endgame"] - } - }, - { - "type": "team", - "name": "average", - "outputPath": "averageScores", - "options": { - "path": "scores" - } - }, - { - "type": "team", - "name": "threshold", - "outputPath": "possibleClimbs", - "options": { - "threshold": 0, - "separator": ", ", - "none": "—", - "paths": { - "climbLevels.climb1": "1", - "climbLevels.climb2": "2", - "climbLevels.climb3": "3", - "climbLevels.climb4": "4" - } - } - }, - { - "type": "team", - "name": "threshold", - "outputPath": "possibleShots", - "options": { - "threshold": 1, - "separator": ", ", - "none": "—", - "paths": { - "averages.upperHub": "U", - "averages.lowerHub": "L" - } - } - } + { + "type": "tmp", + "name": "countActions", + "outputPath": "counts", + "options": { + "all": true + } + }, + { + "type": "team", + "name": "countActions", + "outputPath": "counts", + "options": { + "all": true + } + }, + { + "type":"team", + "name":"test", + "outputPath":"", + "options":{} + }, + { + "type":"tmp", + "name":"test", + "outputPath":"", + "options":{} + } ] \ No newline at end of file diff --git a/config/analysis-pipeline2.json b/config/analysis-pipeline2.json new file mode 100644 index 00000000..d0b67b74 --- /dev/null +++ b/config/analysis-pipeline2.json @@ -0,0 +1,453 @@ +[ + { + "type": "tmp", + "name": "countActions", + "outputPath": "counts", + "options": { + "all": true + } + }, + { + "type": "team", + "name": "countActions", + "outputPath": "counts", + "options": { + "all": true + } + }, + { + "type": "team", + "name": "sum", + "outputPath": "counts.balls", + "options": { + "addends": [ + "counts.upperHub", + "counts.lowerHub", + "counts.miss" + ] + } + }, + + { + "type": "team", + "name": "aggregateArray", + "outputPath": "aggregatedActions", + "options": { + "path": "actionQueue" + } + }, + + { + "type": "team", + "name": "countMatches", + "outputPath": "temp.totalTimeMs", + "options": { + "weight": 150000 + } + }, + { + "type": "team", + "name": "ratio", + "outputPath": "timePerBall", + "options": { + "numerator": ["temp.totalTimeMs"], + "denominator": ["counts.balls"] + }, + "divByZero": 150000 + }, + + { + "type": "tmp", + "name": "cycle", + "outputPath": "cycle", + "options": { + "pickups": [ + "groundPickup" + ], + "scores": [ + "upperHub", + "lowerHub" + ], + "misses": [ + "miss" + ] + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "cycle.all", + "options": { + "path": "cycle.all" + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "cycle.allComplete", + "options": { + "path": "cycle.allComplete" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "cycle.averageTime", + "options": { + "arrayPath": "cycle.all", + "valuePath": "timeDifferential" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "cycle.averageTimeComplete", + "options": { + "arrayPath": "cycle.allComplete", + "valuePath": "timeDifferential" + } + }, + { + "type": "team", + "name": "average", + "outputPath": "averages", + "options": { + "path": "counts" + } + }, + + { + "type": "tmp", + "name": "ratio", + "outputPath": "accuracy", + "options": { + "numerator": [ + "counts.lowerHub", + "counts.upperHub" + ], + "denominator": [ + "counts.lowerHub", + "counts.upperHub", + "counts.miss" + ], + "divByZero": 0 + } + }, + { + "type": "team", + "name": "ratio", + "outputPath": "accuracy", + "options": { + "numerator": [ + "counts.lowerHub", + "counts.upperHub" + ], + "denominator": [ + "counts.lowerHub", + "counts.upperHub", + "counts.miss" + ], + "divByZero": 0 + } + }, + + + + { + "type": "tmp", + "name": "actionTime", + "outputPath": "tarmacCrossTime", + "options": { + "actionId": "leaveTarmac" + } + }, + { + "type": "team", + "name": "average", + "outputPath": "averages.tarmacCrossTime", + "options": { + "path": "tarmacCrossTime" + } + }, + { + "type": "tmp", + "name": "actionTime", + "outputPath": "startClimbTime", + "options": { + "actionId": "startClimb" + } + }, + { + "type": "team", + "name": "average", + "outputPath": "averages.startClimbTime", + "options": { + "path": "startClimbTime" + } + }, + { + "type": "tmp", + "name": "actionTimeFilter", + "outputPath": "autoActions", + "options": { + "timeMin": 131000 + } + }, + { + "type": "tmp", + "name": "countActions", + "outputPath": "countsAuto", + "options": { + "all": true, + "actionArrayPath": "autoActions" + } + }, + { + "type": "tmp", + "name": "weightedSum", + "outputPath": "scores.auto", + "options": { + "weightedPaths": { + "countsAuto.leaveTarmac": 2, + "countsAuto.lowerHub": 2, + "countsAuto.upperHub": 4 + } + } + }, + + { + "type": "tmp", + "name": "actionTimeFilter", + "outputPath": "teleopActions", + "options": { + "timeMax": 131000 + } + }, + { + "type": "tmp", + "name": "countActions", + "outputPath": "countsTeleop", + "options": { + "all": true, + "actionArrayPath": "teleopActions" + } + }, + { + "type": "tmp", + "name": "weightedSum", + "outputPath": "scores.teleop", + "options": { + "weightedPaths": { + "countsTeleop.lowerHub": 1, + "countsTeleop.upperHub": 2 + } + } + }, + + { + "type": "tmp", + "name": "finalActionOccurence", + "outputPath": "finalClimbState", + "options": { + "ids": ["climb1","climb2","climb3","climb4","climbF"], + "default": { + "id": "noClimb", + "ts": 0 + } + } + }, + + { + "type": "tmp", + "name": "map", + "outputPath": "climbCounts.noClimb", + "options": { + "path": "finalClimbState.id", + "map": { + "noClimb": 1, + "climbF": 0, + "climb1": 0, + "climb2": 0, + "climb3": 0, + "climb4": 0 + } + } + }, + { + "type": "tmp", + "name": "map", + "outputPath": "climbCounts.climbF", + "options": { + "path": "finalClimbState.id", + "map": { + "noClimb": 0, + "climbF": 1, + "climb1": 0, + "climb2": 0, + "climb3": 0, + "climb4": 0 + } + } + }, + { + "type": "tmp", + "name": "map", + "outputPath": "climbCounts.climb1", + "options": { + "path": "finalClimbState.id", + "map": { + "noClimb": 0, + "climbF": 0, + "climb1": 1, + "climb2": 0, + "climb3": 0, + "climb4": 0 + } + } + }, + { + "type": "tmp", + "name": "map", + "outputPath": "climbCounts.climb2", + "options": { + "path": "finalClimbState.id", + "map": { + "noClimb": 0, + "climbF": 0, + "climb1": 0, + "climb2": 1, + "climb3": 0, + "climb4": 0 + } + } + }, + { + "type": "tmp", + "name": "map", + "outputPath": "climbCounts.climb3", + "options": { + "path": "finalClimbState.id", + "map": { + "noClimb": 0, + "climbF": 0, + "climb1": 0, + "climb2": 0, + "climb3": 1, + "climb4": 0 + } + } + }, + { + "type": "tmp", + "name": "map", + "outputPath": "climbCounts.climb4", + "options": { + "path": "finalClimbState.id", + "map": { + "noClimb": 0, + "climbF": 0, + "climb1": 0, + "climb2": 0, + "climb3": 0, + "climb4": 1 + } + } + }, + { + "type": "team", + "name": "average", + "outputPath": "climbLevels", + "options": { + "path": "climbCounts" + } + }, + { + "type": "team", + "name": "average", + "outputPath": "averages.endClimbTime", + "options": { + "path": "finalClimbState.ts" + } + }, + { + "type": "tmp", + "name": "subtract", + "outputPath": "climbDuration", + "options": { + "minuend": "startClimbTime", + "subtrahend": "finalClimbState.ts" + } + }, + { + "type": "team", + "name": "subtract", + "outputPath": "averages.climbDuration", + "options": { + "minuend": "averages.startClimbTime", + "subtrahend": "averages.endClimbTime" + } + }, + { + "type": "tmp", + "name": "map", + "outputPath": "scores.endgame", + "options": { + "path": "finalClimbState.id", + "map": { + "noClimb": 0, + "climbF": 0, + "climb1": 4, + "climb2": 6, + "climb3": 10, + "climb4": 15 + } + } + }, + + { + "type": "tmp", + "name": "sum", + "outputPath": "scores.all", + "options": { + "addends": ["scores.teleop","scores.auto","scores.endgame"] + } + }, + { + "type": "team", + "name": "average", + "outputPath": "averageScores", + "options": { + "path": "scores" + } + }, + { + "type": "team", + "name": "threshold", + "outputPath": "possibleClimbs", + "options": { + "threshold": 0, + "separator": ", ", + "none": "—", + "paths": { + "climbLevels.climb1": "1", + "climbLevels.climb2": "2", + "climbLevels.climb3": "3", + "climbLevels.climb4": "4" + } + } + }, + { + "type": "team", + "name": "threshold", + "outputPath": "possibleShots", + "options": { + "threshold": 1, + "separator": ", ", + "none": "—", + "paths": { + "averages.upperHub": "U", + "averages.lowerHub": "L" + } + } + } +] \ No newline at end of file diff --git a/config/match-scouting.json b/config/match-scouting.json index c9b222db..8feb61c9 100644 --- a/config/match-scouting.json +++ b/config/match-scouting.json @@ -1,4 +1,4 @@ -{ + { "timing": { "totalTime": 150000, "timeTransitions": { @@ -6,248 +6,655 @@ "layer": 1, "displayText": "Auto" }, - "131000": { - "layer": 3, + "135000": { + "layer": 1, "displayText": "Teleop" } } }, "layout": { - "gridRows": 5, - "gridColumns": 5, + "gridRows": 6, + "gridColumns": 9, "layers": [ [ { "id": "startGame", "displayText": "Start", - "gridArea": ["1", "2", "2", "5"], - "class": "gray timer", + "gridArea": ["3", "2", "5", "9"], + "class": "silver timer", "type": "match-control", "executables": [] } - ], + ], [ { "id": "startGame", "displayText": "Start", - "gridArea": ["1", "2", "2", "5"], - "class": "gray timer", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", "type": "match-control", "executables": [] }, { - "id": "leaveTarmac", - "displayText": "Leave Tarmac Zone", - "gridArea": ["3", "2", "4", "5"], - "class": "gray largeAction", + "id": "leftCommunity", + "displayText": "Leave Community", + "gridArea": ["2", "4", "3", "7"], + "class": "gray", "type": "action", - "executables": [{"type": "hide", "args": []}] + "executables": [] + }, + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] }, { "id": "undo", "displayText": "Undo", - "gridArea": ["5", "2", "6", "3"], + "gridArea": ["1", "8", "2", "10"], "class": "green", "type": "undo", "executables": [] }, { - "id": "groundPickup", - "displayText": "Ball Pickup", - "gridArea": ["5", "1", "6", "2"], - "class": "navy", + "id": "conePickup", + "displayText": "Cone", + "gridArea": ["3", "1", "4", "3"], + "class": "yellow", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type": "layer", "args":[1,3]}] }, { - "id": "upperHub", - "displayText": "Upper Hub", - "gridArea": ["4", "5", "5", "6"], - "class": "orange", - "type": "action", - "executables": [{"type": "position", "args":[]}] - }, - { - "id": "lowerHub", - "displayText": "Lower Hub", - "gridArea": ["5", "5", "6", "6"], - "class": "orange", - "type": "action", - "executables": [{"type": "position", "args":[]}] - }, + "id": "cubePickup", + "displayText": "Cube", + "gridArea": ["3", "8", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[1,4]}] + }, { - "id": "miss", - "displayText": "Miss", - "gridArea": ["5", "4", "6", "5"], - "class": "red", + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type": "layer", "args":[1,2]}] } ], [ { "id": "startGame", "displayText": "Start", - "gridArea": ["1", "2", "2", "5"], - "class": "gray", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", "type": "match-control", "executables": [] }, { "id": "undo", "displayText": "Undo", - "gridArea": ["5", "2", "6", "3"], + "gridArea": ["1", "8", "2", "10"], "class": "green", "type": "undo", "executables": [] }, { - "id": "groundPickup", - "displayText": "Ball Pickup", - "gridArea": ["5", "1", "6", "2"], - "class": "navy", + "id": "platformDocked", + "displayText": "Docked", + "gridArea": ["3", "3", "4", "5"], + "class": "gray", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type": "layer", "args":[2,1]}] }, { - "id": "upperHub", - "displayText": "Upper Hub", - "gridArea": ["4", "5", "5", "6"], - "class": "orange", + "id": "platformEngaged", + "displayText": "Engaged", + "gridArea": ["3", "6", "4", "8"], + "class": "gray border", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type": "layer", "args":[2,1]}] }, { - "id": "lowerHub", - "displayText": "Lower Hub", - "gridArea": ["5", "5", "6", "6"], + "id": "platformLeave", + "displayText": "Leave", + "gridArea": ["5", "3", "6", "5"], "class": "orange", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type": "layer", "args":[2,1]}] }, { - "id": "miss", - "displayText": "Miss", - "gridArea": ["5", "4", "6", "5"], + "id": "platformFall", + "displayText": "Fall", + "gridArea": ["5", "6", "6", "8"], "class": "red", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type": "layer", "args":[2,1]}] } ], [ { "id": "startGame", "displayText": "Start", - "gridArea": ["1", "2", "2", "5"], - "class": "gray", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", "type": "match-control", "executables": [] }, { "id": "undo", "displayText": "Undo", - "gridArea": ["5", "2", "6", "3"], + "gridArea": ["1", "8", "2", "10"], "class": "green", "type": "undo", "executables": [] }, { - "id": "groundPickup", - "displayText": "Ball Pickup", - "gridArea": ["5", "1", "6", "2"], - "class": "navy", + "id": "conePickupGrid", + "displayText": "Grid", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type":"layer","args":[3,5]}] }, - { - "id": "upperHub", - "displayText": "Upper Hub", + { + "id": "conePickupCommunity", + "displayText": "Comm", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[3,5]}] + }, + { + "id": "conePickupFloor", + "displayText": "Floor", "gridArea": ["4", "5", "5", "6"], - "class": "orange", + "class": "yellow", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type":"layer","args":[3,5]}] }, - { - "id": "lowerHub", - "displayText": "Lower Hub", - "gridArea": ["5", "5", "6", "6"], - "class": "orange", + { + "id": "conePickupChute", + "displayText": "Chute", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type":"layer","args":[3,5]}] }, - { - "id": "miss", - "displayText": "Miss", - "gridArea": ["5", "4", "6", "5"], - "class": "red", + { + "id": "conePickupShelf", + "displayText": "Shelf", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", "type": "action", - "executables": [{"type": "position", "args":[]}] + "executables": [{"type":"layer","args":[3,5]}] + } + + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] }, { - "id": "startClimb", - "displayText": "Start Climb", - "gridArea": ["3", "2", "4", "5"], - "class": "gray", + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "cubePickupGrid", + "displayText": "Grid", + "gridArea": ["4", "3", "5", "4"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupCommunity", + "displayText": "Comm", + "gridArea": ["4", "4", "5", "5"], + "class": "highlight", "type": "action", - "executables": [{"type": "layer", "args":[3,4]}] + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupFloor", + "displayText": "Ground", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupChute", + "displayText": "Chute", + "gridArea": ["4", "6", "5", "7"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupShelf", + "displayText": "Shelf", + "gridArea": ["4", "7", "5", "8"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] } + ], [ { "id": "startGame", "displayText": "Start", - "gridArea": ["1", "2", "2", "5"], - "class": "gray", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", "type": "match-control", "executables": [] }, + { + "id": "leftCommunity", + "displayText": "Leave Community", + "gridArea": ["2", "4", "3", "7"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] + }, { "id": "undo", "displayText": "Undo", - "gridArea": ["5", "2", "6", "5"], + "gridArea": ["1", "8", "2", "10"], "class": "green", "type": "undo", "executables": [] }, { - "id": "climb1", - "displayText": "1", - "gridArea": ["3", "1", "4", "2"], - "class": "pink", + "id": "conePickup", + "displayText": "Cone", + "gridArea": ["3", "1", "4", "3"], + "class": "yellow", "type": "action", - "executables": [{"type":"climbHighlight","args":[]}] + "executables": [{"type": "layer", "args":[5,3]}] }, { - "id": "climb2", - "displayText": "2", - "gridArea": ["3", "2", "4", "3"], - "class": "pink", + "id": "dropCone", + "displayText": "Drop", + "gridArea": ["3", "8", "4", "10"], + "class": "red", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", "type": "action", - "executables": [{"type":"climbHighlight","args":[]}] + "executables": [{"type": "layer", "args":[5,2]}] + }, + { + "id": "placement31", + "displayText": " ", + "gridArea": ["4", "1", "5", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement33", + "displayText": " ", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement34", + "displayText": " ", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement36", + "displayText": " ", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement37", + "displayText": " ", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + + { + "id": "placement39", + "displayText": " ", + "gridArea": ["4", "9", "5", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement21", + "displayText": " ", + "gridArea": ["5", "1", "6", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement23", + "displayText": " ", + "gridArea": ["5", "3", "6", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement24", + "displayText": " ", + "gridArea": ["5", "4", "6", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement26", + "displayText": " ", + "gridArea": ["5", "6", "6", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement27", + "displayText": " ", + "gridArea": ["5", "7", "6", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement29", + "displayText": " ", + "gridArea": ["5", "9", "6", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] }, { - "id": "climb3", - "displayText": "3", - "gridArea": ["3", "3", "4", "4"], - "class": "pink", + "id": "leftCommunity", + "displayText": "Leave Community", + "gridArea": ["2", "4", "3", "7"], + "class": "gray", "type": "action", - "executables": [{"type":"climbHighlight","args":[]}] + "executables": [] }, { - "id": "climb4", - "displayText": "4", - "gridArea": ["3", "4", "4", "5"], - "class": "pink", + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", "type": "action", - "executables": [{"type":"climbHighlight","args":[]}] + "executables": [] }, { - "id": "climbF", - "displayText": "Fall", - "gridArea": ["3", "5", "4", "6"], + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "dropCube", + "displayText": "Drop", + "gridArea": ["3", "1", "4", "3"], "class": "red", "type": "action", - "executables": [{"type": "layer", "args":[4,3]}] + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "cubePickup", + "displayText": "Cube", + "gridArea": ["3", "8", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,4]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,2]}] + }, + { + "id": "placement32", + "displayText": " ", + "gridArea": ["4", "2", "5", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement35", + "displayText": " ", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement38", + "displayText": " ", + "gridArea": ["4", "8", "5", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement22", + "displayText": " ", + "gridArea": ["5", "2", "6", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement25", + "displayText": " ", + "gridArea": ["5", "5", "6", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement28", + "displayText": " ", + "gridArea": ["5", "8", "6", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] } ] ] diff --git a/config/match-scouting2.json b/config/match-scouting2.json new file mode 100644 index 00000000..7a170893 --- /dev/null +++ b/config/match-scouting2.json @@ -0,0 +1,2016 @@ + { + "timing": { + "totalTime": 150000, + "timeTransitions": { + "149990": { + "layer": 1, + "displayText": "Auto" + }, + "13500": { + "layer": 1, + "displayText": "Teleop" + } + } + }, + "layout": { + "gridRows": 6, + "gridColumns": 9, + "layers": [ + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["3", "2", "5", "9"], + "class": "silver timer", + "type": "match-control", + "executables": [] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "leftCommunity", + "displayText": "Left Community", + "gridArea": ["2", "4", "3", "7"], + "class": "gray", + "type": "action", + "executables": [{"type":"hide","args":[]}] + }, + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "conePickup", + "displayText": "Cone", + "gridArea": ["3", "1", "4", "3"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[1,3]}] + }, + { + "id": "cubePickup", + "displayText": "Cube", + "gridArea": ["3", "8", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[1,4]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[1,2]}] + }, + { + "id": "placement31", + "displayText": " ", + "gridArea": ["4", "1", "5", "2"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement32", + "displayText": " ", + "gridArea": ["4", "2", "5", "3"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement33", + "displayText": " ", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement34", + "displayText": " ", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement35", + "displayText": " ", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement36", + "displayText": " ", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement37", + "displayText": " ", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement38", + "displayText": " ", + "gridArea": ["4", "8", "5", "9"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement39", + "displayText": " ", + "gridArea": ["4", "9", "5", "10"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement21", + "displayText": " ", + "gridArea": ["5", "1", "6", "2"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement22", + "displayText": " ", + "gridArea": ["5", "2", "6", "3"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement23", + "displayText": " ", + "gridArea": ["5", "3", "6", "4"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement24", + "displayText": " ", + "gridArea": ["5", "4", "6", "5"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement25", + "displayText": " ", + "gridArea": ["5", "5", "6", "6"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement26", + "displayText": " ", + "gridArea": ["5", "6", "6", "7"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement27", + "displayText": " ", + "gridArea": ["5", "7", "6", "8"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement28", + "displayText": " ", + "gridArea": ["5", "8", "6", "9"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement29", + "displayText": " ", + "gridArea": ["5", "9", "6", "10"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": [] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "platformDocked", + "displayText": "Docked", + "gridArea": ["3", "3", "4", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[2,1]}] + }, + { + "id": "platformEngaged", + "displayText": "Engaged", + "gridArea": ["3", "6", "4", "8"], + "class": "gray border", + "type": "action", + "executables": [{"type": "layer", "args":[2,1]}] + }, + { + "id": "platformLeave", + "displayText": "Leave", + "gridArea": ["5", "3", "6", "5"], + "class": "orange", + "type": "action", + "executables": [{"type": "layer", "args":[2,1]}] + }, + { + "id": "platformFall", + "displayText": "Fall", + "gridArea": ["5", "6", "6", "8"], + "class": "red", + "type": "action", + "executables": [{"type": "layer", "args":[2,1]}] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "conePickupGrid", + "displayText": "Grid", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[3,5]}] + }, + { + "id": "conePickupCommunity", + "displayText": "Comm", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[3,5]}] + }, + { + "id": "conePickupFloor", + "displayText": "Floor", + "gridArea": ["4", "5", "5", "6"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[3,5]}] + }, + { + "id": "conePickupChute", + "displayText": "Chute", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[3,5]}] + }, + { + "id": "conePickupShelf", + "displayText": "Shelf", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[3,5]}] + } + + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "cubePickupGrid", + "displayText": "Grid", + "gridArea": ["4", "3", "5", "4"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupCommunity", + "displayText": "Comm", + "gridArea": ["4", "4", "5", "5"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupFloor", + "displayText": "Ground", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupChute", + "displayText": "Chute", + "gridArea": ["4", "6", "5", "7"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + }, + { + "id": "cubePickupShelf", + "displayText": "Shelf", + "gridArea": ["4", "7", "5", "8"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[4,6]}] + } + + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "leftCommunity", + "displayText": "Left Community", + "gridArea": ["2", "4", "3", "7"], + "class": "gray", + "type": "action", + "executables": [{"type":"hide","args":[]}] + }, + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "conePickup", + "displayText": "Cone", + "gridArea": ["3", "1", "4", "3"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,3]}] + }, + { + "id": "dropCone", + "displayText": "Drop", + "gridArea": ["3", "8", "4", "10"], + "class": "red", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,2]}] + }, + { + "id": "placement31", + "displayText": " ", + "gridArea": ["4", "1", "5", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement32", + "displayText": " ", + "gridArea": ["4", "2", "5", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement33", + "displayText": " ", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement34", + "displayText": " ", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement35", + "displayText": " ", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement36", + "displayText": " ", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement37", + "displayText": " ", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement38", + "displayText": " ", + "gridArea": ["4", "8", "5", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement39", + "displayText": " ", + "gridArea": ["4", "9", "5", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement21", + "displayText": " ", + "gridArea": ["5", "1", "6", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement22", + "displayText": " ", + "gridArea": ["5", "2", "6", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement23", + "displayText": " ", + "gridArea": ["5", "3", "6", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement24", + "displayText": " ", + "gridArea": ["5", "4", "6", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement25", + "displayText": " ", + "gridArea": ["5", "5", "6", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement26", + "displayText": " ", + "gridArea": ["5", "6", "6", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement27", + "displayText": " ", + "gridArea": ["5", "7", "6", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement28", + "displayText": " ", + "gridArea": ["5", "8", "6", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement29", + "displayText": " ", + "gridArea": ["5", "9", "6", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[5,1]}] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "leftCommunity", + "displayText": "Left Community", + "gridArea": ["2", "4", "3", "7"], + "class": "gray", + "type": "action", + "executables": [{"type":"hide","args":[]}] + }, + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "dropCube", + "displayText": "Drop", + "gridArea": ["3", "1", "4", "3"], + "class": "red", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "cubePickup", + "displayText": "Cube", + "gridArea": ["3", "8", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,4]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,2]}] + }, + { + "id": "placement31", + "displayText": " ", + "gridArea": ["4", "1", "5", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement32", + "displayText": " ", + "gridArea": ["4", "2", "5", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement33", + "displayText": " ", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement34", + "displayText": " ", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement35", + "displayText": " ", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement36", + "displayText": " ", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement37", + "displayText": " ", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement38", + "displayText": " ", + "gridArea": ["4", "8", "5", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement39", + "displayText": " ", + "gridArea": ["4", "9", "5", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement21", + "displayText": " ", + "gridArea": ["5", "1", "6", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement22", + "displayText": " ", + "gridArea": ["5", "2", "6", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement23", + "displayText": " ", + "gridArea": ["5", "3", "6", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement24", + "displayText": " ", + "gridArea": ["5", "4", "6", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement25", + "displayText": " ", + "gridArea": ["5", "5", "6", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement26", + "displayText": " ", + "gridArea": ["5", "6", "6", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement27", + "displayText": " ", + "gridArea": ["5", "7", "6", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement28", + "displayText": " ", + "gridArea": ["5", "8", "6", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement29", + "displayText": " ", + "gridArea": ["5", "9", "6", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[6,1]}] + } + ], + + + + + + + + + + + + + + + + + + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["3", "2", "5", "9"], + "class": "silver timer", + "type": "match-control", + "executables": [] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "conePickup", + "displayText": "Cone", + "gridArea": ["3", "1", "4", "3"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[7,9]}] + }, + { + "id": "cubePickup", + "displayText": "Cube", + "gridArea": ["3", "8", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[7,10]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[7,8]}] + }, + { + "id": "placement31", + "displayText": " ", + "gridArea": ["4", "1", "5", "2"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement32", + "displayText": " ", + "gridArea": ["4", "2", "5", "3"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement33", + "displayText": " ", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement34", + "displayText": " ", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement35", + "displayText": " ", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement36", + "displayText": " ", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement37", + "displayText": " ", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement38", + "displayText": " ", + "gridArea": ["4", "8", "5", "9"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement39", + "displayText": " ", + "gridArea": ["4", "9", "5", "10"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement21", + "displayText": " ", + "gridArea": ["5", "1", "6", "2"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement22", + "displayText": " ", + "gridArea": ["5", "2", "6", "3"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement23", + "displayText": " ", + "gridArea": ["5", "3", "6", "4"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement24", + "displayText": " ", + "gridArea": ["5", "4", "6", "5"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement25", + "displayText": " ", + "gridArea": ["5", "5", "6", "6"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement26", + "displayText": " ", + "gridArea": ["5", "6", "6", "7"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement27", + "displayText": " ", + "gridArea": ["5", "7", "6", "8"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement28", + "displayText": " ", + "gridArea": ["5", "8", "6", "9"], + "class": "highlight", + "type": "action", + "executables": [] + }, + { + "id": "placement29", + "displayText": " ", + "gridArea": ["5", "9", "6", "10"], + "class": "yellow", + "type": "action", + "executables": [] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": [] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "gray timer", + "type": "match-control", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "platformDocked", + "displayText": "Docked", + "gridArea": ["3", "3", "4", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[8,7]}] + }, + { + "id": "platformEngaged", + "displayText": "Engaged", + "gridArea": ["3", "6", "4", "8"], + "class": "gray border", + "type": "action", + "executables": [{"type": "layer", "args":[8,7]}] + }, + { + "id": "platformLeave", + "displayText": "Leave", + "gridArea": ["5", "3", "6", "5"], + "class": "orange", + "type": "action", + "executables": [{"type": "layer", "args":[8,7]}] + }, + { + "id": "platformFall", + "displayText": "Fall", + "gridArea": ["5", "6", "6", "8"], + "class": "red", + "type": "action", + "executables": [{"type": "layer", "args":[8,7]}] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "conePickupGrid", + "displayText": "Grid", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[9,11]}] + }, + { + "id": "conePickupCommunity", + "displayText": "Comm", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[9,11]}] + }, + { + "id": "conePickupFloor", + "displayText": "Floor", + "gridArea": ["4", "5", "5", "6"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[9,11]}] + }, + { + "id": "conePickupChute", + "displayText": "Chute", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[9,11]}] + }, + { + "id": "conePickupShelf", + "displayText": "Shelf", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type":"layer","args":[9,11]}] + } + + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "cubePickupGrid", + "displayText": "Grid", + "gridArea": ["4", "3", "5", "4"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[10,12]}] + }, + { + "id": "cubePickupCommunity", + "displayText": "Comm", + "gridArea": ["4", "4", "5", "5"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[10,12]}] + }, + { + "id": "cubePickupFloor", + "displayText": "Ground", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[10,12]}] + }, + { + "id": "cubePickupChute", + "displayText": "Chute", + "gridArea": ["4", "6", "5", "7"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[10,12]}] + }, + { + "id": "cubePickupShelf", + "displayText": "Shelf", + "gridArea": ["4", "7", "5", "8"], + "class": "highlight", + "type": "action", + "executables": [{"type":"layer","args":[10,12]}] + } + + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "conePickup", + "displayText": "Cone", + "gridArea": ["3", "1", "4", "3"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,9]}] + }, + { + "id": "dropCone", + "displayText": "Drop", + "gridArea": ["3", "8", "4", "10"], + "class": "red", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,8]}] + }, + { + "id": "placement31", + "displayText": " ", + "gridArea": ["4", "1", "5", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement32", + "displayText": " ", + "gridArea": ["4", "2", "5", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement33", + "displayText": " ", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement34", + "displayText": " ", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement35", + "displayText": " ", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement36", + "displayText": " ", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement37", + "displayText": " ", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement38", + "displayText": " ", + "gridArea": ["4", "8", "5", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement39", + "displayText": " ", + "gridArea": ["4", "9", "5", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement21", + "displayText": " ", + "gridArea": ["5", "1", "6", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement22", + "displayText": " ", + "gridArea": ["5", "2", "6", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement23", + "displayText": " ", + "gridArea": ["5", "3", "6", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement24", + "displayText": " ", + "gridArea": ["5", "4", "6", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement25", + "displayText": " ", + "gridArea": ["5", "5", "6", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement26", + "displayText": " ", + "gridArea": ["5", "6", "6", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement27", + "displayText": " ", + "gridArea": ["5", "7", "6", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement28", + "displayText": " ", + "gridArea": ["5", "8", "6", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement29", + "displayText": " ", + "gridArea": ["5", "9", "6", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[11,7]}] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["1", "4", "2", "7"], + "class": "silver timer", + "type": "match-control", + "executables": [] + }, + + { + "id": "broken", + "displayText": "Disabled", + "gridArea": ["1", "1", "2", "3"], + "class": "red", + "type": "action", + "executables": [] + }, + { + "id": "undo", + "displayText": "Undo", + "gridArea": ["1", "8", "2", "10"], + "class": "green", + "type": "undo", + "executables": [] + }, + { + "id": "dropCube", + "displayText": "Drop", + "gridArea": ["3", "1", "4", "3"], + "class": "red", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "cubePickup", + "displayText": "Cube", + "gridArea": ["3", "8", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[12,10]}] + }, + { + "id": "chargingDock", + "displayText": "Charging Dock", + "gridArea": ["3", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,8]}] + }, + { + "id": "placement31", + "displayText": " ", + "gridArea": ["4", "1", "5", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement32", + "displayText": " ", + "gridArea": ["4", "2", "5", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement33", + "displayText": " ", + "gridArea": ["4", "3", "5", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement34", + "displayText": " ", + "gridArea": ["4", "4", "5", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement35", + "displayText": " ", + "gridArea": ["4", "5", "5", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement36", + "displayText": " ", + "gridArea": ["4", "6", "5", "7"], + "class": "yellow", + "type": "action", + "executables": {"type": "layer", "args":[12,7]}] + }, + { + "id": "placement37", + "displayText": " ", + "gridArea": ["4", "7", "5", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement38", + "displayText": " ", + "gridArea": ["4", "8", "5", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement39", + "displayText": " ", + "gridArea": ["4", "9", "5", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement21", + "displayText": " ", + "gridArea": ["5", "1", "6", "2"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement22", + "displayText": " ", + "gridArea": ["5", "2", "6", "3"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement23", + "displayText": " ", + "gridArea": ["5", "3", "6", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement24", + "displayText": " ", + "gridArea": ["5", "4", "6", "5"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement25", + "displayText": " ", + "gridArea": ["5", "5", "6", "6"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement26", + "displayText": " ", + "gridArea": ["5", "6", "6", "7"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement27", + "displayText": " ", + "gridArea": ["5", "7", "6", "8"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement28", + "displayText": " ", + "gridArea": ["5", "8", "6", "9"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement29", + "displayText": " ", + "gridArea": ["5", "9", "6", "10"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement11", + "displayText": " ", + "gridArea": ["6", "1", "7", "2"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement12", + "displayText": " ", + "gridArea": ["6", "2", "7", "3"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement13", + "displayText": " ", + "gridArea": ["6", "3", "7", "4"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement14", + "displayText": " ", + "gridArea": ["6", "4", "7", "5"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement15", + "displayText": " ", + "gridArea": ["6", "5", "7", "6"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement16", + "displayText": " ", + "gridArea": ["6", "6", "7", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement17", + "displayText": " ", + "gridArea": ["6", "7", "7", "8"], + "class": "gray", + "type": "action", + "executables": {"type": "layer", "args":[12,7]}] + }, + { + "id": "placement18", + "displayText": " ", + "gridArea": ["6", "8", "7", "9"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[12,7]}] + }, + { + "id": "placement19", + "displayText": " ", + "gridArea": ["6", "9", "7", "10"], + "class": "gray", + "type": "action", + "executables": {"type": "layer", "args":[12,7]}] + } + ] + ] + } +} \ No newline at end of file diff --git a/replit.nix b/replit.nix new file mode 100644 index 00000000..11e88c02 --- /dev/null +++ b/replit.nix @@ -0,0 +1,6 @@ +{ pkgs }: { + deps = [ + pkgs.nodejs-16_x + pkgs.cowsay + ]; +} \ No newline at end of file diff --git a/src/analysis/transformers/testingtesting123.js b/src/analysis/transformers/testingtesting123.js new file mode 100644 index 00000000..a1482b91 --- /dev/null +++ b/src/analysis/transformers/testingtesting123.js @@ -0,0 +1,28 @@ +const { getPath, setPath } = require("../../lib/util"); +const {DataTransformer} = require("../DataTransformer"); + +module.exports = { + /** + * @type {DataTransformer} + * @param options.addends {String[]} array of MatchTeamPerformance outputPaths + */ + tmp: new DataTransformer("test", (dataset, outputPath, options) => { + for (const tmp of dataset.tmps) { + console.log(tmp) + } + + return dataset; + }), + + /** + * @type {DataTransformer} + * @param options.addends {String[]} array of Team outputPaths + */ + team: new DataTransformer("test", (dataset, outputPath, options) => { + for (const [teamNumber, team] of Object.entries(dataset.teams)) { + console.log(team) + } + + return dataset; + }) +} \ No newline at end of file diff --git a/src/scouting/public/css/match-scouting.css b/src/scouting/public/css/match-scouting.css index 26112831..ebbfe355 100644 --- a/src/scouting/public/css/match-scouting.css +++ b/src/scouting/public/css/match-scouting.css @@ -44,6 +44,11 @@ background-color: #2D2D2D } +.silver { + border: 3px solid #2d2d2d; + background-color: #919191 +} + .green { background-color: #4caf50 } @@ -53,15 +58,23 @@ } .red { - background-color: #ff4436 + background-color: #ff4436; } .navy { - background-color: #3f51b5 + background-color: #3f51b5; } .pink { - background-color: #f06292 + background-color: #f06292; +} + +.yellow{ + background-color:#d8c24f; +} + +.border{ + border: 4px solid #37cde1; } .highlight { From 5d891bfc3aa67b3e3fdbffa446ac5335a32e51d4 Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Mon, 16 Jan 2023 19:14:24 +0000 Subject: [PATCH 03/20] started analysis-modules and analysis-pipeline, made initial version of countHybrid data transformer --- config/analysis-modules.json | 90 ++++++++++++++++++- config/analysis-pipeline.json | 79 ++++++++++++++++ config/match-scouting.json | 16 ++-- src/analysis/modules/Bar/index.js | 1 + src/analysis/transformers/countHybrid.js | 38 ++++++++ .../transformers/testingtesting123.js | 4 +- src/scouting/public/css/match-scouting.css | 6 +- 7 files changed, 220 insertions(+), 14 deletions(-) create mode 100644 src/analysis/transformers/countHybrid.js diff --git a/config/analysis-modules.json b/config/analysis-modules.json index e120b9bf..5d208ec7 100644 --- a/config/analysis-modules.json +++ b/config/analysis-modules.json @@ -3,16 +3,100 @@ "view":"team", "module":"Stats", "position":"side", - "name":"Stats that we hope work", + "name":"Charging Dock Averages", "options": { "list":[ { - "name":"Charging Dock", - "path":"counts.chargingDock", + "name":"Attempts", + "path":"averages.chargingDock", + "decimals":2, + "sort":1 + }, + { + "name":"Docked", + "path":"averages.platformDocked", + "decimals":2, + "sort":1 + }, + { + "name":"Engaged", + "path":"averages.platformEngaged", + "decimals":2, + "sort":1 + }, + { + "name":"Left", + "path":"averages.platformLeave", + "decimals":2, + "sort":1 + }, + { + "name":"Fell", + "path":"averages.platformFall", "decimals":2, "sort":1 } ] } + }, + { + "view":"team", + "module": "Pie", + "position":"main", + "name":"Cone Pickups", + "options":{ + "slices":[ + { + "name":"Grid", + "path":"averages.conePickupGrid" + }, + { + "name":"Community", + "path":"averages.conePickupCommunity" + }, + { + "name":"Floor", + "path":"averages.conePickupFloor" + }, + { + "name":"Chute", + "path":"averages.conePickupChute" + }, + { + "name":"Shelf", + "path":"averages.conePickupShelf" + } + ] + } + }, + { + "view":"team", + "module": "Pie", + "position":"main", + "name":"Cube Pickups", + "options":{ + "slices":[ + { + "name":"Grid", + "path":"averages.cubePickupGrid" + }, + { + "name":"Community", + "path":"averages.cubePickupCommunity" + }, + { + "name":"Floor", + "path":"averages.cubePickupFloor" + }, + { + "name":"Chute", + "path":"averages.cubePickupChute" + }, + { + "name":"Shelf", + "path":"averages.cubePickupShelf" + } + ] } + } ] \ No newline at end of file diff --git a/config/analysis-pipeline.json b/config/analysis-pipeline.json index ad09ff60..9558d02f 100644 --- a/config/analysis-pipeline.json +++ b/config/analysis-pipeline.json @@ -15,6 +15,85 @@ "all": true } }, + { + "type": "team", + "name": "sum", + "outputPath": "counts.totalScored", + "options":{ + "addends":[ + "counts.placement11", + "counts.placement12", + "counts.placement13", + "counts.placement14", + "counts.placement15", + "counts.placement16", + "counts.placement17", + "counts.placement18", + "counts.placement19", + "counts.placement21", + "counts.placement22", + "counts.placement23", + "counts.placement24", + "counts.placement25", + "counts.placement26", + "counts.placement27", + "counts.placement28", + "counts.placement29", + "counts.placement31", + "counts.placement32", + "counts.placement33", + "counts.placement34", + "counts.placement35", + "counts.placement36", + "counts.placement37", + "counts.placement38", + "counts.placement39" + ] + } + }, + { + "type":"tmp", + "name":"countHybrid", + "outputPath": "counts.hybrid", + "options":{ + "pickup":["conePickupGrid","conePickupCommunity","conePickupFloor","conePickupChute","conePickupShelf","cubePickupGrid","cubePickupCommunity","cubePickupFloor","cubePickupChute","cubePickupShelf"], + "hybrid":["placement11","placement12","placement13","placement14","placement15","placement16","placement17","placement18","placement19"] + } + }, + { + "type": "team", + "name": "countMatches", + "outputPath": "temp.totalTimeMs", + "options": { + "weight": 150000 + } + }, + { + "type": "team", + "name": "ratio", + "outputPath": "timePerCone", + "options": { + "numerator": ["temp.totalTimeMs"], + "denominator": ["counts.balls"] + }, + "divByZero": 150000 + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "aggregatedActions", + "options": { + "path": "actionQueue" + } + }, + { + "type": "team", + "name": "average", + "outputPath": "averages", + "options": { + "path": "counts" + } + }, { "type":"team", "name":"test", diff --git a/config/match-scouting.json b/config/match-scouting.json index 8feb61c9..29ce2161 100644 --- a/config/match-scouting.json +++ b/config/match-scouting.json @@ -107,7 +107,7 @@ "gridArea": ["3", "3", "4", "5"], "class": "gray", "type": "action", - "executables": [{"type": "layer", "args":[2,1]}] + "executables": [] }, { "id": "platformEngaged", @@ -115,7 +115,7 @@ "gridArea": ["3", "6", "4", "8"], "class": "gray border", "type": "action", - "executables": [{"type": "layer", "args":[2,1]}] + "executables": [] }, { "id": "platformLeave", @@ -155,7 +155,7 @@ "id": "conePickupGrid", "displayText": "Grid", "gridArea": ["4", "3", "5", "4"], - "class": "yellow", + "class": "yellow button-padding", "type": "action", "executables": [{"type":"layer","args":[3,5]}] }, @@ -163,7 +163,7 @@ "id": "conePickupCommunity", "displayText": "Comm", "gridArea": ["4", "4", "5", "5"], - "class": "yellow", + "class": "yellow button-padding", "type": "action", "executables": [{"type":"layer","args":[3,5]}] }, @@ -171,7 +171,7 @@ "id": "conePickupFloor", "displayText": "Floor", "gridArea": ["4", "5", "5", "6"], - "class": "yellow", + "class": "yellow button-padding", "type": "action", "executables": [{"type":"layer","args":[3,5]}] }, @@ -179,7 +179,7 @@ "id": "conePickupChute", "displayText": "Chute", "gridArea": ["4", "6", "5", "7"], - "class": "yellow", + "class": "yellow button-padding", "type": "action", "executables": [{"type":"layer","args":[3,5]}] }, @@ -187,7 +187,7 @@ "id": "conePickupShelf", "displayText": "Shelf", "gridArea": ["4", "7", "5", "8"], - "class": "yellow", + "class": "yellow button-padding", "type": "action", "executables": [{"type":"layer","args":[3,5]}] } @@ -228,7 +228,7 @@ }, { "id": "cubePickupFloor", - "displayText": "Ground", + "displayText": "Floor", "gridArea": ["4", "5", "5", "6"], "class": "highlight", "type": "action", diff --git a/src/analysis/modules/Bar/index.js b/src/analysis/modules/Bar/index.js index 29165604..6da3438e 100644 --- a/src/analysis/modules/Bar/index.js +++ b/src/analysis/modules/Bar/index.js @@ -1,3 +1,4 @@ + class Bar { container; moduleConfig; diff --git a/src/analysis/transformers/countHybrid.js b/src/analysis/transformers/countHybrid.js new file mode 100644 index 00000000..a7b0c709 --- /dev/null +++ b/src/analysis/transformers/countHybrid.js @@ -0,0 +1,38 @@ +const {DataTransformer} = require("../DataTransformer"); + +module.exports = { + /** + count number of each piece placed in hybrid slots + * @type {DataTransformer} + * @param options.pickup {String[]} pickup ids for pieces + * @param options.hybrid {String[]} ids of hybrid slots + */ + tmp: new DataTransformer("countHybrid", (dataset, outputPath, options) => { + for(let tmp of dataset.tmps){ + var heldPiece = ""; + var placements= {} + for(let id of options.pickup){ + placements[id] = 0; + } + for(let action of getPath(tmp,"actionQueue")){ + if(options.pickup.contains(action.id)){ + heldPiece = action.id + } + if(options.hybrid.contains(action.id)){ + console.log(`placed ${heldPiece} in ${action.id}`) + placements[heldPiece] = placements[heldPiece] +1; + } + } + setPath(tmp,outputPath,placements); + } + return dataset; + }), + + /** + * @type {DataTransformer} + * @param options.example {String} example parameter description + */ + team: new DataTransformer("countHybrid", (dataset, outputPath, options) => { + return dataset; + }) +} \ No newline at end of file diff --git a/src/analysis/transformers/testingtesting123.js b/src/analysis/transformers/testingtesting123.js index a1482b91..c7bbadab 100644 --- a/src/analysis/transformers/testingtesting123.js +++ b/src/analysis/transformers/testingtesting123.js @@ -8,7 +8,7 @@ module.exports = { */ tmp: new DataTransformer("test", (dataset, outputPath, options) => { for (const tmp of dataset.tmps) { - console.log(tmp) + // console.log(tmp) } return dataset; @@ -20,7 +20,7 @@ module.exports = { */ team: new DataTransformer("test", (dataset, outputPath, options) => { for (const [teamNumber, team] of Object.entries(dataset.teams)) { - console.log(team) + // console.log(team) } return dataset; diff --git a/src/scouting/public/css/match-scouting.css b/src/scouting/public/css/match-scouting.css index ebbfe355..efc12ead 100644 --- a/src/scouting/public/css/match-scouting.css +++ b/src/scouting/public/css/match-scouting.css @@ -14,7 +14,7 @@ width: 99%; height: 93.5%; display: grid; - grid-gap: .75vw; + grid-gap: 3vw; border-radius: var(--border-radius, 16px); padding: .5vmin } @@ -44,6 +44,10 @@ background-color: #2D2D2D } +.button-padding { + padding: 5vw; +} + .silver { border: 3px solid #2d2d2d; background-color: #919191 From dc7b1445179f3126277a2f2cc82e70b65c65f000 Mon Sep 17 00:00:00 2001 From: alannaping Date: Thu, 19 Jan 2023 11:10:43 -0600 Subject: [PATCH 04/20] Made a new route for checklist --- src/checklist/checklist.js | 13 + src/checklist/public/css/global.css | 28 + src/checklist/public/css/internal.css | 59 ++ src/checklist/public/css/style.css | 370 +++++++++++ src/checklist/public/css/ui-elements.css | 124 ++++ .../public/icons/android-chrome-192x192.png | Bin 0 -> 27308 bytes .../public/icons/android-chrome-512x512.png | Bin 0 -> 84452 bytes .../public/icons/apple-touch-icon.png | Bin 0 -> 11665 bytes src/checklist/public/icons/browserconfig.xml | 9 + src/checklist/public/icons/favicon-16x16.png | Bin 0 -> 982 bytes src/checklist/public/icons/favicon-32x32.png | Bin 0 -> 1719 bytes src/checklist/public/icons/favicon.ico | Bin 0 -> 15086 bytes src/checklist/public/icons/mstile-150x150.png | Bin 0 -> 5236 bytes .../public/icons/safari-pinned-tab.svg | 591 ++++++++++++++++++ src/checklist/public/icons/site.webmanifest | 19 + src/checklist/public/img/spinner.svg | 6 + src/checklist/public/js/script.js | 248 ++++++++ src/checklist/public/js/ui-elements.js | 162 +++++ src/checklist/routes/api.js | 67 ++ src/checklist/views/index.ejs | 62 ++ 20 files changed, 1758 insertions(+) create mode 100644 src/checklist/checklist.js create mode 100644 src/checklist/public/css/global.css create mode 100644 src/checklist/public/css/internal.css create mode 100644 src/checklist/public/css/style.css create mode 100644 src/checklist/public/css/ui-elements.css create mode 100644 src/checklist/public/icons/android-chrome-192x192.png create mode 100644 src/checklist/public/icons/android-chrome-512x512.png create mode 100644 src/checklist/public/icons/apple-touch-icon.png create mode 100644 src/checklist/public/icons/browserconfig.xml create mode 100644 src/checklist/public/icons/favicon-16x16.png create mode 100644 src/checklist/public/icons/favicon-32x32.png create mode 100644 src/checklist/public/icons/favicon.ico create mode 100644 src/checklist/public/icons/mstile-150x150.png create mode 100644 src/checklist/public/icons/safari-pinned-tab.svg create mode 100644 src/checklist/public/icons/site.webmanifest create mode 100644 src/checklist/public/img/spinner.svg create mode 100644 src/checklist/public/js/script.js create mode 100644 src/checklist/public/js/ui-elements.js create mode 100644 src/checklist/routes/api.js create mode 100644 src/checklist/views/index.ejs diff --git a/src/checklist/checklist.js b/src/checklist/checklist.js new file mode 100644 index 00000000..9571dda1 --- /dev/null +++ b/src/checklist/checklist.js @@ -0,0 +1,13 @@ +express = require("express"); + +let router = express.Router(); + +router.use(express.static(__dirname + "/public")); + +router.get("/", (req,res) => { + res.render(__dirname + "/views/index.ejs"); +}) + +router.use("/api", require("./routes/api.js")); + +module.exports = router; \ No newline at end of file diff --git a/src/checklist/public/css/global.css b/src/checklist/public/css/global.css new file mode 100644 index 00000000..6ab477d6 --- /dev/null +++ b/src/checklist/public/css/global.css @@ -0,0 +1,28 @@ +:root { + --bg: #EFEFEF; + --text: #232323; + --bg-alt: #FEFEFE; + --accent: #30a2ff; + --accent-alt: #ff6030; + --error: #ff5166; + --light-gray: #bebebe; + --gray: #757575; + --green: #4caf50; + --placeholder: #a3a3a3; + --border-radius: 16px; + --font: Cairo, sans-serif +} +html { + overflow: hidden; + height: 100%; + position: fixed; +} + +body { + height: 100%; + position: relative; +} + +* { + font-family: var(--font, sans-serif) +} \ No newline at end of file diff --git a/src/checklist/public/css/internal.css b/src/checklist/public/css/internal.css new file mode 100644 index 00000000..bd38a33b --- /dev/null +++ b/src/checklist/public/css/internal.css @@ -0,0 +1,59 @@ +/***************************************************************** + !!!WARNING!!! + EVERYTHING HERE IS VERY IMPORTANT +DO NOT REMOVE OR MODIFY ANYTHING UNLESS YOU KNOW WHAT YOU'RE DOING +*****************************************************************/ +* { + box-sizing: border-box; +} + +body { + margin: 0; +} + +html { + height: 100%; + width: 100%; +} + +#spinner { + height: 300px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; +} + +.main-svg { + border-radius: var(--border-radius); +} + +/* width */ +::-webkit-scrollbar { + width: 10px; +} + +/* Track */ +::-webkit-scrollbar-track { + background: transparent; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background: var(--gray); + filter: saturate(0.8); + transition: 0.2s background; + border-radius: 999px; + width: 4px; + border: 3px solid rgba(0,0,0,0); + background-clip: padding-box; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + background: var(--text); + width: 4px; + border: 3px solid rgba(0,0,0,0); + background-clip: padding-box; +} \ No newline at end of file diff --git a/src/checklist/public/css/style.css b/src/checklist/public/css/style.css new file mode 100644 index 00000000..2e55b80e --- /dev/null +++ b/src/checklist/public/css/style.css @@ -0,0 +1,370 @@ +body { + margin: 0; + overflow: hidden; +} + +.access-input { + border-radius: 8px; + border: 2px solid var(--text); + background-color: var(--bg-alt); + color: var(--text); + padding: 8px 12px; + font-size: 1.5em; +} + +#menu { + height: 100%; + width: 50px; + display: flex; + flex-direction: column; + align-items: center; + padding: 16px 0; + grid-area: 1 / 1 / 2 / 2; + background-color: var(--bg-alt); + opacity: 0; + transition: 0.3s visibility, opacity, width; + visibility: hidden; + border-right: 1px solid var(--text); + transition: 0.3s all; + z-index: 999; + position: fixed; + top: 0; + left: 0; +} + +#menu.expanded { + width: 200px; +} + +#menu > * { + display: flex; + flex-direction: row; + align-items: center; + margin-bottom: 12px; + cursor: pointer; + color: var(--text); + text-decoration: none; +} + +#menu > * > i { + font-size: 1.75rem; +} + +#menu > * > div { + font-size: 1.25rem; + width: 0; + overflow: hidden; + white-space: pre; + opacity: 0; + transition: 0.3s opacity, margin; +} + +#menu.expanded > * > div { + width: unset; + margin-left: 10px; + opacity: 1; +} + +#menu-icon { + color: var(--accent); + transition: 0.3s transform cubic-bezier(.17,.67,.89,1.29); + transform: rotate(270deg); + margin-bottom: 16px; +} + +#menu-icon-container { + border-bottom: 2px solid black; + margin-bottom: 16px; +} + +#menu.expanded #menu-icon { + transform: rotate(90deg); +} + +#admin-panel { + margin-left: 50px; + height: 100vh; + display: flex; + flex-direction: column; + padding: 25px 5%; + color: var(--text); + background: var(--bg); + opacity: 0; + transition: 0.3s visibility, opacity; + visibility: hidden; + grid-area: 1 / 2 / 2 / 3; +} + +#menu.visible { + visibility: unset; + opacity: 1; + transition: visibility 0s 0s, opacity 0.3s 0s, 0.3s width; +} + +#admin-panel.visible { + visibility: unset; + opacity: 1; + transition: visibility 0s 0s, opacity 0.3s 0s; +} + +#scouters { + padding: 21px; + padding-top: 24px; + border: 2px solid var(--text); + border-radius: 10px; + position: relative; + margin-top: 10px; + min-height: 200px; + max-height: 360px; +} + +#scouter-list { + display: flex; + overflow-x: auto; + gap: 10px; + height: 100%; +} + +.scouter { + height: 150px; + width: 200px; + border-radius: 10px; + border: 2px solid var(--text); + position: relative; + color: var(--text); + background-color: var(--bg-alt); + font: 1em/1em "Cairo"; + display: flex; + flex-direction: column; + justify-content: space-evenly; + align-items: center; + padding: 6px 25px 0; +} + +.scouter > .scouter-id { + font-size: 1.3rem; + text-overflow: ellipsis; + width: 100%; + text-align: center; + overflow-y: visible; + overflow-x: clip +} +.scouter > .robot-number { + font-size: 3rem; + font-weight: bold; +} +.scouter > .match-number { + position: absolute; + left: 50%; + top: -1px; + transform: translateX(-50%); + border-radius: 0 0 10px 10px; + font-weight: bold; + font-size: 1.15rem; + color: var(--bg-alt); + padding: 0 12px 2px; + line-height: 1em; + background-color: var(--text); + border: 2px solid var(--text); +} +.scouter > .scouter-status { + font-size: 1.3rem; + font-weight: bold; +} + +#start-scouting { + border: 2px solid var(--text); + background-color: var(--accent); + color: var(--bg-alt); + border-radius: 10px; + position: absolute; + bottom: -3px; + left: 50%; + transform: translate(-50%, 50%); + font: 1.4em/1em Cairo; + padding: 5px 20px; + cursor: pointer; +} + +#matches { + padding: 20px; + padding-top: 24px; + border: 2px solid var(--text); + border-radius: 10px; + position: relative; + margin-top: 32px; + min-height: 0; +} + +.title { + position: absolute; + background-color: var(--bg); + border-radius: 10px; + top: 0px; + left: 40px; + font: 2em/1em "Cairo"; + padding: 0 6px; + transform: translateY(-50%); +} + +#match-list { + height: 100%; + width: 100%; + overflow-y: auto; + overflow-x: hidden; + gap: 12px; + display: flex; + flex-direction: column; +} + +#matches .match { + display: grid; + width: 100%; + grid-template-columns: calc(.75 * 80px + 14px) repeat(2,1fr); + grid-template-rows: 30% 70%; + border: 2px solid var(--text); + border-radius: 10px; + padding: 10px 15px; + background: var(--bg-alt); +} + +.match .match-select { + width: calc(.75 * 80px); + height: calc(.75 * 80px); + margin-right: 14px; + appearance: none; + background-color: var(--text); + display: grid; + place-content: center; + border-radius: 10px; + align-self: center; +} + + +.match .match-select::before { + content: ""; + width: calc(.4 * 80px); + height: calc(.4 * 80px); + transform: scale(0); + border-radius: 16px; + transition: 120ms transform cubic-bezier(.37,-0.02,.47,1.77); + box-shadow: inset calc(.75 * 80px) calc(.75 * 80px) var(--accent); +} + +.match .match-select:checked::before { + transform: scale(1); +} + +.match .match-header { + grid-area: 1 / 1 / 2 / 4; + font-size: 1.2em; + line-height: 1em; +} + +.match .match-teams { + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: center; + text-align: center; + font: 1.8em/calc(80px * .75) "Cairo"; + color: var(--bg-alt); +} + +.match .match-teams.red { + background: #ff6666; + border-radius: 10px 0 0 10px; +} + +.match .match-teams.blue { + background: #6666ff; + border-radius: 0 10px 10px 0; +} + +@media only screen and (max-height: 500px), screen and (max-width: 700px) { + #admin-panel { + padding: 15px 2.5%; + } + + .scouter { + height: 100px; + padding: 6px 10px 0; + width: 115px; + } + + html { + font-size: 70%; + } + + #scouters { + min-height: 150px; + } + + .scouter > .match-number { + line-height: 0.7em; + padding: 0 8px 3px; + font-size: 1.3rem; + } + + body { + grid-template-columns: auto 1fr; + } + + #menu { + padding: 10px 0; + } + + #menu > * > i { + font-size: 2rem; + } + + #menu > * > div { + font-size: 1.75rem; + } +} + +@media only screen and (max-width: 550px) { + .match .match-teams { + font: 1.3rem/1em Cairo; + } + + #matches .match { + grid-template-columns: calc(.75 * 65px) repeat(2,1fr); + } + + .match .match-select { + width: calc(.75 * 50px); + height: calc(.75 * 50px); + margin-right: 0; + } + + .match .match-select::before { + width: calc(.4 * 50px); + height: calc(.4 * 50px); + } + + #scouter-list { + gap: 6px; + flex-wrap: wrap; + justify-content: space-evenly; + } + + #scouters { + padding: 21px 12px; + min-height: unset; + } + + #match-list { + gap: 8px; + } +} + +@media only screen and (max-width: 450px) { + #matches { + padding: 16px 0px 12px 4px; + } + + #matches .match { + padding: 8px 6px; + } +} diff --git a/src/checklist/public/css/ui-elements.css b/src/checklist/public/css/ui-elements.css new file mode 100644 index 00000000..d1782ce7 --- /dev/null +++ b/src/checklist/public/css/ui-elements.css @@ -0,0 +1,124 @@ +.modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 99; + background-color: var(--bg-alt); + border-radius: 10px; + padding: 40px; + transition: 0.3s all; + opacity: 0; + visibility: hidden; + display: flex; + flex-direction: column; +} + +.modal.visible { + visibility: unset; + opacity: 1; + transition: visibility 0s 0s, opacity 0.3s 0s; +} + +.modal-blind { + position: fixed; + top: 0; + left: 0; + height: 100%;; + width: 100%; + background-color: black; + z-index: 98; + cursor: pointer; + transition: 0.3s all; + opacity: 0; + visibility: hidden; +} + +.modal-blind.visible { + visibility: unset; + opacity: 0.7; + transition: visibility 0s 0s, opacity 0.3s 0s; +} + +.modal-close { + font-size: 1.5em; + color: var(--text); + cursor: pointer; + position: absolute; + transform: translate(50%, -50%); + top: 25px; + right: 25px; +} + +.modal.large { + min-width: 800px; + min-height: 500px; +} + +.modal.medium { + min-width: 600px; + min-height: 375px; +} + +.modal.small { + min-width: 400px; + min-height: 250px; +} + +.modal.main-center { + justify-content: center; +} + +.modal.alt-center { + align-items: center; +} + +.modal img { + width: 100%; + height: 100%; + object-fit: contain; + border-radius: 10px; +} + +.modal.image > img { + height: 100%; +} + +.modal > div.header { + font-size: 2.5em; + font-weight: bold; + line-height: 1.5em; + margin-bottom: 8px; +} + +.modal > div.text { + font-size: 1.25em; +} + +.modal > button { + border: none; + background-color: var(--accent); + border-radius: 10px; + color: white; + font-size: 1.5em; + padding: 4px 20px; + margin-top: 20px; +} + +.popup { + position: fixed; + top: 0px; + left: 50%; + border-radius: 8px; + color: white; + margin: 0; + font-size: 1.4rem; + line-height: 1.75rem; + padding: 6px 18px; + text-align: center; + transition: 0.6s cubic-bezier(.76,-0.47,.24,1.47); + z-index: 100; + transform: translate(-50%, -100%); + box-shadow: 0 0 2px var(--text); + max-width: 100%; +} diff --git a/src/checklist/public/icons/android-chrome-192x192.png b/src/checklist/public/icons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..9cce477535258cddbc579ccf4dc3b125c27b4755 GIT binary patch literal 27308 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

*@-x>P!uF`wb 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%0AaMBTx-UC7!S3)oQ;b_RA0JJRL^9xD^rX7#qqn zF9{u>vSaY9qCWf&D)8e{KXv|>M1ILICT{6!7ll7K-7 zJY3x>z=x`Tcs~ol2fCt|nLx|>)d8A@8z+ki^M2!it_7Cezu(kNp zh@WfW{R&;!Jp|+K^U?PY=Srj|riuH(e$jt=)P~WEa@qBld{_qI;<@23^mEamRkDt2 zTTT=vJW@oUcv5rh%8Db9lg^92Nxh(r%q@oy@4i5L^n5D+|KtS{ECCjilm+@F1bJG3 z?ZT#BMhGS+poo;1KX9YK@VfI*M`KW8)8^J>(^-;X{*fwa+?n$IL<=(w)+S}CP_b6! zl9T;R5Ta4bOw8!}h-sRH#xYjHMnd+FK-4HiwT}>cgsubMUxJXt9;l>=L+h6>ciP{p z^Ap}BqXX7;H#R67D>GeeYq2VSypGnAaW+|r_D^3U+Fr&hNkf>rY>tJNH5byvv#cb8 z(oAeND?x!`C!AhoVO_t%A|kHzrJ14OVEm-S*Y&R5e)#g@IaO%Vy)fq^)b&A5s(cBOry7w-3LXT&L| zx*62f*yfHlQHy%I=6NH}XLDfF1?=|JBbp3ib>Ma~kzC01w+vbGfGC4^%na-s{EI@E zox6x-$l9A6R@!$S7+wOh7pdXS398Kv6-MV%W7bE>qnN?oq($$-3K2T&HRPnBzcR)f zVXHOO%EZ{kg#STR$RfOGvb%M+Wk4Mjs*`wDZH5x=(h@FW7;7l_qU3+>)1v~++=ZLB zS<05jwBRnt2tk!6X_UC&l$1t%7)|S%v8ncxPy=V`u3*}F1S4!`^ai}JlAsVv>;(A7 z<`lTknGdhZ;_K7ZdGAcGBRGs!_G;W*ee}=@`SeacXqGG!?x*P8NT<8-C1qjWpsfo` zW#W7{hS_MR7~J8XG&@0bvv|_z;;Tsz3eGto7xH7~c~mSiP(1%u+R{+=1Pk1Bk_8R~ z((^0ZC;f-O!TRHvai=%pB9BW8A=4}qJb>6cJ_A=!@`hA#8~p{2^}k+!U~rx>v*My? zd^ltdwC0|F(LrqofT@hUy|QID>2k&&8amb>RXTKbk!PT6Hd-W4yh@$Zs2M4#IAM}Lh%KrY2F50=AVCJMy)%EirLX( zRZ^1OL}YKjxz$?)rEU9W$w~_)b|S&zj9;eG#9{U0Pd}XSzjw$)RYT1C&2jOTL#qHr zt~*wVc%>8#T^M5Kjbzj39DfAoJZ?6qG5ee!b!+RJe9pu_Z8EB?7qu8h=tUpPlE&Yj zc8&UxxKW0(TmMluROCoHJ+nNqYpGeYd?Pb|tsyU$yOWtk7lYD)NeHDfu%3hJjJe(M zR+e};ccOD5PQAj#uR8PMj8b|){hdJcKNu!?Ml5l!NKiSs4!xveQA6%f5ablf@U#*3)?%WOjLwu~)9OE|#SlN)3q6nv<;mCwTUw^Q=TqF0Ufo1R|Fk#< zZzy{%cF4*H^ULfaI-9#FV&<$|)KVH&KPp@7CPDiuuNVK2BxyOc`+1khQ1(J>_Y(9S=rf{SlOA_IMi7=`9B_ZZU$D?j~XUsJ#*&&GO)Eb{bug@ ze;XK`rqO&DeDPA(a#1yM2Rb;}n}4%01G;!Rm;t{zxR?L{9@#tBsECf5VDN9Hv2ocw zRR9b%3bP3c4mFN^GA;}bP%?mXz!Zl1r#f(Kd46PYe2{byVJtu0b&&YfB*mh literal 0 HcmV?d00001 diff --git a/src/checklist/public/icons/browserconfig.xml b/src/checklist/public/icons/browserconfig.xml new file mode 100644 index 00000000..0cfc3dc1 --- /dev/null +++ b/src/checklist/public/icons/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #fefefe + + + diff --git a/src/checklist/public/icons/favicon-16x16.png b/src/checklist/public/icons/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..a6155191fec558c459faa0207841a7f36f4912ac GIT binary patch literal 982 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>gFDo1EoS7t%4tAAzv zgdBbQn#PWN%cY7f%V!+hzHmu3lOT`Heoy9gCr>XjjqlGbO6F5`ca{{Hvr+cy4*83< z0jkB#GI!-efVMCudAqwrm+YBp0_1QOctjR6FmMZlFeAgPITAoY_7YEDSN3O2yeuqI zQYpR)K%s}8E{-7*mqYtAZsXRA@ilXxK_s_Gj zFIk{%%Q1DD?U|?CuR89u$ue1osn}9A{Q7(*=ZS^a=gp6Azwo$l+fT3xJ+gEpd$~ zNl7e8wMs5Z1yT$~21Z7@21dGu#vukKRzPHArfpzgWndt%QEw)ShTQy=%(P0}8WP`3 zQw3@WuL_AM2}&$iC@9KL%gjktD5)$+RmdzTNoHWEnDh7(4@Y5`hQ=xX(`P)N1~D)z zbL%B@3o8qIPZnVoR&Z%BIh?|*yg5YS^o=Vgj+{9nbA!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+081CvI8Plzi}fx)8xqi8e) z@Pxqs|NlSN#&!bJ%C?dqzhDLiR=v_4Pkyr4EPT9oQc;rZo3n+QoYtpB-0vSU-pr9& zQywD6c=Dj-+PmBLTx$}4XcD#DnN9WLE*0BXt|emA=Ls3@%-tru;EbrifvHR%{zObU z&aHM?|53nA(|^&EmuVN5X-!y_`E|BWm1B_8(R=ah+FS1^H~#pY9>%=>*|&@f;lEaA zzdq+DWp{(8C+k@6G{tKFoX{h47wwl!o8b_7_>|l3l@kp-dHXw3bn35vy3OA0#ih|^ zv56~7?=;W{j7i?^F5B-iSf>LyoCO|{#S9GG!XV7ZFl&wkP>{XE)7O>#851uHidrirr}%)U|du=n@*s4W|g#02i#aw{+MfQm7oT!Lxcl_3`D76T)IvOIA zxO(F!Tfrr#c~`$#ecgi7Ki2hc)Owd4u`1@=CMHG?l4t*UeJ%gSjXf*Zm3~DFZ|{BrHzE0dHR#Mw%S=3R4^^|n|EoW?Y@N%FMsJ^d2yAC zNB!ZMn+Jbbdf%^Xxe@5T|M#I8EDW8=k3}4gx2P9=6nd2O=~IY`>0H(og*=%I$JSM( z`)J5+E|~f)-Sy%E6M-FNcRjfe^zJ-4TY>q9P=R*I?{l%Y_x_$Myzd_SXUkPZsyoye zoA_h<<=9ts?o{`mKXI{KLG}vuwOnbh>k9q^egP zb70;j7YW_0?`RX${Vx8oc*WoAQxbOeWIk=FTXO1mw8>lhoc#%>Z`l2R_{4ti zpX0yxfBw%rV|nK)sdBwOV1iREag8WRNi0dVN-jzTQVd20Mn<{@M!JT^AqFN^KxAa5 zZD3$!U?8wjZzhU{-29Zxv`X9>65mWy1!@Sd3W+EQN-S3>D9TUE%t=)!sVqoU$Sf#H zW?-n8^Y{}FM`4(T#wq{PXFQ(m_pwD+_y17GV}vaA`0(oWiWUIYi;~jVmXP loH-(Mg#C1b#{w@shF9W(C7+y3rvj~D@O1TaS?83{1ON<|HRAvP literal 0 HcmV?d00001 diff --git a/src/checklist/public/icons/favicon.ico b/src/checklist/public/icons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e426fc5322423a04c382bd5a39e0cd88a42d53eb GIT binary patch literal 15086 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 literal 0 HcmV?d00001 diff --git a/src/checklist/public/icons/mstile-150x150.png b/src/checklist/public/icons/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..cc7b02acc6e6d317f1cc7ea2b02fbc96be9bd104 GIT binary patch literal 5236 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 literal 0 HcmV?d00001 diff --git a/src/checklist/public/icons/safari-pinned-tab.svg b/src/checklist/public/icons/safari-pinned-tab.svg new file mode 100644 index 00000000..c421bfae --- /dev/null +++ b/src/checklist/public/icons/safari-pinned-tab.svg @@ -0,0 +1,591 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + diff --git a/src/checklist/public/icons/site.webmanifest b/src/checklist/public/icons/site.webmanifest new file mode 100644 index 00000000..b9319c60 --- /dev/null +++ b/src/checklist/public/icons/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#fefefe", + "background_color": "#fefefe", + "display": "standalone" +} diff --git a/src/checklist/public/img/spinner.svg b/src/checklist/public/img/spinner.svg new file mode 100644 index 00000000..c0e7e57e --- /dev/null +++ b/src/checklist/public/img/spinner.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/checklist/public/js/script.js b/src/checklist/public/js/script.js new file mode 100644 index 00000000..0a3dc3c7 --- /dev/null +++ b/src/checklist/public/js/script.js @@ -0,0 +1,248 @@ +const SCOUTER_STATUS = { + "NEW": 0, //scouters who have connected but have not sent their state data + "WAITING": 1, //scouters not actively in the process of scouting (dont have the scouting ui open) + "SCOUTING": 2, //scouters actively scouting a match + "COMPLETE": 3, +} +const SCOUTER_STATUS_REVERSE = { + "0": "NEW", + "1": "WAITING", + "2": "SCOUTING", + "3": "COMPLETE" +} + +const scouters = {}; + +;(async () => { + const authRequest = await fetch("./api/auth").then(res => res.json()) + + if (authRequest.status !== 2) { + const authModal = new Modal("small", false).header("Sign In") + const accessCodeInput = createDOMElement("input", "access-input") + accessCodeInput.placeholder = "Access Code" + accessCodeInput.type = "password" + accessCodeInput.addEventListener("keydown", (e) => { + if (e.keyCode == 13) { + validate(accessCodeInput.value, authModal) + } + }) + authModal.element.appendChild(accessCodeInput) + authModal.action("Submit", async () => { + validate(accessCodeInput.value, authModal) + }) + } else { + await constructApp() + } + + async function validate(accessCode, authModal) { + const auth = await fetch("./api/auth", { + headers: { + Authorization: accessCode + } + }).then(res => res.json()) + + if (auth.status === 1) { + await constructApp(accessCode) + authModal.modalExit() + } else { + new Popup("error", "Wrong Access Code") + } + } +})() + +async function constructApp(accessCode) { + await updateScouters(accessCode); + setInterval(() => updateScouters(accessCode), 2500); + + await updateMatches(accessCode) + setInterval(() => updateMatches(accessCode), 2500); + + document.querySelector("#start-scouting").addEventListener("click", () => { + fetch("/admin/api/enterMatch", { + headers: { + Authorization: accessCode + } + }); + console.log("ENTER MATCH!") + }) + + let menuExpanded = false + + document.querySelector("#admin-panel").classList.add("visible") + document.querySelector("#menu").classList.add("visible") + + document.querySelector("#menu-icon").addEventListener("click", () => { + if (menuExpanded) { + document.querySelector("#menu").classList.remove("expanded") + } else { + document.querySelector("#menu").classList.add("expanded") + } + menuExpanded = !menuExpanded + }) +} + +async function updateScouters(accessCode) { //scouter fetch interval (every 2.5s) + let scouterList = await (await fetch("./api/scouters", { + headers: { + Authorization: accessCode + } + })).json(); + + for (let scouter of scouterList) { + if (scouter.timestamp in scouters) { + scouters[scouter.timestamp].updateScouterElement(scouter.state); + } else { + if (scouter.state.status == SCOUTER_STATUS.COMPLETE || !scouter.state.connected) continue; //it's already submitted/disconnected, dont show it. + scouters[scouter.timestamp] = new ScouterDisplay(scouter); + } + if (scouter.state.status == SCOUTER_STATUS.COMPLETE || !scouter.state.connected) { //prune offline/complete scouters from the list + setTimeout(() => { + if (scouters[scouter.timestamp] && (scouters[scouter.timestamp].scouter.state.status == SCOUTER_STATUS.COMPLETE || !scouters[scouter.timestamp].scouter.state.connected)) { + scouters[scouter.timestamp].destruct(); + delete scouters[scouter.timestamp] + } + }, 15000) + } + } + console.log(scouters) + //prune scouters that no longer exist + for (let timestamp in scouters) { + if (!scouterList.find(x=>x.timestamp = timestamp)) { //they no longer exist + scouters[timestamp].destruct(); + delete scouters[timestamp]; + } + } +} + +async function updateMatches(accessCode) { + let {allMatches, currentMatch} = await (await fetch(`/admin/api/matches`, { + headers: { + Authorization: accessCode + } + })).json(); + + //clear matches view + document.querySelector("#match-list").innerHTML = ""; + + //rebuild matches view + for (let match of allMatches) { + let matchElement = document.createElement("div"); + matchElement.classList.add("match"); + matchElement.innerHTML = ` +

${match.number} - ${match.match_string.toUpperCase().split("_")[0]}-${match.match_string.toUpperCase().split("_")[1]}
+ +
+
+ ` + document.querySelector("#match-list").appendChild(matchElement); + + if (currentMatch.match_string == match.match_string) { //check the box if it is selected + matchElement.querySelector(".match-select").checked = true; + } + + //add the robot numbers to match + for (let color of ["red","blue"]) { + for (let robotNumber of match.robots[color]) { + let text = document.createElement("div"); + text.innerText = robotNumber; + matchElement.querySelector(`.match-teams.${color}`).appendChild(text) + } + } + + //checkbox functionality + let checkbox = matchElement.querySelector(".match-select") + checkbox.addEventListener("input", () => { + if (!checkbox.checked) { //if its already selected, do nothing + checkbox.checked = true; + return; + } + + checkbox.checked = false; //set it to unchecked while processing the request + + //send a post request with the new match + fetch("/admin/api/setMatch", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: accessCode + }, + body: JSON.stringify(match), + }).then((res) => res.json()).then((success) => { + if (success === true) { //if the match is successfully updated on the server-side + new Popup("success", `Match ${match.number} - ${match.match_string.toUpperCase()} Selected!`,2000); + for (let box of document.querySelectorAll(".match .match-select")) { //deselect all boxes + box.checked = false; + } + checkbox.checked = true; //select the correct box + } else { + new Popup("error", "Failed to Select Match!"); + } + }).catch(e => new Popup("error", "Failed to Select Match!")); + }) + } +} + +class ScouterDisplay { + scouterElement; + scouter; + + constructor (scouter) { + this.scouter = scouter; + + this.scouterElement = document.createElement("div"); + this.scouterElement.innerHTML = ` +
+
+
+
+ `; + this.scouterElement.classList.add("scouter"); + + document.querySelector("#scouter-list").appendChild(this.scouterElement); + + this.updateScouterElement(); + + } + updateScouterElement(state) { + + //update state + this.scouter.state = state || this.scouter.state; + + //write all text + this.scouterElement.querySelector(".scouter-id").innerText = this.scouter.state.scouterId; + this.scouterElement.querySelector(".match-number").innerText = this.scouter.state.matchNumber; + this.scouterElement.querySelector(".robot-number").innerText = this.scouter.state.robotNumber; + + //update color + const SCOUTER_STATUS_COLOR = { + "0": "var(--text)", //NEW + "1": "#ffa500", //WAITING + "2": "var(--accent)", //SCOUTING + "3": "var(--green)" //COMPLETE + } + const DISCONNECTED_COLOR = "var(--error)"; + + if (this.scouter.state.status == SCOUTER_STATUS.NEW) { + this.scouterElement.style.display = "none"; + } else { + this.scouterElement.style.display = "flex"; + } + + if (!this.scouter.state.connected && !(this.scouter.state.status == SCOUTER_STATUS.COMPLETE)) { //disconneted and not complete + this.scouterElement.querySelector(".scouter-status").style.color = DISCONNECTED_COLOR; + this.scouterElement.style.borderColor = DISCONNECTED_COLOR; + this.scouterElement.querySelector(".match-number").style.backgroundColor = DISCONNECTED_COLOR; + this.scouterElement.querySelector(".match-number").style.borderColor = DISCONNECTED_COLOR; + this.scouterElement.querySelector(".scouter-status").innerText = "DISCONNECTED"; + } else { + this.scouterElement.querySelector(".scouter-status").style.color = SCOUTER_STATUS_COLOR[this.scouter.state.status]; + this.scouterElement.style.borderColor = SCOUTER_STATUS_COLOR[this.scouter.state.status]; + this.scouterElement.querySelector(".match-number").style.backgroundColor = SCOUTER_STATUS_COLOR[this.scouter.state.status]; + this.scouterElement.querySelector(".match-number").style.borderColor = SCOUTER_STATUS_COLOR[this.scouter.state.status]; + this.scouterElement.querySelector(".scouter-status").innerText = SCOUTER_STATUS_REVERSE[this.scouter.state.status]; + } + } + destruct() { + this.scouterElement.parentElement.removeChild(this.scouterElement); + } +} \ No newline at end of file diff --git a/src/checklist/public/js/ui-elements.js b/src/checklist/public/js/ui-elements.js new file mode 100644 index 00000000..231ac169 --- /dev/null +++ b/src/checklist/public/js/ui-elements.js @@ -0,0 +1,162 @@ +class Modal { + blind; // dimmed background element (closes modal on click) + close; // close button element + element; // main modal container element + modalExit; // function to close and destroy the modal + dismissButton; // optional dismiss button element + cancel; // cancel function passed in + + constructor(size, closable=true) { + this.blind = document.createElement("div") + this.blind.classList = "modal-blind" + this.element = document.createElement("div") + this.element.classList = `modal ${size}` + + this.modalExit = () => { + hideFade(this.blind) + hideFade(this.element) + setTimeout(() => { + document.body.removeChild(this.blind) + document.body.removeChild(this.element) + }, 300) + } + + if (closable) { + this.close = document.createElement("i") + this.close.classList = "fa fa-times modal-close" + + this.blind.addEventListener("click", this.modalExit) + this.close.addEventListener("click", this.modalExit) + this.element.appendChild(this.close) + } + document.body.appendChild(this.blind) + document.body.appendChild(this.element) + this.blind.offsetHeight + this.element.offsetHeight + showFade(this.blind) + showFade(this.element) + return this + } + + assignCancel(cancelFunction) { + this.blind.addEventListener("click", cancelFunction) + this.close.addEventListener("click", cancelFunction) + this.dismissButton ? this.dismissButton.addEventListener("click", cancelFunction) : null + this.cancel = cancelFunction + } + + header(text) { + const headerElement = document.createElement("div") + headerElement.innerHTML = text + headerElement.classList.add("header") + this.element.appendChild(headerElement) + return this + } + + text(text) { + const textElement = document.createElement("div") + textElement.innerHTML = text + textElement.classList.add("text") + this.element.appendChild(textElement) + return this + } + + image(src) { + const imgElement = document.createElement("img") + imgElement.src = src + this.element.appendChild(imgElement) + return this + } + + center(horizontal = true, vertical = false) { + if (vertical) { + this.element.classList.add("main-center") + } + + if (horizontal) { + this.element.classList.add("alt-center") + } + + return this + } + + dismiss(buttonText = "Dismiss") { + const buttonElement = document.createElement("button") + buttonElement.innerText = buttonText + buttonElement.addEventListener("click", this.modalExit) + this.cancel ? buttonElement.addEventListener("click", this.cancel) : null + this.element.appendChild(buttonElement) + + return this + } + + action(buttonText, func) { + const buttonElement = document.createElement("button") + buttonElement.innerText = buttonText + buttonElement.addEventListener("click", func); + this.element.appendChild(buttonElement); + + return this + } +} + +function showFade(element) { + element.classList.add("visible") +} + +function hideFade(element) { + element.classList.remove("visible") +} + +class Popup { + static types = { + "error": { + "prefix": "Error: ", + "color": "var(--error)" + }, + + "notice": { + "prefix": "", + "color": "var(--accent)" + }, + + "success": { + "prefix": "", + "color": "var(--green)" + }, + } + popupElement; + + constructor(type, text, duration = 5000) { + this.popupElement = document.createElement("p") + this.popupElement.classList = "popup" + this.popupElement.style.backgroundColor = Popup.types[type].color + this.popupElement.innerText = Popup.types[type].prefix + text + document.body.appendChild(this.popupElement) + this.popupElement.offsetHeight + this.popupElement.style.top = "55px" + setTimeout(() => { + this.popupElement.style.top = "0" + setTimeout(() => { + document.body.removeChild(this.popupElement) + }, 600) + }, duration) + } + setText(text) { + this.popupElement.innerText = text; + return this; + } + setType(type) { + this.popupElement.style.backgroundColor = Popup.types[type].color; + return this; + } +} + +function createDOMElement(tag, classes, id) { + const element = document.createElement(tag) + element.classList = classes + if (id) { + element.id = id + } + return element +} \ No newline at end of file diff --git a/src/checklist/routes/api.js b/src/checklist/routes/api.js new file mode 100644 index 00000000..90b05c44 --- /dev/null +++ b/src/checklist/routes/api.js @@ -0,0 +1,67 @@ +const { Router } = require("express"); +const ScoutingSync = require("../../scouting/scouting-sync")(); +let router = Router(); +const config = require("../../../config/config.json"); +const { TeamMatchPerformance } = require("../../lib/db"); + + +router.use((req,res,next) => { + if (!ScoutingSync.initialized) { + res.status(503).send("Scouting Sync not initialized yet!"); + } else { + next() + } +}) + +router.get("/auth", (req, res) => { + if (config.secrets.ACCESS_CODE === "") { + res.json({status: 2}) + } else if (config.secrets.ACCESS_CODE == req.headers.authorization) { + res.json({status: 1}) + } else { + res.json({status: 0}) + } +}) + +router.get("/scouters", (req,res) => { + if (req.headers.authorization === config.secrets.ACCESS_CODE) { + res.json(ScoutingSync.getScouters()) + } else { + res.json({error: "Not Authorized"}) + } +}); + +router.get("/data", async (req,res) => { + res.json(await TeamMatchPerformance.find()); +}) + + +router.get("/enterMatch", (req,res) => { + if (req.headers.authorization === config.secrets.ACCESS_CODE) { + for (let scouter of ScoutingSync.scouters) { + if (scouter.state.status == ScoutingSync.SCOUTER_STATUS.WAITING) + scouter.updateState({status: ScoutingSync.SCOUTER_STATUS.SCOUTING}); + scouter.socket.emit("enterMatch"); + } + res.json(true); + } else { + res.json({error: "Not Authorized"}) + } +}) +router.post("/setMatch", (req,res) => { + if (req.headers.authorization === config.secrets.ACCESS_CODE) { + ScoutingSync.setMatch(req.body); + ScoutingSync.assignScouters(); + res.json(true); + } else { + res.json({error: "Not Authorized"}) + } +}); + +router.get("/matches", async (req,res) => { + res.json({ + "allMatches": await ScoutingSync.getMatches(), + "currentMatch": ScoutingSync.match + }) +}) +module.exports = router; \ No newline at end of file diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs new file mode 100644 index 00000000..837e3ebc --- /dev/null +++ b/src/checklist/views/index.ejs @@ -0,0 +1,62 @@ + + + + + + + + SPOT - Admin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 6b6e6ea3f3c37519c0c5cad64e1568dc3956e4be Mon Sep 17 00:00:00 2001 From: alannaping Date: Thu, 19 Jan 2023 11:50:29 -0600 Subject: [PATCH 05/20] Started working on page formatting for picklist --- src/app.js | 1 + src/checklist/public/js/script.js | 349 +++++++++--------------------- src/checklist/views/index.ejs | 14 +- 3 files changed, 111 insertions(+), 253 deletions(-) diff --git a/src/app.js b/src/app.js index 587be139..f297e563 100644 --- a/src/app.js +++ b/src/app.js @@ -18,6 +18,7 @@ if (fs.existsSync("config/config.json")) { app.use("/analysis", require("./analysis/analysis.js")); app.use("/admin", require("./admin/admin.js")); app.use("/setup", require("./setup/setup.js")); + app.use("/checklist", require("./checklist/checklist.js")); } else { console.log(chalk.cyan.bold.underline("config.json not detected! First time setup flow enabled on server.")) app.use("/",require("./setup/setup.js")); diff --git a/src/checklist/public/js/script.js b/src/checklist/public/js/script.js index 0a3dc3c7..aeff2dc1 100644 --- a/src/checklist/public/js/script.js +++ b/src/checklist/public/js/script.js @@ -1,248 +1,101 @@ -const SCOUTER_STATUS = { - "NEW": 0, //scouters who have connected but have not sent their state data - "WAITING": 1, //scouters not actively in the process of scouting (dont have the scouting ui open) - "SCOUTING": 2, //scouters actively scouting a match - "COMPLETE": 3, -} -const SCOUTER_STATUS_REVERSE = { - "0": "NEW", - "1": "WAITING", - "2": "SCOUTING", - "3": "COMPLETE" -} - -const scouters = {}; - -;(async () => { - const authRequest = await fetch("./api/auth").then(res => res.json()) - - if (authRequest.status !== 2) { - const authModal = new Modal("small", false).header("Sign In") - const accessCodeInput = createDOMElement("input", "access-input") - accessCodeInput.placeholder = "Access Code" - accessCodeInput.type = "password" - accessCodeInput.addEventListener("keydown", (e) => { - if (e.keyCode == 13) { - validate(accessCodeInput.value, authModal) - } - }) - authModal.element.appendChild(accessCodeInput) - authModal.action("Submit", async () => { - validate(accessCodeInput.value, authModal) - }) - } else { - await constructApp() - } - - async function validate(accessCode, authModal) { - const auth = await fetch("./api/auth", { - headers: { - Authorization: accessCode - } - }).then(res => res.json()) - - if (auth.status === 1) { - await constructApp(accessCode) - authModal.modalExit() - } else { - new Popup("error", "Wrong Access Code") - } - } -})() - -async function constructApp(accessCode) { - await updateScouters(accessCode); - setInterval(() => updateScouters(accessCode), 2500); - - await updateMatches(accessCode) - setInterval(() => updateMatches(accessCode), 2500); - - document.querySelector("#start-scouting").addEventListener("click", () => { - fetch("/admin/api/enterMatch", { - headers: { - Authorization: accessCode - } - }); - console.log("ENTER MATCH!") - }) - - let menuExpanded = false - - document.querySelector("#admin-panel").classList.add("visible") - document.querySelector("#menu").classList.add("visible") - - document.querySelector("#menu-icon").addEventListener("click", () => { - if (menuExpanded) { - document.querySelector("#menu").classList.remove("expanded") - } else { - document.querySelector("#menu").classList.add("expanded") - } - menuExpanded = !menuExpanded - }) -} - -async function updateScouters(accessCode) { //scouter fetch interval (every 2.5s) - let scouterList = await (await fetch("./api/scouters", { - headers: { - Authorization: accessCode - } - })).json(); - - for (let scouter of scouterList) { - if (scouter.timestamp in scouters) { - scouters[scouter.timestamp].updateScouterElement(scouter.state); - } else { - if (scouter.state.status == SCOUTER_STATUS.COMPLETE || !scouter.state.connected) continue; //it's already submitted/disconnected, dont show it. - scouters[scouter.timestamp] = new ScouterDisplay(scouter); - } - if (scouter.state.status == SCOUTER_STATUS.COMPLETE || !scouter.state.connected) { //prune offline/complete scouters from the list - setTimeout(() => { - if (scouters[scouter.timestamp] && (scouters[scouter.timestamp].scouter.state.status == SCOUTER_STATUS.COMPLETE || !scouters[scouter.timestamp].scouter.state.connected)) { - scouters[scouter.timestamp].destruct(); - delete scouters[scouter.timestamp] - } - }, 15000) - } - } - console.log(scouters) - //prune scouters that no longer exist - for (let timestamp in scouters) { - if (!scouterList.find(x=>x.timestamp = timestamp)) { //they no longer exist - scouters[timestamp].destruct(); - delete scouters[timestamp]; - } - } -} - -async function updateMatches(accessCode) { - let {allMatches, currentMatch} = await (await fetch(`/admin/api/matches`, { - headers: { - Authorization: accessCode - } - })).json(); - - //clear matches view - document.querySelector("#match-list").innerHTML = ""; - - //rebuild matches view - for (let match of allMatches) { - let matchElement = document.createElement("div"); - matchElement.classList.add("match"); - matchElement.innerHTML = ` -
${match.number} - ${match.match_string.toUpperCase().split("_")[0]}-${match.match_string.toUpperCase().split("_")[1]}
- -
-
- ` - document.querySelector("#match-list").appendChild(matchElement); - - if (currentMatch.match_string == match.match_string) { //check the box if it is selected - matchElement.querySelector(".match-select").checked = true; - } - - //add the robot numbers to match - for (let color of ["red","blue"]) { - for (let robotNumber of match.robots[color]) { - let text = document.createElement("div"); - text.innerText = robotNumber; - matchElement.querySelector(`.match-teams.${color}`).appendChild(text) - } - } - - //checkbox functionality - let checkbox = matchElement.querySelector(".match-select") - checkbox.addEventListener("input", () => { - if (!checkbox.checked) { //if its already selected, do nothing - checkbox.checked = true; - return; - } - - checkbox.checked = false; //set it to unchecked while processing the request - - //send a post request with the new match - fetch("/admin/api/setMatch", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: accessCode - }, - body: JSON.stringify(match), - }).then((res) => res.json()).then((success) => { - if (success === true) { //if the match is successfully updated on the server-side - new Popup("success", `Match ${match.number} - ${match.match_string.toUpperCase()} Selected!`,2000); - for (let box of document.querySelectorAll(".match .match-select")) { //deselect all boxes - box.checked = false; - } - checkbox.checked = true; //select the correct box - } else { - new Popup("error", "Failed to Select Match!"); - } - }).catch(e => new Popup("error", "Failed to Select Match!")); - }) - } -} - -class ScouterDisplay { - scouterElement; - scouter; - - constructor (scouter) { - this.scouter = scouter; - - this.scouterElement = document.createElement("div"); - this.scouterElement.innerHTML = ` -
-
-
-
- `; - this.scouterElement.classList.add("scouter"); - - document.querySelector("#scouter-list").appendChild(this.scouterElement); - - this.updateScouterElement(); - - } - updateScouterElement(state) { - - //update state - this.scouter.state = state || this.scouter.state; - - //write all text - this.scouterElement.querySelector(".scouter-id").innerText = this.scouter.state.scouterId; - this.scouterElement.querySelector(".match-number").innerText = this.scouter.state.matchNumber; - this.scouterElement.querySelector(".robot-number").innerText = this.scouter.state.robotNumber; - - //update color - const SCOUTER_STATUS_COLOR = { - "0": "var(--text)", //NEW - "1": "#ffa500", //WAITING - "2": "var(--accent)", //SCOUTING - "3": "var(--green)" //COMPLETE - } - const DISCONNECTED_COLOR = "var(--error)"; - - if (this.scouter.state.status == SCOUTER_STATUS.NEW) { - this.scouterElement.style.display = "none"; - } else { - this.scouterElement.style.display = "flex"; - } - - if (!this.scouter.state.connected && !(this.scouter.state.status == SCOUTER_STATUS.COMPLETE)) { //disconneted and not complete - this.scouterElement.querySelector(".scouter-status").style.color = DISCONNECTED_COLOR; - this.scouterElement.style.borderColor = DISCONNECTED_COLOR; - this.scouterElement.querySelector(".match-number").style.backgroundColor = DISCONNECTED_COLOR; - this.scouterElement.querySelector(".match-number").style.borderColor = DISCONNECTED_COLOR; - this.scouterElement.querySelector(".scouter-status").innerText = "DISCONNECTED"; - } else { - this.scouterElement.querySelector(".scouter-status").style.color = SCOUTER_STATUS_COLOR[this.scouter.state.status]; - this.scouterElement.style.borderColor = SCOUTER_STATUS_COLOR[this.scouter.state.status]; - this.scouterElement.querySelector(".match-number").style.backgroundColor = SCOUTER_STATUS_COLOR[this.scouter.state.status]; - this.scouterElement.querySelector(".match-number").style.borderColor = SCOUTER_STATUS_COLOR[this.scouter.state.status]; - this.scouterElement.querySelector(".scouter-status").innerText = SCOUTER_STATUS_REVERSE[this.scouter.state.status]; - } - } - destruct() { - this.scouterElement.parentElement.removeChild(this.scouterElement); - } -} \ No newline at end of file + +// temporarily not using access code + +// ;(async () => { +// const authRequest = await fetch("./api/auth").then(res => res.json()) + +// if (authRequest.status !== 2) { +// const authModal = new Modal("small", false).header("Sign In") +// const accessCodeInput = createDOMElement("input", "access-input") +// accessCodeInput.placeholder = "Access Code" +// accessCodeInput.type = "password" +// accessCodeInput.addEventListener("keydown", (e) => { +// if (e.keyCode == 13) { +// validate(accessCodeInput.value, authModal) +// } +// }) +// authModal.element.appendChild(accessCodeInput) +// authModal.action("Submit", async () => { +// validate(accessCodeInput.value, authModal) +// }) +// } else { +// await constructApp() +// } + +// async function validate(accessCode, authModal) { +// const auth = await fetch("./api/auth", { +// headers: { +// Authorization: accessCode +// } +// }).then(res => res.json()) + +// if (auth.status === 1) { +// await constructApp(accessCode) +// authModal.modalExit() +// } else { +// new Popup("error", "Wrong Access Code") +// } +// } +// })() + +// async function constructApp(accessCode) { + +// } + +(async () => { + + async function loadTeams(dataset, modulesConfig) { + //get blue alliance teams + const allTeams = await fetchTeams() + //add to sidebar team list + for (const [teamNumber, team] of Object.entries(dataset.teams)) { + const teamContainer = constructTeam(teamNumber, team, allTeams) + teamList.appendChild(teamContainer) + } + + //enable sidebar if sidebar modules exist + if (modulesConfig.map(m => m.position).includes("side")) { + teamView.classList.add("side-enabled") + } else { + teamView.classList.remove("side-enabled") + } + + //get all team modules, create and store module classes, then append their placeholder containers to lists + for (const module of modulesConfig.filter(m => m.view == "team")) { + const moduleObject = new moduleClasses[module.module](module) + if (module.position == "main") { + mainList.appendChild(moduleObject.container) + } else { + sideList.appendChild(moduleObject.container) + } + modules.team.push(moduleObject) + } + } + + function constructTeam(teamNumber, team, allTeams) { + //create and populate sidebar element + const teamContainer = createDOMElement("div", "team-container") + const teamNumDisplay = createDOMElement("div", "team-number") + teamNumDisplay.innerText = teamNumber + teamContainer.setAttribute("num", teamNumber) + teamContainer.appendChild(teamNumDisplay) + if (allTeams) { + const teamNameDisplay = createDOMElement("div", "team-name") + teamNameDisplay.innerText = allTeams[teamNumber] + teamContainer.appendChild(teamNameDisplay) + } + + //switch to team on click of sidebar team, set module data + teamContainer.addEventListener("click", async () => { + await setTeamModules(teamNumber) + displayTeam(teamContainer) + }) + + return teamContainer + } + + + + + +}) \ No newline at end of file diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs index 837e3ebc..62f3e5b1 100644 --- a/src/checklist/views/index.ejs +++ b/src/checklist/views/index.ejs @@ -5,7 +5,7 @@ - SPOT - Admin + SPOT - Pick List @@ -29,9 +29,12 @@ - +
- + + + + - + \ No newline at end of file From 1844a6553505819cebbc4c5e578c8fc8c33ec743 Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Fri, 20 Jan 2023 00:24:09 +0000 Subject: [PATCH 06/20] Initial commit From 119d6e60a769226f0f1f6885c755b2b2afe2664b Mon Sep 17 00:00:00 2001 From: alannaping Date: Fri, 20 Jan 2023 11:26:29 -0600 Subject: [PATCH 07/20] working on loading teams into the page --- src/checklist/manual/teams.json | 66 +++ src/checklist/manual/tmps.json | 1 + src/checklist/public/css/style.css | 692 ++++++++++++++-------------- src/checklist/public/js/elements.js | 12 + src/checklist/public/js/script.js | 128 ++--- src/checklist/views/index.ejs | 9 +- 6 files changed, 473 insertions(+), 435 deletions(-) create mode 100644 src/checklist/manual/teams.json create mode 100644 src/checklist/manual/tmps.json create mode 100644 src/checklist/public/js/elements.js diff --git a/src/checklist/manual/teams.json b/src/checklist/manual/teams.json new file mode 100644 index 00000000..c7b76e7e --- /dev/null +++ b/src/checklist/manual/teams.json @@ -0,0 +1,66 @@ +{ + "drivetrain": { + "3360": "T", + "3655": "S", + "1741": "T", + "8818": "T", + "1792": "T", + "8793": "WC", + "5484": "T", + "4944": "T", + "3142": "T", + "6340": "T", + "2468": "S", + "6749": "T", + "6358": "T", + "3959": "S", + "4188": "S", + "5152": "S", + "7072": "T", + "4089": "S", + "6401": "T", + "4099": "S", + "8006": "T", + "6453": "T", + "1577": "S", + "8022": "T", + "6586": "T", + "4735": "T", + "2337": "S", + "1391": "T", + "2220": "S", + "1261": "T", + "971": "T", + "2080": "WC", + "2056": "T", + "4400": "DD", + "4611": "S", + "2383": "T", + "341": "S", + "85": "T", + "70": "M", + "33": "S", + "1403": "T", + "1555": "M", + "1671": "S", + "3357": "T", + "3749": "T", + "3792": "T", + "8574": "T", + "8537": "T", + "3171": "T", + "386": "T", + "876": "T", + "125": "S", + "3952": "M", + "3986": "T", + "4003": "S", + "4414": "S", + "4905": "T", + "3932": "T", + "649": "S", + "503": "S", + "5406": "T", + "5985": "T" + } +} \ No newline at end of file diff --git a/src/checklist/manual/tmps.json b/src/checklist/manual/tmps.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/src/checklist/manual/tmps.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/checklist/public/css/style.css b/src/checklist/public/css/style.css index 2e55b80e..37753d08 100644 --- a/src/checklist/public/css/style.css +++ b/src/checklist/public/css/style.css @@ -1,370 +1,382 @@ body { - margin: 0; + background-color: var(--bg); + } + + #app { + height: 100vh; + width: 100%; + display: grid; + grid-template-columns: 275px auto; + grid-template-rows: 75px 30px auto; + transition: 0.3s visibility, opacity; + opacity: 0; + visibility: hidden; + background-color: var(--bg); overflow: hidden; -} - -.access-input { - border-radius: 8px; - border: 2px solid var(--text); + } + + #app.visible { + visibility: unset; + opacity: 1; + transition: visibility 0s 0s, opacity 0.3s 0s; + } + + #sidebar { + grid-area: 3 / 1 / 4 / 2; background-color: var(--bg-alt); - color: var(--text); - padding: 8px 12px; - font-size: 1.5em; -} - -#menu { + padding: 16px; + display: grid; + min-height: 0; + grid-template-rows: auto auto 1fr; + } + + #header { + background: var(--text); + border-bottom: 0.25vh solid white; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 0 20px; + box-shadow: 0 0 12px -2px #ff6030; + grid-area: 1 / 2 / 2 / 4; + } + + #dashboard { + grid-area: 2 / 2 / 4 / 4; + overflow: hidden; + position: relative; + } + + #team-view { height: 100%; - width: 50px; + width: 100%; + display: grid; + grid-template-rows: 1fr; + overflow: hidden; + grid-template-columns: 1fr; + } + + #team-view.side-enabled { + grid-template-columns: 65% 35%; + } + + #main-list { + grid-area: 1 / 1 / 2 / 2; display: flex; flex-direction: column; align-items: center; - padding: 16px 0; - grid-area: 1 / 1 / 2 / 2; - background-color: var(--bg-alt); - opacity: 0; - transition: 0.3s visibility, opacity, width; - visibility: hidden; - border-right: 1px solid var(--text); - transition: 0.3s all; - z-index: 999; - position: fixed; - top: 0; - left: 0; -} - -#menu.expanded { - width: 200px; -} - -#menu > * { + gap: 20px; + overflow-y: scroll; + padding: 40px 0; + } + + #main-list > div.hidden { + display: none; + } + + #side-list { + grid-area: 1 / 2 / 2 / 3; + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + overflow-y: scroll; + padding: 40px 0; + } + + #side-list > div.hidden { + display: none; + } + + #logo { + height: 100%; + width: 100%; display: flex; flex-direction: row; align-items: center; - margin-bottom: 12px; + justify-content: center; + grid-area: 1 / 1 / 3 / 2; + gap: 14px; + background-color: var(--accent-alt); + } + + #logo-img { + height: 50px;; + } + + #logo-text { + color: white; + font-size: 2.5rem; + } + + #search { + position: relative; + } + + #search-container { + padding: 20px 0; + border-top: 1px solid var(--accent-alt); + border-bottom: 1px solid var(--accent-alt); + box-shadow: 0 -7px 10px -10px var(--accent-alt); + border-radius: 8px 8px 0 0; + margin: 20px 0; + } + + #search-input { + border-radius: var(--border-radius); + border: none; + width: 100%; + font-size: 1.2rem; + padding: 0 40px 0 12px; + background-color: var(--bg); + border: 2px solid var(--accent-alt); + } + + #search-icon { + position: absolute; + top: 50%; + right: 12px; + font-size: 1.2rem; + transform: translateY(-50%); + transition: 0.3s color; + } + + #search-input:focus + #search-icon, #search-input:valid + #search-icon { + color: var(--accent) + } + + #loading-bar-container { + position: absolute; + top: 0; + left: 0; + height: 4px; + width: 100%; + z-index: 99; + } + + #loading-bar { + background-color: var(--accent-alt); + height: 100%; + width: 0%; + transition: 0.3s all; + } + + .team-container { + border-radius: 16px; + border: 2px solid var(--accent-alt); + background-color: var(--bg); + padding: 16px; + border-top-width: 6px; + display: flex; + flex-direction: column; + gap: 8px; cursor: pointer; + transition: 0.3s border, 0.3s background-color; + } + + .team-container > .team-number { + text-align: center; + color: var(--accent-alt); + font-weight: 600; + font-size: 1.5rem; + line-height: 1.5rem; + transition: 0.3s color; + } + + .team-container > .team-name { + text-align: center; + font-size: 1.2rem; + line-height: 1.2rem; color: var(--text); - text-decoration: none; -} - -#menu > * > i { - font-size: 1.75rem; -} - -#menu > * > div { - font-size: 1.25rem; - width: 0; - overflow: hidden; - white-space: pre; - opacity: 0; - transition: 0.3s opacity, margin; -} - -#menu.expanded > * > div { - width: unset; - margin-left: 10px; - opacity: 1; -} - -#menu-icon { - color: var(--accent); - transition: 0.3s transform cubic-bezier(.17,.67,.89,1.29); - transform: rotate(270deg); - margin-bottom: 16px; -} - -#menu-icon-container { - border-bottom: 2px solid black; - margin-bottom: 16px; -} - -#menu.expanded #menu-icon { - transform: rotate(90deg); -} - -#admin-panel { - margin-left: 50px; - height: 100vh; + transition: 0.3s color; + } + + .team-container.selected { + background-color: var(--accent-alt); + border-top-width: 2px; + } + + .team-container.selected > .team-number, .team-container.selected > .team-name { + color: var(--bg-alt); + } + + #team-list { display: flex; flex-direction: column; - padding: 25px 5%; - color: var(--text); - background: var(--bg); - opacity: 0; + gap: 20px; + overflow: auto; + padding-right: 2px; + } + + #welcome { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 32px; + } + + #welcome-text { + font-size: 4rem; + line-height: 1em; + color: var(--accent-alt); + text-align: center; + } + + #welcome-subtext { + font-size: 2.5rem; + line-height: 1em; + margin-bottom: 40px; + } + + #dashboard > div { + width: 100%; transition: 0.3s visibility, opacity; + opacity: 0; + position: absolute; + top: 0; + left: 0; visibility: hidden; - grid-area: 1 / 2 / 2 / 3; -} - -#menu.visible { - visibility: unset; - opacity: 1; - transition: visibility 0s 0s, opacity 0.3s 0s, 0.3s width; -} - -#admin-panel.visible { + } + + #dashboard > div.visible { visibility: unset; + height: calc(100vh - 75px); opacity: 1; transition: visibility 0s 0s, opacity 0.3s 0s; -} - -#scouters { - padding: 21px; - padding-top: 24px; - border: 2px solid var(--text); - border-radius: 10px; - position: relative; - margin-top: 10px; - min-height: 200px; - max-height: 360px; -} - -#scouter-list { - display: flex; - overflow-x: auto; - gap: 10px; - height: 100%; -} - -.scouter { - height: 150px; - width: 200px; - border-radius: 10px; - border: 2px solid var(--text); - position: relative; + } + + #match-view-switch { + background-color: var(--bg); + border: 3px solid var(--accent); + border-radius: var(--border-radius); + font-size: 1.3rem; + width: 100%; color: var(--text); - background-color: var(--bg-alt); - font: 1em/1em "Cairo"; + cursor: pointer; + transition: 0.3s color, 0.3s background-color + } + + #match-view-switch.selected { + background-color: var(--accent); + color: var(--bg-alt) + } + + #match-view { display: flex; flex-direction: column; - justify-content: space-evenly; align-items: center; - padding: 6px 25px 0; -} - -.scouter > .scouter-id { - font-size: 1.3rem; - text-overflow: ellipsis; - width: 100%; - text-align: center; - overflow-y: visible; - overflow-x: clip -} -.scouter > .robot-number { + gap: 10px; + } + + #select-teams-text { font-size: 3rem; - font-weight: bold; -} -.scouter > .match-number { - position: absolute; - left: 50%; - top: -1px; - transform: translateX(-50%); - border-radius: 0 0 10px 10px; - font-weight: bold; - font-size: 1.15rem; - color: var(--bg-alt); - padding: 0 12px 2px; line-height: 1em; - background-color: var(--text); - border: 2px solid var(--text); -} -.scouter > .scouter-status { - font-size: 1.3rem; - font-weight: bold; -} - -#start-scouting { - border: 2px solid var(--text); - background-color: var(--accent); - color: var(--bg-alt); - border-radius: 10px; - position: absolute; - bottom: -3px; - left: 50%; - transform: translate(-50%, 50%); - font: 1.4em/1em Cairo; - padding: 5px 20px; - cursor: pointer; -} - -#matches { - padding: 20px; - padding-top: 24px; - border: 2px solid var(--text); - border-radius: 10px; - position: relative; + color: var(--accent-alt); margin-top: 32px; - min-height: 0; -} - -.title { - position: absolute; - background-color: var(--bg); - border-radius: 10px; - top: 0px; - left: 40px; - font: 2em/1em "Cairo"; - padding: 0 6px; - transform: translateY(-50%); -} - -#match-list { - height: 100%; - width: 100%; - overflow-y: auto; - overflow-x: hidden; - gap: 12px; + margin-bottom: 10px; + } + + #team-selects { display: flex; - flex-direction: column; -} - -#matches .match { - display: grid; - width: 100%; - grid-template-columns: calc(.75 * 80px + 14px) repeat(2,1fr); - grid-template-rows: 30% 70%; - border: 2px solid var(--text); - border-radius: 10px; - padding: 10px 15px; - background: var(--bg-alt); -} - -.match .match-select { - width: calc(.75 * 80px); - height: calc(.75 * 80px); - margin-right: 14px; - appearance: none; - background-color: var(--text); - display: grid; - place-content: center; - border-radius: 10px; - align-self: center; -} - - -.match .match-select::before { - content: ""; - width: calc(.4 * 80px); - height: calc(.4 * 80px); - transform: scale(0); - border-radius: 16px; - transition: 120ms transform cubic-bezier(.37,-0.02,.47,1.77); - box-shadow: inset calc(.75 * 80px) calc(.75 * 80px) var(--accent); -} - -.match .match-select:checked::before { - transform: scale(1); -} - -.match .match-header { - grid-area: 1 / 1 / 2 / 4; - font-size: 1.2em; - line-height: 1em; -} - -.match .match-teams { + flex-direction: row; + gap: 16px; + align-items: center; + } + + #alliance-vs { + font-size: 1.5rem; + } + + .alliance-selects { display: flex; flex-direction: row; - justify-content: space-evenly; + gap: 8px; + } + + .alliance-selects > select { + width: 90px; + } + + .select { + border-radius: var(--border-radius); + border: 2px solid var(--text); + font-size: 1.2rem; + text-align: center; + color: var(--text); + background-color: var(--bg-alt); + transition: 0.2s border-color; + } + + .select > option { + text-align: center; + } + + .select.filled { + border-color: var(--accent-alt); + } + + #match-select-label { + font-size: 1.4em; + /* font-weight: 00; */ + } + + #match-select-container { + display: flex; + align-items: center; + gap: 10px; + } + + #match-select { + width: 100px; + } + + #alliance-module-container { + display: flex; + flex-direction: row; + width: 100%; + overflow-y: scroll; + } + + .alliance-modules { + width: 50%; + display: flex; + flex-direction: column; align-items: center; - text-align: center; - font: 1.8em/calc(80px * .75) "Cairo"; - color: var(--bg-alt); -} - -.match .match-teams.red { - background: #ff6666; - border-radius: 10px 0 0 10px; -} - -.match .match-teams.blue { - background: #6666ff; - border-radius: 0 10px 10px 0; -} - -@media only screen and (max-height: 500px), screen and (max-width: 700px) { - #admin-panel { - padding: 15px 2.5%; - } - - .scouter { - height: 100px; - padding: 6px 10px 0; - width: 115px; - } - - html { - font-size: 70%; - } - - #scouters { - min-height: 150px; - } - - .scouter > .match-number { - line-height: 0.7em; - padding: 0 8px 3px; - font-size: 1.3rem; - } - - body { - grid-template-columns: auto 1fr; - } - - #menu { - padding: 10px 0; - } - - #menu > * > i { - font-size: 2rem; - } - - #menu > * > div { - font-size: 1.75rem; - } -} - -@media only screen and (max-width: 550px) { - .match .match-teams { - font: 1.3rem/1em Cairo; - } - - #matches .match { - grid-template-columns: calc(.75 * 65px) repeat(2,1fr); - } - - .match .match-select { - width: calc(.75 * 50px); - height: calc(.75 * 50px); - margin-right: 0; - } - - .match .match-select::before { - width: calc(.4 * 50px); - height: calc(.4 * 50px); - } - - #scouter-list { - gap: 6px; - flex-wrap: wrap; - justify-content: space-evenly; - } - - #scouters { - padding: 21px 12px; - min-height: unset; - } - - #match-list { - gap: 8px; - } -} - -@media only screen and (max-width: 450px) { - #matches { - padding: 16px 0px 12px 4px; - } - - #matches .match { - padding: 8px 6px; - } -} + gap: 16px; + overflow-y: scroll; + padding: 10px 0 20px 0; + } + + .alliance-modules.hidden { + visibility: hidden; + } + + .alliance-modules > div.hidden { + display: none; + } + + @media screen and (max-width: 1100px) { + html { + font-size: 75%; + } + + #app { + grid-template-columns: 200px auto; + grid-template-rows: 40px 20px auto; + } + + #dashboard > div.visible { + height: calc(100vh - 40px); + } + + #main-list { + padding: 25px 0; + } + + #side-list { + padding: 25px 0; + } + } + \ No newline at end of file diff --git a/src/checklist/public/js/elements.js b/src/checklist/public/js/elements.js new file mode 100644 index 00000000..f216bf1a --- /dev/null +++ b/src/checklist/public/js/elements.js @@ -0,0 +1,12 @@ +const spinner = document.getElementById("spinner") +const app = document.getElementById("app") +const loadingBar = document.getElementById("loading-bar") +const header = document.getElementById("header") +const sidebar = document.getElementById("sidebar") +const pickList = document.getElementById("pick-list") +const teamView = document.getElementById("team-view") +const matchView = document.getElementById("match-view") +const welcomeView = document.getElementById("welcome") +const mainList = document.getElementById("main-list") +const sideList = document.getElementById("side-list") +const searchInput = document.getElementById("search-input") diff --git a/src/checklist/public/js/script.js b/src/checklist/public/js/script.js index aeff2dc1..f4a0d3f7 100644 --- a/src/checklist/public/js/script.js +++ b/src/checklist/public/js/script.js @@ -1,101 +1,41 @@ -// temporarily not using access code -// ;(async () => { -// const authRequest = await fetch("./api/auth").then(res => res.json()) - -// if (authRequest.status !== 2) { -// const authModal = new Modal("small", false).header("Sign In") -// const accessCodeInput = createDOMElement("input", "access-input") -// accessCodeInput.placeholder = "Access Code" -// accessCodeInput.type = "password" -// accessCodeInput.addEventListener("keydown", (e) => { -// if (e.keyCode == 13) { -// validate(accessCodeInput.value, authModal) -// } -// }) -// authModal.element.appendChild(accessCodeInput) -// authModal.action("Submit", async () => { -// validate(accessCodeInput.value, authModal) -// }) -// } else { -// await constructApp() -// } - -// async function validate(accessCode, authModal) { -// const auth = await fetch("./api/auth", { -// headers: { -// Authorization: accessCode -// } -// }).then(res => res.json()) - -// if (auth.status === 1) { -// await constructApp(accessCode) -// authModal.modalExit() -// } else { -// new Popup("error", "Wrong Access Code") -// } -// } -// })() - -// async function constructApp(accessCode) { - -// } - -(async () => { - - async function loadTeams(dataset, modulesConfig) { - //get blue alliance teams - const allTeams = await fetchTeams() - //add to sidebar team list - for (const [teamNumber, team] of Object.entries(dataset.teams)) { - const teamContainer = constructTeam(teamNumber, team, allTeams) - teamList.appendChild(teamContainer) - } - - //enable sidebar if sidebar modules exist - if (modulesConfig.map(m => m.position).includes("side")) { - teamView.classList.add("side-enabled") - } else { - teamView.classList.remove("side-enabled") - } - - //get all team modules, create and store module classes, then append their placeholder containers to lists - for (const module of modulesConfig.filter(m => m.view == "team")) { - const moduleObject = new moduleClasses[module.module](module) - if (module.position == "main") { - mainList.appendChild(moduleObject.container) - } else { - sideList.appendChild(moduleObject.container) - } - modules.team.push(moduleObject) - } +async function fetchTeams() { + const teams = await fetch(`/analysis/api/teams`).then(res => res.json()) + return teams.reduce((acc, t) => { + acc[t.team_number] = t.nickname + return acc + }, {}) +} +async function loadTeamsPickList(dataset, modulesConfig) { + //get blue alliance teams + const allTeams = await fetchTeams(); + //add to sidebar team list + for (const [teamNumber, team] of Object.entries(dataset.teams)) { + const PickListTeamContainer = constructTeam(teamNumber, team, allTeams) + pickList.appendChild(PickListTeamContainer) } - function constructTeam(teamNumber, team, allTeams) { - //create and populate sidebar element - const teamContainer = createDOMElement("div", "team-container") - const teamNumDisplay = createDOMElement("div", "team-number") - teamNumDisplay.innerText = teamNumber - teamContainer.setAttribute("num", teamNumber) - teamContainer.appendChild(teamNumDisplay) - if (allTeams) { - const teamNameDisplay = createDOMElement("div", "team-name") - teamNameDisplay.innerText = allTeams[teamNumber] - teamContainer.appendChild(teamNameDisplay) - } - - //switch to team on click of sidebar team, set module data - teamContainer.addEventListener("click", async () => { - await setTeamModules(teamNumber) - displayTeam(teamContainer) - }) - - return teamContainer +} + +function constructTeam(teamNumber, team, allTeams) { + //create and populate sidebar element + const teamContainer = createDOMElement("div", "team-container") + const teamNumDisplay = createDOMElement("div", "team-number") + teamNumDisplay.innerText = teamNumber + teamContainer.setAttribute("num", teamNumber) + teamContainer.appendChild(teamNumDisplay) + if (allTeams) { + const teamNameDisplay = createDOMElement("div", "team-name") + teamNameDisplay.innerText = allTeams[teamNumber] + teamContainer.appendChild(teamNameDisplay) } + //switch to team on click of sidebar team, set module data + teamContainer.addEventListener("click", async () => { + await setTeamModules(teamNumber) + displayTeam(teamContainer) + }) - - - -}) \ No newline at end of file + return teamContainer +} \ No newline at end of file diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs index 62f3e5b1..44c5fd57 100644 --- a/src/checklist/views/index.ejs +++ b/src/checklist/views/index.ejs @@ -29,9 +29,16 @@ -
+ +
+ + +
+
+
+ - - - + \ No newline at end of file From d5759ce44cea64074e3a121e881568d62141162e Mon Sep 17 00:00:00 2001 From: alannaping Date: Tue, 24 Jan 2023 11:49:52 -0600 Subject: [PATCH 10/20] Made the buttons toggle --- src/checklist/public/css/style.css | 85 +++++------------------------- src/checklist/public/js/script.js | 6 +-- 2 files changed, 16 insertions(+), 75 deletions(-) diff --git a/src/checklist/public/css/style.css b/src/checklist/public/css/style.css index 37753d08..566e1a6b 100644 --- a/src/checklist/public/css/style.css +++ b/src/checklist/public/css/style.css @@ -193,14 +193,25 @@ body { } .team-container.selected { - background-color: var(--accent-alt); + background-color: var(--bg); border-top-width: 2px; } .team-container.selected > .team-number, .team-container.selected > .team-name { - color: var(--bg-alt); + color: var(--accent-alt); } + /* when team-container is hidden*/ + + .team-container.selected.hidden { + background-color: var(--accent-alt); + border-top-width: 2px; + } + + .team-container.selected.hidden > .team-number, .team-container.selected.hidden > .team-name { + color: var(--bg); + } + #team-list { display: flex; flex-direction: column; @@ -247,28 +258,6 @@ body { transition: visibility 0s 0s, opacity 0.3s 0s; } - #match-view-switch { - background-color: var(--bg); - border: 3px solid var(--accent); - border-radius: var(--border-radius); - font-size: 1.3rem; - width: 100%; - color: var(--text); - cursor: pointer; - transition: 0.3s color, 0.3s background-color - } - - #match-view-switch.selected { - background-color: var(--accent); - color: var(--bg-alt) - } - - #match-view { - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - } #select-teams-text { font-size: 3rem; @@ -285,20 +274,6 @@ body { align-items: center; } - #alliance-vs { - font-size: 1.5rem; - } - - .alliance-selects { - display: flex; - flex-direction: row; - gap: 8px; - } - - .alliance-selects > select { - width: 90px; - } - .select { border-radius: var(--border-radius); border: 2px solid var(--text); @@ -322,40 +297,6 @@ body { /* font-weight: 00; */ } - #match-select-container { - display: flex; - align-items: center; - gap: 10px; - } - - #match-select { - width: 100px; - } - - #alliance-module-container { - display: flex; - flex-direction: row; - width: 100%; - overflow-y: scroll; - } - - .alliance-modules { - width: 50%; - display: flex; - flex-direction: column; - align-items: center; - gap: 16px; - overflow-y: scroll; - padding: 10px 0 20px 0; - } - - .alliance-modules.hidden { - visibility: hidden; - } - - .alliance-modules > div.hidden { - display: none; - } @media screen and (max-width: 1100px) { html { diff --git a/src/checklist/public/js/script.js b/src/checklist/public/js/script.js index c3205a70..7155be83 100644 --- a/src/checklist/public/js/script.js +++ b/src/checklist/public/js/script.js @@ -41,9 +41,9 @@ function constructTeam(teamNumber, team, allTeams) { // teamContainer.addEventListener("click", async () => { - // await teamContainer.enableButton(); // only enables them currently - //document.getElementById(teamContainer).disabled = false; - teamContainer.classList.add("selected") + teamContainer.classList.toggle("hidden"); + + //teamContainer.classList.add("selected") displayTeam(teamContainer) }) From 50b82b655f803101f1ab9be4ec185d67c8ef0425 Mon Sep 17 00:00:00 2001 From: alannaping Date: Wed, 25 Jan 2023 11:58:50 -0600 Subject: [PATCH 11/20] Added scrollbar --- src/checklist/public/css/style.css | 25 ++++++++++++------------- src/checklist/public/js/script.js | 6 +----- src/checklist/views/index.ejs | 7 +++---- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/checklist/public/css/style.css b/src/checklist/public/css/style.css index 566e1a6b..cd572789 100644 --- a/src/checklist/public/css/style.css +++ b/src/checklist/public/css/style.css @@ -193,25 +193,14 @@ body { } .team-container.selected { - background-color: var(--bg); + background-color: var(--accent-alt); border-top-width: 2px; } .team-container.selected > .team-number, .team-container.selected > .team-name { - color: var(--accent-alt); - } - - /* when team-container is hidden*/ - - .team-container.selected.hidden { - background-color: var(--accent-alt); - border-top-width: 2px; - } - - .team-container.selected.hidden > .team-number, .team-container.selected.hidden > .team-name { color: var(--bg); } - + #team-list { display: flex; flex-direction: column; @@ -219,6 +208,16 @@ body { overflow: auto; padding-right: 2px; } + + #pick-list { + height: 100%; + width: 50%; + overflow-y: auto; + overflow-x: hidden; + gap: 20px; + display: flex; + flex-direction: column; + } #welcome { display: flex; diff --git a/src/checklist/public/js/script.js b/src/checklist/public/js/script.js index 7155be83..15a4fd2b 100644 --- a/src/checklist/public/js/script.js +++ b/src/checklist/public/js/script.js @@ -21,7 +21,7 @@ async function loadTeamsPickList(dataset) { //reset UI and switch to team view function displayTeam(teamContainer) { //clearInterface() - teamContainer.classList.add("selected") + teamContainer.classList.toggle("selected") showFade(teamView) } @@ -39,11 +39,7 @@ function constructTeam(teamNumber, team, allTeams) { teamContainer.appendChild(teamNameDisplay) } - // teamContainer.addEventListener("click", async () => { - teamContainer.classList.toggle("hidden"); - - //teamContainer.classList.add("selected") displayTeam(teamContainer) }) diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs index ac4493c0..f82afaab 100644 --- a/src/checklist/views/index.ejs +++ b/src/checklist/views/index.ejs @@ -29,17 +29,16 @@ - +
-
- - + + From a858523aa7581fee596781ff19277f59a0c97685 Mon Sep 17 00:00:00 2001 From: alannaping Date: Wed, 25 Jan 2023 12:03:16 -0600 Subject: [PATCH 12/20] Added temporary header --- src/checklist/public/css/style.css | 2 ++ src/checklist/views/index.ejs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/checklist/public/css/style.css b/src/checklist/public/css/style.css index cd572789..a1f7e25b 100644 --- a/src/checklist/public/css/style.css +++ b/src/checklist/public/css/style.css @@ -32,6 +32,8 @@ body { #header { background: var(--text); + height: 100px; + width: 100px; border-bottom: 0.25vh solid white; display: flex; flex-direction: row; diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs index f82afaab..28a3637e 100644 --- a/src/checklist/views/index.ejs +++ b/src/checklist/views/index.ejs @@ -29,6 +29,9 @@ + + +
From 696c5eed9815a89ad61dbdcf1cc10e3cda4a7b6d Mon Sep 17 00:00:00 2001 From: alannaping Date: Thu, 26 Jan 2023 11:41:56 -0600 Subject: [PATCH 13/20] finished formatting of checklist --- src/checklist/public/css/style.css | 31 ++++++++++++++++++++---------- src/checklist/views/index.ejs | 7 ++++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/checklist/public/css/style.css b/src/checklist/public/css/style.css index a1f7e25b..aa927b8f 100644 --- a/src/checklist/public/css/style.css +++ b/src/checklist/public/css/style.css @@ -92,14 +92,15 @@ body { } #logo { - height: 100%; + height: 95px; width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: center; grid-area: 1 / 1 / 3 / 2; - gap: 14px; + gap: 25px; + background-color: var(--accent-alt); } @@ -169,27 +170,34 @@ body { border: 2px solid var(--accent-alt); background-color: var(--bg); padding: 16px; - border-top-width: 6px; + border-top-width: 2px; display: flex; flex-direction: column; - gap: 8px; + gap: 10px; cursor: pointer; transition: 0.3s border, 0.3s background-color; + width: 75%; + justify-items: center; + } .team-container > .team-number { text-align: center; + text-justify: center; + width: 100%; color: var(--accent-alt); font-weight: 600; - font-size: 1.5rem; - line-height: 1.5rem; + font-size: 2.0rem; + line-height: 2.0rem; transition: 0.3s color; } .team-container > .team-name { text-align: center; - font-size: 1.2rem; - line-height: 1.2rem; + text-justify: center; + width: 100%; + font-size: 1.8rem; + line-height: 1.6rem; color: var(--text); transition: 0.3s color; } @@ -208,17 +216,20 @@ body { flex-direction: column; gap: 20px; overflow: auto; - padding-right: 2px; + padding-right: 20px; + padding-left:20px; } #pick-list { height: 100%; - width: 50%; + width: 100%; overflow-y: auto; overflow-x: hidden; gap: 20px; display: flex; flex-direction: column; + padding-top: 20px; + align-items: center; } #welcome { diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs index 28a3637e..be9d2793 100644 --- a/src/checklist/views/index.ejs +++ b/src/checklist/views/index.ejs @@ -30,7 +30,12 @@ - + + +
From 187d99a3a398d80dd5db85c21a2e3de93a8db316 Mon Sep 17 00:00:00 2001 From: alannaping Date: Thu, 26 Jan 2023 11:50:10 -0600 Subject: [PATCH 14/20] Started documentation --- src/checklist/public/js/elements.js | 10 ---------- src/checklist/public/js/script.js | 14 +++++++------- src/checklist/views/index.ejs | 8 +------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/checklist/public/js/elements.js b/src/checklist/public/js/elements.js index f216bf1a..e8fca351 100644 --- a/src/checklist/public/js/elements.js +++ b/src/checklist/public/js/elements.js @@ -1,12 +1,2 @@ -const spinner = document.getElementById("spinner") -const app = document.getElementById("app") -const loadingBar = document.getElementById("loading-bar") const header = document.getElementById("header") -const sidebar = document.getElementById("sidebar") const pickList = document.getElementById("pick-list") -const teamView = document.getElementById("team-view") -const matchView = document.getElementById("match-view") -const welcomeView = document.getElementById("welcome") -const mainList = document.getElementById("main-list") -const sideList = document.getElementById("side-list") -const searchInput = document.getElementById("search-input") diff --git a/src/checklist/public/js/script.js b/src/checklist/public/js/script.js index 15a4fd2b..7f8c3041 100644 --- a/src/checklist/public/js/script.js +++ b/src/checklist/public/js/script.js @@ -1,5 +1,5 @@ - +//get teams from the team list async function fetchTeams() { const teams = await fetch(`/analysis/api/teams`).then(res => res.json()) return teams.reduce((acc, t) => { @@ -10,7 +10,7 @@ async function fetchTeams() { async function loadTeamsPickList(dataset) { //get blue alliance teams const allTeams = await fetchTeams(); - //add to sidebar team list + //add to pick list for (const [teamNumber, team] of Object.entries(dataset.teams)) { const PickListTeamContainer = constructTeam(teamNumber, team, allTeams) pickList.appendChild(PickListTeamContainer) @@ -18,17 +18,16 @@ async function loadTeamsPickList(dataset) { } -//reset UI and switch to team view +//makes buttons toggleable function displayTeam(teamContainer) { - //clearInterface() teamContainer.classList.toggle("selected") showFade(teamView) } - +// create team containers function constructTeam(teamNumber, team, allTeams) { - const teamContainer = createDOMElement("button", "team-container") // need to activate the button + const teamContainer = createDOMElement("button", "team-container") const teamNumDisplay = createDOMElement("div", "team-number") teamNumDisplay.innerText = teamNumber teamContainer.setAttribute("num", teamNumber) @@ -38,7 +37,7 @@ function constructTeam(teamNumber, team, allTeams) { teamNameDisplay.innerText = allTeams[teamNumber] teamContainer.appendChild(teamNameDisplay) } - + // listens for button clicked teamContainer.addEventListener("click", async () => { displayTeam(teamContainer) }) @@ -46,6 +45,7 @@ function constructTeam(teamNumber, team, allTeams) { return teamContainer } +// grabs data set async function fetchDataset() { return await fetch("/analysis/api/dataset").then(res => res.json()) } diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs index be9d2793..c1e3cd0d 100644 --- a/src/checklist/views/index.ejs +++ b/src/checklist/views/index.ejs @@ -29,21 +29,15 @@ - + - -
-
-
- -
From 1f5da4d16a8ee9779d6bb3320a951eec44496f8e Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Fri, 27 Jan 2023 01:38:14 +0000 Subject: [PATCH 15/20] almost done --- config/analysis-modules.json | 353 +++++++++++++++++---- config/analysis-pipeline.json | 374 +++++++++++++++++++++-- config/analysis-pipeline2.json | 4 - config/match-scouting.json | 101 +++++- src/analysis/modules/Grid/index.js | 19 +- src/analysis/modules/Grid/style.css | 14 +- src/analysis/public/css/style.css | 2 +- src/analysis/transformers/countHybrid.js | 4 +- src/analysis/transformers/cycle.js | 2 +- src/analysis/transformers/map.js | 1 + src/scouting/public/js/match-scouting.js | 2 +- 11 files changed, 761 insertions(+), 115 deletions(-) diff --git a/config/analysis-modules.json b/config/analysis-modules.json index 4590df16..54e03a74 100644 --- a/config/analysis-modules.json +++ b/config/analysis-modules.json @@ -35,15 +35,56 @@ "path":"averages.platformFall", "decimals":2, "sort":1 - } + }, + { + "name":"Engage Time", + "path":"dockCycle.averageTime", + "decimals":2, + "sort":-1, + "multiplier":0.001, + "unit":" sec" + } ] } }, + { + "view":"team", + "module":"Stats", + "position":"side", + "name":"Cycle Times", + "options":{ + "list":[ + { + "name":"Overall", + "path":"cycle.averageTime", + "decimals":2, + "sort":-1, + "multiplier":0.001, + "unit":" sec" + },{ + "name":"Cone", + "path":"coneCycle.averageTime", + "decimals":2, + "sort":-1, + "multiplier":0.001, + "unit":" sec" + }, + { + "name":"Cube", + "path":"cubeCycle.averageTime", + "decimals":2, + "sort":-1, + "multiplier":0.001, + "unit":" sec" + } + ] + } + }, { "view":"team", "module": "Pie", "position":"main", - "name":"Cone Pickups", + "name":"Avg Cone Pickups", "options":{ "slices":[ { @@ -73,7 +114,7 @@ "view":"team", "module": "Pie", "position":"main", - "name":"Cube Pickups", + "name":"Avg Cube Pickups", "options":{ "slices":[ { @@ -103,175 +144,369 @@ "view":"team", "module":"Grid", "position":"main", - "name":"Placements", + "name":"Avg Auto Placements", "options":{ "rows":3, "cols":9, + "decimals":2, + "min":0, + "max":1, "cells":[ { "x":1, "y":1, - "path":"averages.placement31", - "class":"yellow" + "path":"avgAuto.placement31", + "hex":"#f2d53e" }, { "x":2, "y":1, - "path":"averages.placement32", - "class":"purple" + "path":"avgAuto.placement32", + "hex":"#a65de4" }, { "x":3, "y":1, - "path":"averages.placement33", - "class":"yellow" + "path":"avgAuto.placement33", + "hex":"#f2d53e" }, { "x":4, "y":1, - "path":"averages.placement34", - "class":"yellow" + "path":"avgAuto.placement34", + "hex":"#f2d53e" }, { "x":5, "y":1, - "path":"averages.placement35", - "class":"purple" + "path":"avgAuto.placement35", + "hex":"#a65de4" }, { "x":6, "y":1, - "path":"averages.placement36", - "class":"yellow" + "path":"avgAuto.placement36", + "hex":"#f2d53e" }, { "x":7, "y":1, - "path":"averages.placement37", - "class":"yellow" + "path":"avgAuto.placement37", + "hex":"#f2d53e" }, { "x":8, "y":1, - "path":"averages.placement38", - "class":"purple" + "path":"avgAuto.placement38", + "hex":"#a65de4" }, { "x":9, "y":1, - "path":"averages.placement39", - "class":"yellow" + "path":"avgAuto.placement39", + "hex":"#f2d53e" }, { "x":1, "y":2, - "path":"averages.placement21", - "class":"yellow" + "path":"avgAuto.placement21", + "hex":"#f2d53e" }, { "x":2, "y":2, - "path":"averages.placement22", - "class":"purple" + "path":"avgAuto.placement22", + "hex":"#a65de4" }, { "x":3, "y":2, - "path":"averages.placement23", - "class":"yellow" + "path":"avgAuto.placement23", + "hex":"#f2d53e" }, { "x":4, "y":2, - "path":"averages.placement24", - "class":"yellow" + "path":"avgAuto.placement24", + "hex":"#f2d53e" }, { "x":5, "y":2, - "path":"averages.placement25", - "class":"purple" + "path":"avgAuto.placement25", + "hex":"#a65de4" }, { "x":6, "y":2, - "path":"averages.placement26", - "class":"yellow" + "path":"avgAuto.placement26", + "hex":"#f2d53e" }, { "x":7, "y":2, - "path":"averages.placement27", - "class":"yellow" + "path":"avgAuto.placement27", + "hex":"#f2d53e" }, { "x":8, "y":2, - "path":"averages.placement28", - "class":"purple" + "path":"avgAuto.placement28", + "hex":"#a65de4" }, { "x":9, "y":2, - "path":"averages.placement29", - "class":"yellow" + "path":"avgAuto.placement29", + "hex":"#f2d53e" }, { "x":1, "y":3, - "path":"averages.placement11", - "class":"gray" + "path":"avgAuto.placement11", + "hex":"#888888" }, { "x":2, "y":3, - "path":"averages.placement12", - "class":"gray" + "path":"avgAuto.placement12", + "hex":"#888888" }, { "x":3, "y":3, - "path":"averages.placement13", - "class":"gray" + "path":"avgAuto.placement13", + "hex":"#888888" }, { "x":4, "y":3, - "path":"averages.placement14", - "class":"gray" + "path":"avgAuto.placement14", + "hex":"#888888" }, { "x":5, "y":3, - "path":"averages.placement15", - "class":"gray" + "path":"avgAuto.placement15", + "hex":"#888888" }, { "x":6, "y":3, - "path":"averages.placement16", - "class":"gray" + "path":"avgAuto.placement16", + "hex":"#888888" }, { "x":7, "y":3, - "path":"averages.placement17", - "class":"gray" + "path":"avgAuto.placement17", + "hex":"#888888" }, { "x":8, "y":3, - "path":"averages.placement18", - "class":"gray" + "path":"avgAuto.placement18", + "hex":"#888888" }, { "x":9, "y":3, - "path":"averages.placement19", - "class":"gray" + "path":"avgAuto.placement19", + "hex":"#888888" } ] } - } + }, + { + "view":"team", + "module":"Grid", + "position":"main", + "name":"Avg Teleop Placements", + "options":{ + "rows":3, + "cols":9, + "min":0, + "max":1, + "decimals":2, + "cells":[ + { + "x":1, + "y":1, + "path":"avgTeleop.placement31", + "hex":"#f2d53e" + + }, + { + "x":2, + "y":1, + "path":"avgTeleop.placement32", + "hex":"#a65de4" + }, + { + "x":3, + "y":1, + "path":"avgTeleop.placement33", + "hex":"#f2d53e" + }, + { + "x":4, + "y":1, + "path":"avgTeleop.placement34", + "hex":"#f2d53e" + }, + { + "x":5, + "y":1, + "path":"avgTeleop.placement35", + "hex":"#a65de4" + }, + { + "x":6, + "y":1, + "path":"avgTeleop.placement36", + "hex":"#f2d53e" + }, + { + "x":7, + "y":1, + "path":"avgTeleop.placement37", + "hex":"#f2d53e" + }, + { + "x":8, + "y":1, + "path":"avgTeleop.placement38", + "hex":"#a65de4" + }, + { + "x":9, + "y":1, + "path":"avgTeleop.placement39", + "hex":"#f2d53e" + }, + { + "x":1, + "y":2, + "path":"avgTeleop.placement21", + "hex":"#f2d53e" + }, + { + "x":2, + "y":2, + "path":"avgTeleop.placement22", + "hex":"#a65de4" + }, + { + "x":3, + "y":2, + "path":"avgTeleop.placement23", + "hex":"#f2d53e" + }, + { + "x":4, + "y":2, + "path":"avgTeleop.placement24", + "hex":"#f2d53e" + }, + { + "x":5, + "y":2, + "path":"avgTeleop.placement25", + "hex":"#a65de4" + }, + { + "x":6, + "y":2, + "path":"avgTeleop.placement26", + "hex":"#f2d53e" + }, + { + "x":7, + "y":2, + "path":"avgTeleop.placement27", + "hex":"#f2d53e" + }, + { + "x":8, + "y":2, + "path":"avgTeleop.placement28", + "hex":"#a65de4" + }, + { + "x":9, + "y":2, + "path":"avgTeleop.placement29", + "hex":"#f2d53e" + }, + { + "x":1, + "y":3, + "path":"avgTeleop.placement11", + "hex":"#888888" + }, + { + "x":2, + "y":3, + "path":"avgTeleop.placement12", + "hex":"#888888" + }, + { + "x":3, + "y":3, + "path":"avgTeleop.placement13", + "hex":"#888888" + }, + { + "x":4, + "y":3, + "path":"avgTeleop.placement14", + "hex":"#888888" + }, + { + "x":5, + "y":3, + "path":"avgTeleop.placement15", + "hex":"#888888" + }, + { + "x":6, + "y":3, + "path":"avgTeleop.placement16", + "hex":"#888888" + }, + { + "x":7, + "y":3, + "path":"avgTeleop.placement17", + "hex":"#888888" + }, + { + "x":8, + "y":3, + "path":"avgTeleop.placement18", + "hex":"#888888" + }, + { + "x":9, + "y":3, + "path":"avgTeleop.placement19", + "hex":"#888888" + } + ] + } + }, + { + "view": "team", + "module": "PerformanceTimePlot", + "name": "Scores Over Time", + "position": "main", + "options": { + "trackedStats": [ + "scores.auto", + "scores.teleop", + "scores.autoDock" + ] + } + } ] \ No newline at end of file diff --git a/config/analysis-pipeline.json b/config/analysis-pipeline.json index 9558d02f..1c7f6c75 100644 --- a/config/analysis-pipeline.json +++ b/config/analysis-pipeline.json @@ -68,16 +68,6 @@ "weight": 150000 } }, - { - "type": "team", - "name": "ratio", - "outputPath": "timePerCone", - "options": { - "numerator": ["temp.totalTimeMs"], - "denominator": ["counts.balls"] - }, - "divByZero": 150000 - }, { "type": "team", "name": "aggregateArray", @@ -94,16 +84,366 @@ "path": "counts" } }, + { + "type":"tmp", + "name":"actionTimeFilter", + "outputPath":"autoFilter", + "options":{ + "timeMin":135000 + } + }, + { + "type":"tmp", + "name":"actionTimeFilter", + "outputPath":"teleopFilter", + "options":{ + "timeMax":135000 + } + }, + { + "type":"tmp", + "name":"countActions", + "outputPath":"countsAuto", + "options":{ + "all":true, + "actionArrayPath":"autoFilter" + } + }, + { + "type":"tmp", + "name":"countActions", + "outputPath":"countsTeleop", + "options":{ + "all":true, + "actionArrayPath":"teleopFilter" + } + }, + { + "type":"tmp", + "name":"countHybrid", + "outputPath":"autoCounts.hybrid", + "options":{ + "pickup":["conePickupGrid","conePickupCommunity","conePickupFloor","conePickupChute","conePickupShelf","cubePickupGrid","cubePickupCommunity","cubePickupFloor","cubePickupChute","cubePickupShelf"], + "hybrid":["placement11","placement12","placement13","placement14","placement15","placement16","placement17","placement18","placement19"], + "actionArrayPath":"autoFilter" + } + }, + { + "type":"tmp", + "name":"countHybrid", + "outputPath":"teleopCounts.hybrid", + "options":{ + "pickup":["conePickupGrid","conePickupCommunity","conePickupFloor","conePickupChute","conePickupShelf","cubePickupGrid","cubePickupCommunity","cubePickupFloor","cubePickupChute","cubePickupShelf"], + "hybrid":["placement11","placement12","placement13","placement14","placement15","placement16","placement17","placement18","placement19"], + "actionArrayPath":"teleopFilter" + } + }, + { + "type":"tmp", + "name":"weightedSum", + "outputPath":"scores.auto", + "options":{ + "weightedPaths":{ + "countsAuto.placement31":6, + "countsAuto.placement32":6, + "countsAuto.placement33":6, + "countsAuto.placement34":6, + "countsAuto.placement35":6, + "countsAuto.placement36":6, + "countsAuto.placement37":6, + "countsAuto.placement38":6, + "countsAuto.placement39":6, + "countsAuto.placement21":4, + "countsAuto.placement22":4, + "countsAuto.placement23":4, + "countsAuto.placement24":4, + "countsAuto.placement25":4, + "countsAuto.placement26":4, + "countsAuto.placement27":4, + "countsAuto.placement28":4, + "countsAuto.placement29":4, + "autoCounts.hybrid.conePickupGrid":3, + "autoCounts.hybrid.conePickupCommunity":3, + "autoCounts.hybrid.conePickupFloor":3, + "autoCounts.hybrid.conePickupChute":3, + "autoCounts.hybrid.conePickupPlatform":3, + "autoCounts.hybrid.cubePickupGrid":3, + "autoCounts.hybrid.cubePickupCommunity":3, + "autoCounts.hybrid.cubePickupFloor":3, + "autoCounts.hybrid.cubePickupChute":3, + "autoCounts.hybrid.cubePickupPlatform":3 + } + } + }, + { + "type":"tmp", + "name":"weightedSum", + "outputPath":"scores.teleop", + "options":{ + "weightedPaths":{ + "countsTeleop.placement31":5, + "countsTeleop.placement32":5, + "countsTeleop.placement33":5, + "countsTeleop.placement34":5, + "countsTeleop.placement35":5, + "countsTeleop.placement36":5, + "countsTeleop.placement37":5, + "countsTeleop.placement38":5, + "countsTeleop.placement39":5, + "countsTeleop.placement21":3, + "countsTeleop.placement22":3, + "countsTeleop.placement23":3, + "countsTeleop.placement24":3, + "countsTeleop.placement25":3, + "countsTeleop.placement26":3, + "countsTeleop.placement27":3, + "countsTeleop.placement28":3, + "countsTeleop.placement29":3, + "teleopCounts.hybrid.conePickupGrid":2, + "teleopCounts.hybrid.conePickupCommunity":2, + "teleopCounts.hybrid.conePickupFloor":2, + "teleopCounts.hybrid.conePickupChute":2, + "teleopCounts.hybrid.conePickupPlatform":2, + "teleopCounts.hybrid.cubePickupGrid":2, + "teleopCounts.hybrid.cubePickupCommunity":2, + "teleopCounts.hybrid.cubePickupFloor":2, + "teleopCounts.hybrid.cubePickupChute":2, + "teleopCounts.hybrid.cubePickupPlatform":2 + } + } + }, + { + "type":"team", + "name":"average", + "outputPath":"avgAuto", + "options":{ + "path":"countsAuto" + } + }, { "type":"team", - "name":"test", - "outputPath":"", - "options":{} + "name":"average", + "outputPath":"avgTeleop", + "options":{ + "path":"countsTeleop" + } }, { "type":"tmp", - "name":"test", - "outputPath":"", - "options":{} - } + "name":"cycle", + "outputPath":"dockCycle", + "options":{ + "pickups":[ + "platformDocked" + ], + "scores":[ + "platformEngaged" + ], + "misses":[ + "platformLeave", + "platformFall" + ] + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "dockCycle.all", + "options": { + "path": "dockCycle.all" + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "dockCycle.allComplete", + "options": { + "path": "dockCycle.allComplete" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "dockCycle.averageTime", + "options": { + "arrayPath": "dockCycle.all", + "valuePath": "timeDifferential" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "dockCycle.averageTimeComplete", + "options": { + "arrayPath": "dockCycle.allComplete", + "valuePath": "timeDifferential" + } + }, + { + "type": "tmp", + "name": "finalActionOccurence", + "outputPath": "finalAutoDock", + "options": { + "actionArrayPath": "autoFilter", + "ids":[ + "platformFall", + "platformEngaged", + "platformLeave", + "platformDocked" + ], + "default": { + "id": "noDock", + "ts": 0 + } + } + }, + { + "type": "tmp", + "name": "map", + "outputPath": "scores.autoDock", + "options": { + "path":"finalAutoDock.id", + "map":{ + "noDock":0, + "platformDock":8, + "platformEngaged":12, + "platformFall":0, + "platformLeave":0 + } + } + }, + + + { + "type":"tmp", + "name":"cycle", + "outputPath":"coneCycle", + "options":{ + "pickups":["conePickupGrid","conePickupCommunity","conePickupFloor","conePickupChute","conePickupShelf"], + "scores":["placement31","placement21","placement33","placement23","placement34","placement24","placement36","placement26","placement37","placement27","placement39","placement29","counts.hybrid.conePickupGrid","counts.hybrid.conePickupCommunity","counts.hybrid.conePickupFloor","counts.hybrid.conePickupChute","counts.hybrid.conePickupPlatform"], + "misses":["dropCone"] + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "coneCycle.all", + "options": { + "path": "coneCycle.all" + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "coneCycle.allComplete", + "options": { + "path": "coneCycle.allComplete" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "coneCycle.averageTime", + "options": { + "arrayPath": "coneCycle.all", + "valuePath": "timeDifferential" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "coneCycle.averageTimeComplete", + "options": { + "arrayPath": "coneCycle.allComplete", + "valuePath": "timeDifferential" + } + }, + { + "type":"tmp", + "name":"cycle", + "outputPath":"cubeCycle", + "options":{ + "pickups":["cubePickupGrid","cubePickupCommunity","cubePickupFloor","cubePickupChute","cubePickupShelf","conePickupGrid","conePickupCommunity","conePickupFloor","conePickupChute","conePickupShelf"], + "scores":["placement32","placement22","placement35","placement25","placement38","placement28","counts.hybrid.cubePickupGrid","counts.hybrid.cubePickupCommunity","counts.hybrid.cubePickupFloor","counts.hybrid.cubePickupChute","counts.hybrid.cubePickupPlatform"], + "misses":["dropCube"] + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "cubeCycle.all", + "options": { + "path": "cubeCycle.all" + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "cubeCycle.allComplete", + "options": { + "path": "cubeCycle.allComplete" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "cubeCycle.averageTime", + "options": { + "arrayPath": "cubeCycle.all", + "valuePath": "timeDifferential" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "cubeCycle.averageTimeComplete", + "options": { + "arrayPath": "cubeCycle.allComplete", + "valuePath": "timeDifferential" + } + }, + { + "type":"tmp", + "name":"cycle", + "outputPath":"cycle", + "options":{ + "pickups":["cubePickupGrid","cubePickupCommunity","cubePickupFloor","cubePickupChute","cubePickupShelf","conePickupGrid","conePickupCommunity","conePickupFloor","conePickupChute","conePickupShelf"], + "scores":["placement32","placement22","placement35","placement25","placement38","placement28","counts.hybrid.cubePickupGrid","counts.hybrid.cubePickupCommunity","counts.hybrid.cubePickupFloor","counts.hybrid.cubePickupChute","counts.hybrid.cubePickupPlatform","placement31","placement21","placement33","placement23","placement34","placement24","placement36","placement26","placement37","placement27","placement39","placement29","counts.hybrid.conePickupGrid","counts.hybrid.conePickupCommunity","counts.hybrid.conePickupFloor","counts.hybrid.conePickupChute","counts.hybrid.conePickupPlatform"], + "misses":["dropCube","dropCone"] + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "cycle.all", + "options": { + "path": "cycle.all" + } + }, + { + "type": "team", + "name": "aggregateArray", + "outputPath": "cycle.allComplete", + "options": { + "path": "cycle.allComplete" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "cycle.averageTime", + "options": { + "arrayPath": "cycle.all", + "valuePath": "timeDifferential" + } + }, + { + "type": "team", + "name": "averageArray", + "outputPath": "cycle.averageTimeComplete", + "options": { + "arrayPath": "cycle.allComplete", + "valuePath": "timeDifferential" + } + } ] \ No newline at end of file diff --git a/config/analysis-pipeline2.json b/config/analysis-pipeline2.json index d0b67b74..93b952a3 100644 --- a/config/analysis-pipeline2.json +++ b/config/analysis-pipeline2.json @@ -150,9 +150,6 @@ "divByZero": 0 } }, - - - { "type": "tmp", "name": "actionTime", @@ -214,7 +211,6 @@ } } }, - { "type": "tmp", "name": "actionTimeFilter", diff --git a/config/match-scouting.json b/config/match-scouting.json index 4d81e120..0c68ac65 100644 --- a/config/match-scouting.json +++ b/config/match-scouting.json @@ -2,14 +2,6 @@ "timing": { "totalTime": 150000, "timeTransitions": { - "149990": { - "layer": 1, - "displayText": "Auto" - }, - "135000": { - "layer": 1, - "displayText": "Teleop" - } } }, "layout": { @@ -20,9 +12,33 @@ { "id": "startGame", "displayText": "Start", - "gridArea": ["3", "2", "5", "9"], + "gridArea": ["4", "1", "6", "10"], "class": "silver timer", "type": "match-control", + "executables": [{"type": "layer", "args":[0,1]}] + }, + { + "id": "prePickupCone", + "displayText": "Cone", + "gridArea": ["2", "1", "4", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[0,7]}] + }, + { + "id": "prePickupCube", + "displayText": "Cube", + "gridArea": ["2", "7", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[0,8]}] + }, + { + "id": "prePickupNone", + "displayText": "None", + "gridArea": ["2", "4", "4", "7"], + "class": "gray", + "type": "action", "executables": [] } ], @@ -36,8 +52,8 @@ "executables": [] }, { - "id": "leftCommunity", - "displayText": "Leave Community", + "id": "crossCommunity", + "displayText": "Cross Community", "gridArea": ["2", "4", "3", "7"], "class": "gray", "type": "action", @@ -262,8 +278,8 @@ "executables": [] }, { - "id": "leftCommunity", - "displayText": "Leave Community", + "id": "crossCommunity", + "displayText": "Cross Community", "gridArea": ["2", "4", "3", "7"], "class": "gray", "type": "action", @@ -489,8 +505,8 @@ "executables": [] }, { - "id": "leftCommunity", - "displayText": "Leave Community", + "id": "crossCommunity", + "displayText": "Cross Community", "gridArea": ["2", "4", "3", "7"], "class": "gray", "type": "action", @@ -656,7 +672,62 @@ "type": "action", "executables": [{"type": "layer", "args":[6,1]}] } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["4", "1", "6", "10"], + "class": "silver timer", + "type": "match-control", + "executables": [{"type": "layer", "args":[7,5]}] + }, + + { + "id": "prePickupCube", + "displayText": "Cube", + "gridArea": ["2", "7", "4", "10"], + "class": "highlight", + "type": "action", + "executables": [{"type": "layer", "args":[7,8]}] + }, + { + "id": "prePickupNone", + "displayText": "None", + "gridArea": ["2", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[7,0]}] + } + ], + [ + { + "id": "startGame", + "displayText": "Start", + "gridArea": ["4", "1", "6", "10"], + "class": "silver timer", + "type": "match-control", + "executables": [{"type": "layer", "args":[8,6]}] + }, + + { + "id": "prePickupCone", + "displayText": "Cone", + "gridArea": ["2", "1", "4", "4"], + "class": "yellow", + "type": "action", + "executables": [{"type": "layer", "args":[8,7]}] + }, + { + "id": "prePickupNone", + "displayText": "None", + "gridArea": ["2", "4", "4", "7"], + "class": "gray", + "type": "action", + "executables": [{"type": "layer", "args":[8,0]}] + } ] + ] } } \ No newline at end of file diff --git a/src/analysis/modules/Grid/index.js b/src/analysis/modules/Grid/index.js index 4ab16234..fd7aa1ad 100644 --- a/src/analysis/modules/Grid/index.js +++ b/src/analysis/modules/Grid/index.js @@ -16,27 +16,34 @@ class Grid { let newObj = { x:cell.x, y:cell.y, - data:getPath(dataset.teams[teams[0]],cell.path,'/'), - class:cell.class, + data:getPath(dataset.teams[teams[0]],cell.path,'/').toFixed(this.moduleConfig.options.decimals), + hex:cell.hex, } return newObj }) data.cells = mapped return data } +/* +*/ setData(data) { var title = createDOMElement('div','header'); title.innerHTML = data.title this.container.appendChild(title) var gridContainer = createDOMElement('div','table') gridContainer.style.display = 'grid'; - gridContainer.style.gridTemplateRows = `repeat(${data.rows},1fr`; - gridContainer.style.gridTemplateColumns = `repeat(${data.cols},1fr`; + gridContainer.style.gridTemplateRows = `repeat(${data.rows},1fr)`; + gridContainer.style.gridTemplateColumns = `repeat(${data.cols},1fr)`; this.container.appendChild(gridContainer) + var slope = 225/(this.moduleConfig.options.max - this.moduleConfig.options.min) + for(let cell of data.cells){ - var cellDiv = createDOMElement('div',cell.class) - cellDiv.innerHTML = cell.data + var opacity = Math.min(30 + slope * (cell.data-this.moduleConfig.options.min),255) + let newHex = cell.hex + Math.floor(opacity).toString(16) + var cellDiv = createDOMElement('div','cell') + cellDiv.style.backgroundColor = newHex; + cellDiv.innerHTML = cell.data; cellDiv.style.gridArea = `${cell.y}/${cell.x}/${cell.y+1}/${cell.x+1}` gridContainer.appendChild(cellDiv) } diff --git a/src/analysis/modules/Grid/style.css b/src/analysis/modules/Grid/style.css index 380de557..c3e46157 100644 --- a/src/analysis/modules/Grid/style.css +++ b/src/analysis/modules/Grid/style.css @@ -17,15 +17,9 @@ text-align:center; font-size: 2em; } +.cell{ + text-align:center; + box-sizing: border-box; + border-radius: calc(var(--border-radius) / 2); -.gray { - background-color: #c7c7c7; -} - -.yellow { - background-color: #f4e180 -} - -.purple{ - background-color: #ddb3ff; } \ No newline at end of file diff --git a/src/analysis/public/css/style.css b/src/analysis/public/css/style.css index 9ec53e4f..e87b2ed0 100644 --- a/src/analysis/public/css/style.css +++ b/src/analysis/public/css/style.css @@ -58,7 +58,7 @@ body { } #team-view.side-enabled { - grid-template-columns: 80% 20%; + grid-template-columns: 75% 25%; } #main-list { diff --git a/src/analysis/transformers/countHybrid.js b/src/analysis/transformers/countHybrid.js index 6b3893b0..2fb3ad12 100644 --- a/src/analysis/transformers/countHybrid.js +++ b/src/analysis/transformers/countHybrid.js @@ -7,15 +7,17 @@ module.exports = { * @type {DataTransformer} * @param options.pickup {String[]} pickup ids for pieces * @param options.hybrid {String[]} ids of hybrid slots + * @param options.actionArrayPath {String} path to array of actions */ tmp: new DataTransformer("countHybrid", (dataset, outputPath, options) => { + var actionArrayPath = options.actionArrayPath || "actionQueue" for(let tmp of dataset.tmps){ var heldPiece = ""; var placements= {} for(let id of options.pickup){ placements[id] = 0; } - for(let action of getPath(tmp,"actionQueue")){ + for(let action of getPath(tmp,actionArrayPath)){ if(options.pickup.includes(action.id)){ heldPiece = action.id } diff --git a/src/analysis/transformers/cycle.js b/src/analysis/transformers/cycle.js index f88622a6..fc661663 100644 --- a/src/analysis/transformers/cycle.js +++ b/src/analysis/transformers/cycle.js @@ -52,7 +52,7 @@ module.exports = { //counts out.cycleCount = out.all.length; out.cycleCountComplete = out.allComplete.length; - + setPath(tmp,outputPath,out); } diff --git a/src/analysis/transformers/map.js b/src/analysis/transformers/map.js index 9c001d25..9ec7ee46 100644 --- a/src/analysis/transformers/map.js +++ b/src/analysis/transformers/map.js @@ -10,6 +10,7 @@ module.exports = { */ tmp: new DataTransformer("map", (dataset, outputPath, options) => { for (let tmp of dataset.tmps) { + console.log(getPath(tmp, options.path,"")) setPath(tmp, outputPath, options.map[getPath(tmp, options.path,"")]) //default to an empty string if there is no value } return dataset; diff --git a/src/scouting/public/js/match-scouting.js b/src/scouting/public/js/match-scouting.js index 847e984c..bbc76b9a 100644 --- a/src/scouting/public/js/match-scouting.js +++ b/src/scouting/public/js/match-scouting.js @@ -124,7 +124,7 @@ let devEnd button.timerInterval = setInterval(() => { if (time <= transitions[0]) { //move to the next transition if it is time displayText = matchScoutingConfig.timing.timeTransitions[transitions[0]].displayText; - showLayer(matchScoutingConfig.timing.timeTransitions[transitions[0]].layer); + // showLayer(matchScoutingConfig.timing.timeTransitions[transitions[0]].layer); transitions.shift() } if (time <= 0) { From e1a3f12841d18ce311168e8a5084964352e74a59 Mon Sep 17 00:00:00 2001 From: alannaping Date: Fri, 27 Jan 2023 10:55:53 -0600 Subject: [PATCH 16/20] documentation and cleaned up unnecesary code --- src/checklist/public/css/style.css | 207 +---------------------- src/checklist/public/css/ui-elements.css | 124 -------------- src/checklist/views/index.ejs | 5 +- 3 files changed, 9 insertions(+), 327 deletions(-) delete mode 100644 src/checklist/public/css/ui-elements.css diff --git a/src/checklist/public/css/style.css b/src/checklist/public/css/style.css index aa927b8f..81b56b8a 100644 --- a/src/checklist/public/css/style.css +++ b/src/checklist/public/css/style.css @@ -21,15 +21,7 @@ body { transition: visibility 0s 0s, opacity 0.3s 0s; } - #sidebar { - grid-area: 3 / 1 / 4 / 2; - background-color: var(--bg-alt); - padding: 16px; - display: grid; - min-height: 0; - grid-template-rows: auto auto 1fr; - } - + /* styling for page header */ #header { background: var(--text); height: 100px; @@ -43,54 +35,7 @@ body { box-shadow: 0 0 12px -2px #ff6030; grid-area: 1 / 2 / 2 / 4; } - - #dashboard { - grid-area: 2 / 2 / 4 / 4; - overflow: hidden; - position: relative; - } - - #team-view { - height: 100%; - width: 100%; - display: grid; - grid-template-rows: 1fr; - overflow: hidden; - grid-template-columns: 1fr; - } - - #team-view.side-enabled { - grid-template-columns: 65% 35%; - } - - #main-list { - grid-area: 1 / 1 / 2 / 2; - display: flex; - flex-direction: column; - align-items: center; - gap: 20px; - overflow-y: scroll; - padding: 40px 0; - } - - #main-list > div.hidden { - display: none; - } - - #side-list { - grid-area: 1 / 2 / 2 / 3; - display: flex; - flex-direction: column; - align-items: center; - gap: 20px; - overflow-y: scroll; - padding: 40px 0; - } - - #side-list > div.hidden { - display: none; - } - + /* styling for logo */ #logo { height: 95px; width: 100%; @@ -112,59 +57,7 @@ body { color: white; font-size: 2.5rem; } - - #search { - position: relative; - } - - #search-container { - padding: 20px 0; - border-top: 1px solid var(--accent-alt); - border-bottom: 1px solid var(--accent-alt); - box-shadow: 0 -7px 10px -10px var(--accent-alt); - border-radius: 8px 8px 0 0; - margin: 20px 0; - } - - #search-input { - border-radius: var(--border-radius); - border: none; - width: 100%; - font-size: 1.2rem; - padding: 0 40px 0 12px; - background-color: var(--bg); - border: 2px solid var(--accent-alt); - } - - #search-icon { - position: absolute; - top: 50%; - right: 12px; - font-size: 1.2rem; - transform: translateY(-50%); - transition: 0.3s color; - } - - #search-input:focus + #search-icon, #search-input:valid + #search-icon { - color: var(--accent) - } - - #loading-bar-container { - position: absolute; - top: 0; - left: 0; - height: 4px; - width: 100%; - z-index: 99; - } - - #loading-bar { - background-color: var(--accent-alt); - height: 100%; - width: 0%; - transition: 0.3s all; - } - + /* styling for buttons */ .team-container { border-radius: 16px; border: 2px solid var(--accent-alt); @@ -180,7 +73,7 @@ body { justify-items: center; } - + /* team number */ .team-container > .team-number { text-align: center; text-justify: center; @@ -191,7 +84,7 @@ body { line-height: 2.0rem; transition: 0.3s color; } - + /* team name */ .team-container > .team-name { text-align: center; text-justify: center; @@ -201,7 +94,7 @@ body { color: var(--text); transition: 0.3s color; } - + /* styling for buttons when selected */ .team-container.selected { background-color: var(--accent-alt); border-top-width: 2px; @@ -210,16 +103,7 @@ body { .team-container.selected > .team-number, .team-container.selected > .team-name { color: var(--bg); } - - #team-list { - display: flex; - flex-direction: column; - gap: 20px; - overflow: auto; - padding-right: 20px; - padding-left:20px; - } - + /* styling for pick list */ #pick-list { height: 100%; width: 100%; @@ -232,83 +116,6 @@ body { align-items: center; } - #welcome { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 32px; - } - - #welcome-text { - font-size: 4rem; - line-height: 1em; - color: var(--accent-alt); - text-align: center; - } - - #welcome-subtext { - font-size: 2.5rem; - line-height: 1em; - margin-bottom: 40px; - } - - #dashboard > div { - width: 100%; - transition: 0.3s visibility, opacity; - opacity: 0; - position: absolute; - top: 0; - left: 0; - visibility: hidden; - } - - #dashboard > div.visible { - visibility: unset; - height: calc(100vh - 75px); - opacity: 1; - transition: visibility 0s 0s, opacity 0.3s 0s; - } - - - #select-teams-text { - font-size: 3rem; - line-height: 1em; - color: var(--accent-alt); - margin-top: 32px; - margin-bottom: 10px; - } - - #team-selects { - display: flex; - flex-direction: row; - gap: 16px; - align-items: center; - } - - .select { - border-radius: var(--border-radius); - border: 2px solid var(--text); - font-size: 1.2rem; - text-align: center; - color: var(--text); - background-color: var(--bg-alt); - transition: 0.2s border-color; - } - - .select > option { - text-align: center; - } - - .select.filled { - border-color: var(--accent-alt); - } - - #match-select-label { - font-size: 1.4em; - /* font-weight: 00; */ - } - @media screen and (max-width: 1100px) { html { diff --git a/src/checklist/public/css/ui-elements.css b/src/checklist/public/css/ui-elements.css deleted file mode 100644 index d1782ce7..00000000 --- a/src/checklist/public/css/ui-elements.css +++ /dev/null @@ -1,124 +0,0 @@ -.modal { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 99; - background-color: var(--bg-alt); - border-radius: 10px; - padding: 40px; - transition: 0.3s all; - opacity: 0; - visibility: hidden; - display: flex; - flex-direction: column; -} - -.modal.visible { - visibility: unset; - opacity: 1; - transition: visibility 0s 0s, opacity 0.3s 0s; -} - -.modal-blind { - position: fixed; - top: 0; - left: 0; - height: 100%;; - width: 100%; - background-color: black; - z-index: 98; - cursor: pointer; - transition: 0.3s all; - opacity: 0; - visibility: hidden; -} - -.modal-blind.visible { - visibility: unset; - opacity: 0.7; - transition: visibility 0s 0s, opacity 0.3s 0s; -} - -.modal-close { - font-size: 1.5em; - color: var(--text); - cursor: pointer; - position: absolute; - transform: translate(50%, -50%); - top: 25px; - right: 25px; -} - -.modal.large { - min-width: 800px; - min-height: 500px; -} - -.modal.medium { - min-width: 600px; - min-height: 375px; -} - -.modal.small { - min-width: 400px; - min-height: 250px; -} - -.modal.main-center { - justify-content: center; -} - -.modal.alt-center { - align-items: center; -} - -.modal img { - width: 100%; - height: 100%; - object-fit: contain; - border-radius: 10px; -} - -.modal.image > img { - height: 100%; -} - -.modal > div.header { - font-size: 2.5em; - font-weight: bold; - line-height: 1.5em; - margin-bottom: 8px; -} - -.modal > div.text { - font-size: 1.25em; -} - -.modal > button { - border: none; - background-color: var(--accent); - border-radius: 10px; - color: white; - font-size: 1.5em; - padding: 4px 20px; - margin-top: 20px; -} - -.popup { - position: fixed; - top: 0px; - left: 50%; - border-radius: 8px; - color: white; - margin: 0; - font-size: 1.4rem; - line-height: 1.75rem; - padding: 6px 18px; - text-align: center; - transition: 0.6s cubic-bezier(.76,-0.47,.24,1.47); - z-index: 100; - transform: translate(-50%, -100%); - box-shadow: 0 0 2px var(--text); - max-width: 100%; -} diff --git a/src/checklist/views/index.ejs b/src/checklist/views/index.ejs index c1e3cd0d..be0d472c 100644 --- a/src/checklist/views/index.ejs +++ b/src/checklist/views/index.ejs @@ -24,22 +24,21 @@ - + - +
- From e1882211b962d9cb69232e6189e2405c44506be2 Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:10:17 +0000 Subject: [PATCH 17/20] finished configs --- config/analysis-modules.json | 453 ++++++++++++++++++++++++++- config/analysis-pipeline.json | 68 +++- src/analysis/modules/Grid/index.js | 3 +- src/analysis/public/js/script.js | 20 +- src/analysis/transformers/average.js | 8 +- src/analysis/transformers/map.js | 1 - 6 files changed, 529 insertions(+), 24 deletions(-) diff --git a/config/analysis-modules.json b/config/analysis-modules.json index 54e03a74..9a20c941 100644 --- a/config/analysis-modules.json +++ b/config/analysis-modules.json @@ -505,8 +505,459 @@ "trackedStats": [ "scores.auto", "scores.teleop", - "scores.autoDock" + "scores.dock", + "scores.total" ] } + }, + { + "view": "match", + "module": "SingleDisplay", + "name": "Alliance Score", + "position": "main", + "options": { + "path": "averageScores.total", + "aggrMethod": "sum", + "decimals": 2 + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Auto Points", + "position": "main", + "options": { + "path": "averageScores.auto", + "sort": 1, + "decimals": 1 + } + }, + { + "view": "match", + "module": "ColumnDisplay", + "name": "Dock Points", + "position": "main", + "options": { + "path": "averageScores.dock", + "sort": 1, + "decimals": 1 + } + }, + + { + "view":"match", + "module": "Pie", + "position":"main", + "name":"Avg Cone Pickups", + "options":{ + "slices":[ + { + "name":"Grid", + "path":"averages.conePickupGrid" + }, + { + "name":"Community", + "path":"averages.conePickupCommunity" + }, + { + "name":"Floor", + "path":"averages.conePickupFloor" + }, + { + "name":"Chute", + "path":"averages.conePickupChute" + }, + { + "name":"Shelf", + "path":"averages.conePickupShelf" + } + ] + } + }, + { + "view":"match", + "module": "Pie", + "position":"main", + "name":"Avg Cube Pickups", + "options":{ + "slices":[ + { + "name":"Grid", + "path":"averages.cubePickupGrid" + }, + { + "name":"Community", + "path":"averages.cubePickupCommunity" + }, + { + "name":"Floor", + "path":"averages.cubePickupFloor" + }, + { + "name":"Chute", + "path":"averages.cubePickupChute" + }, + { + "name":"Shelf", + "path":"averages.cubePickupShelf" + } + ] + } + }, + { + "view":"match", + "module":"Grid", + "position":"main", + "name":"Avg Auto Placements", + "options":{ + "rows":3, + "cols":9, + "decimals":2, + "min":0, + "max":1, + "cells":[ + { + "x":1, + "y":1, + "path":"avgAuto.placement31", + "hex":"#f2d53e" + + }, + { + "x":2, + "y":1, + "path":"avgAuto.placement32", + "hex":"#a65de4" + }, + { + "x":3, + "y":1, + "path":"avgAuto.placement33", + "hex":"#f2d53e" + }, + { + "x":4, + "y":1, + "path":"avgAuto.placement34", + "hex":"#f2d53e" + }, + { + "x":5, + "y":1, + "path":"avgAuto.placement35", + "hex":"#a65de4" + }, + { + "x":6, + "y":1, + "path":"avgAuto.placement36", + "hex":"#f2d53e" + }, + { + "x":7, + "y":1, + "path":"avgAuto.placement37", + "hex":"#f2d53e" + }, + { + "x":8, + "y":1, + "path":"avgAuto.placement38", + "hex":"#a65de4" + }, + { + "x":9, + "y":1, + "path":"avgAuto.placement39", + "hex":"#f2d53e" + }, + { + "x":1, + "y":2, + "path":"avgAuto.placement21", + "hex":"#f2d53e" + }, + { + "x":2, + "y":2, + "path":"avgAuto.placement22", + "hex":"#a65de4" + }, + { + "x":3, + "y":2, + "path":"avgAuto.placement23", + "hex":"#f2d53e" + }, + { + "x":4, + "y":2, + "path":"avgAuto.placement24", + "hex":"#f2d53e" + }, + { + "x":5, + "y":2, + "path":"avgAuto.placement25", + "hex":"#a65de4" + }, + { + "x":6, + "y":2, + "path":"avgAuto.placement26", + "hex":"#f2d53e" + }, + { + "x":7, + "y":2, + "path":"avgAuto.placement27", + "hex":"#f2d53e" + }, + { + "x":8, + "y":2, + "path":"avgAuto.placement28", + "hex":"#a65de4" + }, + { + "x":9, + "y":2, + "path":"avgAuto.placement29", + "hex":"#f2d53e" + }, + { + "x":1, + "y":3, + "path":"avgAuto.placement11", + "hex":"#888888" + }, + { + "x":2, + "y":3, + "path":"avgAuto.placement12", + "hex":"#888888" + }, + { + "x":3, + "y":3, + "path":"avgAuto.placement13", + "hex":"#888888" + }, + { + "x":4, + "y":3, + "path":"avgAuto.placement14", + "hex":"#888888" + }, + { + "x":5, + "y":3, + "path":"avgAuto.placement15", + "hex":"#888888" + }, + { + "x":6, + "y":3, + "path":"avgAuto.placement16", + "hex":"#888888" + }, + { + "x":7, + "y":3, + "path":"avgAuto.placement17", + "hex":"#888888" + }, + { + "x":8, + "y":3, + "path":"avgAuto.placement18", + "hex":"#888888" + }, + { + "x":9, + "y":3, + "path":"avgAuto.placement19", + "hex":"#888888" + } + ] + } + }, + { + "view":"match", + "module":"Grid", + "position":"main", + "name":"Avg Teleop Placements", + "options":{ + "rows":3, + "cols":9, + "min":0, + "max":1, + "decimals":2, + "cells":[ + { + "x":1, + "y":1, + "path":"avgTeleop.placement31", + "hex":"#f2d53e" + + }, + { + "x":2, + "y":1, + "path":"avgTeleop.placement32", + "hex":"#a65de4" + }, + { + "x":3, + "y":1, + "path":"avgTeleop.placement33", + "hex":"#f2d53e" + }, + { + "x":4, + "y":1, + "path":"avgTeleop.placement34", + "hex":"#f2d53e" + }, + { + "x":5, + "y":1, + "path":"avgTeleop.placement35", + "hex":"#a65de4" + }, + { + "x":6, + "y":1, + "path":"avgTeleop.placement36", + "hex":"#f2d53e" + }, + { + "x":7, + "y":1, + "path":"avgTeleop.placement37", + "hex":"#f2d53e" + }, + { + "x":8, + "y":1, + "path":"avgTeleop.placement38", + "hex":"#a65de4" + }, + { + "x":9, + "y":1, + "path":"avgTeleop.placement39", + "hex":"#f2d53e" + }, + { + "x":1, + "y":2, + "path":"avgTeleop.placement21", + "hex":"#f2d53e" + }, + { + "x":2, + "y":2, + "path":"avgTeleop.placement22", + "hex":"#a65de4" + }, + { + "x":3, + "y":2, + "path":"avgTeleop.placement23", + "hex":"#f2d53e" + }, + { + "x":4, + "y":2, + "path":"avgTeleop.placement24", + "hex":"#f2d53e" + }, + { + "x":5, + "y":2, + "path":"avgTeleop.placement25", + "hex":"#a65de4" + }, + { + "x":6, + "y":2, + "path":"avgTeleop.placement26", + "hex":"#f2d53e" + }, + { + "x":7, + "y":2, + "path":"avgTeleop.placement27", + "hex":"#f2d53e" + }, + { + "x":8, + "y":2, + "path":"avgTeleop.placement28", + "hex":"#a65de4" + }, + { + "x":9, + "y":2, + "path":"avgTeleop.placement29", + "hex":"#f2d53e" + }, + { + "x":1, + "y":3, + "path":"avgTeleop.placement11", + "hex":"#888888" + }, + { + "x":2, + "y":3, + "path":"avgTeleop.placement12", + "hex":"#888888" + }, + { + "x":3, + "y":3, + "path":"avgTeleop.placement13", + "hex":"#888888" + }, + { + "x":4, + "y":3, + "path":"avgTeleop.placement14", + "hex":"#888888" + }, + { + "x":5, + "y":3, + "path":"avgTeleop.placement15", + "hex":"#888888" + }, + { + "x":6, + "y":3, + "path":"avgTeleop.placement16", + "hex":"#888888" + }, + { + "x":7, + "y":3, + "path":"avgTeleop.placement17", + "hex":"#888888" + }, + { + "x":8, + "y":3, + "path":"avgTeleop.placement18", + "hex":"#888888" + }, + { + "x":9, + "y":3, + "path":"avgTeleop.placement19", + "hex":"#888888" + } + ] } + } ] \ No newline at end of file diff --git a/config/analysis-pipeline.json b/config/analysis-pipeline.json index 1c7f6c75..bdbbb9b9 100644 --- a/config/analysis-pipeline.json +++ b/config/analysis-pipeline.json @@ -297,6 +297,24 @@ } } }, + { + "type": "tmp", + "name": "finalActionOccurence", + "outputPath": "finalTeleopDock", + "options": { + "actionArrayPath": "teleopFilter", + "ids":[ + "platformFall", + "platformEngaged", + "platformLeave", + "platformDocked" + ], + "default": { + "id": "noDock", + "ts": 0 + } + } + }, { "type": "tmp", "name": "map", @@ -312,8 +330,37 @@ } } }, - - + { + "type": "tmp", + "name": "map", + "outputPath": "scores.teleopDock", + "options": { + "path":"finalTeleopDock.id", + "map":{ + "noDock":0, + "platformDock":6, + "platformEngaged":10, + "platformFall":0, + "platformLeave":0 + } + } + }, + { + "type":"tmp", + "name":"sum", + "outputPath":"scores.dock", + "options":{ + "addends":["scores.autoDock","scores.teleopDock"] + } + }, + { + "type":"tmp", + "name":"sum", + "outputPath":"scores.total", + "options":{ + "addends":["scores.auto","scores.teleop","scores.dock"] + } + }, { "type":"tmp", "name":"cycle", @@ -349,15 +396,6 @@ "valuePath": "timeDifferential" } }, - { - "type": "team", - "name": "averageArray", - "outputPath": "coneCycle.averageTimeComplete", - "options": { - "arrayPath": "coneCycle.allComplete", - "valuePath": "timeDifferential" - } - }, { "type":"tmp", "name":"cycle", @@ -445,5 +483,13 @@ "arrayPath": "cycle.allComplete", "valuePath": "timeDifferential" } + }, + { + "type": "team", + "name": "average", + "outputPath": "averageScores", + "options": { + "path": "scores" + } } ] \ No newline at end of file diff --git a/src/analysis/modules/Grid/index.js b/src/analysis/modules/Grid/index.js index fd7aa1ad..89a1f912 100644 --- a/src/analysis/modules/Grid/index.js +++ b/src/analysis/modules/Grid/index.js @@ -16,7 +16,7 @@ class Grid { let newObj = { x:cell.x, y:cell.y, - data:getPath(dataset.teams[teams[0]],cell.path,'/').toFixed(this.moduleConfig.options.decimals), + data:teams.map((team)=>{let data = getPath(dataset.teams[team],cell.path,0).toFixed(this.moduleConfig.options.decimals);console.log(data);return data }).reduce((acc, i) => acc + parseFloat(i), 0).toFixed(this.moduleConfig.options.decimals), hex:cell.hex, } return newObj @@ -28,6 +28,7 @@ class Grid { */ setData(data) { + this.container.innerHTML=""; var title = createDOMElement('div','header'); title.innerHTML = data.title this.container.appendChild(title) diff --git a/src/analysis/public/js/script.js b/src/analysis/public/js/script.js index 5e0310f2..9d9d363c 100644 --- a/src/analysis/public/js/script.js +++ b/src/analysis/public/js/script.js @@ -53,8 +53,11 @@ if ('serviceWorker' in navigator) { const allTeams = await fetchTeams() //add to sidebar team list for (const [teamNumber, team] of Object.entries(dataset.teams)) { - const teamContainer = constructTeam(teamNumber, team, allTeams) - teamList.appendChild(teamContainer) + // console.log(`team: ${Object.keys(team)}\n num: ${teamNumber}\n allTeams: ${allTeams[teamNumber]}`) + if(allTeams[teamNumber]){ + const teamContainer = constructTeam(teamNumber, team, allTeams) + teamList.appendChild(teamContainer) + } } //enable sidebar if sidebar modules exist @@ -136,14 +139,17 @@ if ('serviceWorker' in navigator) { //get all dropdowns const teamSelects = Array.from(document.querySelectorAll(".alliance-selects")).map(g => Array.from(g.children)).flat() - + const allTeams = await fetchTeams() //populate dropdowns with team numbers for (const teamSelect of teamSelects) { for (const team of Object.keys(dataset.teams)) { - const option = createDOMElement("option") - option.innerText = team - option.value = team - teamSelect.appendChild(option) + // console.log(team) + if(allTeams[team]){ + const option = createDOMElement("option") + option.innerText = team + option.value = team + teamSelect.appendChild(option) + } } //on dropdown change diff --git a/src/analysis/transformers/average.js b/src/analysis/transformers/average.js index b83ea398..0316f068 100644 --- a/src/analysis/transformers/average.js +++ b/src/analysis/transformers/average.js @@ -10,11 +10,13 @@ module.exports = { for (const [teamNumber, team] of Object.entries(dataset.teams)) { const teamTmps = dataset.tmps.filter(x=>x.robotNumber == teamNumber); //only the tmps that are this team's const pathResult = getPath(teamTmps[0], options.path) - if (typeof pathResult == "object" && pathResult !== null) { //average all properties in object + + if (typeof pathResult == "object" && pathResult !== null) { //average all properties in object let out = {}; for (let subpath in getPath(teamTmps[0], options.path)) { - const filteredTeamTmps = teamTmps.filter((tmp) => getPath(tmp, `${options.path}.${subpath}`) !== null) - let average = filteredTeamTmps.reduce((acc, tmp) => { + + const filteredTeamTmps = teamTmps.filter((tmp) => getPath(tmp, `${options.path}.${subpath}`,null) !== null) + let average = filteredTeamTmps.reduce((acc, tmp) => { return acc + getPath(tmp, `${options.path}.${subpath}`) //if this is causing an error, your tmps may not have the same schema (eg. some keys (which you are trying to average) are not defined in some tmps) }, 0) / filteredTeamTmps.length; out[subpath] = average; diff --git a/src/analysis/transformers/map.js b/src/analysis/transformers/map.js index 9ec7ee46..9c001d25 100644 --- a/src/analysis/transformers/map.js +++ b/src/analysis/transformers/map.js @@ -10,7 +10,6 @@ module.exports = { */ tmp: new DataTransformer("map", (dataset, outputPath, options) => { for (let tmp of dataset.tmps) { - console.log(getPath(tmp, options.path,"")) setPath(tmp, outputPath, options.map[getPath(tmp, options.path,"")]) //default to an empty string if there is no value } return dataset; From 494739e7e10f90da86e692108b3bb0713e2f600b Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:44:09 +0000 Subject: [PATCH 18/20] fixed readme and removed unnecessary button in match scouting --- README.md | 229 ++++++++++--------------------------- config/match-scouting.json | 27 +---- 2 files changed, 61 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index 1ddbf469..487b8d7d 100644 --- a/README.md +++ b/README.md @@ -1,177 +1,64 @@ -# Configuring a Repl +# SPOT - Scouting Platforms On Time +SPOT is an open-source modular scouting app framework for FRC developed by Team 3061 Huskie Robotics. SPOT provides a simple platform upon which a team can build a scouting app with little to no prior experience. -Every new repl comes with a `.replit` and a `replit.nix` file that let you configure your repl to do just about anything in any language! +## Features -### `replit.nix` +* Easy to use platform for data entry throughout matches. +* Analysis page to display detailed statistics and charts about matches and teams. +* Admin view for live scouter management at competition. +* Preconfigured for the 2022 Rapid React game with no additional game-specific customization required. +* Easy deployment experience when teams run a server on Glitch +* Optimized for teams who have internet access while scouting, completely functional for teams with no internet access. +* Quick setup with a built-in first-run wizard to walk teams through configuring their scouting app, connecting to The Blue Alliance, and setting up their database. +* Completely configurable analysis and scouting view without the need for a single line of code. -Every new repl is now a Nix repl, which means you can install any package available on Nix, and support any number of languages in a single repl. You can search for a list of available packages [here](https://search.nixos.org/packages). +SPOT is built with HTML, JS, CSS, and Node.js and operates with a MongoDB database. -The `replit.nix` file should look something like the example below. The `deps` array specifies which Nix packages you would like to be available in your environment. +### SPOT is designed for a wide range of teams with a wide range of expertise and requirements: -```nix -{ pkgs }: { - deps = [ - pkgs.cowsay - ]; -} -``` -### Learn More About Nix +1. A team that is new to scouting and isn't sure what information to gather or how to analyze their data. SPOT comes as a preconfigured app that can be deployed with minimal setup and will provide all the basic functionality needed by most teams. +2. A team that has some experience with scouting and wants to collect specific data that may not be part of the default configuration but that doesn't have significant programming experience. SPOT can be configured by modifying configuration files rather than by writing code. +3. A team that has experience with scouting and wants to perform custom analysis operations or advanced custom scouting interfaces and has some programming experience. SPOT can utilize user-provided analysis modules to extend its functionality beyond the default configuration without requiring teams to modify the base code. +4. A team that has significant experience with scouting and significant programming expertise. SPOT is open source, documented, and designed to extensible such that teams can start from this code base and build their own custom scouting system. -If you'd like to learn more about Nix, here are some great resources: - -#### Written Guides -- [Getting started with Nix](/programming-ide/getting-started-nix) — Our own getting started guide -- [Building with Nix on Replit](https://docs.replit.com/tutorials/30-build-with-nix) — Deploy a production web stack on Replit with Nix -- [Nix Pills](https://nixos.org/guides/nix-pills/) — Guided introduction to Nix -- [Nix Package Manager Guide](https://nixos.org/manual/nix/stable/) — A comprehensive guide of the Nix Package Manager -- [A tour of Nix](https://nixcloud.io/tour) — Learn the nix language itself - -#### Video Guides -- [Nixology](https://www.youtube.com/playlist?list=PLRGI9KQ3_HP_OFRG6R-p4iFgMSK1t5BHs) — A series of videos introducing Nix in a practical way -- [Taking the Nix pill](https://www.youtube.com/watch?v=QwLWIy2KleE) — An introduction to what Nix is, how it works, and a walkthrough of publishing several new languages to Replit within an hour. -- [Nix: A Deep Dive](https://www.youtube.com/watch?v=TsZte_9GfPE) — A deep dive on Nix: what Nix is, why you should use it, and how it works. - - -### `.replit` - -The `.replit` file allows you to configure many options for your repl, most basic of which is the `run` command. - -Check out how to use the `.replit` file to configure a repl to enable [Clojure](https://clojure.org): - - - -`.replit` files follow the [toml configuration format](https://learnxinyminutes.com/docs/toml/) and look something like this: - -```toml -# The command that is executed when the run button is clicked. -run = ["cargo", "run"] - -# The default file opened in the editor. -entrypoint = "src/main.rs" - -# Setting environment variables -[env] -FOO="foo" - -# Packager configuration for the Universal Package Manager -# See https://github.com/replit/upm for supported languages. -[packager] -language = "rust" - - [packager.features] - # Enables the package search sidebar - packageSearch = true - # Enabled package guessing - guessImports = false - -# Per language configuration: language. -[languages.rust] -# The glob pattern to match files for this programming language -pattern = "**/*.rs" - - # LSP configuration for code intelligence - [languages.rust.languageServer] - start = ["rust-analyzer"] -``` - -In the code above, the strings in the array assigned to `run` are executed in order in the shell whenever you hit the "Run" button. - -The `language` configuration option helps the IDE understand how to provide features like [packaging](https://blog.replit.com/upm) and [code intelligence](https://blog.replit.com/intel). - -And the `[languages.rust]` `pattern` option is configured so that all files ending with `.rs` are treated as Rust files. The name is user-defined and doesn't have any special meaning, we could have used `[languages.rs]` instead. - -We can now set up a language server specifically for Rust. Which is what we do with the next configuration option: `[languages.rust.languageServer]`. [Language servers](https://microsoft.github.io/language-server-protocol/#:~:text=A%20Language%20Server%20is%20meant,servers%20and%20development%20tools%20communicate.) add smart features to your editor like code intelligence, go-to-definition, and documentation on hover. - -Since repls are fully configurable, you're not limited to just one language. For example, you could install Clojure and its language server using `replit.nix`, add a `[languages.clojure]` configuration option to the above `.replit` file that matched all Clojure files and have code intelligence enabled for both languages in the same repl. - -### `.replit` reference - -A `Command` can either be a string or a list of strings. If the `Command` is a string (`"node index.js"`), it will be executed via `sh -c ""`. If the Command is a list of strings (`["node", "index.js"]`), it will be directly executed with the list of strings passed as arguments. When possible, it is preferred to pass a list of strings. - -- `run` - - **Type:** `Command` - - **Description:** The command to run the repl. -- `entrypoint` - - **Type:** `string` - - **Description:** The name of the main file including the extension. This is the file that will be run, and shown by default when opening the editor. -- `onBoot` - - **Type:** `Command` - - **Description:** The command that executes after your repl has booted. -- `compile` - - **Type:** `Command` - - **Description:** The shell command to compile the repl before the `run` command. Only for compiled languages like C, C++, and Java. -- `audio` - - **Type:** `boolean` - - **Description:** Enables [system-wide audio](https://docs.replit.com/misc/playing-audio-replit) for the repl when configured to `true`. -- `language` - - **Type:** `string` - - **Description:** Reserved. During a GitHub import, this tells the workspace which language should be used when creating the repl. For new repls, this option will always be Nix, so this field should generally not be touched. -- `[env]` - - **Description:** Set environment variables. Don't put secrets here—use the Secrets tab in the left sidebar. - - **Example:** `VIRTUAL_ENV = "/home/runner/${REPL_SLUG}/venv"` -- `interpreter` - - **Description:** Specifies the interpreter, which should be a compliant [prybar binary](https://github.com/replit/prybar). - - `command` - - **Type:** `[string]` - - **Description:** This is the command that will be run to start the interpreter. It has higher precedence than the `run` command (i.e. `interpreter` command will run instead of the `run` command). - - `prompt` - - **Type:** `[byte]` - - **Description:** This is the prompt used to detect running state, if unspecified it defaults to `[0xEE, 0xA7]`. -- `[unitTest]` - - Enables unit testing to the repl. - - `language` - - **Type:** `string` - - **Description:** The language you want the unit tests to run. Supported strings: `java`, `python`, and `nodejs`. -- `[packager]` - - **Description:** Package management configuration. Learn more about installing packages [here](https://docs.replit.com/repls/packages/#DirectImports). - - `afterInstall` - - **Type:** `Command` - - **Description:** The command that is executed after a new package is installed. - - `ignoredPaths` - - **Type:** `[string]` - - **Description:** List of paths to ignore while attempting to guess packages. - - `ignoredPackages` - - **Type:** `[string]` - - **Description:** List of modules to never attempt to guess a package for, when installing packages. - - `language` - - **Type:** `string` - - **Description:** Specifies the language to use for package operations. See available languages in the [Universal Package Manager](https://github.com/replit/upm) repository. - - `[packager.features]` - - **Description:** UPM features that are supported by the specified languages. - - `packageSearch` - - **Type:** Boolean - - **Description:** When set to `true`, enables a package search panel in the sidebar. - - `guessImports` - - **Type:** Boolean - - **Description:** When set to `true`, UPM will attempt to guess which packages need to be installed prior to running the repl. -- `[languages.]` - - **Description:** Per-language configuration. The language name has no special meaning other than to allow multiple languages to be configured at once. - - `pattern` - - **Type:** `string` - - **Description:** A [glob](https://en.wikipedia.org/wiki/Glob_(programming)) used to identify which files belong to this language configuration (`**/*.js`) - - `syntax` - - **Type:** `string` - - **Description:** The language to use for syntax highlighting. - - `[languages..languageServer]` - - **Description:** Configuration for setting up [LSP](https://microsoft.github.io/language-server-protocol/) for this language. This allows for code intelligence (autocomplete, underlined errors, etc...). - - `start` - - **Type:** `Command` - - **Description:** The command used to start the LSP server for the specified language. -- `[nix]` - - **Description:** Where you specify a [Nix channel](https://nixos.wiki/wiki/Nix_channels). - - `channel` - - **Type:** `string` - - **Description:** A nix channel id. -- `[debugger]` - - **Description:** Advanced users only. See field types & docstrings [here](https://gist.github.com/Bardia95/98987c69c6970b1bb0698b863e2a84de#file-dot-replit-debugger-config-go), and in the advanced section below. - -### Example configurations -#### Beginner -##### [LaTeX](https://replit.com/@ZachAtReplit/LaTeX?v=1#.replit) -##### [Clojure](https://replit.com/@replit/Clojure?v=1#.replit) -#### Advanced -##### [Python](https://replit.com/@replit/Python?v=1) -##### [HTML, CSS, JS](https://replit.com/@replit/HTML-CSS-JS?v=1#.replit) -##### [Java](https://replit.com/@replit/Java-Beta?v=1#.replit) -##### [Node.js](https://replit.com/@replit/Nodejs?v=1#.replit) -##### [C++](https://replit.com/@replit/CPlusPlus?v=1) \ No newline at end of file + +## Guiding Principles +* SPOT's goal is to provide a universal scouting app platform that can function with any past or future FIRST Robotics game by just changing the configuration. +* SPOT does not require you to write code to configure, set up, or use SPOT. +* SPOT does not require you to be online for its most basic features (scouting, data analysis) + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. If you have any questions, feel free to contact us [spot@team3061.org](mailto:spot@team3061.org). + +### [Quickstart](https://docs.google.com/document/d/1dATXMC5U7aT0SfnYEOWFiafaeWbu8opabNglWSFCSPE/edit?usp=sharing) + +## Usage + +This is a guide on how to use SPOT as well as its features, with a text and a video walkthrough. + +### [Usage Guide](https://docs.google.com/document/d/16n0msw98T-HM7h5cdLdA946QHSB-SX_6boAxm6i8aos/edit?usp=sharing) + +## Configuration + +This is a guide to configuring SPOT. You can configure the buttons displayed in the scouting view as well as how data is transformed and displayed. + +### [Configuration Guide](https://docs.google.com/document/d/1_FHr61p2eROtALV-fx0giBmp7oqVfTxc7K5_kCuS-8Q/edit?usp=sharing) + + +## Contributing + +Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on contributing to SPOT. + +## Documentation +Documentation of the core codebase is currently partially included in files. Check back soon for in-depth documentation later! + +## Authors + +* **Dylan Schmit** - *Initial work* - [cyandev](https://github.com/cyandev) +* **Nithilan Kalidoss** - *Initial work* - [nithilan4](https://github.com/nithilan4) + +See also the list of [contributors](https://github.com/HuskieRobotics/SPOT/contributors) who participated in this project. +## License + +This project is licensed under the Apache License 2.0 - see the [LICENSE.md](LICENSE) file for details \ No newline at end of file diff --git a/config/match-scouting.json b/config/match-scouting.json index 0c68ac65..1ea2ab70 100644 --- a/config/match-scouting.json +++ b/config/match-scouting.json @@ -51,14 +51,7 @@ "type": "match-control", "executables": [] }, - { - "id": "crossCommunity", - "displayText": "Cross Community", - "gridArea": ["2", "4", "3", "7"], - "class": "gray", - "type": "action", - "executables": [] - }, + { "id": "broken", "displayText": "Disabled", @@ -277,14 +270,7 @@ "type": "match-control", "executables": [] }, - { - "id": "crossCommunity", - "displayText": "Cross Community", - "gridArea": ["2", "4", "3", "7"], - "class": "gray", - "type": "action", - "executables": [] - }, + { "id": "broken", "displayText": "Disabled", @@ -504,14 +490,7 @@ "type": "match-control", "executables": [] }, - { - "id": "crossCommunity", - "displayText": "Cross Community", - "gridArea": ["2", "4", "3", "7"], - "class": "gray", - "type": "action", - "executables": [] - }, + { "id": "broken", "displayText": "Disabled", From 7cd71851b6eef1d9e819d2da037816bff7429a8e Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Wed, 1 Feb 2023 21:59:16 +0000 Subject: [PATCH 19/20] implemented demo config for admin --- src/admin/routes/api.js | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/admin/routes/api.js b/src/admin/routes/api.js index 90b05c44..efe21c71 100644 --- a/src/admin/routes/api.js +++ b/src/admin/routes/api.js @@ -4,6 +4,7 @@ let router = Router(); const config = require("../../../config/config.json"); const { TeamMatchPerformance } = require("../../lib/db"); +const DEMO = true; router.use((req,res,next) => { if (!ScoutingSync.initialized) { @@ -14,6 +15,7 @@ router.use((req,res,next) => { }) router.get("/auth", (req, res) => { + if(!DEMO){ if (config.secrets.ACCESS_CODE === "") { res.json({status: 2}) } else if (config.secrets.ACCESS_CODE == req.headers.authorization) { @@ -21,14 +23,21 @@ router.get("/auth", (req, res) => { } else { res.json({status: 0}) } + } else { + res.json({status: 2}) + } }) router.get("/scouters", (req,res) => { + if(!DEMO){ if (req.headers.authorization === config.secrets.ACCESS_CODE) { res.json(ScoutingSync.getScouters()) } else { res.json({error: "Not Authorized"}) } + } else { + res.json(ScoutingSync.getScouters()) + } }); router.get("/data", async (req,res) => { @@ -36,7 +45,8 @@ router.get("/data", async (req,res) => { }) -router.get("/enterMatch", (req,res) => { +router.get("/enterMatch", async (req,res) => { + if(!DEMO){ if (req.headers.authorization === config.secrets.ACCESS_CODE) { for (let scouter of ScoutingSync.scouters) { if (scouter.state.status == ScoutingSync.SCOUTER_STATUS.WAITING) @@ -47,8 +57,21 @@ router.get("/enterMatch", (req,res) => { } else { res.json({error: "Not Authorized"}) } + } else { + let test = await ScoutingSync.getMatches() + res.json({ + "allMatches": test, + "currentMatch": ScoutingSync.match + }) + for (let scouter of ScoutingSync.scouters) { + if (scouter.state.status == ScoutingSync.SCOUTER_STATUS.WAITING) + scouter.socket.emit("enterMatch"); + } + res.json(true); + } }) router.post("/setMatch", (req,res) => { + if(!DEMO){ if (req.headers.authorization === config.secrets.ACCESS_CODE) { ScoutingSync.setMatch(req.body); ScoutingSync.assignScouters(); @@ -56,6 +79,11 @@ router.post("/setMatch", (req,res) => { } else { res.json({error: "Not Authorized"}) } + } else { + ScoutingSync.setMatch(req.body); + ScoutingSync.assignScouters(); + res.json(true); + } }); router.get("/matches", async (req,res) => { From 375853298678495f3e35d3d18abceed90dfa0329 Mon Sep 17 00:00:00 2001 From: M4RZTI4N <52054167+maaz-zubair-99@users.noreply.github.com> Date: Wed, 1 Feb 2023 22:41:43 +0000 Subject: [PATCH 20/20] implemented demo changes for scouters --- src/scouting/scouting-sync.js | 36 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/scouting/scouting-sync.js b/src/scouting/scouting-sync.js index 42d06413..0ed4da47 100644 --- a/src/scouting/scouting-sync.js +++ b/src/scouting/scouting-sync.js @@ -9,7 +9,7 @@ const {TeamMatchPerformance} = require("../lib/db.js"); const axios = require("axios"); const config = require("../../config/config.json"); const chalk = require("chalk"); - +const DEMO = true; module.exports = (server) => { if (!ScoutingSync.initialized) { if (!server) { @@ -145,18 +145,24 @@ class ScoutingSync { x.state.status == ScoutingSync.SCOUTER_STATUS.WAITING && x.state.connected); - //if anyone is scouting the match, tell all waiting scouters to start - if (ScoutingSync.scouters.filter(x=> - x.state.matchNumber == ScoutingSync.match.number && - x.state.status == ScoutingSync.SCOUTER_STATUS.SCOUTING - ).length > 0) { - for (let scouter of currentMatchWaitingScouters) { - scouter.socket.emit("enterMatch"); - } - } else if (currentMatchWaitingScouters.length >= 6) { //if there are 6 scouters waiting, enter match. - for (let scouter of currentMatchWaitingScouters) { - scouter.socket.emit("enterMatch"); - } + if(!DEMO){ + //if anyone is scouting the match, tell all waiting scouters to start + if (ScoutingSync.scouters.filter(x=> + x.state.matchNumber == ScoutingSync.match.number && + x.state.status == ScoutingSync.SCOUTER_STATUS.SCOUTING + ).length > 0) { + for (let scouter of currentMatchWaitingScouters) { + scouter.socket.emit("enterMatch"); + } + } else if (currentMatchWaitingScouters.length >= 6) { //if there are 6 scouters waiting, enter match. + for (let scouter of currentMatchWaitingScouters) { + scouter.socket.emit("enterMatch"); + } + } + } else { + for (let scouter of currentMatchWaitingScouters) { //for demo, everyone should enter immediately + scouter.socket.emit("enterMatch"); + } } } @@ -205,8 +211,12 @@ class Scouter { }) this.socket.on("syncData", async (clientTeamMatchPerformanceIds, requestTeamMatchPerformances) => { + if(!DEMO){ const serverTeamMatchPerformanceIds = (await TeamMatchPerformance.find()).map(teamMatchPerformance => teamMatchPerformance.matchId) requestTeamMatchPerformances(clientTeamMatchPerformanceIds.filter(clientTeamMatchPerformanceId => !serverTeamMatchPerformanceIds.includes(clientTeamMatchPerformanceId))) + } else { + requestTeamMatchPerformances([]) //dont request any tmps for the demo + } }) // comment out the clearData command // this.socket.on("clearData", async () => {