diff --git a/2026/rebuilt_config.js b/2026/rebuilt_config.js index 49851dfb0..ee20a3521 100644 --- a/2026/rebuilt_config.js +++ b/2026/rebuilt_config.js @@ -78,11 +78,15 @@ var config_data = ` { "name": "Fuel Scored", "code": "afs", "expectedMax": 32, + "altInc1": 10, + "altInc2": 5, "type": "counter" }, { "name": "Pass from Neutral Zone", "code": "apn", "expectedMax": 60, + "altInc1": 10, + "altInc2": 5, "type": "counter" }, { "name": "Climb (L1)", @@ -121,16 +125,22 @@ var config_data = ` { "name": "Fuel Scored", "code": "tfs", "expectedMax": 150, + "altInc1": 10, + "altInc2": 5, "type": "counter" }, { "name": "Pass from Neutral Zone", "code": "pnz", "expectedMax": 250, + "altInc1": 10, + "altInc2": 5, "type": "counter" }, { "name": "Pass from Opp
Alliance Zone", "code": "poa", "expectedMax": 250, + "altInc1": 10, + "altInc2": 5, "type": "counter" }, { "name": "Pickup from Depot", @@ -153,7 +163,7 @@ var config_data = ` "choices": { "1": "Level 1
", "2": "Level 2
", - "3": "Level 2
", + "3": "Level 3
", "a": "Attempted
", "x": "Not Attempted" }, diff --git a/README.md b/README.md index 7f29d3067..f236d325a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Scouting P.A.S.S. -#### A scouting system for FIRST FRC competitions developed by [PWNAGE - Team #2451](https://pwnagerobotics.org). +#### A scouting system for FIRST FRC competitions developed by [PWNAGE - Team #2451](https://pwnagerobotics.org) Live Demo . Pit Scouting . Getting Started . FAQ @@ -77,7 +77,7 @@ User defined fields can be of several different types: * Cycle Timer - Start the timer and with 1 click track cycle times of robots. * Field Image - Using an image of the field, select positions on the field. (Use to record starting point or shooting locations) -These should cover most of your scouting team's data collection needs. PWNAGE's 2020 Infinite Recharge configuration file is included as an example. The import of the configuration file is in index.html and would need to be updated to import a different configuration file. Only import one configuration file. +These should cover most of your scouting team's data collection needs. The REBUIT (2026) configuration file is included as an example. The import of the configuration file is in index.html and would need to be updated to import a different configuration file. Only import one configuration file. Since this is an HTML/JavaScript web page, scouters can use almost any device that has a web browser. If the device has a touchscreen the screen can be used to swipe back and forth between pages and interact with the data elements. The webpage only needs to be loaded once. Once loaded the functionality and data is stored locally in the webpage and doesn't need to be reloaded. The QR code generation and clear button only resets the form and does not cause the page to reload. This means that a cellular or WiFi connection is not needed at the competition as long as the webpage is loaded before the event. @@ -108,7 +108,7 @@ Note: For this to work, the schedule has to be posted to The Blue Alliance. Tha ## Pit Scouting: -ScountingPASS now supports Pit Scouting +ScoutingPASS now supports Pit Scouting To access the pit scouting page, add '/pit.html' to the end of your URL. (i.e. http://pwnagerobotics.github.io/ScoutingPASS/pit.html) @@ -218,6 +218,7 @@ Distributed under the GNU GPL v3.0 License. See `LICENSE` for more information. 2026 Season Updates diff --git a/docs/Configuration.md b/docs/Configuration.md index 660377a60..b2a0e763e 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -207,6 +207,9 @@ Special sub-elements of text are "match" and "team". These sub-types will updat "code": "mc", "type": "counter", "defaultValue": 0, + "altInc1": 10, + "altInc2": 5, + "expectedMax": 99, "tooltip": "Put help or more descriptive text here" } ``` @@ -214,6 +217,9 @@ The counter is displayed with two buttons labeled "-" and "+" to increase or dec Specific attributes of counter are: * defaultValue (optional) - set the field to start at a certain value +* altInc1 (optional) - Add an additional button to increment/decrement the counter by the specified amount +* altInc2 (optional) - Add a 2nd additional button to increment/decrement the counter by the specified amount +* expectedMax (optional) - used by the data generation program to generate reasonable expected values The counter element will be set back to the defaultValue value when the Clear Form button is pressed. If there is no defaultValue it will be reset to zero. @@ -304,6 +310,7 @@ There are no specific attributes to this element. "showFlip": "false", "showUndo": "false", "shape": "circle 12 black red true", + "expectedMax": 20, "cycleTimer": "tct" } ``` @@ -326,6 +333,7 @@ Specific attributes of Cycle Timer are: * fillColor - color to fill in the shape (Default value: none) * fill - if true, fill in the shape with the fillColor, otherwise do not fill in the shape * Note: Use predefined [HTML color names](https://www.w3schools.com/colors/colors_names.asp) only +* expectedMax (optional) - used by the data generation program to generate reasonable expected values * cycleTimer (optional) - tie clicks into a cycle timer to start new cycles every time the image is clicked. The element field_image has been deprecated in favor of the new more flexible clickable_image. It is currently still supported but may be removed from future releases. diff --git a/index.html b/index.html index a0b2c321a..5c616e206 100644 --- a/index.html +++ b/index.html @@ -17,7 +17,7 @@
-

PWNAGE
Scouting 2026

+

PWNAGE
Scouting PASS

pre-match

@@ -27,7 +27,7 @@

pre-match

-

PWnAGE
Scouting 2026

+

PWnAGE
Scouting PASS

Auton

@@ -37,7 +37,7 @@

Auton

-

PWnAGE
Scouting 2026

+

PWnAGE
Scouting PASS

Teleop

@@ -47,7 +47,7 @@

Teleop

-

PWnAGE
Scouting 2026

+

PWnAGE
Scouting PASS

Endgame

@@ -57,7 +57,7 @@

Endgame

-

PWnAGE
Scouting 2026

+

PWnAGE
Scouting PASS

Miscellaneous

@@ -106,3 +106,4 @@

Generate QR Code

+ diff --git a/pit.html b/pit.html index d2ec33f4f..11afb3a97 100644 --- a/pit.html +++ b/pit.html @@ -17,7 +17,7 @@
-

PWNAGE
Scouting 2020

+

PWNAGE
Scouting PASS

Pit Scouting

@@ -27,7 +27,7 @@

Pit Scouting

-

PWnAGE
Scouting 2020

+

PWnAGE
Scouting PASS

Generate QR Code

@@ -60,3 +60,4 @@

Generate QR Code

+ diff --git a/resources/js/TBAInterface.js b/resources/js/TBAInterface.js index 8887e1e4b..178528c12 100644 --- a/resources/js/TBAInterface.js +++ b/resources/js/TBAInterface.js @@ -1,4 +1,4 @@ -// TBAInterface funcitons to pull data from TheBlueAlliance.com +// TBAInterface functions to pull data from TheBlueAlliance.com var teams = null; var schedule = null; var authKey = "uTHeEfPigDp9huQCpLNkWK7FBQIb01Qrzvt4MAjh9z2WQDkrsvNE77ch6bOPvPb6"; @@ -25,7 +25,7 @@ function getTeams(eventCode) { } /** - * Get schefule for event + * Get schedule for event * * @param {eventCode} eventCode the event code (i.e. 2020caln) to pull the team list */ diff --git a/resources/js/scoutingPASS.js b/resources/js/scoutingPASS.js index 9c5517c45..ca83ba4bd 100644 --- a/resources/js/scoutingPASS.js +++ b/resources/js/scoutingPASS.js @@ -149,68 +149,123 @@ function addTimer(table, idx, name, data) { } function addCounter(table, idx, name, data) { - var row = table.insertRow(idx); - var cell1 = row.insertCell(0); - cell1.style.width = ColWidth; - cell1.classList.add("title"); + const row = table.insertRow(idx); + const hasExtraInc = data.hasOwnProperty('altInc1') || data.hasOwnProperty('altInc2'); + + // Error Handling if (!data.hasOwnProperty('code')) { - cell1.innerHTML = `Error: No code specified for ${name}`; + const errorCell = row.insertCell(0); + errorCell.classList.add("title"); + errorCell.innerHTML = `Error: No code specified for ${name}`; return idx + 1; } - var cell2 = row.insertCell(1); - cell2.style.width = ColWidth; - cell1.innerHTML = name + ' '; - if (data.hasOwnProperty('tooltip')) { - cell1.setAttribute("title", data.tooltip); - } - cell2.classList.add("field"); - var button1 = document.createElement("input"); - button1.setAttribute("type", "button"); - button1.setAttribute("id", "minus_" + data.code); - button1.setAttribute("onclick", "counter(this.parentElement, -1)"); - button1.setAttribute("value", "-"); - cell2.appendChild(button1); - - var inp = document.createElement("input"); - inp.classList.add("counter"); - inp.setAttribute("id", "input_" + data.code); - inp.setAttribute("type", "text"); - if (enableGoogleSheets && data.hasOwnProperty('gsCol')) { - inp.setAttribute("name", data.gsCol); + // Create title cell + const titleCell = row.insertCell(0); + titleCell.classList.add("title"); + if (data.hasOwnProperty('tooltip')) { + titleCell.setAttribute("title", data.tooltip); + } + + let controlCell; + + if (hasExtraInc) { + // When extra increments exist, use a single cell with colspan + titleCell.setAttribute("colspan", 2); + titleCell.style.cssText = 'text-align: center; vertical-align: middle;'; + + // Create wrapper div for flex layout + const wrapper = document.createElement("div"); + wrapper.style.cssText = 'display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%;'; + + // Create label + const label = document.createElement("div"); + label.textContent = name; + label.style.cssText = 'margin-bottom: 4px;'; + wrapper.appendChild(label); + + titleCell.appendChild(wrapper); + controlCell = wrapper; } else { - inp.setAttribute("name", data.code); + // Standard two-cell layout + titleCell.style.width = ColWidth; + titleCell.innerHTML = `${name} `; + + controlCell = row.insertCell(1); + controlCell.style.width = ColWidth; + controlCell.classList.add("field"); + controlCell.style.cssText = 'text-align: center !important; vertical-align: middle;'; + } + + // Create centered container for buttons + const centerContainer = document.createElement("div"); + centerContainer.style.cssText = 'display: flex; justify-content: center; align-items: center; width: 100%;'; + + // Create button group + const buttonGroup = document.createElement("div"); + buttonGroup.style.cssText = 'display: inline-flex; align-items: center; gap: 10px;'; + + // Helper to create input elements + const createInput = (type, id, value, incrementValue) => { + const input = document.createElement("input"); + input.type = type; + if (id) input.id = id; + if (value !== undefined) input.value = value; + if (incrementValue !== undefined) { + input.onclick = function() { + counter(this.parentElement.parentElement.parentElement, incrementValue); + }; + } + // Prevent double-tap zoom on buttons + if (type === "button") { + input.style.touchAction = 'manipulation'; + } + return input; + }; + + // Build buttons from left to right + if (data.altInc1) { + buttonGroup.appendChild(createInput("button", `minusInc1_${data.code}`, -data.altInc1, -data.altInc1)); + } + + if (data.altInc2) { + buttonGroup.appendChild(createInput("button", `minusInc2_${data.code}`, -data.altInc2, -data.altInc2)); + } + + buttonGroup.appendChild(createInput("button", `minus_${data.code}`, "-", -1)); + + // Create main counter input + const counterInput = createInput("text", `input_${data.code}`, 0); + counterInput.classList.add("counter"); + counterInput.name = (enableGoogleSheets && data.gsCol) ? data.gsCol : data.code; + counterInput.disabled = true; + counterInput.maxLength = 4; + counterInput.style.cssText = 'background-color: black; color: white; border: none; text-align: center; width: 3ch;'; + buttonGroup.appendChild(counterInput); + + buttonGroup.appendChild(createInput("button", `plus_${data.code}`, "+", 1)); + + if (data.altInc2) { + buttonGroup.appendChild(createInput("button", `plusInc2_${data.code}`, `+${data.altInc2}`, data.altInc2)); + } + + if (data.altInc1) { + buttonGroup.appendChild(createInput("button", `plusInc1_${data.code}`, `+${data.altInc1}`, data.altInc1)); } - inp.setAttribute("style", "background-color: black; color: white;border: none; text-align: center;"); - inp.setAttribute("disabled", ""); - inp.setAttribute("value", 0); - inp.setAttribute("size", 2); - inp.setAttribute("maxLength", 2); - cell2.appendChild(inp); - var button2 = document.createElement("input"); - button2.setAttribute("type", "button"); - button2.setAttribute("id", "plus_" + data.code); - button2.setAttribute("onclick", "counter(this.parentElement, 1)"); - button2.setAttribute("value", "+"); - cell2.appendChild(button2); + // Nest: centerContainer -> buttonGroup + centerContainer.appendChild(buttonGroup); + controlCell.appendChild(centerContainer); - if (data.hasOwnProperty('cycleTimer')) { - if (data.cycleTimer != "") { - inp = document.createElement('input'); - inp.setAttribute("hidden", ""); - inp.setAttribute("id", "cycleTimer_" + data.code); - inp.setAttribute("value", data.cycleTimer); - cell.appendChild(inp); - } + // Add hidden metadata fields directly to controlCell + if (data.cycleTimer) { + const timerInput = createInput("hidden", `cycleTimer_${data.code}`, data.cycleTimer); + controlCell.appendChild(timerInput); } if (data.hasOwnProperty('defaultValue')) { - var def = document.createElement("input"); - def.setAttribute("id", "default_" + data.code) - def.setAttribute("type", "hidden"); - def.setAttribute("value", data.defaultValue); - cell2.appendChild(def); + const defaultInput = createInput("hidden", `default_${data.code}`, data.defaultValue); + controlCell.appendChild(defaultInput); } return idx + 1; @@ -1432,3 +1487,24 @@ window.onload = function () { } } }; + + + + + + + + + + + + + + + + + + + + +