diff --git a/2023/CU_Pit_config.js b/2023/CU_Pit_config.js new file mode 100644 index 000000000..daddca160 --- /dev/null +++ b/2023/CU_Pit_config.js @@ -0,0 +1,89 @@ +var config_data = ` +{ + "title": "Scouting PASS 2023", + "page_title": "Charged Up", + "pitConfig": "true", + "prematch": [ + { "name": "Team Number", + "code": "t", + "type": "number" + }, + { "name": "Width", + "code": "wid", + "type": "number", + "defaultValue": "0" + }, + { "name": "Weight", + "code": "wei", + "type": "number", + "defaultValue": "0" + }, + { "name": "Drivetrain", + "code": "drv", + "type": "radio", + "choices": { + "s": "Swerve
", + "w": "West Coast/Tank
", + "b": "Butterfly/Grashopper
", + "m": "Mechanum
", + "o": "Other" + }, + "defaultValue": "o" + }, + { "name": "Other Drivetrain", + "code": "odt", + "type": "text", + "size": 20, + "maxSize": 50 + }, + { "name": "Swerve Ratio", + "code": "sr", + "type": "radio", + "choices": { + "1": "L1
", + "2": "L2
", + "3": "L3
", + "4": "L4
", + "x": "Not Swerve" + }, + "defaultValue":"x" + }, + { "name": "Drivetrain Motor", + "code": "mot", + "type": "radio", + "choices": { + "n": "Neo
", + "f": "Falcon
", + "c": "CIM
", + "x": "Other
" + }, + "defaultValue":"x" + }, + { "name": "Floor pickup Cones", + "code": "fco", + "type": "bool" + }, + { "name": "Floor pickup Cubes", + "code": "fcu", + "type": "bool" + }, + { "name": "Cross Charging Station", + "code": "ccs", + "type": "bool" + }, + { "name": "Autos", + "code": "aut", + "type": "text", + "size": 20, + "maxSize": 250 + } + ], + "auton": [ + ], + "teleop": [ + ], + "endgame": [ + ], + "postmatch": [ + ] +}`; diff --git a/Excel/2023 Excel Skeleton.xlsm b/Excel/2023 Excel Skeleton.xlsm index cf1d3d3f9..4e005810b 100644 Binary files a/Excel/2023 Excel Skeleton.xlsm and b/Excel/2023 Excel Skeleton.xlsm differ diff --git a/Excel/QRReader.bas b/Excel/QRReader.bas index 1d4e546ed..32e7d5231 100644 --- a/Excel/QRReader.bas +++ b/Excel/QRReader.bas @@ -1,23 +1,51 @@ Attribute VB_Name = "QRReader" -Sub process1QRCodeInput() + +Sub Save1QR() saveData (getInput()) + ActiveWorkbook.Save End Sub -Sub process6QRCodeInput() +Sub processHardCodedData() + saveData ("s=fudd;e=2022carv;l=qm;m=2;r=r2;t=2451;as=[35];asg=[3,4];acc=1;acs=1;am=1;ad=e;tct=[8.3,7.3,6.7,7.1,5.5,5.8,5.4];tsg=[5,6,7,8,9,1,2];tfc=0;wf=0;wd=0;who=;lnk=1;fpu=b;dt=9.9;fs=e;dn=2;ds=v;ls=5;dr=x;sd=1;sr=5;die=0;tip=0;dc=0;all=1;co=PWNAGE") + + ActiveWorkbook.Save +End Sub + +Sub processQRCodeInput() saveData (getInput()) saveData (getInput()) saveData (getInput()) saveData (getInput()) saveData (getInput()) saveData (getInput()) + ActiveWorkbook.Save +End Sub + +Sub Save1PitQR() + savePitData (getInput()) + ActiveWorkbook.Save End Sub Public Function getInput() - getInput = InputBox("Scan QR Code", "Match Scouting Input") + getInput = InputBox("Scan QR Code", "2023 Match Scouting Input") End Function +'Public Function Scaner() +' Dim addIn As COMAddIn +' Dim automationObject As Object +' Set addIn = Application.COMAddIns("QRReader") +' Set automationObject = addIn.Object +' Dim out As String +' out = automationObject.Scaner +' Scaner = out +'End Function + +Sub test() + saveData ("s=fudd;e=2022carv;l=qm;m=2;r=r2;t=2451;as=[35];asg=[3,4];acc=1;acs=1;am=1;ad=e;tct=[8.3,7.3,6.7,7.1,5.5,5.8,5.4];tsg=[5,6,7,8,9,1,2];tfc=0;wf=0;wd=0;who=;lnk=1;fpu=b;dt=9.9;fs=e;dn=2;ds=v;ls=5;dr=x;sd=1;sr=5;die=0;tip=0;dc=0;all=1;co=PWNAGE") +End Sub -Sub testSaveData() - saveData ("s=fff;e=1234;l=qm;m=1234;r=r1;t=1234;as=;ae=Y;al=2;ao=2;ai=1;aa=Y;at=N;ax=Y;lp=2;op=1;ip=3;rc=pass;f=0;pc=pass;ss=;c=pass;b=N;ca=x;cb=x;cs=slow;p=N;ds=x;dr=x;pl=x;tr=N;wd=N;if=N;d=N;to=N;be=N;cf=N") +Sub dbm(inp As String) + Dim r + r = MsgBox(inp, vbDefaultButton1 + vbInformation, "Debug", "help.hlp", 1000) End Sub Public Function ArrayLen(arr As Variant) As Integer @@ -41,22 +69,51 @@ Sub saveData(inp As String) ' Set up map ' Fields for every year - mapper.Add "s", "scouter" - mapper.Add "e", "eventCode" - mapper.Add "l", "matchLevel" - mapper.Add "m", "matchNumber" - mapper.Add "r", "robot" - mapper.Add "t", "teamNumber" - - ' Additional custom mapping - 'mapper.Add "f", "fouls" - 'mapper.Add "c", "climb" - 'mapper.Add "dr", "defenseRating" - 'mapper.Add "d", "died" - 'mapper.Add "to", "tippedOver" - 'mapper.Add "cf", "cardFouls" - 'mapper.Add "co", "comments" + mapper.add "s", "scouter" + mapper.add "e", "eventCode" + mapper.add "l", "matchLevel" + mapper.add "m", "matchNumber" + mapper.add "r", "robot" + mapper.add "t", "teamNumber" + + ' 2023 Fields + ' Auto + mapper.add "as", "autoStartingLocation" + mapper.add "asg", "autoScoredGrid" + mapper.add "acc", "autoCrossedCable" + mapper.add "acs", "autoCrossedChargingStation" + mapper.add "am", "autoMobility" + mapper.add "ad", "autoDocked" + ' Teleop + mapper.add "tct", "cycleTimes" + mapper.add "tsg", "scoredGrid" + mapper.add "tfc", "feedCount" + mapper.add "wf", "wasFed" + mapper.add "wd", "wasDefended" + mapper.add "who", "whoDefended" + mapper.add "lnk", "smartLinks" + mapper.add "fpu", "floorPickUp" + mapper.add "dt", "dockingTime" + mapper.add "fs", "finalState" + mapper.add "dn", "numOfRobotsDocked" + + 'Endgame + mapper.add "ds", "driverSkill" + mapper.add "ls", "linksScored" + mapper.add "dr", "defenseRating" + mapper.add "sd", "swerveDrive" + mapper.add "sr", "speedRating" + mapper.add "die", "diedOrTipped" + mapper.add "tip", "tippy" + mapper.add "dc", "droppedCones" + mapper.add "all", "goodPartner" + mapper.add "co", "comments" + + If inp = "Camera" Then + Exit Sub + End If + If inp = "" Then Exit Sub End If @@ -77,7 +134,7 @@ Sub saveData(inp As String) If mapper.Exists(key) Then key = mapper(key) End If - data.Add key, value + data.add key, value Next tableexists = False @@ -88,7 +145,7 @@ Sub saveData(inp As String) 'Loop through each sheet and table in the workbook For Each sht In ThisWorkbook.Worksheets For Each tbl In sht.ListObjects - If tbl.Name = tableName Then + If tbl.name = tableName Then tableexists = True Set table = tbl Set ws = sht @@ -100,7 +157,7 @@ Sub saveData(inp As String) 'Set table = ws.ListObjects(tableName) Else Dim tablerange As Range - ws.ListObjects.Add(xlSrcRange, Range("A1:AO1"), , xlYes).Name = tableName + ws.ListObjects.add(xlSrcRange, Range("A1:CZ1"), , xlYes).name = tableName i = 0 Set table = ws.ListObjects(tableName) For Each key In data.Keys @@ -111,10 +168,283 @@ Sub saveData(inp As String) Dim newrow As ListRow - Set newrow = table.ListRows.Add + Set newrow = table.ListRows.add + + For Each str In data.Keys + ' Specific data manipulation + If str = "autoStartingLocation" Then + data(str) = stripShootingLocation(data(str)) + End If + + newrow.Range(table.ListColumns(str).Index) = data(str) + Next + End If +End Sub +Sub savePitData(inp As String) + Dim fields + Dim par + Dim value + Dim key + Dim table As ListObject + Dim ws As Worksheet + Set ws = ActiveSheet + Dim mapper + Set mapper = CreateObject("Scripting.Dictionary") + Dim data + Set data = CreateObject("Scripting.Dictionary") + Dim tableName As String + tableName = "PitData" + + ' Set up map + mapper.add "t", "teamNumber" + mapper.add "wid", "width" + mapper.add "wei", "weight" + mapper.add "drv", "drivetrain" + mapper.add "odt", "otherDrivetrain" + mapper.add "sr", "swerveRatio" + mapper.add "mot", "drivetrainMotor" + mapper.add "fco", "floorPickUpCones" + mapper.add "fcu", "floorPickUpCubes" + mapper.add "ccs", "crossCS" + mapper.add "aut", "autos" + + If inp = "Camera" Then + Exit Sub + End If + + If inp = "" Then + Exit Sub + End If + + ' MsgBox (inp) + + fields = Split(inp, ";") + If ArrayLen(fields) > 0 Then + Dim i As Integer + Dim str + + i = 0 + + For Each str In fields + par = Split(str, "=") + key = par(0) + value = par(1) + If mapper.Exists(key) Then + key = mapper(key) + End If + data.add key, value + Next + + tableexists = False + + Dim tbl As ListObject + Dim sht As Worksheet + + 'Loop through each sheet and table in the workbook + For Each sht In ThisWorkbook.Worksheets + For Each tbl In sht.ListObjects + If tbl.name = tableName Then + tableexists = True + Set table = tbl + Set ws = sht + End If + Next tbl + Next sht + + If tableexists Then + ' Set table = ws.ListObjects(tableName) + Else + Dim tablerange As Range + ws.ListObjects.add(xlSrcRange, Range("A1:CZ1"), , xlYes).name = tableName + i = 0 + Set table = ws.ListObjects(tableName) + For Each key In data.Keys + table.Range(i + 1) = key + i = i + 1 + Next + End If + + Dim newrow As ListRow + Set newrow = table.ListRows.add + For Each str In data.Keys newrow.Range(table.ListColumns(str).Index) = data(str) Next End If End Sub + + +Public Function stripShootingLocation(str As Variant) + If str <> "" Then + stripShootingLocation = Mid(str, 2, Len(str) - 2) + Else + stripShootingLocation = "" + End If +End Function + +Private Function countGrid(ByRef myCell As Range, ByVal countType As Integer) As Integer + Dim myArrStr As String + Dim myArr() As String + + Dim highNumArr() As String + highNumArr = Split("1,2,3,4,5,6,7,8,9", ",") + + Dim medNumArr() As String + medNumArr = Split("10,11,12,13,14,15,16,17,18", ",") + + Dim lowNumArr() As String + lowNumArr = Split("19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36", ",") + + Dim coneNumArr() As String + coneNumArr = Split("1,3,4,6,7,9,10,12,13,15,16,18,19,20,21,22,23,24,25,26,27", ",") + + Dim cubeNumArr() As String + cubeNumArr = Split("2,5,8,11,14,17,28,29,30,31,32,33,34,35,36", ",") + + Dim totalGamePieces As Integer + Dim highGamePieces As Integer + Dim medGamePieces As Integer + Dim lowGamePieces As Integer + Dim coneCount As Integer + Dim cubeCount As Integer + + totalGamePieces = 0 + highGamePieces = 0 + medGamePieces = 0 + lowGamePieces = 0 + coneCount = 0 + cubeCount = 0 + + If (myCell.Cells.count > 1) Then + MsgBox ("Pass in only 1 Cell") + countGrid = -1 + Exit Function + End If + + If Mid(myCell, 1, 1) = "[" And Mid(myCell, Len(myCell), 1) = "]" Then + myArrStr = Mid(myCell, 2, Len(myCell) - 2) + Else + MsgBox ("Cell not formatted correctly") + countGrid = -1 + Exit Function + End If + + myArr = Split(myArrStr, ",") + + Dim item As Variant + For Each item In myArr + + totalGamePieces = totalGamePieces + 1 + If Not IsError(Application.match(item, highNumArr, 0)) Then + highGamePieces = highGamePieces + 1 + End If + If Not IsError(Application.match(item, medNumArr, 0)) Then + medGamePieces = medGamePieces + 1 + End If + If Not IsError(Application.match(item, lowNumArr, 0)) Then + lowGamePieces = lowGamePieces + 1 + End If + If Not IsError(Application.match(item, coneNumArr, 0)) Then + coneCount = coneCount + 1 + End If + If Not IsError(Application.match(item, cubeNumArr, 0)) Then + cubeCount = cubeCount + 1 + End If + + Next item + + If countType = 1 Then + ' Total game pieces + countGrid = totalGamePieces + ElseIf countType = 2 Then + ' High game pieces + countGrid = highGamePieces + ElseIf countType = 3 Then + ' Med game pieces + countGrid = medGamePieces + ElseIf countType = 4 Then + ' Low game pieces + countGrid = lowGamePieces + ElseIf countType = 5 Then + ' Cones + countGrid = coneCount + ElseIf countType = 6 Then + ' Cubes + countGrid = cubeCount + Else + ' Unsupported countType + MsgBox ("Unrecognized countType") + countGrid = -1 + End If + +End Function + +Private Function getTotalCount(ByRef myCell As Range) As Integer + getTotalCount = countGrid(myCell, 1) +End Function + +Private Function getHighCount(ByRef myCell As Range) As Integer + getHighCount = countGrid(myCell, 2) +End Function + +Private Function getMedCount(ByRef myCell As Range) As Integer + getMedCount = countGrid(myCell, 3) +End Function + +Private Function getLowCount(ByRef myCell As Range) As Integer + getLowCount = countGrid(myCell, 4) +End Function + +Private Function getConeCount(ByRef myCell As Range) As Integer + getConeCount = countGrid(myCell, 5) +End Function + +Private Function getCubeCount(ByRef myCell As Range) As Integer + getCubeCount = countGrid(myCell, 6) +End Function + +Private Function getAvgCycleTime(ByRef myCell As Range) As Double + Dim myArrStr As String + Dim myArr() As String + + If (myCell.Cells.count > 1) Then + MsgBox ("Pass in only 1 Cell") + getAvgCycleTime = -1 + Exit Function + End If + + If Mid(myCell, 1, 1) = "[" And Mid(myCell, Len(myCell), 1) = "]" Then + myArrStr = Mid(myCell, 2, Len(myCell) - 2) + Else + MsgBox ("Cell not formatted correctly") + getAvgCycleTime = -1 + Exit Function + End If + + myArr = Split(myArrStr, ",") + + Dim total As Double + Dim count As Integer + + total = 0 + count = 0 + + Dim item As Variant + For Each item In myArr + + Dim numStr As String + Dim num As Double + + num = WorksheetFunction.Sum(0 & item) + total = total + num + count = count + 1 + Next item + + If count = 0 Then + getAvgCycleTime = 0 + Else + getAvgCycleTime = total / count + End If + +End Function diff --git a/README.md b/README.md index e14c82056..65a6f2142 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ #### A scouting system for FIRST FRC competitions developed by [PWNAGE - Team #2451](https://pwnagerobotics.org). -Live Demo . Getting Started . FAQ +Live Demo . Pit Scouting . Getting Started . FAQ ![Stars](https://img.shields.io/github/stars/PWNAGERobotics/ScoutingPASS?style=plastic) ![Forks](https://img.shields.io/github/forks/PWNAGERobotics/ScoutingPASS?style=plastic) ![watchers](https://img.shields.io/github/watchers/PWNAGERobotics/ScoutingPASS?style=plastic)

@@ -18,6 +18,7 @@
  • Live Demo
  • Description
  • Getting Started
  • +
  • Pit Scouting
  • How We Scout
  • Contributing
  • FAQ
  • @@ -100,6 +101,21 @@ To enable The Blue Alliance API: Note: In order for this to work, the schedule has to be posted to The Blue Alliance. That usually doesn't happen until just before the event. (a few days to a few hours) To test this you can point it to a past event. Set the event to 2020ilch. Reload the page to load the schedule for that event. Select Match 6 and Blue-2. You should see it populate the Team # to 2451, and the next line will show the team name, PWNAGE. +

    (back to top)

    +
    + +## Pit Scouting: + +ScountingPASS 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) + +It works almost exactly like the main scouting pages, except there is only one page of input. Once your scouters have filled out the information, swipe left to display the QR code. That QR code can be scanned to push the data to Excel. + +The default configuration file is 2023/CU_Pit_config.js. You can modify that configuration to meet your needs. + +We realize that you may not want to lug your computer around the pits to scan each QR code. Our recommendation is to have your scouters take screenshot of the QR codes. Then when they are back to the scouting computer, just go through the photos to scan each QR code. +

    (back to top)

    @@ -206,3 +222,5 @@ Scouting PASS continues to evolve. Here are the changes for the 2023 Season: * Don't allow the cycle timer to add a Zero time. (Thanks [jckwik](https://github.com/jckwik)!!) * Added Display Data and Copy Data buttons on QR screen (Thanks [tails618](https://github.com/tails618)!!) * Bug fixes to clickable images (Thanks [jacob6838](https://github.com/jacob6838)!!) +* Allow ability link Cycle Timer with Counter (Thanks DaBushinator!!) +* Add pit scouting functionality diff --git a/pit.html b/pit.html new file mode 100644 index 000000000..729cdabd9 --- /dev/null +++ b/pit.html @@ -0,0 +1,61 @@ + + + + + Scouting PASS + + + + + + + + + + + +
    +
    +
    +

    PWNAGE
    Scouting 2020

    +

    Pit Scouting

    +

    + + +
    +

    +
    + +
    +

    PWnAGE
    Scouting 2020

    +

    Generate QR Code

    +

    +

    + + +
      +
    + +
    +
     
      +
    +

    +
    +
     
    +

    +

    +
    + +
    +
    + +
    +
    +
    +
    + +
    + diff --git a/resources/css/scoutingPASS.css b/resources/css/scoutingPASS.css index 871e6dd59..4572694f8 100644 --- a/resources/css/scoutingPASS.css +++ b/resources/css/scoutingPASS.css @@ -38,7 +38,7 @@ html, body{ } .button { - font-family: Alexis; + font-family: Alexis; font-size: 16px; } @@ -62,12 +62,12 @@ html, body{ #qrHeader1, #qrHeader2 { color: black; } - + .field-image { margin: 2vw; } -.field-image-src{ +.field-image-src{ background-position: center; border: 1px solid; } @@ -104,16 +104,17 @@ html, body{ #qr-code{ text-align: center; + color: black; } h1{ font-family: Alexis; text-align: center; border-style: groove; - border-color: white; + border-color: white; border-width: 2px; font-size: 40px; - padding: 10px; + padding: 10px; font-weight: 350; } @@ -153,7 +154,7 @@ h2{ } #nextButton1, #nextButton2, #nextButton9, #nextButton10, .clearForm, .submitForm, #displayButton, #copyButton { - font-family: Alexis; + font-family: Alexis; padding: 10px; background-color: rgb(151, 199, 240); text-decoration: none; @@ -162,14 +163,14 @@ h2{ #nextButton3, #nextButton4, #nextButton5, #nextButton6, #nextButton7, #nextButton8 { font-family: Alexis; - background-color: black; + background-color: black; padding: 10px; color: white; } .undoButton, .flipButton { font-family: Alexis; - background-color: black; + background-color: black; padding: 10px; color: white; margin-left: 20px; @@ -177,16 +178,16 @@ h2{ #prevButton7, #prevButton8, #prevButton9{ font-family: Alexis; - background-color: rgb(151, 199, 240); + background-color: rgb(151, 199, 240); padding: 10px; } #prevButton1, #prevButton2, #prevButton3, #prevButton4, #prevButton5, #prevButton6{ font-family: Alexis; - background-color: black; + background-color: black; padding: 10px; color: white; - + } #prematch_table{ diff --git a/resources/js/scoutingPASS.js b/resources/js/scoutingPASS.js index 30b1291bf..303a13467 100644 --- a/resources/js/scoutingPASS.js +++ b/resources/js/scoutingPASS.js @@ -11,6 +11,7 @@ var initialX = null; var xThreshold = 0.3; var slide = 0; var enableGoogleSheets = false; +var pitScouting = false; var checkboxAs = 'YN'; // Options @@ -669,13 +670,17 @@ function configure() { } if (mydata.hasOwnProperty('enable_google_sheets')) { - if ((mydata.enable_google_sheets == 'true') || - (mydata.enable_google_sheets == 'True') || - (mydata.enable_google_sheets == 'TRUE')) { + if (mydata.enable_google_sheets.toUpperCase() == 'TRUE') { enableGoogleSheets = true; } } + if (mydata.hasOwnProperty('pitConfig')) { + if (mydata.pitConfig.toUpperCase() == 'TRUE') { + pitScouting = true; + } + } + if (mydata.hasOwnProperty('checkboxAs')) { // Supported modes // YN - Y or N @@ -923,13 +928,18 @@ function getData(useStr) { } function updateQRHeader() { - var str = 'Event: !EVENT! Match: !MATCH! Robot: !ROBOT! Team: !TEAM!'; - - str = str - .replace('!EVENT!', document.getElementById("input_e").value) - .replace('!MATCH!', document.getElementById("input_m").value) - .replace('!ROBOT!', document.getElementById("display_r").value) - .replace('!TEAM!', document.getElementById("input_t").value); + let str = 'Event: !EVENT! Match: !MATCH! Robot: !ROBOT! Team: !TEAM!'; + + if (!pitScouting) { + str = str + .replace('!EVENT!', document.getElementById("input_e").value) + .replace('!MATCH!', document.getElementById("input_m").value) + .replace('!ROBOT!', document.getElementById("display_r").value) + .replace('!TEAM!', document.getElementById("input_t").value); + } else { + str = 'Pit Scouting - Team !TEAM!' + .replace('!TEAM!', document.getElementById("input_t").value); + } document.getElementById("display_qr-info").textContent = str; } @@ -937,9 +947,11 @@ function updateQRHeader() { function qr_regenerate() { // Validate required pre-match date (event, match, level, robot, scouter) - if (validateData() == false) { - // Don't allow a swipe until all required data is filled in - return false + if (!pitScouting) { + if (validateData() == false) { + // Don't allow a swipe until all required data is filled in + return false + } } // Get data @@ -960,18 +972,22 @@ function clearForm() { var match = 0; var e = 0; - swipePage(-5) - - // Increment match - match = parseInt(document.getElementById("input_m").value) - if (match == NaN) { - document.getElementById("input_m").value = "" + if (pitScouting) { + swipePage(-1); } else { - document.getElementById("input_m").value = match + 1 - } + swipePage(-5); + + // Increment match + match = parseInt(document.getElementById("input_m").value) + if (match == NaN) { + document.getElementById("input_m").value = "" + } else { + document.getElementById("input_m").value = match + 1 + } - // Robot - resetRobot() + // Robot + resetRobot() + } // Clear XY coordinates inputs = document.querySelectorAll("[id*='XY_']"); @@ -1433,11 +1449,17 @@ function copyData(){ } window.onload = function () { - var ret = configure(); + let ret = configure(); if (ret != -1) { - var ec = document.getElementById("input_e").value; - getTeams(ec); - getSchedule(ec); + let ece = document.getElementById("input_e"); + let ec = null; + if (ece != null) { + ec = ece.value; + } + if (ec != null) { + getTeams(ec); + getSchedule(ec); + } this.drawFields(); if (enableGoogleSheets) { console.log("Enabling Google Sheets.");