diff --git a/.github/workflows/dashboard-workflow.yml b/.github/workflows/dashboard-workflow.yml new file mode 100644 index 00000000..4b3604a4 --- /dev/null +++ b/.github/workflows/dashboard-workflow.yml @@ -0,0 +1,75 @@ +name: Makefile CI + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + make_dashboard: + name: Build Dashboard + runs-on: ubuntu-latest + steps: + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + architecture: x64 + + - run: sudo apt update + + - run: sudo apt install curl + + - name: Install NPM + run: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash + + - name: Install npm deps + run: cd dashboard && npm ci + + - id: cache-pipenv + uses: actions/cache@v3 + with: + path: ~/.local/share/virtualenvs + key: ${{ runner.os }}-pipenv-${{ hashFiles('dashboard/Pipfile.lock') }} + + - name: Install dependencies + if: steps.cache-pipenv.outputs.cache-hit != 'true' + run: | + cd dashboard && pipenv install --deploy --dev + + - run: cd dashboard && pipenv run make stage + + - uses: actions/upload-artifact@v2 + with: + name: Dashboard_Transpiled + path: dashboard/dist + + publish: + # Only run on tags + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + name: Publish + needs: [make_dashboard] + + steps: + - uses: actions/download-artifact@v2 + with: + name: Dashboard_Transpiled + path: Dashboard_Transpiled/ + + - uses: papeloto/action-zip@v1 + with: + files: Dashboard_Transpiled/ + dest: Dashboard_Transpiled.zip + + - name: Upload Dashboard to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: "*.zip" + tag: ${{ github.ref }} + overwrite: true + prerelease: true + body: "Tidal Force Robotics, Automated with github ci/cd." + file_glob: true diff --git a/.gitignore b/.gitignore index 74acd7d6..9741c3a1 100644 --- a/.gitignore +++ b/.gitignore @@ -169,5 +169,13 @@ networktables.json # Add to tree but only when neccicary (adding a map or smthn) simgui-window.json +# =========== +# Dashboard +# =========== + +.parcel-cache/ +node_modules/ +dashboard.zip + # sysid ignores *.SysId diff --git a/dashboard/Makefile b/dashboard/Makefile new file mode 100644 index 00000000..06b28de5 --- /dev/null +++ b/dashboard/Makefile @@ -0,0 +1,32 @@ +# +# FRC 1721 Tidal Force +# 2023-2024 +# + +ROOT := $(PWD) +STAGEDIR := $(ROOT)/dist/ +DASH_HASH := $(shell git describe --tags --always) + +all: help +help: # List all commands and their descriptions + @echo + @cat Makefile | grep ': #' --color=never | sed '/^\t/d' | sed 's/: / /' | column -t -s '#' + @echo + +ready: # Run the necessary commands for development + pipenv install + npm ci +build: # Run `parcel build' and inject NetworkTables + npm run build + ./build.sh +run: # Run a live server of the dashboard + cd dist && pipenv run python -m pynetworktables2js +dev: # Automatically run `make build' whenever a file changes in src/ (requires program `entr') + find src/ | entr sh -c 'make build' +clean: # Run `git clean -fdX + git clean -fdX +stage: # Prepares dist/ for deployment + rm -rf dist/ + make build + cp entrypoint.sh $(STAGEDIR) + cp run.bat $(STAGEDIR) diff --git a/dashboard/Pipfile b/dashboard/Pipfile new file mode 100644 index 00000000..2a2de19d --- /dev/null +++ b/dashboard/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pynetworktables2js = {ref = "refs/pull/45/head", git = "git+https://github.com/robotpy/pynetworktables2js.git"} + +[dev-packages] + +[requires] +python_version = "3.11" diff --git a/dashboard/Pipfile.lock b/dashboard/Pipfile.lock new file mode 100644 index 00000000..91a509de --- /dev/null +++ b/dashboard/Pipfile.lock @@ -0,0 +1,93 @@ +{ + "_meta": { + "hash": { + "sha256": "175d87a73f9bf98c9711cbcce8e73b393aa4cdc894fe04d65ac01e9ef325d57a" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.11" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "cbor2": { + "hashes": [ + "sha256:1206180f66a9ad23e692cf457610c877f186ad303a1264b6c5335015b7bee83e", + "sha256:1a5770bdf4340de55679efe6c38fc6d64529fda547e7a85eb0217a82717a8235", + "sha256:37ba4f719384bd4ea317e92a8763ea343e205f3112c8241778fd9dbc64ae1498", + "sha256:39d94852dd61bda5b3d2bfe74e7b194a7199937d270f90099beec3e7584f0c9b", + "sha256:41f7501338228b27dac88c1197928cf8985f6fc775f59be89c6fdaddb4e69658", + "sha256:42155a20be46312fad2ceb85a408e2d90da059c2d36a65e0b99abca57c5357fd", + "sha256:425ae919120b9d05b4794b3e5faf6584fc47a9d61db059d4f00ce16ae93a3f63", + "sha256:4384a56afef0b908b61c8ea3cca3e257a316427ace3411308f51ee301b23adf9", + "sha256:4bb35f3b1ebd4b7b37628f0cd5c839f3008dec669194a2a4a33d91bab7f8663b", + "sha256:4bfa417dbb8b4581ad3c2312469899518596551cfb0fe5bdaf8a6921cff69d7e", + "sha256:4eae4d56314f22920a28bf7affefdfc918646877ce3b16220dc6cf38a584aa41", + "sha256:4ee46e6dbc8e2cf302a022fec513d57dba65e9d5ec495bcd1ad97a5dbdbab249", + "sha256:50019fea3cb07fa9b2b53772a52b4243e87de232591570c4c272b3ebdb419493", + "sha256:537da7bfee97ee44a11b300c034c18e674af6a5dc4718a6fba141037f099c7ec", + "sha256:559a0c1ec8dcedd6142b81727403e0f5a2e8f4c18e8bb3c548107ec39af4e9cb", + "sha256:5c511ff6356d6f4292ced856d5048a24ee61a85634816f29dadf1f089e8cb4f9", + "sha256:5c99fd8bbc6bbf3bf4d6b2996594ae633b778b27b0531559487950762c4e1e3f", + "sha256:60e7e0073291096605de27de3ce006148cf9a095199160439555f14f93d044d5", + "sha256:65532ba929beebe1c63317ad00c79d4936b60a5c29a3c329d2aa7df4e72ad907", + "sha256:67e2be461320197495fff55f250b111d4125a0a2d02e6256e41f8598adc3ad3f", + "sha256:6f3827ae14c009df9b37790f1da5cd1f9d64f7ffec472a49ebf865c0af6b77e9", + "sha256:93b949a66bec40dd0ca87a6d026136fea2cf1660120f921199a47ac8027af253", + "sha256:93d601ca92d917f769370a5e6c3ead62dca6451b2b603915e4fcf300083b9fcd", + "sha256:9e45d5aa8e484b4bf57240d8e7949389f1c9d4073758abb30954386321b55c9d", + "sha256:a11876abd50b9f70d114fcdbb0b5a3249ccd7d321465f0350028fd6d2317e114", + "sha256:a18be0af9241883bc67a036c1f33e3f9956d31337ccd412194bf759bc1095e03", + "sha256:b5d53826ad0c92fcb004b2a475896610b51e0ca010f6c37d762aae44ab0807b2", + "sha256:c85ab7697252af2240e939707c935ea18081ccb580d4b5b9a94b04148ab2c32b", + "sha256:c8cc64acc606b7f2a4b673a1d6cde5a9cb1860a6ce27b353e269c9535efbd62c", + "sha256:d6ab54a9282dd99a3a70d0f64706d3b3592e7920564a93101caa74dec322346c", + "sha256:dc77cac985f7f7a20f2d8b1957d1e79393d7df823f61c7c6173d3a0011c1d770", + "sha256:dca639c8ff81b9f0c92faf97324adfdbfb5c2a5bb97f249606c6f5b94c77cc0d", + "sha256:e3317e7dfb4f3180be90bcd853204558d89f119b624c2168153b53dea305e79d", + "sha256:efb81920d80410b8e80a4a6a8b06ec9b766be0ae7f3029af8ae4b30914edcfa3", + "sha256:f41e4a439f642954ed728dc18915098b5f2ebec7029eaebe52c06c52b6a9a63a", + "sha256:f9e192f461a9f8f6082df28c035b006d153904213dc8640bed8a72d72bbc9475", + "sha256:fd77c558decdba2a2a7a463e6346d53781d2163bacf205f77b999f561ba4ac73" + ], + "markers": "python_version >= '3.8'", + "version": "==5.5.1" + }, + "pynetworktables": { + "hashes": [ + "sha256:54604b1993515c43890be058263f158c1d168c69bedd802f0de644964cf3840d", + "sha256:91167948e66c29d5c90f85f3e249403f4d2ebf11368b93f9b968ef315388c73c" + ], + "markers": "python_version >= '3.5'", + "version": "==2021.0.0" + }, + "pynetworktables2js": { + "git": "git+https://github.com/robotpy/pynetworktables2js.git", + "ref": "d0313bc6b47f834379907f5b89f27e9a515e1766" + }, + "tornado": { + "hashes": [ + "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0", + "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63", + "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263", + "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052", + "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f", + "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee", + "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78", + "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579", + "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212", + "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e", + "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2" + ], + "markers": "python_version >= '3.8'", + "version": "==6.4" + } + }, + "develop": {} +} diff --git a/dashboard/README.md b/dashboard/README.md new file mode 100644 index 00000000..96732bc8 --- /dev/null +++ b/dashboard/README.md @@ -0,0 +1,33 @@ +# FRC 1721 Dashboard +## Getting Started +- Make sure you have sed installed +- Make sure Robot sim is running +- Make sure npm is installed + +Make sure you are in the dashboard/ folder. + +Install deps + +``` sh +npm ci +``` + +Build webpage + +``` sh +make build +``` + +(NOT REQUIRED) You can build automatically when a file changes in src/ (requires 'entr', `paru -S entr` `paman install entr`) + +``` sh +make dev +``` + +Run webpage + +``` sh +make run +``` + +`make run` can be left running when you run `make build`, and doesn't need to be run again to update the webpage diff --git a/dashboard/build.sh b/dashboard/build.sh new file mode 100755 index 00000000..e0289bdf --- /dev/null +++ b/dashboard/build.sh @@ -0,0 +1,12 @@ +sed -i -e 's/

<\/p>/ + +

+ + +
+ + + + diff --git a/dashboard/src/jquery.min.js b/dashboard/src/jquery.min.js new file mode 100644 index 00000000..7f37b5d9 --- /dev/null +++ b/dashboard/src/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0 + loadCameraOnConnect({ + container: "#cam1", // id of camera div + proto: null, // url scheme + host: "10.17.21.11", // ip + port: 5800, + image_url: "/", + data_url: "/", + wait_img: require("./assets/no_signal.png"), + error_img: require("./assets/error.png"), + attrs: { + width: 320, + height: 240, + }, + }); + */ +}); + +function onRobotConnection(connected) { + $("#robotstate").text(connected ? "Connected" : "Disconnected"); + $("#robotAddress").text( + connected ? NetworkTables.getRobotAddress() : "Disconnected" + ); +} + +function onNetworkTablesConnection(connected) { + if (connected) { + $("#connectstate").text("Connected"); + + // clear the table + $("#nt tbody > tr").remove(); + } else { + $("#connectstate").text("Disconnected"); + } +} + +function onValueChanged(key, value, isNew) { + // key thing here: we're using the various NetworkTable keys as + // the id of the elements that we're appending, for simplicity. However, + // the key names aren't always valid HTML identifiers, so we use + // the NetworkTables.keyToId() function to convert them appropriately + + if (isNew) { + var tr = $('
').appendTo($("#nt-table")); + $('
').text(key).appendTo(tr); + $('
') + .attr("id", NetworkTables.keyToId(key)) + .text(value) + .appendTo(tr); + } else { + // similarly, use keySelector to convert the key to a valid jQuery + // selector. This should work for class names also, not just for ids + $("#" + NetworkTables.keySelector(key)).text(value); + } + + if (key.includes("/SmartDashboard/Audio")) { + countDownAlerts(key, value); + } + + if (key === "/SmartDashboard/Autonomous/options") { + var options = NetworkTables.getValue( + "/SmartDashboard/Autonomous/options" + ); + } + + if (key.includes("/SmartDashboard/Swerve/")) { + if (key.includes("desired")) { + wheel = key.split("/").at(-1); + console.log(wheel); + $(".swerve-desired ." + wheel).css( + "transform", + "rotate(" + value + "deg)" + ); + } else { + wheel = key.split("/").at(-1).replace(" desired", ""); + console.log(wheel); + $(".swerve ." + wheel).css("transform", "rotate(" + value + "deg)"); + } + } +} diff --git a/dashboard/src/style.scss b/dashboard/src/style.scss new file mode 100644 index 00000000..32b0182a --- /dev/null +++ b/dashboard/src/style.scss @@ -0,0 +1,87 @@ +@font-face { + font-family: "FiraCode Nerd Font"; + src: url("asset/FiraCodeNerdFontPropo-Retina.ttf") format("truetype"); +} + +html, +body { + margin: 0; + padding: 0; + overflow: hidden; + height: 528px; + width: 1366px; +} + +html { + transform-origin: center; + // transform: scale(0.65) rotate(30deg); +} + +* { + font-family: "FiraCode Nerd Font"; + color: #ebdbb2; + font-size: 20px; + box-sizing: border-box; +} + +body { + display: flex; + flex-direction: column; + background: #ff0000; +} + +.cameras { + background: #1d2021; +} + +#nt-table { + .table-info { + display: flex; + flex-direction: row; + .table-label { + flex: 1; + } + .table-area { + flex: 2; + } + } +} + +main { + height: 100%; + overflow-y: scroll; +} + +.swerve { + width: 320px; + height: 320px; + background: #3c3836; + position: relative; + + &-actual, + &-desired { + width: 320px; + height: 320px; + padding: 25px; + display: flex; + flex-direction: column; + justify-content: space-between; + } + + &-desired { + position: absolute; + top: 0; + left: 0; + } + + &-row { + display: flex; + flex-direction: row; + justify-content: space-between; + } + + .wheel { + height: 120px; + width: 120px; + } +} diff --git a/rio/Makefile b/rio/Makefile index 39b195d2..da44898b 100644 --- a/rio/Makefile +++ b/rio/Makefile @@ -14,7 +14,7 @@ sim: ## Run the simulator python -m robotpy sim deploy: ## Deploy to the robot (with netconsole) - python -m robotpy deploy --nc + python -m robotpy deploy --nc --no-install --skip-test push: ## Deploy to the robot (push only, no netconsole) python -m robotpy deploy diff --git a/rio/Pipfile b/rio/Pipfile index ef016fa0..93b9794d 100644 --- a/rio/Pipfile +++ b/rio/Pipfile @@ -15,3 +15,6 @@ robotpy-commands-v2 = "2024.2.1" [requires] python_version = "3.11" + +[pipenv] +allow_prereleases = true diff --git a/rio/commands/intakeRotationMAN.py b/rio/commands/intakeRotationMAN.py index 2cedb1c5..95a17f16 100644 --- a/rio/commands/intakeRotationMAN.py +++ b/rio/commands/intakeRotationMAN.py @@ -16,9 +16,6 @@ def __init__(self, angle: float, subsystem): # requested speed self.angle = angle - # TODO change current limit later in amps - self.intakeSubsystem.liftCurrentLimit(1) - def execute(self): self.intakeSubsystem.manualLift(self.angle) diff --git a/rio/commands/intakeRotationPID.py b/rio/commands/intakeRotationPID.py deleted file mode 100644 index 137fad63..00000000 --- a/rio/commands/intakeRotationPID.py +++ /dev/null @@ -1,29 +0,0 @@ -import commands2 - -from subsystems.intake import IntakeSubsystem - - -class IntakeRotationPID(commands2.Command): - def __init__(self, angle, subsystem): - """ - rotates the intake - supposed to be used with - presets - """ - super().__init__() - - # local subsystem instance - self.intakeSubsystem = subsystem - - # requested speed - self.angle = angle - - # TODO change current limit later in amps - self.intakeSubsystem.liftCurrentLimit(1) - - def initialize(self): - self.intakeSubsystem.lift(self.angle) - - def end(self, interrupted: bool): - self.intakeSubsystem.lift(0) - return True diff --git a/rio/commands/intakeSuck.py b/rio/commands/intakeUntilNote.py similarity index 63% rename from rio/commands/intakeSuck.py rename to rio/commands/intakeUntilNote.py index 18499ec4..26cfcd7d 100644 --- a/rio/commands/intakeSuck.py +++ b/rio/commands/intakeUntilNote.py @@ -1,9 +1,10 @@ +import logging import commands2 from subsystems.intake import IntakeSubsystem -class IntakeSuck(commands2.Command): +class intakeUntilNote(commands2.Command): def __init__(self, speed: float, subsystem: IntakeSubsystem): """ takes in the rings @@ -16,12 +17,16 @@ def __init__(self, speed: float, subsystem: IntakeSubsystem): # requested speed self.speed = speed - # TODO change current limit later in amps - self.intakeSubsystem.intakeCurrentLimit(30) + def initialize(self): + logging.debug(f"Running command Intake Suck (manual) with speed {self.speed}") def execute(self): self.intakeSubsystem.intake(self.speed) + def isFinished(self): + return self.intakeSubsystem.switchPress() + def end(self, interrupted: bool): self.intakeSubsystem.intake(0) + logging.debug(f"Intake suck done") return True diff --git a/rio/commands/rotateIntake.py b/rio/commands/rotateIntake.py new file mode 100644 index 00000000..72a0be00 --- /dev/null +++ b/rio/commands/rotateIntake.py @@ -0,0 +1,38 @@ +import commands2 +import logging + +from subsystems.intake import IntakeSubsystem + + +class RotateIntake(commands2.Command): + def __init__(self, angle: float, subsystem: IntakeSubsystem): + """ + rotates the intake + supposed to be used with + presets + """ + super().__init__() + + # local subsystem instance + self.intakeSubsystem = subsystem + + # requested speed + self.angle = angle + + def initialize(self): + logging.info(f"Moving intake to {self.angle}") + + def execute(self): + self.intakeSubsystem.lift(self.angle) + logging.info( + f"Still moving... {self.intakeSubsystem.getAngle()} to {self.angle}" + ) + + def isFinished(self): + e = 1 # How close before we're done + + return abs(self.intakeSubsystem.getAngle() - self.angle) < e + + def end(self, interrupted: bool): + logging.info(f"Done moving! Finished normally: {interrupted}") + return True diff --git a/rio/commands/setIntakeSpeed.py b/rio/commands/setIntakeSpeed.py new file mode 100644 index 00000000..6015af9b --- /dev/null +++ b/rio/commands/setIntakeSpeed.py @@ -0,0 +1,33 @@ +import logging +import commands2 + +from subsystems.intake import IntakeSubsystem + + +class SetIntakeSpeed(commands2.Command): + def __init__(self, speed: float, subsystem: IntakeSubsystem): + """ + takes in the rings + """ + super().__init__() + + # local subsystem instance + self.intakeSubsystem = subsystem + + # requested speed + self.speed = speed + + def initialize(self): + logging.debug(f"Running command Intake Suck (manual) with speed {self.speed}") + + def execute(self): + if self.speed > 0: + if not self.intakeSubsystem.switchPress(): + self.intakeSubsystem.intake(self.speed) + else: + self.intakeSubsystem.intake(self.speed) + + def end(self, interrupted: bool): + self.intakeSubsystem.intake(0) + logging.debug(f"Intake suck done") + return True diff --git a/rio/constants.py b/rio/constants.py index ed7b1c49..b53e2a70 100644 --- a/rio/constants.py +++ b/rio/constants.py @@ -173,7 +173,7 @@ class IntakeConstants: kLiftConversion = 1 # Configured feb 12 by joe # lift pid - kLiftP = 0 - kLiftI = 0 - kLiftD = 0 + kLiftP = 1.2 + kLiftI = 0.0001 + kLiftD = 0.5 kLiftFF = 0 diff --git a/rio/robot.py b/rio/robot.py index bbce6218..adf10685 100644 --- a/rio/robot.py +++ b/rio/robot.py @@ -7,6 +7,7 @@ import commands2 import wpilib +import logging from robotcontainer import RobotContainer @@ -18,6 +19,12 @@ def robotInit(self): self.container = RobotContainer() self.autonomousCommand = None + if not wpilib.RobotBase.isReal(): + # Do some things if the robot is NOT real + + # Ovverride default logging + logging.basicConfig(level=logging.DEBUG) + def autonomousInit(self) -> None: self.autonomousCommand = self.container.getAutonomousCommand() diff --git a/rio/robotcontainer.py b/rio/robotcontainer.py index dcbb0e87..2fe5c344 100644 --- a/rio/robotcontainer.py +++ b/rio/robotcontainer.py @@ -26,11 +26,13 @@ from subsystems.shooter import Shooter from subsystems.intake import IntakeSubsystem +from commands.setIntakeSpeed import SetIntakeSpeed +from commands.rotateIntake import RotateIntake from commands.FlyWheelSpeed import FlyWheelSpeed -from commands.intakeSuck import IntakeSuck from commands.intakeRotationMAN import IntakeRotationMAN from commands.shooterROT import ShooterROT from commands.manualRot import manualROT +from commands.intakeUntilNote import intakeUntilNote class RobotContainer: @@ -90,44 +92,36 @@ def configureButtonBindings(self) -> None: instantiating a :GenericHID or one of its subclasses (Joystick or XboxController), and then passing it to a JoystickButton. """ - #_____INTAKE_KEYBINDS_____ + # _____INTAKE_KEYBINDS_____ # intaking - self.opController.x().whileTrue(IntakeSuck(0.4, self.intake)) + + self.opController.x().onTrue( + commands2.SequentialCommand( + RotateIntake(60, self.intake), + intakeUntilNote(0.5, self.intake), + RotateIntake(0, self.intake), + ) + ) + self.opController.y().whileTrue(RotateIntake(-0.5, self.intake)) # moving intake self.opController.pov(90).whileTrue(IntakeRotationMAN(1, self.intake)) # out self.opController.pov(270).whileTrue(IntakeRotationMAN(-1, self.intake)) # in - #_____POST_INTAKE_KEYBINDS_____ - - #speaker command group - self.opController.a().whileTrue( - commands2.SequentialCommandGroup( - FlyWheelSpeed(1.0, self.shooter), #power flywheels - commands2.WaitCommand(3), #wait for flywheels to get up to speed - IntakeSuck(-0.4, self.intake) #push note out of intake - ) - ) - - #amp command group - self.opController.b().whileTrue( - commands2.ParallelCommandGroup( - FlyWheelSpeed(0.05, self.shooter), #power flywheels at highly reduced speed - IntakeSuck(-0.4, self.intake) #push note out of intake - ) - ) + # _____POST_INTAKE_KEYBINDS_____ - #moving shooter + # moving shooter self.opController.pov(0).whileTrue(manualROT(0.5, self.shooter)) self.opController.pov(180).whileTrue(manualROT(-0.5, self.shooter)) - #PID shooter rotation (NOT CURRENTLY WORKING) - - #self.opController.pov(0).whileTrue(ShooterROT(0,self.shooter)) # out - #self.opController.pov(180).whileTrue(ShooterROT(40,self.shooter)) # out + # PID shooter rotation (NOT CURRENTLY WORKING) + # self.opController.pov(0).whileTrue(ShooterROT(0,self.shooter)) # out + # self.opController.pov(180).whileTrue(ShooterROT(40,self.shooter)) # out + self.opController.a().whileTrue(RotateIntake(0, self.intake)) + self.opController.b().whileTrue(RotateIntake(60, self.intake)) def disablePIDSubsystems(self) -> None: """Disables all ProfiledPIDSubsystem and PIDSubsystem instances. diff --git a/rio/simgui-ds.json b/rio/simgui-ds.json index 5723a1f0..d1370b6e 100644 --- a/rio/simgui-ds.json +++ b/rio/simgui-ds.json @@ -16,13 +16,7 @@ } ], "axisCount": 3, - "buttonCount": 10, - "buttonKeys": [ - 90, - 88, - 67, - 86 - ], + "buttonCount": 0, "povCount": 0 }, { @@ -39,12 +33,10 @@ "axisCount": 2, "buttonCount": 6, "buttonKeys": [ - 260, - 268, - 266, - 261, - 269, - 267 + 65, + 66, + 88, + 89 ], "povCount": 0 }, @@ -65,8 +57,8 @@ "name": "Driver Joystick" }, { - "guid": "030000006d0400001dc2000014400000", - "useGamepad": true + "guid": "Keyboard1", + "name": "Operator Stick" } ] } diff --git a/rio/simgui-window.json b/rio/simgui-window.json index 2be9fa9e..4efe8f91 100644 --- a/rio/simgui-window.json +++ b/rio/simgui-window.json @@ -5,13 +5,13 @@ "MainWindow": { "GLOBAL": { "fps": "120", - "height": "699", - "maximized": "1", + "height": "780", + "maximized": "0", "style": "0", "userScale": "2", - "width": "1366", - "xpos": "0", - "ypos": "20" + "width": "1426", + "xpos": "334", + "ypos": "95" } }, "Table": { @@ -26,7 +26,7 @@ "###FMS": { "Collapsed": "0", "Pos": "5,540", - "Size": "283,140" + "Size": "169,184" }, "###Joysticks": { "Collapsed": "0", @@ -36,16 +36,16 @@ "###NetworkTables": { "Collapsed": "0", "Pos": "269,170", - "Size": "750,395" + "Size": "902,395" }, "###NetworkTables Info": { "Collapsed": "0", "Pos": "269,27", - "Size": "750,145" + "Size": "900,145" }, "###Other Devices": { "Collapsed": "0", - "Pos": "1025,20", + "Pos": "1176,21", "Size": "250,695" }, "###System Joysticks": { @@ -53,20 +53,72 @@ "Pos": "127,257", "Size": "192,218" }, - "###Timing": { - "Collapsed": "0", - "Pos": "5,150", - "Size": "135,150" + "MainWindow": { + "GLOBAL": { + "fps": "120", + "height": "720", + "maximized": "0", + "style": "0", + "userScale": "2", + "width": "1280", + "xpos": "636", + "ypos": "329" + } }, - "Debug##Default": { - "Collapsed": "0", - "Pos": "60,60", - "Size": "400,400" + "Table": { + "0xE56EC1C2,4": { + "Column 0 Weight": "1.0000", + "Column 1 Weight": "1.0000", + "Column 2 Weight": "1.0000", + "Column 3 Weight": "1.0000" + } }, - "Robot State": { - "Collapsed": "0", - "Pos": "5,20", - "Size": "92,116" + "Window": { + "###FMS": { + "Collapsed": "0", + "Pos": "5,540", + "Size": "32,35" + }, + "###Joysticks": { + "Collapsed": "0", + "Pos": "250,465", + "Size": "32,35" + }, + "###NetworkTables": { + "Collapsed": "0", + "Pos": "250,277", + "Size": "750,185" + }, + "###NetworkTables Info": { + "Collapsed": "0", + "Pos": "250,130", + "Size": "750,145" + }, + "###Other Devices": { + "Collapsed": "0", + "Pos": "1025,20", + "Size": "250,695" + }, + "###System Joysticks": { + "Collapsed": "0", + "Pos": "16,322", + "Size": "32,35" + }, + "###Timing": { + "Collapsed": "0", + "Pos": "7,129", + "Size": "32,35" + }, + "Debug##Default": { + "Collapsed": "0", + "Pos": "60,60", + "Size": "400,400" + }, + "Robot State": { + "Collapsed": "0", + "Pos": "5,20", + "Size": "32,35" + } } - } +} } diff --git a/rio/simgui.json b/rio/simgui.json index aa30446a..e5ab25b3 100644 --- a/rio/simgui.json +++ b/rio/simgui.json @@ -5,6 +5,16 @@ "header": { "open": true } + }, + "SPARK MAX [10]": { + "header": { + "open": true + } + }, + "SPARK MAX [9]": { + "header": { + "open": true + } } } }, diff --git a/rio/subsystems/intake.py b/rio/subsystems/intake.py index 2156504b..81f1c608 100644 --- a/rio/subsystems/intake.py +++ b/rio/subsystems/intake.py @@ -53,11 +53,11 @@ def __init__(self) -> None: self.liftPID.setFF(IntakeConstants.kLiftFF) # limit switches - self.rightLimitSwitch = self.intakeMotor.getForwardLimitSwitch( + self.limtSwitch = self.intakeMotor.getForwardLimitSwitch( SparkMaxLimitSwitch.Type.kNormallyOpen ) - self.rightLimitSwitch.enableLimitSwitch(True) + self.limtSwitch.enableLimitSwitch(True) self.intakeMotor.burnFlash() @@ -67,20 +67,17 @@ def periodic(self) -> None: self.sd.putNumber("Thermals/Lift", self.liftMotor.getMotorTemperature()) def intake(self, speed): - if speed > 0: - if not self.rightLimitSwitch.get(): - self.intakeMotor.set(speed) - else: - self.intakeMotor.set(speed) + self.intakeMotor.set(speed) + + def switchPress(self): + return self.limtSwitch.get() + + def getAngle(self) -> float: + """Return the current angle""" + return self.liftEncoder.getPosition() def manualLift(self, speed): self.liftMotor.set(speed) def lift(self, angle: float): self.liftPID.setReference(angle, CANSparkMax.ControlType.kPosition) - - def liftCurrentLimit(self, current): - self.liftMotor.setSmartCurrentLimit(current) - - def intakeCurrentLimit(self, current): - self.intakeMotor.setSmartCurrentLimit(current)